Revert "Upgrade gRPC to 1.26.0"

This reverts commit dfbf87c9196a15d491e9cbb81ee2d3da416cc0c2.
diff --git a/third_party/BUILD b/third_party/BUILD
index b23e483..db95461 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -45,6 +45,7 @@
         "//third_party/py/six:srcs",
         "//third_party/rules_python:srcs",
         "//third_party/zlib:srcs",
+        "//third_party/nanopb:srcs",
         "@googleapis//:srcs",
         "@remoteapis//:srcs",
     ],
@@ -466,15 +467,8 @@
 java_import(
     name = "opencensus-api",
     jars = [
-        "opencensus/opencensus-api-0.24.0.jar",
-        "opencensus/opencensus-contrib-grpc-metrics-0.24.0.jar",
-    ],
-)
-
-java_import(
-    name = "perfmark-api",
-    jars = [
-        "perfmark/perfmark-api-0.19.0.jar",
+        "opencensus/opencensus-api-0.19.2.jar",
+        "opencensus/opencensus-contrib-grpc-metrics-0.19.2.jar",
     ],
 )
 
diff --git a/third_party/grpc/BUILD b/third_party/grpc/BUILD
index 19b83b9..e180260 100644
--- a/third_party/grpc/BUILD
+++ b/third_party/grpc/BUILD
@@ -48,14 +48,13 @@
 filegroup(
     name = "bootstrap-grpc-jars",
     srcs = [
-        "grpc-api-1.26.0.jar",
-        "grpc-auth-1.26.0.jar",
-        "grpc-context-1.26.0.jar",
-        "grpc-core-1.26.0.jar",
-        "grpc-netty-1.26.0.jar",
-        "grpc-protobuf-1.26.0.jar",
-        "grpc-protobuf-lite-1.26.0.jar",
-        "grpc-stub-1.26.0.jar",
+        "grpc-auth-1.20.0.jar",
+        "grpc-context-1.20.0.jar",
+        "grpc-core-1.20.0.jar",
+        "grpc-netty-1.20.0.jar",
+        "grpc-protobuf-1.20.0.jar",
+        "grpc-protobuf-lite-1.20.0.jar",
+        "grpc-stub-1.20.0.jar",
     ],
 )
 
@@ -65,7 +64,6 @@
     runtime_deps = [
         "//third_party:netty",
         "//third_party:opencensus-api",
-        "//third_party:perfmark-api",
     ],
     deps = [
         "//third_party:guava",
@@ -83,19 +81,1617 @@
     deps = ["//third_party/protobuf:protoc_lib"],
 )
 
-alias(
+cc_binary(
     name = "cpp_plugin",
-    actual = "@com_github_grpc_grpc//src/compiler:grpc_cpp_plugin",
+    srcs = ["src/compiler/cpp_plugin.cc"],
+    copts = [
+        "-Ithird_party/grpc/include",
+        "-Ithird_party/grpc",
+    ],
+    linkopts = [
+        "-lm",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [":grpc_plugin_support"],
 )
 
-alias(
-    name = "grpc++_codegen_proto",
-    actual = "@com_github_grpc_grpc//:grpc++_codegen_proto",
+GPR_PUBLIC_HDRS = [
+    "include/grpc/support/alloc.h",
+    "include/grpc/support/atm.h",
+    "include/grpc/support/atm_gcc_atomic.h",
+    "include/grpc/support/atm_gcc_sync.h",
+    "include/grpc/support/atm_windows.h",
+    "include/grpc/support/cpu.h",
+    "include/grpc/support/log.h",
+    "include/grpc/support/log_windows.h",
+    "include/grpc/support/port_platform.h",
+    "include/grpc/support/string_util.h",
+    "include/grpc/support/sync.h",
+    "include/grpc/support/sync_custom.h",
+    "include/grpc/support/sync_generic.h",
+    "include/grpc/support/sync_posix.h",
+    "include/grpc/support/sync_windows.h",
+    "include/grpc/support/thd_id.h",
+    "include/grpc/support/time.h",
+]
+
+GRPC_PUBLIC_HDRS = [
+    "include/grpc/byte_buffer.h",
+    "include/grpc/byte_buffer_reader.h",
+    "include/grpc/compression.h",
+    "include/grpc/fork.h",
+    "include/grpc/grpc.h",
+    "include/grpc/grpc_posix.h",
+    "include/grpc/grpc_security_constants.h",
+    "include/grpc/slice.h",
+    "include/grpc/slice_buffer.h",
+    "include/grpc/status.h",
+    "include/grpc/load_reporting.h",
+    "include/grpc/support/workaround_list.h",
+]
+
+GRPCXX_SRCS = [
+    "src/cpp/client/channel_cc.cc",
+    "src/cpp/client/client_context.cc",
+    "src/cpp/client/client_interceptor.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.cc",
+    "src/cpp/client/generic_stub.cc",
+    "src/cpp/common/alarm.cc",
+    "src/cpp/common/channel_arguments.cc",
+    "src/cpp/common/channel_filter.cc",
+    "src/cpp/common/completion_queue_cc.cc",
+    "src/cpp/common/core_codegen.cc",
+    "src/cpp/common/resource_quota_cc.cc",
+    "src/cpp/common/rpc_method.cc",
+    "src/cpp/common/version_cc.cc",
+    "src/cpp/server/async_generic_service.cc",
+    "src/cpp/server/channel_argument_option.cc",
+    "src/cpp/server/create_default_thread_pool.cc",
+    "src/cpp/server/dynamic_thread_pool.cc",
+    "src/cpp/server/health/default_health_check_service.cc",
+    "src/cpp/server/health/health_check_service.cc",
+    "src/cpp/server/health/health_check_service_server_builder_option.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/thread_manager/thread_manager.cc",
+    "src/cpp/util/byte_buffer_cc.cc",
+    "src/cpp/util/status.cc",
+    "src/cpp/util/string_ref.cc",
+    "src/cpp/util/time_cc.cc",
+]
+
+GRPCXX_HDRS = [
+    "src/cpp/client/create_channel_internal.h",
+    "src/cpp/common/channel_filter.h",
+    "src/cpp/server/dynamic_thread_pool.h",
+    "src/cpp/server/health/default_health_check_service.h",
+    "src/cpp/server/thread_pool_interface.h",
+    "src/cpp/thread_manager/thread_manager.h",
+]
+
+GRPCXX_PUBLIC_HDRS = [
+    "include/grpc++/alarm.h",
+    "include/grpc++/channel.h",
+    "include/grpc++/client_context.h",
+    "include/grpc++/completion_queue.h",
+    "include/grpc++/create_channel.h",
+    "include/grpc++/create_channel_posix.h",
+    "include/grpc++/ext/health_check_service_server_builder_option.h",
+    "include/grpc++/generic/async_generic_service.h",
+    "include/grpc++/generic/generic_stub.h",
+    "include/grpc++/grpc++.h",
+    "include/grpc++/health_check_service_interface.h",
+    "include/grpc++/impl/call.h",
+    "include/grpc++/impl/channel_argument_option.h",
+    "include/grpc++/impl/client_unary_call.h",
+    "include/grpc++/impl/codegen/core_codegen.h",
+    "include/grpc++/impl/grpc_library.h",
+    "include/grpc++/impl/method_handler_impl.h",
+    "include/grpc++/impl/rpc_method.h",
+    "include/grpc++/impl/rpc_service_method.h",
+    "include/grpc++/impl/serialization_traits.h",
+    "include/grpc++/impl/server_builder_option.h",
+    "include/grpc++/impl/server_builder_plugin.h",
+    "include/grpc++/impl/server_initializer.h",
+    "include/grpc++/impl/service_type.h",
+    "include/grpc++/impl/sync_cxx11.h",
+    "include/grpc++/impl/sync_no_cxx11.h",
+    "include/grpc++/resource_quota.h",
+    "include/grpc++/security/auth_context.h",
+    "include/grpc++/security/auth_metadata_processor.h",
+    "include/grpc++/security/credentials.h",
+    "include/grpc++/security/server_credentials.h",
+    "include/grpc++/server.h",
+    "include/grpc++/server_builder.h",
+    "include/grpc++/server_context.h",
+    "include/grpc++/server_posix.h",
+    "include/grpc++/support/async_stream.h",
+    "include/grpc++/support/async_unary_call.h",
+    "include/grpc++/support/byte_buffer.h",
+    "include/grpc++/support/channel_arguments.h",
+    "include/grpc++/support/config.h",
+    "include/grpc++/support/slice.h",
+    "include/grpc++/support/status.h",
+    "include/grpc++/support/status_code_enum.h",
+    "include/grpc++/support/string_ref.h",
+    "include/grpc++/support/stub_options.h",
+    "include/grpc++/support/sync_stream.h",
+    "include/grpc++/support/time.h",
+    "include/grpcpp/alarm.h",
+    "include/grpcpp/alarm_impl.h",
+    "include/grpcpp/channel.h",
+    "include/grpcpp/client_context.h",
+    "include/grpcpp/completion_queue.h",
+    "include/grpcpp/create_channel.h",
+    "include/grpcpp/create_channel_posix.h",
+    "include/grpcpp/ext/health_check_service_server_builder_option.h",
+    "include/grpcpp/generic/async_generic_service.h",
+    "include/grpcpp/generic/generic_stub.h",
+    "include/grpcpp/grpcpp.h",
+    "include/grpcpp/health_check_service_interface.h",
+    "include/grpcpp/impl/call.h",
+    "include/grpcpp/impl/channel_argument_option.h",
+    "include/grpcpp/impl/client_unary_call.h",
+    "include/grpcpp/impl/codegen/core_codegen.h",
+    "include/grpcpp/impl/grpc_library.h",
+    "include/grpcpp/impl/method_handler_impl.h",
+    "include/grpcpp/impl/rpc_method.h",
+    "include/grpcpp/impl/rpc_service_method.h",
+    "include/grpcpp/impl/serialization_traits.h",
+    "include/grpcpp/impl/server_builder_option.h",
+    "include/grpcpp/impl/server_builder_plugin.h",
+    "include/grpcpp/impl/server_initializer.h",
+    "include/grpcpp/impl/service_type.h",
+    "include/grpcpp/impl/sync_cxx11.h",
+    "include/grpcpp/impl/sync_no_cxx11.h",
+    "include/grpcpp/resource_quota.h",
+    "include/grpcpp/security/auth_context.h",
+    "include/grpcpp/security/auth_metadata_processor.h",
+    "include/grpcpp/security/credentials.h",
+    "include/grpcpp/security/server_credentials.h",
+    "include/grpcpp/server.h",
+    "include/grpcpp/server_builder.h",
+    "include/grpcpp/server_context.h",
+    "include/grpcpp/server_posix.h",
+    "include/grpcpp/support/async_stream.h",
+    "include/grpcpp/support/async_unary_call.h",
+    "include/grpcpp/support/byte_buffer.h",
+    "include/grpcpp/support/channel_arguments.h",
+    "include/grpcpp/support/client_callback.h",
+    "include/grpcpp/support/client_interceptor.h",
+    "include/grpcpp/support/config.h",
+    "include/grpcpp/support/interceptor.h",
+    "include/grpcpp/support/proto_buffer_reader.h",
+    "include/grpcpp/support/proto_buffer_writer.h",
+    "include/grpcpp/support/server_callback.h",
+    "include/grpcpp/support/server_interceptor.h",
+    "include/grpcpp/support/slice.h",
+    "include/grpcpp/support/status.h",
+    "include/grpcpp/support/status_code_enum.h",
+    "include/grpcpp/support/string_ref.h",
+    "include/grpcpp/support/stub_options.h",
+    "include/grpcpp/support/sync_stream.h",
+    "include/grpcpp/support/time.h",
+]
+
+grpc_cc_library(
+    name = "gpr",
+    language = "c++",
+    public_hdrs = GPR_PUBLIC_HDRS,
+    standalone = True,
+    deps = [
+        "gpr_base",
+    ],
 )
 
-alias(
+grpc_cc_library(
+    name = "grpc_unsecure",
+    srcs = [
+        "src/core/lib/surface/init.cc",
+        "src/core/lib/surface/init_unsecure.cc",
+        "src/core/plugin_registry/grpc_unsecure_plugin_registry.cc",
+    ],
+    language = "c++",
+    public_hdrs = GRPC_PUBLIC_HDRS,
+    standalone = True,
+    deps = [
+        "grpc_common",
+        "grpc_lb_policy_grpclb",
+        "grpc_lb_policy_xds",
+    ],
+)
+
+cc_library(
+    name = "grpc++_public_hdrs",
+    hdrs = GRPCXX_PUBLIC_HDRS,
+)
+
+grpc_cc_library(
     name = "grpc++_unsecure",
-    actual = "@com_github_grpc_grpc//:grpc++_unsecure",
+    srcs = [
+        "src/cpp/client/insecure_credentials.cc",
+        "src/cpp/common/insecure_create_auth_context.cc",
+        "src/cpp/server/insecure_server_credentials.cc",
+    ],
+    language = "c++",
+    standalone = True,
+    deps = [
+        "gpr",
+        "grpc++_base_unsecure",
+        "grpc++_codegen_base",
+        "grpc++_codegen_base_src",
+        "grpc++_codegen_proto",
+        "grpc_unsecure",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_plugin_support",
+    srcs = [
+        "src/compiler/cpp_generator.cc",
+        "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",
+    ],
+    hdrs = [
+        "src/compiler/config.h",
+        "src/compiler/cpp_generator.h",
+        "src/compiler/cpp_generator_helpers.h",
+        "src/compiler/csharp_generator.h",
+        "src/compiler/csharp_generator_helpers.h",
+        "src/compiler/generator_helpers.h",
+        "src/compiler/node_generator.h",
+        "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/protobuf_plugin.h",
+        "src/compiler/python_generator.h",
+        "src/compiler/python_generator_helpers.h",
+        "src/compiler/python_private_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",
+        "src/compiler/schema_interface.h",
+    ],
+    external_deps = [
+        "protobuf:protoc_lib",
+    ],
+    language = "c++",
+    deps = [
+        "grpc++_config_proto",
+    ],
+)
+
+grpc_cc_library(
+    name = "census",
+    srcs = [
+        "src/core/ext/filters/census/grpc_context.cc",
+    ],
+    language = "c++",
+    public_hdrs = [
+        "include/grpc/census.h",
+    ],
+    deps = [
+        "grpc_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "gpr_base",
+    srcs = [
+        "src/core/lib/gpr/alloc.cc",
+        "src/core/lib/gpr/arena.cc",
+        "src/core/lib/gpr/atm.cc",
+        "src/core/lib/gpr/cpu_iphone.cc",
+        "src/core/lib/gpr/cpu_linux.cc",
+        "src/core/lib/gpr/cpu_posix.cc",
+        "src/core/lib/gpr/cpu_windows.cc",
+        "src/core/lib/gpr/env_linux.cc",
+        "src/core/lib/gpr/env_posix.cc",
+        "src/core/lib/gpr/env_windows.cc",
+        "src/core/lib/gpr/host_port.cc",
+        "src/core/lib/gpr/log.cc",
+        "src/core/lib/gpr/log_android.cc",
+        "src/core/lib/gpr/log_linux.cc",
+        "src/core/lib/gpr/log_posix.cc",
+        "src/core/lib/gpr/log_windows.cc",
+        "src/core/lib/gpr/mpscq.cc",
+        "src/core/lib/gpr/murmur_hash.cc",
+        "src/core/lib/gpr/string.cc",
+        "src/core/lib/gpr/string_posix.cc",
+        "src/core/lib/gpr/string_util_windows.cc",
+        "src/core/lib/gpr/string_windows.cc",
+        "src/core/lib/gpr/sync.cc",
+        "src/core/lib/gpr/sync_posix.cc",
+        "src/core/lib/gpr/sync_windows.cc",
+        "src/core/lib/gpr/time.cc",
+        "src/core/lib/gpr/time_posix.cc",
+        "src/core/lib/gpr/time_precise.cc",
+        "src/core/lib/gpr/time_windows.cc",
+        "src/core/lib/gpr/tls_pthread.cc",
+        "src/core/lib/gpr/tmpfile_msys.cc",
+        "src/core/lib/gpr/tmpfile_posix.cc",
+        "src/core/lib/gpr/tmpfile_windows.cc",
+        "src/core/lib/gpr/wrap_memcpy.cc",
+        "src/core/lib/gprpp/fork.cc",
+        "src/core/lib/gprpp/thd_posix.cc",
+        "src/core/lib/gprpp/thd_windows.cc",
+        "src/core/lib/profiling/basic_timers.cc",
+        "src/core/lib/profiling/stap_timers.cc",
+    ],
+    hdrs = [
+        "src/core/lib/gpr/alloc.h",
+        "src/core/lib/gpr/arena.h",
+        "src/core/lib/gpr/env.h",
+        "src/core/lib/gpr/host_port.h",
+        "src/core/lib/gpr/mpscq.h",
+        "src/core/lib/gpr/murmur_hash.h",
+        "src/core/lib/gpr/spinlock.h",
+        "src/core/lib/gpr/string.h",
+        "src/core/lib/gpr/string_windows.h",
+        "src/core/lib/gpr/time_precise.h",
+        "src/core/lib/gpr/tls.h",
+        "src/core/lib/gpr/tls_gcc.h",
+        "src/core/lib/gpr/tls_msvc.h",
+        "src/core/lib/gpr/tls_pthread.h",
+        "src/core/lib/gpr/tmpfile.h",
+        "src/core/lib/gpr/useful.h",
+        "src/core/lib/gprpp/abstract.h",
+        "src/core/lib/gprpp/fork.h",
+        "src/core/lib/gprpp/manual_constructor.h",
+        "src/core/lib/gprpp/memory.h",
+        "src/core/lib/gprpp/mutex_lock.h",
+        "src/core/lib/gprpp/thd.h",
+        "src/core/lib/profiling/timers.h",
+    ],
+    language = "c++",
+    public_hdrs = GPR_PUBLIC_HDRS,
+    deps = [
+        "gpr_codegen",
+    ],
+)
+
+grpc_cc_library(
+    name = "gpr_codegen",
+    language = "c++",
+    public_hdrs = [
+        "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/fork.h",
+        "include/grpc/impl/codegen/gpr_slice.h",
+        "include/grpc/impl/codegen/gpr_types.h",
+        "include/grpc/impl/codegen/log.h",
+        "include/grpc/impl/codegen/port_platform.h",
+        "include/grpc/impl/codegen/sync.h",
+        "include/grpc/impl/codegen/sync_custom.h",
+        "include/grpc/impl/codegen/sync_generic.h",
+        "include/grpc/impl/codegen/sync_posix.h",
+        "include/grpc/impl/codegen/sync_windows.h",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_trace",
+    srcs = ["src/core/lib/debug/trace.cc"],
+    hdrs = ["src/core/lib/debug/trace.h"],
+    language = "c++",
+    public_hdrs = GRPC_PUBLIC_HDRS,
+    deps = [
+        "grpc_codegen",
+        ":gpr",
+    ],
+)
+
+grpc_cc_library(
+    name = "atomic",
+    hdrs = [
+        "src/core/lib/gprpp/atomic_with_atm.h",
+        "src/core/lib/gprpp/atomic_with_std.h",
+    ],
+    language = "c++",
+    public_hdrs = [
+        "src/core/lib/gprpp/atomic.h",
+    ],
+    deps = [
+        "gpr",
+    ],
+)
+
+grpc_cc_library(
+    name = "inlined_vector",
+    language = "c++",
+    public_hdrs = [
+        "src/core/lib/gprpp/inlined_vector.h",
+    ],
+    deps = [
+        "gpr_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "debug_location",
+    language = "c++",
+    public_hdrs = ["src/core/lib/gprpp/debug_location.h"],
+)
+
+grpc_cc_library(
+    name = "orphanable",
+    language = "c++",
+    public_hdrs = ["src/core/lib/gprpp/orphanable.h"],
+    deps = [
+        "debug_location",
+        "gpr_base",
+        "grpc_trace",
+        "ref_counted",
+        "ref_counted_ptr",
+    ],
+)
+
+grpc_cc_library(
+    name = "ref_counted",
+    language = "c++",
+    public_hdrs = ["src/core/lib/gprpp/ref_counted.h"],
+    deps = [
+        "debug_location",
+        "gpr_base",
+        "grpc_trace",
+        "ref_counted_ptr",
+    ],
+)
+
+grpc_cc_library(
+    name = "ref_counted_ptr",
+    language = "c++",
+    public_hdrs = ["src/core/lib/gprpp/ref_counted_ptr.h"],
+    deps = [
+        "gpr_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_base_c",
+    srcs = [
+        "src/core/lib/avl/avl.cc",
+        "src/core/lib/backoff/backoff.cc",
+        "src/core/lib/channel/channel_args.cc",
+        "src/core/lib/channel/channel_stack.cc",
+        "src/core/lib/channel/channel_stack_builder.cc",
+        "src/core/lib/channel/channel_trace.cc",
+        "src/core/lib/channel/channelz.cc",
+        "src/core/lib/channel/channelz_registry.cc",
+        "src/core/lib/channel/connected_channel.cc",
+        "src/core/lib/channel/handshaker.cc",
+        "src/core/lib/channel/handshaker_factory.cc",
+        "src/core/lib/channel/handshaker_registry.cc",
+        "src/core/lib/channel/status_util.cc",
+        "src/core/lib/compression/compression.cc",
+        "src/core/lib/compression/compression_internal.cc",
+        "src/core/lib/compression/message_compress.cc",
+        "src/core/lib/compression/stream_compression.cc",
+        "src/core/lib/compression/stream_compression_gzip.cc",
+        "src/core/lib/compression/stream_compression_identity.cc",
+        "src/core/lib/debug/stats.cc",
+        "src/core/lib/debug/stats_data.cc",
+        "src/core/lib/http/format_request.cc",
+        "src/core/lib/http/httpcli.cc",
+        "src/core/lib/http/parser.cc",
+        "src/core/lib/iomgr/buffer_list.cc",
+        "src/core/lib/iomgr/call_combiner.cc",
+        "src/core/lib/iomgr/combiner.cc",
+        "src/core/lib/iomgr/endpoint.cc",
+        "src/core/lib/iomgr/endpoint_pair_posix.cc",
+        "src/core/lib/iomgr/endpoint_pair_uv.cc",
+        "src/core/lib/iomgr/endpoint_pair_windows.cc",
+        "src/core/lib/iomgr/error.cc",
+        "src/core/lib/iomgr/ev_epoll1_linux.cc",
+        "src/core/lib/iomgr/ev_epollex_linux.cc",
+        "src/core/lib/iomgr/ev_poll_posix.cc",
+        "src/core/lib/iomgr/ev_posix.cc",
+        "src/core/lib/iomgr/ev_windows.cc",
+        "src/core/lib/iomgr/exec_ctx.cc",
+        "src/core/lib/iomgr/executor.cc",
+        "src/core/lib/iomgr/fork_posix.cc",
+        "src/core/lib/iomgr/fork_windows.cc",
+        "src/core/lib/iomgr/gethostname_fallback.cc",
+        "src/core/lib/iomgr/gethostname_host_name_max.cc",
+        "src/core/lib/iomgr/gethostname_sysconf.cc",
+        "src/core/lib/iomgr/internal_errqueue.cc",
+        "src/core/lib/iomgr/iocp_windows.cc",
+        "src/core/lib/iomgr/iomgr.cc",
+        "src/core/lib/iomgr/iomgr_custom.cc",
+        "src/core/lib/iomgr/iomgr_internal.cc",
+        "src/core/lib/iomgr/iomgr_posix.cc",
+        "src/core/lib/iomgr/iomgr_windows.cc",
+        "src/core/lib/iomgr/is_epollexclusive_available.cc",
+        "src/core/lib/iomgr/load_file.cc",
+        "src/core/lib/iomgr/lockfree_event.cc",
+        "src/core/lib/iomgr/network_status_tracker.cc",
+        "src/core/lib/iomgr/polling_entity.cc",
+        "src/core/lib/iomgr/pollset.cc",
+        "src/core/lib/iomgr/pollset_custom.cc",
+        "src/core/lib/iomgr/pollset_set.cc",
+        "src/core/lib/iomgr/pollset_set_custom.cc",
+        "src/core/lib/iomgr/pollset_set_windows.cc",
+        "src/core/lib/iomgr/pollset_uv.cc",
+        "src/core/lib/iomgr/pollset_windows.cc",
+        "src/core/lib/iomgr/resolve_address.cc",
+        "src/core/lib/iomgr/resolve_address_custom.cc",
+        "src/core/lib/iomgr/resolve_address_posix.cc",
+        "src/core/lib/iomgr/resolve_address_windows.cc",
+        "src/core/lib/iomgr/resource_quota.cc",
+        "src/core/lib/iomgr/sockaddr_utils.cc",
+        "src/core/lib/iomgr/socket_factory_posix.cc",
+        "src/core/lib/iomgr/socket_mutator.cc",
+        "src/core/lib/iomgr/socket_utils_common_posix.cc",
+        "src/core/lib/iomgr/socket_utils_linux.cc",
+        "src/core/lib/iomgr/socket_utils_posix.cc",
+        "src/core/lib/iomgr/socket_utils_windows.cc",
+        "src/core/lib/iomgr/socket_windows.cc",
+        "src/core/lib/iomgr/tcp_client.cc",
+        "src/core/lib/iomgr/tcp_client_custom.cc",
+        "src/core/lib/iomgr/tcp_client_posix.cc",
+        "src/core/lib/iomgr/tcp_client_windows.cc",
+        "src/core/lib/iomgr/tcp_custom.cc",
+        "src/core/lib/iomgr/tcp_posix.cc",
+        "src/core/lib/iomgr/tcp_server.cc",
+        "src/core/lib/iomgr/tcp_server_custom.cc",
+        "src/core/lib/iomgr/tcp_server_posix.cc",
+        "src/core/lib/iomgr/tcp_server_utils_posix_common.cc",
+        "src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc",
+        "src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc",
+        "src/core/lib/iomgr/tcp_server_windows.cc",
+        "src/core/lib/iomgr/tcp_uv.cc",
+        "src/core/lib/iomgr/tcp_windows.cc",
+        "src/core/lib/iomgr/time_averaged_stats.cc",
+        "src/core/lib/iomgr/timer.cc",
+        "src/core/lib/iomgr/timer_custom.cc",
+        "src/core/lib/iomgr/timer_generic.cc",
+        "src/core/lib/iomgr/timer_heap.cc",
+        "src/core/lib/iomgr/timer_manager.cc",
+        "src/core/lib/iomgr/timer_uv.cc",
+        "src/core/lib/iomgr/udp_server.cc",
+        "src/core/lib/iomgr/unix_sockets_posix.cc",
+        "src/core/lib/iomgr/unix_sockets_posix_noop.cc",
+        "src/core/lib/iomgr/wakeup_fd_cv.cc",
+        "src/core/lib/iomgr/wakeup_fd_eventfd.cc",
+        "src/core/lib/iomgr/wakeup_fd_nospecial.cc",
+        "src/core/lib/iomgr/wakeup_fd_pipe.cc",
+        "src/core/lib/iomgr/wakeup_fd_posix.cc",
+        "src/core/lib/json/json.cc",
+        "src/core/lib/json/json_reader.cc",
+        "src/core/lib/json/json_string.cc",
+        "src/core/lib/json/json_writer.cc",
+        "src/core/lib/slice/b64.cc",
+        "src/core/lib/slice/percent_encoding.cc",
+        "src/core/lib/slice/slice.cc",
+        "src/core/lib/slice/slice_buffer.cc",
+        "src/core/lib/slice/slice_intern.cc",
+        "src/core/lib/slice/slice_string_helpers.cc",
+        "src/core/lib/surface/api_trace.cc",
+        "src/core/lib/surface/byte_buffer.cc",
+        "src/core/lib/surface/byte_buffer_reader.cc",
+        "src/core/lib/surface/call.cc",
+        "src/core/lib/surface/call_details.cc",
+        "src/core/lib/surface/call_log_batch.cc",
+        "src/core/lib/surface/channel.cc",
+        "src/core/lib/surface/channel_init.cc",
+        "src/core/lib/surface/channel_ping.cc",
+        "src/core/lib/surface/channel_stack_type.cc",
+        "src/core/lib/surface/completion_queue.cc",
+        "src/core/lib/surface/completion_queue_factory.cc",
+        "src/core/lib/surface/event_string.cc",
+        "src/core/lib/surface/metadata_array.cc",
+        "src/core/lib/surface/server.cc",
+        "src/core/lib/surface/validate_metadata.cc",
+        "src/core/lib/surface/version.cc",
+        "src/core/lib/transport/bdp_estimator.cc",
+        "src/core/lib/transport/byte_stream.cc",
+        "src/core/lib/transport/connectivity_state.cc",
+        "src/core/lib/transport/error_utils.cc",
+        "src/core/lib/transport/metadata.cc",
+        "src/core/lib/transport/metadata_batch.cc",
+        "src/core/lib/transport/pid_controller.cc",
+        "src/core/lib/transport/service_config.cc",
+        "src/core/lib/transport/static_metadata.cc",
+        "src/core/lib/transport/status_conversion.cc",
+        "src/core/lib/transport/status_metadata.cc",
+        "src/core/lib/transport/timeout_encoding.cc",
+        "src/core/lib/transport/transport.cc",
+        "src/core/lib/transport/transport_op_string.cc",
+        "src/core/lib/uri/uri_parser.cc",
+    ],
+    hdrs = [
+        "src/core/lib/avl/avl.h",
+        "src/core/lib/backoff/backoff.h",
+        "src/core/lib/channel/channel_args.h",
+        "src/core/lib/channel/channel_stack.h",
+        "src/core/lib/channel/channel_stack_builder.h",
+        "src/core/lib/channel/channel_trace.h",
+        "src/core/lib/channel/channelz.h",
+        "src/core/lib/channel/channelz_registry.h",
+        "src/core/lib/channel/connected_channel.h",
+        "src/core/lib/channel/context.h",
+        "src/core/lib/channel/handshaker.h",
+        "src/core/lib/channel/handshaker_factory.h",
+        "src/core/lib/channel/handshaker_registry.h",
+        "src/core/lib/channel/status_util.h",
+        "src/core/lib/compression/algorithm_metadata.h",
+        "src/core/lib/compression/compression_internal.h",
+        "src/core/lib/compression/message_compress.h",
+        "src/core/lib/compression/stream_compression.h",
+        "src/core/lib/compression/stream_compression_gzip.h",
+        "src/core/lib/compression/stream_compression_identity.h",
+        "src/core/lib/debug/stats.h",
+        "src/core/lib/debug/stats_data.h",
+        "src/core/lib/http/format_request.h",
+        "src/core/lib/http/httpcli.h",
+        "src/core/lib/http/parser.h",
+        "src/core/lib/iomgr/block_annotate.h",
+        "src/core/lib/iomgr/buffer_list.h",
+        "src/core/lib/iomgr/call_combiner.h",
+        "src/core/lib/iomgr/closure.h",
+        "src/core/lib/iomgr/combiner.h",
+        "src/core/lib/iomgr/dynamic_annotations.h",
+        "src/core/lib/iomgr/endpoint.h",
+        "src/core/lib/iomgr/endpoint_pair.h",
+        "src/core/lib/iomgr/error.h",
+        "src/core/lib/iomgr/error_internal.h",
+        "src/core/lib/iomgr/ev_epoll1_linux.h",
+        "src/core/lib/iomgr/ev_epollex_linux.h",
+        "src/core/lib/iomgr/ev_poll_posix.h",
+        "src/core/lib/iomgr/ev_posix.h",
+        "src/core/lib/iomgr/exec_ctx.h",
+        "src/core/lib/iomgr/executor.h",
+        "src/core/lib/iomgr/gethostname.h",
+        "src/core/lib/iomgr/gevent_util.h",
+        "src/core/lib/iomgr/internal_errqueue.h",
+        "src/core/lib/iomgr/iocp_windows.h",
+        "src/core/lib/iomgr/iomgr.h",
+        "src/core/lib/iomgr/iomgr_custom.h",
+        "src/core/lib/iomgr/iomgr_internal.h",
+        "src/core/lib/iomgr/iomgr_posix.h",
+        "src/core/lib/iomgr/is_epollexclusive_available.h",
+        "src/core/lib/iomgr/load_file.h",
+        "src/core/lib/iomgr/lockfree_event.h",
+        "src/core/lib/iomgr/nameser.h",
+        "src/core/lib/iomgr/network_status_tracker.h",
+        "src/core/lib/iomgr/polling_entity.h",
+        "src/core/lib/iomgr/pollset.h",
+        "src/core/lib/iomgr/pollset_custom.h",
+        "src/core/lib/iomgr/pollset_set.h",
+        "src/core/lib/iomgr/pollset_set_custom.h",
+        "src/core/lib/iomgr/pollset_set_windows.h",
+        "src/core/lib/iomgr/pollset_uv.h",
+        "src/core/lib/iomgr/pollset_windows.h",
+        "src/core/lib/iomgr/port.h",
+        "src/core/lib/iomgr/resolve_address.h",
+        "src/core/lib/iomgr/resolve_address_custom.h",
+        "src/core/lib/iomgr/resource_quota.h",
+        "src/core/lib/iomgr/sockaddr.h",
+        "src/core/lib/iomgr/sockaddr_custom.h",
+        "src/core/lib/iomgr/sockaddr_posix.h",
+        "src/core/lib/iomgr/sockaddr_utils.h",
+        "src/core/lib/iomgr/sockaddr_windows.h",
+        "src/core/lib/iomgr/socket_factory_posix.h",
+        "src/core/lib/iomgr/socket_mutator.h",
+        "src/core/lib/iomgr/socket_utils.h",
+        "src/core/lib/iomgr/socket_utils_posix.h",
+        "src/core/lib/iomgr/socket_windows.h",
+        "src/core/lib/iomgr/sys_epoll_wrapper.h",
+        "src/core/lib/iomgr/tcp_client.h",
+        "src/core/lib/iomgr/tcp_client_posix.h",
+        "src/core/lib/iomgr/tcp_custom.h",
+        "src/core/lib/iomgr/tcp_posix.h",
+        "src/core/lib/iomgr/tcp_server.h",
+        "src/core/lib/iomgr/tcp_server_utils_posix.h",
+        "src/core/lib/iomgr/tcp_windows.h",
+        "src/core/lib/iomgr/time_averaged_stats.h",
+        "src/core/lib/iomgr/timer.h",
+        "src/core/lib/iomgr/timer_custom.h",
+        "src/core/lib/iomgr/timer_generic.h",
+        "src/core/lib/iomgr/timer_heap.h",
+        "src/core/lib/iomgr/timer_manager.h",
+        "src/core/lib/iomgr/udp_server.h",
+        "src/core/lib/iomgr/unix_sockets_posix.h",
+        "src/core/lib/iomgr/wakeup_fd_cv.h",
+        "src/core/lib/iomgr/wakeup_fd_pipe.h",
+        "src/core/lib/iomgr/wakeup_fd_posix.h",
+        "src/core/lib/json/json.h",
+        "src/core/lib/json/json_common.h",
+        "src/core/lib/json/json_reader.h",
+        "src/core/lib/json/json_writer.h",
+        "src/core/lib/slice/b64.h",
+        "src/core/lib/slice/percent_encoding.h",
+        "src/core/lib/slice/slice_hash_table.h",
+        "src/core/lib/slice/slice_internal.h",
+        "src/core/lib/slice/slice_string_helpers.h",
+        "src/core/lib/slice/slice_weak_hash_table.h",
+        "src/core/lib/surface/api_trace.h",
+        "src/core/lib/surface/call.h",
+        "src/core/lib/surface/call_test_only.h",
+        "src/core/lib/surface/channel.h",
+        "src/core/lib/surface/channel_init.h",
+        "src/core/lib/surface/channel_stack_type.h",
+        "src/core/lib/surface/completion_queue.h",
+        "src/core/lib/surface/completion_queue_factory.h",
+        "src/core/lib/surface/event_string.h",
+        "src/core/lib/surface/init.h",
+        "src/core/lib/surface/lame_client.h",
+        "src/core/lib/surface/server.h",
+        "src/core/lib/surface/validate_metadata.h",
+        "src/core/lib/transport/bdp_estimator.h",
+        "src/core/lib/transport/byte_stream.h",
+        "src/core/lib/transport/connectivity_state.h",
+        "src/core/lib/transport/error_utils.h",
+        "src/core/lib/transport/http2_errors.h",
+        "src/core/lib/transport/metadata.h",
+        "src/core/lib/transport/metadata_batch.h",
+        "src/core/lib/transport/pid_controller.h",
+        "src/core/lib/transport/service_config.h",
+        "src/core/lib/transport/static_metadata.h",
+        "src/core/lib/transport/status_conversion.h",
+        "src/core/lib/transport/status_metadata.h",
+        "src/core/lib/transport/timeout_encoding.h",
+        "src/core/lib/transport/transport.h",
+        "src/core/lib/transport/transport_impl.h",
+        "src/core/lib/uri/uri_parser.h",
+    ],
+    external_deps = [
+        "zlib",
+    ],
+    language = "c++",
+    public_hdrs = GRPC_PUBLIC_HDRS,
+    deps = [
+        "gpr_base",
+        "grpc_codegen",
+        "grpc_trace",
+        "inlined_vector",
+        "orphanable",
+        "ref_counted",
+        "ref_counted_ptr",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_base",
+    srcs = [
+        "src/core/lib/surface/lame_client.cc",
+    ],
+    language = "c++",
+    deps = [
+        "atomic",
+        "grpc_base_c",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_common",
+    language = "c++",
+    deps = [
+        "grpc_base",
+        # standard plugins
+        "census",
+        "grpc_deadline_filter",
+        "grpc_client_authority_filter",
+        "grpc_lb_policy_pick_first",
+        "grpc_lb_policy_round_robin",
+        "grpc_max_age_filter",
+        "grpc_message_size_filter",
+        "grpc_resolver_dns_ares",
+        "grpc_resolver_fake",
+        "grpc_resolver_dns_native",
+        "grpc_resolver_sockaddr",
+        "grpc_transport_chttp2_client_insecure",
+        "grpc_transport_chttp2_server_insecure",
+        "grpc_transport_inproc",
+        "grpc_workaround_cronet_compression_filter",
+        "grpc_server_backward_compatibility",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_cfstream",
+    srcs = [
+        "src/core/lib/iomgr/cfstream_handle.cc",
+        "src/core/lib/iomgr/endpoint_cfstream.cc",
+        "src/core/lib/iomgr/error_cfstream.cc",
+        "src/core/lib/iomgr/iomgr_posix_cfstream.cc",
+        "src/core/lib/iomgr/tcp_client_cfstream.cc",
+    ],
+    hdrs = [
+        "src/core/lib/iomgr/cfstream_handle.h",
+        "src/core/lib/iomgr/endpoint_cfstream.h",
+        "src/core/lib/iomgr/error_cfstream.h",
+    ],
+    deps = [
+        ":gpr_base",
+        ":grpc_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_client_channel",
+    srcs = [
+        "src/core/ext/filters/client_channel/backup_poller.cc",
+        "src/core/ext/filters/client_channel/channel_connectivity.cc",
+        "src/core/ext/filters/client_channel/client_channel.cc",
+        "src/core/ext/filters/client_channel/client_channel_channelz.cc",
+        "src/core/ext/filters/client_channel/client_channel_factory.cc",
+        "src/core/ext/filters/client_channel/client_channel_plugin.cc",
+        "src/core/ext/filters/client_channel/connector.cc",
+        "src/core/ext/filters/client_channel/health/health_check_client.cc",
+        "src/core/ext/filters/client_channel/http_connect_handshaker.cc",
+        "src/core/ext/filters/client_channel/http_proxy.cc",
+        "src/core/ext/filters/client_channel/lb_policy.cc",
+        "src/core/ext/filters/client_channel/lb_policy_registry.cc",
+        "src/core/ext/filters/client_channel/parse_address.cc",
+        "src/core/ext/filters/client_channel/proxy_mapper.cc",
+        "src/core/ext/filters/client_channel/proxy_mapper_registry.cc",
+        "src/core/ext/filters/client_channel/request_routing.cc",
+        "src/core/ext/filters/client_channel/resolver.cc",
+        "src/core/ext/filters/client_channel/resolver_registry.cc",
+        "src/core/ext/filters/client_channel/resolver_result_parsing.cc",
+        "src/core/ext/filters/client_channel/retry_throttle.cc",
+        "src/core/ext/filters/client_channel/server_address.cc",
+        "src/core/ext/filters/client_channel/subchannel.cc",
+        "src/core/ext/filters/client_channel/subchannel_index.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/client_channel/backup_poller.h",
+        "src/core/ext/filters/client_channel/client_channel.h",
+        "src/core/ext/filters/client_channel/client_channel_channelz.h",
+        "src/core/ext/filters/client_channel/client_channel_factory.h",
+        "src/core/ext/filters/client_channel/connector.h",
+        "src/core/ext/filters/client_channel/health/health_check_client.h",
+        "src/core/ext/filters/client_channel/http_connect_handshaker.h",
+        "src/core/ext/filters/client_channel/http_proxy.h",
+        "src/core/ext/filters/client_channel/lb_policy.h",
+        "src/core/ext/filters/client_channel/lb_policy_factory.h",
+        "src/core/ext/filters/client_channel/lb_policy_registry.h",
+        "src/core/ext/filters/client_channel/parse_address.h",
+        "src/core/ext/filters/client_channel/proxy_mapper.h",
+        "src/core/ext/filters/client_channel/proxy_mapper_registry.h",
+        "src/core/ext/filters/client_channel/request_routing.h",
+        "src/core/ext/filters/client_channel/resolver.h",
+        "src/core/ext/filters/client_channel/resolver_factory.h",
+        "src/core/ext/filters/client_channel/resolver_registry.h",
+        "src/core/ext/filters/client_channel/resolver_result_parsing.h",
+        "src/core/ext/filters/client_channel/retry_throttle.h",
+        "src/core/ext/filters/client_channel/server_address.h",
+        "src/core/ext/filters/client_channel/subchannel.h",
+        "src/core/ext/filters/client_channel/subchannel_index.h",
+    ],
+    language = "c++",
+    deps = [
+        "gpr_base",
+        "grpc_base",
+        "grpc_client_authority_filter",
+        "grpc_deadline_filter",
+        "health_proto",
+        "inlined_vector",
+        "orphanable",
+        "ref_counted",
+        "ref_counted_ptr",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_max_age_filter",
+    srcs = [
+        "src/core/ext/filters/max_age/max_age_filter.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/max_age/max_age_filter.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_deadline_filter",
+    srcs = [
+        "src/core/ext/filters/deadline/deadline_filter.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/deadline/deadline_filter.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_client_authority_filter",
+    srcs = [
+        "src/core/ext/filters/http/client_authority_filter.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/http/client_authority_filter.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_message_size_filter",
+    srcs = [
+        "src/core/ext/filters/message_size/message_size_filter.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/message_size/message_size_filter.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_http_filters",
+    srcs = [
+        "src/core/ext/filters/http/client/http_client_filter.cc",
+        "src/core/ext/filters/http/http_filters_plugin.cc",
+        "src/core/ext/filters/http/message_compress/message_compress_filter.cc",
+        "src/core/ext/filters/http/server/http_server_filter.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/http/client/http_client_filter.h",
+        "src/core/ext/filters/http/message_compress/message_compress_filter.h",
+        "src/core/ext/filters/http/server/http_server_filter.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_workaround_cronet_compression_filter",
+    srcs = [
+        "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_server_backward_compatibility",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_codegen",
+    language = "c++",
+    public_hdrs = [
+        "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/slice.h",
+    ],
+    deps = [
+        "gpr_codegen",
+    ],
+)
+
+grpc_cc_library(
+    name = "health_proto",
+    srcs = [
+        "src/core/ext/filters/client_channel/health/health.pb.c",
+    ],
+    hdrs = [
+        "src/core/ext/filters/client_channel/health/health.pb.h",
+    ],
+    external_deps = [
+        "nanopb",
+    ],
+    language = "c++",
+)
+
+grpc_cc_library(
+    name = "grpclb_proto",
+    srcs = [
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c",
+    ],
+    hdrs = [
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h",
+    ],
+    external_deps = [
+        "nanopb",
+    ],
+    language = "c++",
+)
+
+grpc_cc_library(
+    name = "grpc_lb_policy_grpclb",
+    srcs = [
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h",
+        "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h",
+    ],
+    external_deps = [
+        "nanopb",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+        "grpc_resolver_fake",
+        "grpclb_proto",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_lb_policy_xds",
+    srcs = [
+        "src/core/ext/filters/client_channel/lb_policy/xds/xds.cc",
+        "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc",
+        "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc",
+        "src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/client_channel/lb_policy/xds/xds.h",
+        "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h",
+        "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h",
+        "src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h",
+    ],
+    external_deps = [
+        "nanopb",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+        "grpc_resolver_fake",
+        "grpclb_proto",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_lb_subchannel_list",
+    hdrs = [
+        "src/core/ext/filters/client_channel/lb_policy/subchannel_list.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_lb_policy_pick_first",
+    srcs = [
+        "src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+        "grpc_lb_subchannel_list",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_lb_policy_round_robin",
+    srcs = [
+        "src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+        "grpc_lb_subchannel_list",
+    ],
+)
+
+grpc_cc_library(
+    name = "lb_server_load_reporting_filter",
+    srcs = [
+        "src/core/ext/filters/load_reporting/server_load_reporting_filter.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/load_reporting/registered_opencensus_objects.h",
+        "src/core/ext/filters/load_reporting/server_load_reporting_filter.h",
+        "src/cpp/server/load_reporter/constants.h",
+    ],
+    external_deps = [
+        "opencensus-stats",
+    ],
+    language = "c++",
+    deps = [
+        "grpc++_base",
+        "grpc_secure",
+    ],
+)
+
+grpc_cc_library(
+    name = "lb_load_data_store",
+    srcs = [
+        "src/cpp/server/load_reporter/load_data_store.cc",
+    ],
+    hdrs = [
+        "src/cpp/server/load_reporter/constants.h",
+        "src/cpp/server/load_reporter/load_data_store.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc++",
+    ],
+)
+
+grpc_cc_library(
+    name = "lb_server_load_reporting_service_server_builder_plugin",
+    srcs = [
+        "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc",
+    ],
+    hdrs = [
+        "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h",
+    ],
+    language = "c++",
+    deps = [
+        "lb_load_reporter_service",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpcpp_server_load_reporting",
+    srcs = [
+        "src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc",
+        "src/cpp/server/load_reporter/util.cc",
+    ],
+    language = "c++",
+    public_hdrs = [
+        "include/grpcpp/ext/server_load_reporting.h",
+    ],
+    deps = [
+        "lb_server_load_reporting_filter",
+        "lb_server_load_reporting_service_server_builder_plugin",
+    ],
+    alwayslink = 1,
+)
+
+grpc_cc_library(
+    name = "lb_load_reporter_service",
+    srcs = [
+        "src/cpp/server/load_reporter/load_reporter_async_service_impl.cc",
+    ],
+    hdrs = [
+        "src/cpp/server/load_reporter/load_reporter_async_service_impl.h",
+    ],
+    language = "c++",
+    deps = [
+        "lb_load_reporter",
+    ],
+)
+
+grpc_cc_library(
+    name = "lb_get_cpu_stats",
+    srcs = [
+        "src/cpp/server/load_reporter/get_cpu_stats_linux.cc",
+        "src/cpp/server/load_reporter/get_cpu_stats_macos.cc",
+        "src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc",
+        "src/cpp/server/load_reporter/get_cpu_stats_windows.cc",
+    ],
+    hdrs = [
+        "src/cpp/server/load_reporter/get_cpu_stats.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc++",
+    ],
+)
+
+grpc_cc_library(
+    name = "lb_load_reporter",
+    srcs = [
+        "src/cpp/server/load_reporter/load_reporter.cc",
+    ],
+    hdrs = [
+        "src/cpp/server/load_reporter/constants.h",
+        "src/cpp/server/load_reporter/load_reporter.h",
+    ],
+    external_deps = [
+        "opencensus-stats",
+    ],
+    language = "c++",
+    deps = [
+        "lb_get_cpu_stats",
+        "lb_load_data_store",
+        "//src/proto/grpc/lb/v1:load_reporter_proto",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_resolver_dns_native",
+    srcs = [
+        "src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
+
+# Only builds stubs because we define GRPC_ARES=0
+grpc_cc_library(
+    name = "grpc_resolver_dns_ares",
+    srcs = [
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h",
+        "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_resolver_sockaddr",
+    srcs = [
+        "src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_resolver_fake",
+    srcs = ["src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc"],
+    hdrs = ["src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"],
+    language = "c++",
+    visibility = ["//test:__subpackages__"],
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_transport_chttp2",
+    srcs = [
+        "src/core/ext/transport/chttp2/transport/bin_decoder.cc",
+        "src/core/ext/transport/chttp2/transport/bin_encoder.cc",
+        "src/core/ext/transport/chttp2/transport/chttp2_plugin.cc",
+        "src/core/ext/transport/chttp2/transport/chttp2_transport.cc",
+        "src/core/ext/transport/chttp2/transport/context_list.cc",
+        "src/core/ext/transport/chttp2/transport/flow_control.cc",
+        "src/core/ext/transport/chttp2/transport/frame_data.cc",
+        "src/core/ext/transport/chttp2/transport/frame_goaway.cc",
+        "src/core/ext/transport/chttp2/transport/frame_ping.cc",
+        "src/core/ext/transport/chttp2/transport/frame_rst_stream.cc",
+        "src/core/ext/transport/chttp2/transport/frame_settings.cc",
+        "src/core/ext/transport/chttp2/transport/frame_window_update.cc",
+        "src/core/ext/transport/chttp2/transport/hpack_encoder.cc",
+        "src/core/ext/transport/chttp2/transport/hpack_parser.cc",
+        "src/core/ext/transport/chttp2/transport/hpack_table.cc",
+        "src/core/ext/transport/chttp2/transport/http2_settings.cc",
+        "src/core/ext/transport/chttp2/transport/huffsyms.cc",
+        "src/core/ext/transport/chttp2/transport/incoming_metadata.cc",
+        "src/core/ext/transport/chttp2/transport/parsing.cc",
+        "src/core/ext/transport/chttp2/transport/stream_lists.cc",
+        "src/core/ext/transport/chttp2/transport/stream_map.cc",
+        "src/core/ext/transport/chttp2/transport/varint.cc",
+        "src/core/ext/transport/chttp2/transport/writing.cc",
+    ],
+    hdrs = [
+        "src/core/ext/transport/chttp2/transport/bin_decoder.h",
+        "src/core/ext/transport/chttp2/transport/bin_encoder.h",
+        "src/core/ext/transport/chttp2/transport/chttp2_transport.h",
+        "src/core/ext/transport/chttp2/transport/context_list.h",
+        "src/core/ext/transport/chttp2/transport/flow_control.h",
+        "src/core/ext/transport/chttp2/transport/frame.h",
+        "src/core/ext/transport/chttp2/transport/frame_data.h",
+        "src/core/ext/transport/chttp2/transport/frame_goaway.h",
+        "src/core/ext/transport/chttp2/transport/frame_ping.h",
+        "src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
+        "src/core/ext/transport/chttp2/transport/frame_settings.h",
+        "src/core/ext/transport/chttp2/transport/frame_window_update.h",
+        "src/core/ext/transport/chttp2/transport/hpack_encoder.h",
+        "src/core/ext/transport/chttp2/transport/hpack_parser.h",
+        "src/core/ext/transport/chttp2/transport/hpack_table.h",
+        "src/core/ext/transport/chttp2/transport/http2_settings.h",
+        "src/core/ext/transport/chttp2/transport/huffsyms.h",
+        "src/core/ext/transport/chttp2/transport/incoming_metadata.h",
+        "src/core/ext/transport/chttp2/transport/internal.h",
+        "src/core/ext/transport/chttp2/transport/stream_map.h",
+        "src/core/ext/transport/chttp2/transport/varint.h",
+    ],
+    language = "c++",
+    deps = [
+        "gpr_base",
+        "grpc_base",
+        "grpc_http_filters",
+        "grpc_transport_chttp2_alpn",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_transport_chttp2_alpn",
+    srcs = [
+        "src/core/ext/transport/chttp2/alpn/alpn.cc",
+    ],
+    hdrs = [
+        "src/core/ext/transport/chttp2/alpn/alpn.h",
+    ],
+    language = "c++",
+    deps = [
+        "gpr",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_transport_chttp2_client_connector",
+    srcs = [
+        "src/core/ext/transport/chttp2/client/authority.cc",
+        "src/core/ext/transport/chttp2/client/chttp2_connector.cc",
+    ],
+    hdrs = [
+        "src/core/ext/transport/chttp2/client/authority.h",
+        "src/core/ext/transport/chttp2/client/chttp2_connector.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+        "grpc_transport_chttp2",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_transport_chttp2_client_insecure",
+    srcs = [
+        "src/core/ext/transport/chttp2/client/insecure/channel_create.cc",
+        "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_client_channel",
+        "grpc_transport_chttp2",
+        "grpc_transport_chttp2_client_connector",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_transport_chttp2_server",
+    srcs = [
+        "src/core/ext/transport/chttp2/server/chttp2_server.cc",
+    ],
+    hdrs = [
+        "src/core/ext/transport/chttp2/server/chttp2_server.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_transport_chttp2",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_transport_chttp2_server_insecure",
+    srcs = [
+        "src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc",
+        "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+        "grpc_transport_chttp2",
+        "grpc_transport_chttp2_server",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_transport_inproc",
+    srcs = [
+        "src/core/ext/transport/inproc/inproc_plugin.cc",
+        "src/core/ext/transport/inproc/inproc_transport.cc",
+    ],
+    hdrs = [
+        "src/core/ext/transport/inproc/inproc_transport.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc++_base_unsecure",
+    srcs = GRPCXX_SRCS,
+    hdrs = GRPCXX_HDRS,
+    language = "c++",
+    public_hdrs = GRPCXX_PUBLIC_HDRS,
+    deps = [
+        "grpc++_codegen_base",
+        "grpc_unsecure",
+        "health_proto",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc++_codegen_base",
+    language = "c++",
+    public_hdrs = [
+        "include/grpc++/impl/codegen/async_stream.h",
+        "include/grpc++/impl/codegen/async_unary_call.h",
+        "include/grpc++/impl/codegen/byte_buffer.h",
+        "include/grpc++/impl/codegen/call.h",
+        "include/grpc++/impl/codegen/call_hook.h",
+        "include/grpc++/impl/codegen/channel_interface.h",
+        "include/grpc++/impl/codegen/client_context.h",
+        "include/grpc++/impl/codegen/client_unary_call.h",
+        "include/grpc++/impl/codegen/completion_queue.h",
+        "include/grpc++/impl/codegen/completion_queue_tag.h",
+        "include/grpc++/impl/codegen/config.h",
+        "include/grpc++/impl/codegen/core_codegen_interface.h",
+        "include/grpc++/impl/codegen/create_auth_context.h",
+        "include/grpc++/impl/codegen/grpc_library.h",
+        "include/grpc++/impl/codegen/metadata_map.h",
+        "include/grpc++/impl/codegen/method_handler_impl.h",
+        "include/grpc++/impl/codegen/rpc_method.h",
+        "include/grpc++/impl/codegen/rpc_service_method.h",
+        "include/grpc++/impl/codegen/security/auth_context.h",
+        "include/grpc++/impl/codegen/serialization_traits.h",
+        "include/grpc++/impl/codegen/server_context.h",
+        "include/grpc++/impl/codegen/server_interface.h",
+        "include/grpc++/impl/codegen/service_type.h",
+        "include/grpc++/impl/codegen/slice.h",
+        "include/grpc++/impl/codegen/status.h",
+        "include/grpc++/impl/codegen/status_code_enum.h",
+        "include/grpc++/impl/codegen/string_ref.h",
+        "include/grpc++/impl/codegen/stub_options.h",
+        "include/grpc++/impl/codegen/sync_stream.h",
+        "include/grpc++/impl/codegen/time.h",
+        "include/grpcpp/impl/codegen/async_generic_service.h",
+        "include/grpcpp/impl/codegen/async_stream.h",
+        "include/grpcpp/impl/codegen/async_unary_call.h",
+        "include/grpcpp/impl/codegen/byte_buffer.h",
+        "include/grpcpp/impl/codegen/call.h",
+        "include/grpcpp/impl/codegen/call_hook.h",
+        "include/grpcpp/impl/codegen/call_op_set.h",
+        "include/grpcpp/impl/codegen/call_op_set_interface.h",
+        "include/grpcpp/impl/codegen/callback_common.h",
+        "include/grpcpp/impl/codegen/channel_interface.h",
+        "include/grpcpp/impl/codegen/client_callback.h",
+        "include/grpcpp/impl/codegen/client_context.h",
+        "include/grpcpp/impl/codegen/client_interceptor.h",
+        "include/grpcpp/impl/codegen/client_unary_call.h",
+        "include/grpcpp/impl/codegen/completion_queue.h",
+        "include/grpcpp/impl/codegen/completion_queue_tag.h",
+        "include/grpcpp/impl/codegen/config.h",
+        "include/grpcpp/impl/codegen/core_codegen_interface.h",
+        "include/grpcpp/impl/codegen/create_auth_context.h",
+        "include/grpcpp/impl/codegen/grpc_library.h",
+        "include/grpcpp/impl/codegen/intercepted_channel.h",
+        "include/grpcpp/impl/codegen/interceptor.h",
+        "include/grpcpp/impl/codegen/interceptor_common.h",
+        "include/grpcpp/impl/codegen/metadata_map.h",
+        "include/grpcpp/impl/codegen/method_handler_impl.h",
+        "include/grpcpp/impl/codegen/rpc_method.h",
+        "include/grpcpp/impl/codegen/rpc_service_method.h",
+        "include/grpcpp/impl/codegen/security/auth_context.h",
+        "include/grpcpp/impl/codegen/serialization_traits.h",
+        "include/grpcpp/impl/codegen/server_callback.h",
+        "include/grpcpp/impl/codegen/server_context.h",
+        "include/grpcpp/impl/codegen/server_interceptor.h",
+        "include/grpcpp/impl/codegen/server_interface.h",
+        "include/grpcpp/impl/codegen/service_type.h",
+        "include/grpcpp/impl/codegen/slice.h",
+        "include/grpcpp/impl/codegen/status.h",
+        "include/grpcpp/impl/codegen/status_code_enum.h",
+        "include/grpcpp/impl/codegen/string_ref.h",
+        "include/grpcpp/impl/codegen/stub_options.h",
+        "include/grpcpp/impl/codegen/sync_stream.h",
+        "include/grpcpp/impl/codegen/time.h",
+    ],
+    deps = [
+        "grpc_codegen",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc++_codegen_base_src",
+    srcs = [
+        "src/cpp/codegen/codegen_init.cc",
+    ],
+    language = "c++",
+    deps = [
+        "grpc++_codegen_base",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc++_codegen_proto",
+    language = "c++",
+    public_hdrs = [
+        "include/grpc++/impl/codegen/proto_utils.h",
+        "include/grpcpp/impl/codegen/proto_buffer_reader.h",
+        "include/grpcpp/impl/codegen/proto_buffer_writer.h",
+        "include/grpcpp/impl/codegen/proto_utils.h",
+    ],
+    deps = [
+        "grpc++_codegen_base",
+        "grpc++_config_proto",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc++_config_proto",
+    external_deps = [
+        "protobuf:protobuf_headers",
+    ],
+    language = "c++",
+    public_hdrs = [
+        "include/grpc++/impl/codegen/config_protobuf.h",
+        "include/grpcpp/impl/codegen/config_protobuf.h",
+    ],
+)
+
+grpc_cc_library(
+    name = "grpc_server_backward_compatibility",
+    srcs = [
+        "src/core/ext/filters/workarounds/workaround_utils.cc",
+    ],
+    hdrs = [
+        "src/core/ext/filters/workarounds/workaround_utils.h",
+    ],
+    language = "c++",
+    deps = [
+        "grpc_base",
+    ],
 )
 
 filegroup(
diff --git a/third_party/grpc/README.bazel.md b/third_party/grpc/README.bazel.md
index 976fcc9..650bd0e 100644
--- a/third_party/grpc/README.bazel.md
+++ b/third_party/grpc/README.bazel.md
@@ -1,13 +1,21 @@
 # How to update the C++ sources of gRPC:
 
-1. Update the gRPC definitions in WORKSPACE file, currently we use 
-   https://github.com/grpc/grpc/archive/v1.26.0.tar.gz
-2. Update the gRPC patch file if necessary, it mostly helps avoid unnecessary dependencies.
-3. Update third_party/grpc/BUILD to redirect targets to @com_github_grpc_grpc if necessary.
+1. `git clone http://github.com/grpc/grpc.git` in a convenient directory
+2. `git checkout <tag>` (current is `v1.18.0`, commithash `007b721f`)
+3. `mkdir -p third_party/grpc/src`
+4. `cp -R <gRPC git tree>/src/{compiler,core,cpp} third_party/grpc/src`
+5. `cp -R <gRPC git tree>/include third_party/grpc`
+6. `rm -rf third_party/grpc/src/core/tsi/test_creds`
+7. Update BUILD files by copying the rules from the BUILD file of gRPC;
+   fix macros in third_party/grpc/build_defs.bzl if necessary
+8. In the `third_party/grpc` directory, apply local patches if necessary:
+   `patch -p3 < netinet_tcp_h.patch`, `patch -p1 < grpc-gettid.patch`
+9. Update //third_party/nanopb if necessary
 
 # How to update the BUILD/bzl sources of gRPC:
 
-1. `git clone http://github.com/grpc/grpc.git` in a convenient directory
+1. Unless you've done so while updating C++ sources,
+   `git clone http://github.com/grpc/grpc.git` in a convenient directory
 2. `git checkout <tag>` (current is `v1.26.0`, commithash `de893acb`)
 3. `mkdir -p third_party/grpc/bazel`
 4. `cp <gRPC git tree>/bazel/{BUILD,cc_grpc_library.bzl,generate_cc.bzl,protobuf.bzl} third_party/grpc/bazel`
@@ -16,9 +24,9 @@
 
 # How to update the Java plugin:
 
-1. Checkout tag `v1.26.0` from https://github.com/grpc/grpc-java
+1. Checkout tag `v1.10.0` from https://github.com/grpc/grpc-java
 2. `cp -R <grpc-java git tree>/compiler/src/java_plugin third_party/grpc/compiler/src`
 
 # How to update the Java code:
 
-Download the necessary jars at version `1.26.0` from maven central.
+Download the necessary jars at version `1.20.0` from maven central.
diff --git a/third_party/grpc/compiler/src/java_plugin/cpp/java_generator.cpp b/third_party/grpc/compiler/src/java_plugin/cpp/java_generator.cpp
index 5232f11..7cb0123 100644
--- a/third_party/grpc/compiler/src/java_plugin/cpp/java_generator.cpp
+++ b/third_party/grpc/compiler/src/java_plugin/cpp/java_generator.cpp
@@ -1,30 +1,12 @@
-/*
- * Copyright 2019 The gRPC Authors
- *
- * Licensed 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 "java_generator.h"
 
 #include <algorithm>
 #include <iostream>
 #include <iterator>
 #include <map>
-#include <set>
 #include <vector>
 #include <google/protobuf/compiler/java/java_names.h>
 #include <google/protobuf/descriptor.h>
-#include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/io/printer.h>
 #include <google/protobuf/io/zero_copy_stream.h>
 
@@ -51,67 +33,9 @@
 using google::protobuf::SourceLocation;
 using std::to_string;
 
-// java keywords from: https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.9
-static std::set<string> java_keywords = {
-  "abstract",
-  "assert",
-  "boolean",
-  "break",
-  "byte",
-  "case",
-  "catch",
-  "char",
-  "class",
-  "const",
-  "continue",
-  "default",
-  "do",
-  "double",
-  "else",
-  "enum",
-  "extends",
-  "final",
-  "finally",
-  "float",
-  "for",
-  "goto",
-  "if",
-  "implements",
-  "import",
-  "instanceof",
-  "int",
-  "interface",
-  "long",
-  "native",
-  "new",
-  "package",
-  "private",
-  "protected",
-  "public",
-  "return",
-  "short",
-  "static",
-  "strictfp",
-  "super",
-  "switch",
-  "synchronized",
-  "this",
-  "throw",
-  "throws",
-  "transient",
-  "try",
-  "void",
-  "volatile",
-  "while",
-  // additional ones added by us
-  "true",
-  "false",
-};
-
 // Adjust a method name prefix identifier to follow the JavaBean spec:
 //   - decapitalize the first letter
 //   - remove embedded underscores & capitalize the following letter
-//  Finally, if the result is a reserved java keyword, append an underscore.
 static string MixedLower(const string& word) {
   string w;
   w += tolower(word[0]);
@@ -124,9 +48,6 @@
       after_underscore = false;
     }
   }
-  if (java_keywords.find(w) != java_keywords.end()) {
-    return w + "_";
-  }
   return w;
 }
 
@@ -157,16 +78,30 @@
   return MixedLower("get_" + method->name() + "_method");
 }
 
+static inline string MethodPropertiesGetterHelperName(const MethodDescriptor* method) {
+  return MixedLower("get_" + method->name() + "_method_helper");
+}
+
 static inline string MethodIdFieldName(const MethodDescriptor* method) {
   return "METHODID_" + ToAllUpperCase(method->name());
 }
 
-static inline bool ShouldGenerateAsLite(const Descriptor* desc) {
-  return false;
-}
-
-static inline string MessageFullJavaName(const Descriptor* desc) {
-  return google::protobuf::compiler::java::ClassName(desc);
+static inline string MessageFullJavaName(bool nano, const Descriptor* desc) {
+  string name = google::protobuf::compiler::java::ClassName(desc);
+  if (nano) {
+    // XXX: Add "nano" to the original package
+    // (https://github.com/grpc/grpc-java/issues/900)
+    if (isupper(name[0])) {
+      // No java package specified.
+      return "nano." + name;
+    }
+    for (size_t i = 0; i < name.size(); ++i) {
+      if ((name[i] == '.') && (i < (name.size() - 1)) && isupper(name[i + 1])) {
+        return name.substr(0, i + 1) + "nano." + name.substr(i + 1);
+      }
+    }
+  }
+  return name;
 }
 
 // TODO(nmittler): Remove once protobuf includes javadoc methods in distribution.
@@ -381,11 +316,14 @@
     (*vars)["arg_in_id"] = to_string(2 * i);
     (*vars)["arg_out_id"] = to_string(2 * i + 1);
     (*vars)["method_name"] = method->name();
-    (*vars)["input_type"] = MessageFullJavaName(method->input_type());
-    (*vars)["output_type"] = MessageFullJavaName(method->output_type());
+    (*vars)["input_type"] = MessageFullJavaName(flavor == ProtoFlavor::NANO,
+                                                method->input_type());
+    (*vars)["output_type"] = MessageFullJavaName(flavor == ProtoFlavor::NANO,
+                                                 method->output_type());
     (*vars)["method_field_name"] = MethodPropertiesFieldName(method);
     (*vars)["method_new_field_name"] = MethodPropertiesGetterName(method);
     (*vars)["method_method_name"] = MethodPropertiesGetterName(method);
+    (*vars)["method_method_name_helper"] = MethodPropertiesGetterHelperName(method);
     bool client_streaming = method->client_streaming();
     bool server_streaming = method->server_streaming();
     if (client_streaming) {
@@ -402,67 +340,150 @@
       }
     }
 
-    if (flavor == ProtoFlavor::LITE) {
-      (*vars)["ProtoUtils"] = "io.grpc.protobuf.lite.ProtoLiteUtils";
-    } else {
-      (*vars)["ProtoUtils"] = "io.grpc.protobuf.ProtoUtils";
-    }
-    p->Print(
-        *vars,
-        "private static volatile $MethodDescriptor$<$input_type$,\n"
-        "    $output_type$> $method_new_field_name$;\n"
-        "\n"
-        "@$RpcMethod$(\n"
-        "    fullMethodName = SERVICE_NAME + '/' + \"$method_name$\",\n"
-        "    requestType = $input_type$.class,\n"
-        "    responseType = $output_type$.class,\n"
-        "    methodType = $MethodType$.$method_type$)\n"
-        "public static $MethodDescriptor$<$input_type$,\n"
-        "    $output_type$> $method_method_name$() {\n"
-        "  $MethodDescriptor$<$input_type$, $output_type$> $method_new_field_name$;\n"
-        "  if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n"
-        "    synchronized ($service_class_name$.class) {\n"
-        "      if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n"
-        "        $service_class_name$.$method_new_field_name$ = $method_new_field_name$ =\n"
-        "            $MethodDescriptor$.<$input_type$, $output_type$>newBuilder()\n"
-        "            .setType($MethodType$.$method_type$)\n"
-        "            .setFullMethodName(generateFullMethodName(SERVICE_NAME, \"$method_name$\"))\n");
-        
-    bool safe = method->options().idempotency_level()
-        == google::protobuf::MethodOptions_IdempotencyLevel_NO_SIDE_EFFECTS;
-    if (safe) {
-      p->Print(*vars, "            .setSafe(true)\n");
-    } else {
-      bool idempotent = method->options().idempotency_level()
-          == google::protobuf::MethodOptions_IdempotencyLevel_IDEMPOTENT;
-      if (idempotent) {
-        p->Print(*vars, "            .setIdempotent(true)\n");
-      }
-    }
-        
-    p->Print(
-        *vars,
-        "            .setSampledToLocalTracing(true)\n"
-        "            .setRequestMarshaller($ProtoUtils$.marshaller(\n"
-        "                $input_type$.getDefaultInstance()))\n"
-        "            .setResponseMarshaller($ProtoUtils$.marshaller(\n"
-        "                $output_type$.getDefaultInstance()))\n");
-
-    (*vars)["proto_method_descriptor_supplier"] = service->name() + "MethodDescriptorSupplier";
-    if (flavor == ProtoFlavor::NORMAL) {
+    if (flavor == ProtoFlavor::NANO) {
+      // TODO(zsurocking): we're creating two NanoFactories for each method right now.
+      // We could instead create static NanoFactories and reuse them if some methods
+      // share the same request or response messages.
       p->Print(
           *vars,
-        "            .setSchemaDescriptor(new $proto_method_descriptor_supplier$(\"$method_name$\"))\n");
-    }
+          "private static final int ARG_IN_$method_field_name$ = $arg_in_id$;\n"
+          "private static final int ARG_OUT_$method_field_name$ = $arg_out_id$;\n"
+          "@$ExperimentalApi$(\"https://github.com/grpc/grpc-java/issues/1901\")\n"
+          "@$Deprecated$ // Use {@link #$method_method_name$()} instead. \n"
+          "public static final $MethodDescriptor$<$input_type$,\n"
+          "    $output_type$> $method_field_name$ = $method_method_name_helper$();\n"
+          "\n"
+          "private static volatile $MethodDescriptor$<$input_type$,\n"
+          "    $output_type$> $method_new_field_name$;\n"
+          "\n"
+          "@$ExperimentalApi$(\"https://github.com/grpc/grpc-java/issues/1901\")\n"
+          "public static $MethodDescriptor$<$input_type$,\n"
+          "    $output_type$> $method_method_name$() {\n"
+          "  return $method_method_name_helper$();\n"
+          "}\n"
+          "\n"
+          "private static $MethodDescriptor$<$input_type$,\n"
+          "    $output_type$> $method_method_name_helper$() {\n"
+          "  $MethodDescriptor$<$input_type$, $output_type$> $method_new_field_name$;\n"
+          "  if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n"
+          "    synchronized ($service_class_name$.class) {\n"
+          "      if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n"
+          "        $service_class_name$.$method_new_field_name$ = $method_new_field_name$ = \n"
+          "            $MethodDescriptor$.<$input_type$, $output_type$>newBuilder()\n"
+          "            .setType($MethodType$.$method_type$)\n"
+          "            .setFullMethodName(generateFullMethodName(\n"
+          "                \"$Package$$service_name$\", \"$method_name$\"))\n"
+          "            .setSampledToLocalTracing(true)\n"
+          "            .setRequestMarshaller($NanoUtils$.<$input_type$>marshaller(\n"
+          "                new NanoFactory<$input_type$>(ARG_IN_$method_field_name$)))\n"
+          "            .setResponseMarshaller($NanoUtils$.<$output_type$>marshaller(\n"
+          "                new NanoFactory<$output_type$>(ARG_OUT_$method_field_name$)))\n"
+          "            .build();\n"
+          "      }\n"
+          "    }\n"
+          "  }\n"
+          "  return $method_new_field_name$;\n"
+          "}\n");
+    } else {
+      if (flavor == ProtoFlavor::LITE) {
+        (*vars)["ProtoUtils"] = "io.grpc.protobuf.lite.ProtoLiteUtils";
+      } else {
+        (*vars)["ProtoUtils"] = "io.grpc.protobuf.ProtoUtils";
+      }
+      p->Print(
+          *vars,
+          "@$ExperimentalApi$(\"https://github.com/grpc/grpc-java/issues/1901\")\n"
+          "@$Deprecated$ // Use {@link #$method_method_name$()} instead. \n"
+          "public static final $MethodDescriptor$<$input_type$,\n"
+          "    $output_type$> $method_field_name$ = $method_method_name_helper$();\n"
+          "\n"
+          "private static volatile $MethodDescriptor$<$input_type$,\n"
+          "    $output_type$> $method_new_field_name$;\n"
+          "\n"
+          "@$ExperimentalApi$(\"https://github.com/grpc/grpc-java/issues/1901\")\n"
+          "public static $MethodDescriptor$<$input_type$,\n"
+          "    $output_type$> $method_method_name$() {\n"
+          "  return $method_method_name_helper$();\n"
+          "}\n"
+          "\n"
+          "private static $MethodDescriptor$<$input_type$,\n"
+          "    $output_type$> $method_method_name_helper$() {\n"
+          "  $MethodDescriptor$<$input_type$, $output_type$> $method_new_field_name$;\n"
+          "  if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n"
+          "    synchronized ($service_class_name$.class) {\n"
+          "      if (($method_new_field_name$ = $service_class_name$.$method_new_field_name$) == null) {\n"
+          "        $service_class_name$.$method_new_field_name$ = $method_new_field_name$ = \n"
+          "            $MethodDescriptor$.<$input_type$, $output_type$>newBuilder()\n"
+          "            .setType($MethodType$.$method_type$)\n"
+          "            .setFullMethodName(generateFullMethodName(\n"
+          "                \"$Package$$service_name$\", \"$method_name$\"))\n"
+          "            .setSampledToLocalTracing(true)\n"
+          "            .setRequestMarshaller($ProtoUtils$.marshaller(\n"
+          "                $input_type$.getDefaultInstance()))\n"
+          "            .setResponseMarshaller($ProtoUtils$.marshaller(\n"
+          "                $output_type$.getDefaultInstance()))\n");
 
+      (*vars)["proto_method_descriptor_supplier"] = service->name() + "MethodDescriptorSupplier";
+      if (flavor == ProtoFlavor::NORMAL) {
+        p->Print(
+            *vars,
+          "                .setSchemaDescriptor(new $proto_method_descriptor_supplier$(\"$method_name$\"))\n");
+      }
+
+      p->Print(
+          *vars,
+          "                .build();\n");
+      p->Print(*vars,
+          "        }\n"
+          "      }\n"
+          "   }\n"
+          "   return $method_new_field_name$;\n"
+          "}\n");
+     
+    }
+  }
+  p->Print("\n");
+
+  if (flavor == ProtoFlavor::NANO) {
     p->Print(
         *vars,
-        "            .build();\n");
-    p->Print(*vars,
-        "      }\n"
-        "    }\n"
+        "private static final class NanoFactory<T extends com.google.protobuf.nano.MessageNano>\n"
+        "    implements io.grpc.protobuf.nano.MessageNanoFactory<T> {\n"
+        "  private final int id;\n"
+        "\n"
+        "  NanoFactory(int id) {\n"
+        "    this.id = id;\n"
         "  }\n"
-        "  return $method_new_field_name$;\n"
+        "\n"
+        "  @$Override$\n"
+        "  public T newInstance() {\n"
+        "    Object o;\n"
+        "    switch (id) {\n");
+    bool generate_nano = true;
+    for (int i = 0; i < service->method_count(); ++i) {
+      const MethodDescriptor* method = service->method(i);
+      (*vars)["input_type"] = MessageFullJavaName(generate_nano,
+                                                  method->input_type());
+      (*vars)["output_type"] = MessageFullJavaName(generate_nano,
+                                                   method->output_type());
+      (*vars)["method_field_name"] = MethodPropertiesFieldName(method);
+      p->Print(
+          *vars,
+          "    case ARG_IN_$method_field_name$:\n"
+          "      o = new $input_type$();\n"
+          "      break;\n"
+          "    case ARG_OUT_$method_field_name$:\n"
+          "      o = new $output_type$();\n"
+          "      break;\n");
+    }
+    p->Print(
+        "    default:\n"
+        "      throw new AssertionError();\n"
+        "    }\n"
+        "    @java.lang.SuppressWarnings(\"unchecked\")\n"
+        "    T t = (T) o;\n"
+        "    return t;\n"
+        "  }\n"
         "}\n"
         "\n");
   }
@@ -487,50 +508,19 @@
 
 static void PrintBindServiceMethodBody(const ServiceDescriptor* service,
                                    std::map<string, string>* vars,
-                                   Printer* p);
-
-// Prints a StubFactory for given service / stub type.
-static void PrintStubFactory(
-    const ServiceDescriptor* service,
-    std::map<string, string>* vars,
-    Printer* p, StubType type) {
-  string stub_type_name;
-  switch (type) {
-    case ASYNC_CLIENT_IMPL:
-      stub_type_name = "";
-      break;
-    case FUTURE_CLIENT_IMPL:
-      stub_type_name = "Future";
-      break;
-    case BLOCKING_CLIENT_IMPL:
-      stub_type_name = "Blocking";
-      break;
-    default:
-      GRPC_CODEGEN_FAIL << "Cannot generate StubFactory for StubType: " << type;
-  }
-  (*vars)["stub_full_name"] = (*vars)["service_name"] + stub_type_name + "Stub";
-  p->Print(
-    *vars,
-    "$StubFactory$<$stub_full_name$> factory =\n"
-    "  new $StubFactory$<$stub_full_name$>() {\n"
-    "    @$Override$\n"
-    "    public $stub_full_name$ newStub($Channel$ channel, $CallOptions$ callOptions) {\n"
-    "      return new $stub_full_name$(channel, callOptions);\n"
-    "    }\n"
-    "  };\n");
-}
+                                   Printer* p,
+                                   bool generate_nano);
 
 // Prints a client interface or implementation class, or a server interface.
 static void PrintStub(
     const ServiceDescriptor* service,
     std::map<string, string>* vars,
-    Printer* p, StubType type) {
+    Printer* p, StubType type, bool generate_nano) {
   const string service_name = service->name();
   (*vars)["service_name"] = service_name;
   (*vars)["abstract_name"] = service_name + "ImplBase";
   string stub_name = service_name;
   string client_name = service_name;
-  string stub_base_class_name = "AbstractStub";
   CallType call_type;
   bool impl_base = false;
   bool interface = false;
@@ -542,7 +532,6 @@
     case ASYNC_CLIENT_IMPL:
       call_type = ASYNC_CALL;
       stub_name += "Stub";
-      stub_base_class_name = "AbstractAsyncStub";
       break;
     case BLOCKING_CLIENT_INTERFACE:
       interface = true;
@@ -551,7 +540,6 @@
       call_type = BLOCKING_CALL;
       stub_name += "BlockingStub";
       client_name += "BlockingClient";
-      stub_base_class_name = "AbstractBlockingStub";
       break;
     case FUTURE_CLIENT_INTERFACE:
       interface = true;
@@ -560,40 +548,29 @@
       call_type = FUTURE_CALL;
       stub_name += "FutureStub";
       client_name += "FutureClient";
-      stub_base_class_name = "AbstractFutureStub";
       break;
     case ASYNC_INTERFACE:
       call_type = ASYNC_CALL;
       interface = true;
-      stub_name += "Stub";
-      stub_base_class_name = "AbstractAsyncStub";
       break;
     default:
       GRPC_CODEGEN_FAIL << "Cannot determine class name for StubType: " << type;
   }
   (*vars)["stub_name"] = stub_name;
   (*vars)["client_name"] = client_name;
-  (*vars)["stub_base_class_name"] = (*vars)[stub_base_class_name];
 
   // Class head
   if (!interface) {
     GrpcWriteServiceDocComment(p, service);
   }
-
-  if (service->options().deprecated()) {
-    p->Print(*vars, "@$Deprecated$\n");
-  }
-
   if (impl_base) {
     p->Print(
         *vars,
-        "public static abstract class $abstract_name$"
-        " implements $BindableService$ {\n");
+        "public static abstract class $abstract_name$ implements $BindableService$ {\n");
   } else {
     p->Print(
         *vars,
-        "public static final class $stub_name$"
-        " extends $stub_base_class_name$<$stub_name$> {\n");
+        "public static final class $stub_name$ extends $AbstractStub$<$stub_name$> {\n");
   }
   p->Indent();
 
@@ -601,9 +578,15 @@
   if (!impl_base && !interface) {
     p->Print(
         *vars,
-        "private $stub_name$(\n"
-        "    $Channel$ channel, $CallOptions$ callOptions) {"
-        "\n");
+        "private $stub_name$($Channel$ channel) {\n");
+    p->Indent();
+    p->Print("super(channel);\n");
+    p->Outdent();
+    p->Print("}\n\n");
+    p->Print(
+        *vars,
+        "private $stub_name$($Channel$ channel,\n"
+        "    $CallOptions$ callOptions) {\n");
     p->Indent();
     p->Print("super(channel, callOptions);\n");
     p->Outdent();
@@ -611,9 +594,8 @@
     p->Print(
         *vars,
         "@$Override$\n"
-        "protected $stub_name$ build(\n"
-        "    $Channel$ channel, $CallOptions$ callOptions) {"
-        "\n");
+        "protected $stub_name$ build($Channel$ channel,\n"
+        "    $CallOptions$ callOptions) {\n");
     p->Indent();
     p->Print(
         *vars,
@@ -625,10 +607,12 @@
   // RPC methods
   for (int i = 0; i < service->method_count(); ++i) {
     const MethodDescriptor* method = service->method(i);
-    (*vars)["input_type"] = MessageFullJavaName(method->input_type());
-    (*vars)["output_type"] = MessageFullJavaName(method->output_type());
+    (*vars)["input_type"] = MessageFullJavaName(generate_nano,
+                                                method->input_type());
+    (*vars)["output_type"] = MessageFullJavaName(generate_nano,
+                                                 method->output_type());
     (*vars)["lower_method_name"] = LowerMethodName(method);
-    (*vars)["method_method_name"] = MethodPropertiesGetterName(method);
+    (*vars)["method_method_name_helper"] = MethodPropertiesGetterHelperName(method);
     bool client_streaming = method->client_streaming();
     bool server_streaming = method->server_streaming();
 
@@ -648,11 +632,6 @@
     if (!interface) {
       GrpcWriteMethodDocComment(p, method);
     }
-
-    if (method->options().deprecated()) {
-      p->Print(*vars, "@$Deprecated$\n");
-    }
-
     p->Print("public ");
     switch (call_type) {
       case BLOCKING_CALL:
@@ -713,11 +692,11 @@
           if (client_streaming) {
             p->Print(
                 *vars,
-                "return asyncUnimplementedStreamingCall($method_method_name$(), responseObserver);\n");
+                "return asyncUnimplementedStreamingCall($method_method_name_helper$(), responseObserver);\n");
           } else {
             p->Print(
                 *vars,
-                "asyncUnimplementedUnaryCall($method_method_name$(), responseObserver);\n");
+                "asyncUnimplementedUnaryCall($method_method_name_helper$(), responseObserver);\n");
           }
           break;
         default:
@@ -738,7 +717,7 @@
           p->Print(
               *vars,
               "return $calls_method$(\n"
-              "    getChannel(), $method_method_name$(), getCallOptions(), $params$);\n");
+              "    getChannel(), $method_method_name_helper$(), getCallOptions(), $params$);\n");
           break;
         case ASYNC_CALL:
           if (server_streaming) {
@@ -762,7 +741,7 @@
           p->Print(
               *vars,
               "$last_line_prefix$$calls_method$(\n"
-              "    getChannel().newCall($method_method_name$(), getCallOptions()), $params$);\n");
+              "    getChannel().newCall($method_method_name_helper$(), getCallOptions()), $params$);\n");
           break;
         case FUTURE_CALL:
           GRPC_CODEGEN_CHECK(!client_streaming && !server_streaming)
@@ -773,7 +752,7 @@
           p->Print(
               *vars,
               "return $calls_method$(\n"
-              "    getChannel().newCall($method_method_name$(), getCallOptions()), request);\n");
+              "    getChannel().newCall($method_method_name_helper$(), getCallOptions()), request);\n");
           break;
       }
     }
@@ -787,7 +766,7 @@
         *vars,
         "@$Override$ public final $ServerServiceDefinition$ bindService() {\n");
     (*vars)["instance"] = "this";
-    PrintBindServiceMethodBody(service, vars, p);
+    PrintBindServiceMethodBody(service, vars, p, generate_nano);
     p->Print("}\n");
   }
 
@@ -805,7 +784,8 @@
 // on Android.
 static void PrintMethodHandlerClass(const ServiceDescriptor* service,
                                    std::map<string, string>* vars,
-                                   Printer* p) {
+                                   Printer* p,
+                                   bool generate_nano) {
   // Sort method ids based on client_streaming() so switch tables are compact.
   std::vector<const MethodDescriptor*> sorted_methods(service->method_count());
   for (int i = 0; i < service->method_count(); ++i) {
@@ -854,8 +834,10 @@
     }
     (*vars)["method_id_name"] = MethodIdFieldName(method);
     (*vars)["lower_method_name"] = LowerMethodName(method);
-    (*vars)["input_type"] = MessageFullJavaName(method->input_type());
-    (*vars)["output_type"] = MessageFullJavaName(method->output_type());
+    (*vars)["input_type"] = MessageFullJavaName(generate_nano,
+                                                method->input_type());
+    (*vars)["output_type"] = MessageFullJavaName(generate_nano,
+                                                 method->output_type());
     p->Print(
         *vars,
         "case $method_id_name$:\n"
@@ -888,8 +870,10 @@
     }
     (*vars)["method_id_name"] = MethodIdFieldName(method);
     (*vars)["lower_method_name"] = LowerMethodName(method);
-    (*vars)["input_type"] = MessageFullJavaName(method->input_type());
-    (*vars)["output_type"] = MessageFullJavaName(method->output_type());
+    (*vars)["input_type"] = MessageFullJavaName(generate_nano,
+                                                method->input_type());
+    (*vars)["output_type"] = MessageFullJavaName(generate_nano,
+                                                 method->output_type());
     p->Print(
         *vars,
         "case $method_id_name$:\n"
@@ -992,8 +976,8 @@
   }
   for (int i = 0; i < service->method_count(); ++i) {
     const MethodDescriptor* method = service->method(i);
-    (*vars)["method_method_name"] = MethodPropertiesGetterName(method);
-    p->Print(*vars, "\n.addMethod($method_method_name$())");
+    (*vars)["method_method_name_helper"] = MethodPropertiesGetterHelperName(method);
+    p->Print(*vars, "\n.addMethod($method_method_name_helper$())");
   }
   p->Print("\n.build();\n");
   p->Outdent();
@@ -1012,7 +996,8 @@
 
 static void PrintBindServiceMethodBody(const ServiceDescriptor* service,
                                    std::map<string, string>* vars,
-                                   Printer* p) {
+                                   Printer* p,
+                                   bool generate_nano) {
   (*vars)["service_name"] = service->name();
   p->Indent();
   p->Print(*vars,
@@ -1023,9 +1008,11 @@
   for (int i = 0; i < service->method_count(); ++i) {
     const MethodDescriptor* method = service->method(i);
     (*vars)["lower_method_name"] = LowerMethodName(method);
-    (*vars)["method_method_name"] = MethodPropertiesGetterName(method);
-    (*vars)["input_type"] = MessageFullJavaName(method->input_type());
-    (*vars)["output_type"] = MessageFullJavaName(method->output_type());
+    (*vars)["method_method_name_helper"] = MethodPropertiesGetterHelperName(method);
+    (*vars)["input_type"] = MessageFullJavaName(generate_nano,
+                                                method->input_type());
+    (*vars)["output_type"] = MessageFullJavaName(generate_nano,
+                                                 method->output_type());
     (*vars)["method_id_name"] = MethodIdFieldName(method);
     bool client_streaming = method->client_streaming();
     bool server_streaming = method->server_streaming();
@@ -1046,7 +1033,7 @@
     p->Indent();
     p->Print(
         *vars,
-        "$method_method_name$(),\n"
+        "$method_method_name_helper$(),\n"
         "$calls_method$(\n");
     p->Indent();
     p->Print(
@@ -1084,14 +1071,7 @@
       *vars,
       "@$Generated$(\n"
       "    value = \"by gRPC proto compiler$grpc_version$\",\n"
-      "    comments = \"Source: $file_name$\")\n");
-
-  if (service->options().deprecated()) {
-    p->Print(*vars, "@$Deprecated$\n");
-  }
-
-  p->Print(
-      *vars,
+      "    comments = \"Source: $file_name$\")\n"
       "public final class $service_class_name$ {\n\n");
   p->Indent();
   p->Print(
@@ -1111,8 +1091,9 @@
       *vars,
       "public static $service_name$Stub newStub($Channel$ channel) {\n");
   p->Indent();
-  PrintStubFactory(service, vars, p, ASYNC_CLIENT_IMPL);
-  p->Print(*vars, "return $service_name$Stub.newStub(factory, channel);\n");
+  p->Print(
+      *vars,
+      "return new $service_name$Stub(channel);\n");
   p->Outdent();
   p->Print("}\n\n");
 
@@ -1124,10 +1105,9 @@
       "public static $service_name$BlockingStub newBlockingStub(\n"
       "    $Channel$ channel) {\n");
   p->Indent();
-  PrintStubFactory(service, vars, p, BLOCKING_CLIENT_IMPL);
   p->Print(
       *vars,
-      "return $service_name$BlockingStub.newStub(factory, channel);\n");
+      "return new $service_name$BlockingStub(channel);\n");
   p->Outdent();
   p->Print("}\n\n");
 
@@ -1139,25 +1119,25 @@
       "public static $service_name$FutureStub newFutureStub(\n"
       "    $Channel$ channel) {\n");
   p->Indent();
-  PrintStubFactory(service, vars, p, FUTURE_CLIENT_IMPL);
   p->Print(
       *vars,
-      "return $service_name$FutureStub.newStub(factory, channel);\n");
+      "return new $service_name$FutureStub(channel);\n");
   p->Outdent();
   p->Print("}\n\n");
 
-  PrintStub(service, vars, p, ABSTRACT_CLASS);
-  PrintStub(service, vars, p, ASYNC_CLIENT_IMPL);
-  PrintStub(service, vars, p, BLOCKING_CLIENT_IMPL);
-  PrintStub(service, vars, p, FUTURE_CLIENT_IMPL);
+  bool generate_nano = flavor == ProtoFlavor::NANO;
+  PrintStub(service, vars, p, ABSTRACT_CLASS, generate_nano);
+  PrintStub(service, vars, p, ASYNC_CLIENT_IMPL, generate_nano);
+  PrintStub(service, vars, p, BLOCKING_CLIENT_IMPL, generate_nano);
+  PrintStub(service, vars, p, FUTURE_CLIENT_IMPL, generate_nano);
 
-  PrintMethodHandlerClass(service, vars, p);
+  PrintMethodHandlerClass(service, vars, p, generate_nano);
   PrintGetServiceDescriptorMethod(service, vars, p, flavor);
   p->Outdent();
   p->Print("}\n");
 }
 
-void PrintImports(Printer* p) {
+void PrintImports(Printer* p, bool generate_nano) {
   p->Print(
       "import static "
       "io.grpc.MethodDescriptor.generateFullMethodName;\n"
@@ -1187,6 +1167,9 @@
       "io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;\n"
       "import static "
       "io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;\n\n");
+  if (generate_nano) {
+    p->Print("import java.io.IOException;\n\n");
+  }
 }
 
 void GenerateService(const ServiceDescriptor* service,
@@ -1216,26 +1199,24 @@
   vars["ProtoMethodDescriptorSupplier"] =
       "io.grpc.protobuf.ProtoMethodDescriptorSupplier";
   vars["AbstractStub"] = "io.grpc.stub.AbstractStub";
-  vars["AbstractAsyncStub"] = "io.grpc.stub.AbstractAsyncStub";
-  vars["AbstractFutureStub"] = "io.grpc.stub.AbstractFutureStub";
-  vars["AbstractBlockingStub"] = "io.grpc.stub.AbstractBlockingStub";
-  vars["StubFactory"] = "io.grpc.stub.AbstractStub.StubFactory";
-  vars["RpcMethod"] = "io.grpc.stub.annotations.RpcMethod";
   vars["MethodDescriptor"] = "io.grpc.MethodDescriptor";
+  vars["NanoUtils"] = "io.grpc.protobuf.nano.NanoUtils";
   vars["StreamObserver"] = "io.grpc.stub.StreamObserver";
   vars["Iterator"] = "java.util.Iterator";
   vars["Generated"] = "javax.annotation.Generated";
   vars["ListenableFuture"] =
       "com.google.common.util.concurrent.ListenableFuture";
+  vars["ExperimentalApi"] = "io.grpc.ExperimentalApi";
 
   Printer printer(out, '$');
-  string package_name = ServiceJavaPackage(service->file());
+  string package_name = ServiceJavaPackage(service->file(),
+                                           flavor == ProtoFlavor::NANO);
   if (!package_name.empty()) {
     printer.Print(
         "package $package_name$;\n\n",
         "package_name", package_name);
   }
-  PrintImports(&printer);
+  PrintImports(&printer, flavor == ProtoFlavor::NANO);
 
   // Package string is used to fully qualify method names.
   vars["Package"] = service->file()->package();
@@ -1245,7 +1226,7 @@
   PrintService(service, &vars, &printer, flavor, disable_version);
 }
 
-string ServiceJavaPackage(const FileDescriptor* file) {
+string ServiceJavaPackage(const FileDescriptor* file, bool nano) {
   string result = google::protobuf::compiler::java::ClassName(file);
   size_t last_dot_pos = result.find_last_of('.');
   if (last_dot_pos != string::npos) {
@@ -1253,6 +1234,12 @@
   } else {
     result = "";
   }
+  if (nano) {
+    if (!result.empty()) {
+      result += ".";
+    }
+    result += "nano";
+  }
   return result;
 }
 
diff --git a/third_party/grpc/compiler/src/java_plugin/cpp/java_generator.h b/third_party/grpc/compiler/src/java_plugin/cpp/java_generator.h
index 41a7fc0..ab265d0 100644
--- a/third_party/grpc/compiler/src/java_plugin/cpp/java_generator.h
+++ b/third_party/grpc/compiler/src/java_plugin/cpp/java_generator.h
@@ -1,19 +1,3 @@
-/*
- * Copyright 2019 The gRPC Authors
- *
- * Licensed 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.
- */
-
 #ifndef NET_GRPC_COMPILER_JAVA_GENERATOR_H_
 #define NET_GRPC_COMPILER_JAVA_GENERATOR_H_
 
@@ -52,11 +36,11 @@
 namespace java_grpc_generator {
 
 enum ProtoFlavor {
-  NORMAL, LITE
+  NORMAL, LITE, NANO
 };
 
 // Returns the package name of the gRPC services defined in the given file.
-string ServiceJavaPackage(const google::protobuf::FileDescriptor* file);
+string ServiceJavaPackage(const google::protobuf::FileDescriptor* file, bool nano);
 
 // Returns the name of the outer class that wraps in all the generated code for
 // the given service.
diff --git a/third_party/grpc/compiler/src/java_plugin/cpp/java_plugin.cpp b/third_party/grpc/compiler/src/java_plugin/cpp/java_plugin.cpp
index 098a4db..b2cbd0b 100644
--- a/third_party/grpc/compiler/src/java_plugin/cpp/java_plugin.cpp
+++ b/third_party/grpc/compiler/src/java_plugin/cpp/java_plugin.cpp
@@ -1,19 +1,3 @@
-/*
- * Copyright 2019 The gRPC Authors
- *
- * Licensed 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.
- */
-
 // Generates Java gRPC service interface out of Protobuf IDL.
 //
 // This is a Proto2 compiler plugin.  See net/proto2/compiler/proto/plugin.proto
@@ -55,14 +39,17 @@
 
     bool disable_version = false;
     for (size_t i = 0; i < options.size(); i++) {
-      if (options[i].first == "lite") {
+      if (options[i].first == "nano") {
+        flavor = java_grpc_generator::ProtoFlavor::NANO;
+      } else if (options[i].first == "lite") {
         flavor = java_grpc_generator::ProtoFlavor::LITE;
       } else if (options[i].first == "noversion") {
         disable_version = true;
       }
     }
 
-    string package_name = java_grpc_generator::ServiceJavaPackage(file);
+    string package_name = java_grpc_generator::ServiceJavaPackage(
+        file, flavor == java_grpc_generator::ProtoFlavor::NANO);
     string package_filename = JavaPackageToDir(package_name);
     for (int i = 0; i < file->service_count(); ++i) {
       const google::protobuf::ServiceDescriptor* service = file->service(i);
diff --git a/third_party/grpc/grpc-api-1.26.0.jar b/third_party/grpc/grpc-api-1.26.0.jar
deleted file mode 100644
index 226d9f86..0000000
--- a/third_party/grpc/grpc-api-1.26.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/grpc/grpc-auth-1.20.0.jar b/third_party/grpc/grpc-auth-1.20.0.jar
new file mode 100644
index 0000000..ef7ddad
--- /dev/null
+++ b/third_party/grpc/grpc-auth-1.20.0.jar
Binary files differ
diff --git a/third_party/grpc/grpc-auth-1.26.0.jar b/third_party/grpc/grpc-auth-1.26.0.jar
deleted file mode 100644
index 59c7417..0000000
--- a/third_party/grpc/grpc-auth-1.26.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/grpc/grpc-context-1.20.0.jar b/third_party/grpc/grpc-context-1.20.0.jar
new file mode 100644
index 0000000..1709dbe
--- /dev/null
+++ b/third_party/grpc/grpc-context-1.20.0.jar
Binary files differ
diff --git a/third_party/grpc/grpc-context-1.26.0.jar b/third_party/grpc/grpc-context-1.26.0.jar
deleted file mode 100644
index f8c0cf6..0000000
--- a/third_party/grpc/grpc-context-1.26.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/grpc/grpc-core-1.20.0.jar b/third_party/grpc/grpc-core-1.20.0.jar
new file mode 100644
index 0000000..a44ec18
--- /dev/null
+++ b/third_party/grpc/grpc-core-1.20.0.jar
Binary files differ
diff --git a/third_party/grpc/grpc-core-1.26.0.jar b/third_party/grpc/grpc-core-1.26.0.jar
deleted file mode 100644
index c7623d5..0000000
--- a/third_party/grpc/grpc-core-1.26.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/grpc/grpc-gettid.patch b/third_party/grpc/grpc-gettid.patch
new file mode 100644
index 0000000..13d148c
--- /dev/null
+++ b/third_party/grpc/grpc-gettid.patch
@@ -0,0 +1,78 @@
+From 57586a1ca7f17b1916aed3dea4ff8de872dbf853 Mon Sep 17 00:00:00 2001
+From: Benjamin Peterson <benjamin@dropbox.com>
+Date: Fri, 3 May 2019 08:11:00 -0700
+Subject: [PATCH] Rename gettid() functions.
+
+glibc 2.30 will declare its own gettid; see https://sourceware.org/git/?p=glibc.git;a=commit;h=1d0fc213824eaa2a8f8c4385daaa698ee8fb7c92. Rename the grpc versions to avoid naming conflicts.
+---
+ src/core/lib/gpr/log_linux.cc          | 6 ++----
+ src/core/lib/gpr/log_posix.cc          | 4 ++--
+ src/core/lib/iomgr/ev_epollex_linux.cc | 4 ++--
+ 3 files changed, 6 insertions(+), 8 deletions(-)
+
+diff --git a/src/core/lib/gpr/log_linux.cc b/src/core/lib/gpr/log_linux.cc
+index 81026e5689b..8b597b4cf2f 100644
+--- a/src/core/lib/gpr/log_linux.cc
++++ b/src/core/lib/gpr/log_linux.cc
+@@ -40,7 +40,7 @@
+ #include <time.h>
+ #include <unistd.h>
+ 
+-static long gettid(void) { return syscall(__NR_gettid); }
++static long sys_gettid(void) { return syscall(__NR_gettid); }
+ 
+ void gpr_log(const char* file, int line, gpr_log_severity severity,
+              const char* format, ...) {
+@@ -70,7 +70,7 @@ void gpr_default_log(gpr_log_func_args* args) {
+   gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
+   struct tm tm;
+   static __thread long tid = 0;
+-  if (tid == 0) tid = gettid();
++  if (tid == 0) tid = sys_gettid();
+ 
+   timer = static_cast<time_t>(now.tv_sec);
+   final_slash = strrchr(args->file, '/');
+diff --git a/src/core/lib/gpr/log_posix.cc b/src/core/lib/gpr/log_posix.cc
+index b6edc14ab6b..2f7c6ce3760 100644
+--- a/src/core/lib/gpr/log_posix.cc
++++ b/src/core/lib/gpr/log_posix.cc
+@@ -31,7 +31,7 @@
+ #include <string.h>
+ #include <time.h>
+ 
+-static intptr_t gettid(void) { return (intptr_t)pthread_self(); }
++static intptr_t sys_gettid(void) { return (intptr_t)pthread_self(); }
+ 
+ void gpr_log(const char* file, int line, gpr_log_severity severity,
+              const char* format, ...) {
+@@ -86,7 +86,7 @@ void gpr_default_log(gpr_log_func_args* args) {
+   char* prefix;
+   gpr_asprintf(&prefix, "%s%s.%09d %7" PRIdPTR " %s:%d]",
+                gpr_log_severity_string(args->severity), time_buffer,
+-               (int)(now.tv_nsec), gettid(), display_file, args->line);
++               (int)(now.tv_nsec), sys_gettid(), display_file, args->line);
+ 
+   fprintf(stderr, "%-70s %s\n", prefix, args->message);
+   gpr_free(prefix);
+diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc
+index c2d80c08ddb..4a83cb6c215 100644
+--- a/src/core/lib/iomgr/ev_epollex_linux.cc
++++ b/src/core/lib/iomgr/ev_epollex_linux.cc
+@@ -1077,7 +1077,7 @@ static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
+ }
+ 
+ #ifndef NDEBUG
+-static long gettid(void) { return syscall(__NR_gettid); }
++static long sys_gettid(void) { return syscall(__NR_gettid); }
+ #endif
+ 
+ /* pollset->mu lock must be held by the caller before calling this.
+@@ -1097,7 +1097,7 @@ static grpc_error* pollset_work(grpc_pollset* pollset,
+ #define WORKER_PTR (&worker)
+ #endif
+ #ifndef NDEBUG
+-  WORKER_PTR->originator = gettid();
++  WORKER_PTR->originator = sys_gettid();
+ #endif
+   if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
+     gpr_log(GPR_INFO,
diff --git a/third_party/grpc/grpc-netty-1.20.0.jar b/third_party/grpc/grpc-netty-1.20.0.jar
new file mode 100644
index 0000000..42a96d1
--- /dev/null
+++ b/third_party/grpc/grpc-netty-1.20.0.jar
Binary files differ
diff --git a/third_party/grpc/grpc-netty-1.26.0.jar b/third_party/grpc/grpc-netty-1.26.0.jar
deleted file mode 100644
index 87bd9bd..0000000
--- a/third_party/grpc/grpc-netty-1.26.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/grpc/grpc-protobuf-1.20.0.jar b/third_party/grpc/grpc-protobuf-1.20.0.jar
new file mode 100644
index 0000000..c0103c1
--- /dev/null
+++ b/third_party/grpc/grpc-protobuf-1.20.0.jar
Binary files differ
diff --git a/third_party/grpc/grpc-protobuf-1.26.0.jar b/third_party/grpc/grpc-protobuf-1.26.0.jar
deleted file mode 100644
index 9bcee3b..0000000
--- a/third_party/grpc/grpc-protobuf-1.26.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/grpc/grpc-protobuf-lite-1.20.0.jar b/third_party/grpc/grpc-protobuf-lite-1.20.0.jar
new file mode 100644
index 0000000..f96144f
--- /dev/null
+++ b/third_party/grpc/grpc-protobuf-lite-1.20.0.jar
Binary files differ
diff --git a/third_party/grpc/grpc-protobuf-lite-1.26.0.jar b/third_party/grpc/grpc-protobuf-lite-1.26.0.jar
deleted file mode 100644
index f2688ee..0000000
--- a/third_party/grpc/grpc-protobuf-lite-1.26.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/grpc/grpc-stub-1.20.0.jar b/third_party/grpc/grpc-stub-1.20.0.jar
new file mode 100644
index 0000000..958602f
--- /dev/null
+++ b/third_party/grpc/grpc-stub-1.20.0.jar
Binary files differ
diff --git a/third_party/grpc/grpc-stub-1.26.0.jar b/third_party/grpc/grpc-stub-1.26.0.jar
deleted file mode 100644
index 6a2e6a6..0000000
--- a/third_party/grpc/grpc-stub-1.26.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/grpc/include/grpc++/alarm.h b/third_party/grpc/include/grpc++/alarm.h
new file mode 100644
index 0000000..dce742e
--- /dev/null
+++ b/third_party/grpc/include/grpc++/alarm.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_ALARM_H
+#define GRPCXX_ALARM_H
+
+#include <grpcpp/alarm.h>
+
+#endif  // GRPCXX_ALARM_H
diff --git a/third_party/grpc/include/grpc++/channel.h b/third_party/grpc/include/grpc++/channel.h
new file mode 100644
index 0000000..b1154ce
--- /dev/null
+++ b/third_party/grpc/include/grpc++/channel.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_CHANNEL_H
+#define GRPCXX_CHANNEL_H
+
+#include <grpcpp/channel.h>
+
+#endif  // GRPCXX_CHANNEL_H
diff --git a/third_party/grpc/include/grpc++/client_context.h b/third_party/grpc/include/grpc++/client_context.h
new file mode 100644
index 0000000..4b23644
--- /dev/null
+++ b/third_party/grpc/include/grpc++/client_context.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_CLIENT_CONTEXT_H
+#define GRPCXX_CLIENT_CONTEXT_H
+
+#include <grpcpp/client_context.h>
+
+#endif  // GRPCXX_CLIENT_CONTEXT_H
diff --git a/third_party/grpc/include/grpc++/completion_queue.h b/third_party/grpc/include/grpc++/completion_queue.h
new file mode 100644
index 0000000..98ef18f
--- /dev/null
+++ b/third_party/grpc/include/grpc++/completion_queue.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_COMPLETION_QUEUE_H
+#define GRPCXX_COMPLETION_QUEUE_H
+
+#include <grpcpp/completion_queue.h>
+
+#endif  // GRPCXX_COMPLETION_QUEUE_H
diff --git a/third_party/grpc/include/grpc++/create_channel.h b/third_party/grpc/include/grpc++/create_channel.h
new file mode 100644
index 0000000..d95f3a97
--- /dev/null
+++ b/third_party/grpc/include/grpc++/create_channel.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_CREATE_CHANNEL_H
+#define GRPCXX_CREATE_CHANNEL_H
+
+#include <grpcpp/create_channel.h>
+
+#endif  // GRPCXX_CREATE_CHANNEL_H
diff --git a/third_party/grpc/include/grpc++/create_channel_posix.h b/third_party/grpc/include/grpc++/create_channel_posix.h
new file mode 100644
index 0000000..8c8983b
--- /dev/null
+++ b/third_party/grpc/include/grpc++/create_channel_posix.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_CREATE_CHANNEL_POSIX_H
+#define GRPCXX_CREATE_CHANNEL_POSIX_H
+
+#include <grpcpp/create_channel_posix.h>
+
+#endif  // GRPCXX_CREATE_CHANNEL_POSIX_H
diff --git a/third_party/grpc/include/grpc++/ext/health_check_service_server_builder_option.h b/third_party/grpc/include/grpc++/ext/health_check_service_server_builder_option.h
new file mode 100644
index 0000000..cb82fc0
--- /dev/null
+++ b/third_party/grpc/include/grpc++/ext/health_check_service_server_builder_option.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
+#define GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
+
+#include <grpcpp/ext/health_check_service_server_builder_option.h>
+
+#endif  // GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
diff --git a/third_party/grpc/include/grpc++/ext/proto_server_reflection_plugin.h b/third_party/grpc/include/grpc++/ext/proto_server_reflection_plugin.h
new file mode 100644
index 0000000..02e21b9
--- /dev/null
+++ b/third_party/grpc/include/grpc++/ext/proto_server_reflection_plugin.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
+#define GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
+
+#include <grpcpp/ext/proto_server_reflection_plugin.h>
+
+#endif  // GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
diff --git a/third_party/grpc/include/grpc++/generic/async_generic_service.h b/third_party/grpc/include/grpc++/generic/async_generic_service.h
new file mode 100644
index 0000000..d3283fac
--- /dev/null
+++ b/third_party/grpc/include/grpc++/generic/async_generic_service.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H
+#define GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H
+
+#include <grpcpp/generic/async_generic_service.h>
+
+#endif  // GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H
diff --git a/third_party/grpc/include/grpc++/generic/generic_stub.h b/third_party/grpc/include/grpc++/generic/generic_stub.h
new file mode 100644
index 0000000..502953b
--- /dev/null
+++ b/third_party/grpc/include/grpc++/generic/generic_stub.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_GENERIC_GENERIC_STUB_H
+#define GRPCXX_GENERIC_GENERIC_STUB_H
+
+#include <grpcpp/generic/generic_stub.h>
+
+#endif  // GRPCXX_GENERIC_GENERIC_STUB_H
diff --git a/third_party/grpc/include/grpc++/grpc++.h b/third_party/grpc/include/grpc++/grpc++.h
new file mode 100644
index 0000000..9f1d7b1
--- /dev/null
+++ b/third_party/grpc/include/grpc++/grpc++.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_GRPCXX_H
+#define GRPCXX_GRPCXX_H
+
+#include <grpcpp/grpcpp.h>
+
+#endif  // GRPCXX_GRPCXX_H
diff --git a/third_party/grpc/include/grpc++/health_check_service_interface.h b/third_party/grpc/include/grpc++/health_check_service_interface.h
new file mode 100644
index 0000000..0cb0668
--- /dev/null
+++ b/third_party/grpc/include/grpc++/health_check_service_interface.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H
+#define GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H
+
+#include <grpcpp/health_check_service_interface.h>
+
+#endif  // GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H
diff --git a/third_party/grpc/include/grpc++/impl/call.h b/third_party/grpc/include/grpc++/impl/call.h
new file mode 100644
index 0000000..b1da2b6
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/call.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CALL_H
+#define GRPCXX_IMPL_CALL_H
+
+#include <grpcpp/impl/call.h>
+
+#endif  // GRPCXX_IMPL_CALL_H
diff --git a/third_party/grpc/include/grpc++/impl/channel_argument_option.h b/third_party/grpc/include/grpc++/impl/channel_argument_option.h
new file mode 100644
index 0000000..3468378
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/channel_argument_option.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H
+#define GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H
+
+#include <grpcpp/impl/channel_argument_option.h>
+
+#endif  // GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H
diff --git a/third_party/grpc/include/grpc++/impl/client_unary_call.h b/third_party/grpc/include/grpc++/impl/client_unary_call.h
new file mode 100644
index 0000000..75e6560
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/client_unary_call.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CLIENT_UNARY_CALL_H
+#define GRPCXX_IMPL_CLIENT_UNARY_CALL_H
+
+#include <grpcpp/impl/client_unary_call.h>
+
+#endif  // GRPCXX_IMPL_CLIENT_UNARY_CALL_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/async_stream.h b/third_party/grpc/include/grpc++/impl/codegen/async_stream.h
new file mode 100644
index 0000000..a034470
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/async_stream.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H
+#define GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/async_stream.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/async_unary_call.h b/third_party/grpc/include/grpc++/impl/codegen/async_unary_call.h
new file mode 100644
index 0000000..2b08920
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/async_unary_call.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
+#define GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
+
+#include <grpcpp/impl/codegen/async_unary_call.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/byte_buffer.h b/third_party/grpc/include/grpc++/impl/codegen/byte_buffer.h
new file mode 100644
index 0000000..b754fa2
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/byte_buffer.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
+#define GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
+
+#include <grpcpp/impl/codegen/byte_buffer.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/call.h b/third_party/grpc/include/grpc++/impl/codegen/call.h
new file mode 100644
index 0000000..dadab54
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/call.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CALL_H
+#define GRPCXX_IMPL_CODEGEN_CALL_H
+
+#include <grpcpp/impl/codegen/call.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CALL_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/call_hook.h b/third_party/grpc/include/grpc++/impl/codegen/call_hook.h
new file mode 100644
index 0000000..cf5ed57
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/call_hook.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CALL_HOOK_H
+#define GRPCXX_IMPL_CODEGEN_CALL_HOOK_H
+
+#include <grpcpp/impl/codegen/call_hook.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CALL_HOOK_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/channel_interface.h b/third_party/grpc/include/grpc++/impl/codegen/channel_interface.h
new file mode 100644
index 0000000..c6e782e
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/channel_interface.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H
+#define GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H
+
+#include <grpcpp/impl/codegen/channel_interface.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/client_context.h b/third_party/grpc/include/grpc++/impl/codegen/client_context.h
new file mode 100644
index 0000000..107532c
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/client_context.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H
+#define GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H
+
+#include <grpcpp/impl/codegen/client_context.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/client_unary_call.h b/third_party/grpc/include/grpc++/impl/codegen/client_unary_call.h
new file mode 100644
index 0000000..f7dff1f
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/client_unary_call.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
+#define GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
+
+#include <grpcpp/impl/codegen/client_unary_call.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/completion_queue.h b/third_party/grpc/include/grpc++/impl/codegen/completion_queue.h
new file mode 100644
index 0000000..1075495
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/completion_queue.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H
+#define GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H
+
+#include <grpcpp/impl/codegen/completion_queue.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/completion_queue_tag.h b/third_party/grpc/include/grpc++/impl/codegen/completion_queue_tag.h
new file mode 100644
index 0000000..994fa2a
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/completion_queue_tag.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
+#define GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
+
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/config.h b/third_party/grpc/include/grpc++/impl/codegen/config.h
new file mode 100644
index 0000000..237bf38
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/config.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CONFIG_H
+#define GRPCXX_IMPL_CODEGEN_CONFIG_H
+
+#include <grpcpp/impl/codegen/config.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CONFIG_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/config_protobuf.h b/third_party/grpc/include/grpc++/impl/codegen/config_protobuf.h
new file mode 100644
index 0000000..debd74a
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/config_protobuf.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H
+#define GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H
+
+#include <grpcpp/impl/codegen/config_protobuf.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/core_codegen.h b/third_party/grpc/include/grpc++/impl/codegen/core_codegen.h
new file mode 100644
index 0000000..ee600a9
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/core_codegen.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
+#define GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
+
+#include <grpcpp/impl/codegen/core_codegen.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/core_codegen_interface.h b/third_party/grpc/include/grpc++/impl/codegen/core_codegen_interface.h
new file mode 100644
index 0000000..03b3f67
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/core_codegen_interface.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
+#define GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
+
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/create_auth_context.h b/third_party/grpc/include/grpc++/impl/codegen/create_auth_context.h
new file mode 100644
index 0000000..ef89229
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/create_auth_context.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
+#define GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
+
+#include <grpcpp/impl/codegen/create_auth_context.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/grpc_library.h b/third_party/grpc/include/grpc++/impl/codegen/grpc_library.h
new file mode 100644
index 0000000..33c3e25
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/grpc_library.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H
+#define GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H
+
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/metadata_map.h b/third_party/grpc/include/grpc++/impl/codegen/metadata_map.h
new file mode 100644
index 0000000..41c5ad3
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/metadata_map.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
+#define GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
+
+#include <grpcpp/impl/codegen/metadata_map.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/method_handler_impl.h b/third_party/grpc/include/grpc++/impl/codegen/method_handler_impl.h
new file mode 100644
index 0000000..0bdb620
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/method_handler_impl.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
+#define GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
+
+#include <grpcpp/impl/codegen/method_handler_impl.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/proto_utils.h b/third_party/grpc/include/grpc++/impl/codegen/proto_utils.h
new file mode 100644
index 0000000..1f47884
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/proto_utils.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_PROTO_UTILS_H
+#define GRPCXX_IMPL_CODEGEN_PROTO_UTILS_H
+
+#include <grpcpp/impl/codegen/proto_utils.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_PROTO_UTILS_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/rpc_method.h b/third_party/grpc/include/grpc++/impl/codegen/rpc_method.h
new file mode 100644
index 0000000..2906c74
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/rpc_method.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_RPC_METHOD_H
+#define GRPCXX_IMPL_CODEGEN_RPC_METHOD_H
+
+#include <grpcpp/impl/codegen/rpc_method.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_RPC_METHOD_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/rpc_service_method.h b/third_party/grpc/include/grpc++/impl/codegen/rpc_service_method.h
new file mode 100644
index 0000000..999c0d5
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/rpc_service_method.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
+#define GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
+
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/security/auth_context.h b/third_party/grpc/include/grpc++/impl/codegen/security/auth_context.h
new file mode 100644
index 0000000..b466373
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/security/auth_context.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
+#define GRPCXX_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
+
+#include <grpcpp/impl/codegen/security/auth_context.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/serialization_traits.h b/third_party/grpc/include/grpc++/impl/codegen/serialization_traits.h
new file mode 100644
index 0000000..480575d
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/serialization_traits.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
+#define GRPCXX_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
+
+#include <grpcpp/impl/codegen/serialization_traits.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/server_context.h b/third_party/grpc/include/grpc++/impl/codegen/server_context.h
new file mode 100644
index 0000000..1c3342d
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/server_context.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_SERVER_CONTEXT_H
+#define GRPCXX_IMPL_CODEGEN_SERVER_CONTEXT_H
+
+#include <grpcpp/impl/codegen/server_context.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_SERVER_CONTEXT_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/server_interface.h b/third_party/grpc/include/grpc++/impl/codegen/server_interface.h
new file mode 100644
index 0000000..ceea44c
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/server_interface.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H
+#define GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H
+
+#include <grpcpp/impl/codegen/server_interface.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/service_type.h b/third_party/grpc/include/grpc++/impl/codegen/service_type.h
new file mode 100644
index 0000000..be02b75
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/service_type.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H
+#define GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H
+
+#include <grpcpp/impl/codegen/service_type.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/slice.h b/third_party/grpc/include/grpc++/impl/codegen/slice.h
new file mode 100644
index 0000000..6714bad
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/slice.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_SLICE_H
+#define GRPCXX_IMPL_CODEGEN_SLICE_H
+
+#include <grpcpp/impl/codegen/slice.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_SLICE_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/status.h b/third_party/grpc/include/grpc++/impl/codegen/status.h
new file mode 100644
index 0000000..6cf9459
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/status.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_STATUS_H
+#define GRPCXX_IMPL_CODEGEN_STATUS_H
+
+#include <grpcpp/impl/codegen/status.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_STATUS_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/status_code_enum.h b/third_party/grpc/include/grpc++/impl/codegen/status_code_enum.h
new file mode 100644
index 0000000..7503eae
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/status_code_enum.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_STATUS_CODE_ENUM_H
+#define GRPCXX_IMPL_CODEGEN_STATUS_CODE_ENUM_H
+
+#include <grpcpp/impl/codegen/status_code_enum.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_STATUS_CODE_ENUM_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/string_ref.h b/third_party/grpc/include/grpc++/impl/codegen/string_ref.h
new file mode 100644
index 0000000..66e250e
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/string_ref.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_STRING_REF_H
+#define GRPCXX_IMPL_CODEGEN_STRING_REF_H
+
+#include <grpcpp/impl/codegen/string_ref.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_STRING_REF_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/stub_options.h b/third_party/grpc/include/grpc++/impl/codegen/stub_options.h
new file mode 100644
index 0000000..07cb4417
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/stub_options.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H
+#define GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H
+
+#include <grpcpp/impl/codegen/stub_options.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/sync_stream.h b/third_party/grpc/include/grpc++/impl/codegen/sync_stream.h
new file mode 100644
index 0000000..1e6ba27
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/sync_stream.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_SYNC_STREAM_H
+#define GRPCXX_IMPL_CODEGEN_SYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/sync_stream.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_SYNC_STREAM_H
diff --git a/third_party/grpc/include/grpc++/impl/codegen/time.h b/third_party/grpc/include/grpc++/impl/codegen/time.h
new file mode 100644
index 0000000..f9b70f8
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/codegen/time.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_CODEGEN_TIME_H
+#define GRPCXX_IMPL_CODEGEN_TIME_H
+
+#include <grpcpp/impl/codegen/time.h>
+
+#endif  // GRPCXX_IMPL_CODEGEN_TIME_H
diff --git a/third_party/grpc/include/grpc++/impl/grpc_library.h b/third_party/grpc/include/grpc++/impl/grpc_library.h
new file mode 100644
index 0000000..f34a281
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/grpc_library.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_GRPC_LIBRARY_H
+#define GRPCXX_IMPL_GRPC_LIBRARY_H
+
+#include <grpcpp/impl/grpc_library.h>
+
+#endif  // GRPCXX_IMPL_GRPC_LIBRARY_H
diff --git a/third_party/grpc/include/grpc++/impl/method_handler_impl.h b/third_party/grpc/include/grpc++/impl/method_handler_impl.h
new file mode 100644
index 0000000..3840f48
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/method_handler_impl.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_METHOD_HANDLER_IMPL_H
+#define GRPCXX_IMPL_METHOD_HANDLER_IMPL_H
+
+#include <grpcpp/impl/method_handler_impl.h>
+
+#endif  // GRPCXX_IMPL_METHOD_HANDLER_IMPL_H
diff --git a/third_party/grpc/include/grpc++/impl/rpc_method.h b/third_party/grpc/include/grpc++/impl/rpc_method.h
new file mode 100644
index 0000000..7cba7c4
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/rpc_method.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_RPC_METHOD_H
+#define GRPCXX_IMPL_RPC_METHOD_H
+
+#include <grpcpp/impl/rpc_method.h>
+
+#endif  // GRPCXX_IMPL_RPC_METHOD_H
diff --git a/third_party/grpc/include/grpc++/impl/rpc_service_method.h b/third_party/grpc/include/grpc++/impl/rpc_service_method.h
new file mode 100644
index 0000000..2c75087
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/rpc_service_method.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_RPC_SERVICE_METHOD_H
+#define GRPCXX_IMPL_RPC_SERVICE_METHOD_H
+
+#include <grpcpp/impl/rpc_service_method.h>
+
+#endif  // GRPCXX_IMPL_RPC_SERVICE_METHOD_H
diff --git a/third_party/grpc/include/grpc++/impl/serialization_traits.h b/third_party/grpc/include/grpc++/impl/serialization_traits.h
new file mode 100644
index 0000000..33b3a0b
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/serialization_traits.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_SERIALIZATION_TRAITS_H
+#define GRPCXX_IMPL_SERIALIZATION_TRAITS_H
+
+#include <grpcpp/impl/serialization_traits.h>
+
+#endif  // GRPCXX_IMPL_SERIALIZATION_TRAITS_H
diff --git a/third_party/grpc/include/grpc++/impl/server_builder_option.h b/third_party/grpc/include/grpc++/impl/server_builder_option.h
new file mode 100644
index 0000000..833f8db
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/server_builder_option.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_SERVER_BUILDER_OPTION_H
+#define GRPCXX_IMPL_SERVER_BUILDER_OPTION_H
+
+#include <grpcpp/impl/server_builder_option.h>
+
+#endif  // GRPCXX_IMPL_SERVER_BUILDER_OPTION_H
diff --git a/third_party/grpc/include/grpc++/impl/server_builder_plugin.h b/third_party/grpc/include/grpc++/impl/server_builder_plugin.h
new file mode 100644
index 0000000..844d32c
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/server_builder_plugin.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H
+#define GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H
+
+#include <grpcpp/impl/server_builder_plugin.h>
+
+#endif  // GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H
diff --git a/third_party/grpc/include/grpc++/impl/server_initializer.h b/third_party/grpc/include/grpc++/impl/server_initializer.h
new file mode 100644
index 0000000..6a1669c
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/server_initializer.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_SERVER_INITIALIZER_H
+#define GRPCXX_IMPL_SERVER_INITIALIZER_H
+
+#include <grpcpp/impl/server_initializer.h>
+
+#endif  // GRPCXX_IMPL_SERVER_INITIALIZER_H
diff --git a/third_party/grpc/include/grpc++/impl/service_type.h b/third_party/grpc/include/grpc++/impl/service_type.h
new file mode 100644
index 0000000..8642216
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/service_type.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_SERVICE_TYPE_H
+#define GRPCXX_IMPL_SERVICE_TYPE_H
+
+#include <grpcpp/impl/service_type.h>
+
+#endif  // GRPCXX_IMPL_SERVICE_TYPE_H
diff --git a/third_party/grpc/include/grpc++/impl/sync_cxx11.h b/third_party/grpc/include/grpc++/impl/sync_cxx11.h
new file mode 100644
index 0000000..8bcfb2c
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/sync_cxx11.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_SYNC_CXX11_H
+#define GRPCXX_IMPL_SYNC_CXX11_H
+
+#include <grpcpp/impl/sync_cxx11.h>
+
+#endif  // GRPCXX_IMPL_SYNC_CXX11_H
diff --git a/third_party/grpc/include/grpc++/impl/sync_no_cxx11.h b/third_party/grpc/include/grpc++/impl/sync_no_cxx11.h
new file mode 100644
index 0000000..5264567
--- /dev/null
+++ b/third_party/grpc/include/grpc++/impl/sync_no_cxx11.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_IMPL_SYNC_NO_CXX11_H
+#define GRPCXX_IMPL_SYNC_NO_CXX11_H
+
+#include <grpcpp/impl/sync_no_cxx11.h>
+
+#endif  // GRPCXX_IMPL_SYNC_NO_CXX11_H
diff --git a/third_party/grpc/include/grpc++/resource_quota.h b/third_party/grpc/include/grpc++/resource_quota.h
new file mode 100644
index 0000000..aad1b56
--- /dev/null
+++ b/third_party/grpc/include/grpc++/resource_quota.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_RESOURCE_QUOTA_H
+#define GRPCXX_RESOURCE_QUOTA_H
+
+#include <grpcpp/resource_quota.h>
+
+#endif  // GRPCXX_RESOURCE_QUOTA_H
diff --git a/third_party/grpc/include/grpc++/security/auth_context.h b/third_party/grpc/include/grpc++/security/auth_context.h
new file mode 100644
index 0000000..9fe59d4
--- /dev/null
+++ b/third_party/grpc/include/grpc++/security/auth_context.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SECURITY_AUTH_CONTEXT_H
+#define GRPCXX_SECURITY_AUTH_CONTEXT_H
+
+#include <grpcpp/security/auth_context.h>
+
+#endif  // GRPCXX_SECURITY_AUTH_CONTEXT_H
diff --git a/third_party/grpc/include/grpc++/security/auth_metadata_processor.h b/third_party/grpc/include/grpc++/security/auth_metadata_processor.h
new file mode 100644
index 0000000..d045313
--- /dev/null
+++ b/third_party/grpc/include/grpc++/security/auth_metadata_processor.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SECURITY_AUTH_METADATA_PROCESSOR_H
+#define GRPCXX_SECURITY_AUTH_METADATA_PROCESSOR_H
+
+#include <grpcpp/security/auth_metadata_processor.h>
+
+#endif  // GRPCXX_SECURITY_AUTH_METADATA_PROCESSOR_H
diff --git a/third_party/grpc/include/grpc++/security/credentials.h b/third_party/grpc/include/grpc++/security/credentials.h
new file mode 100644
index 0000000..9404418
--- /dev/null
+++ b/third_party/grpc/include/grpc++/security/credentials.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SECURITY_CREDENTIALS_H
+#define GRPCXX_SECURITY_CREDENTIALS_H
+
+#include <grpcpp/security/credentials.h>
+
+#endif  // GRPCXX_SECURITY_CREDENTIALS_H
diff --git a/third_party/grpc/include/grpc++/security/server_credentials.h b/third_party/grpc/include/grpc++/security/server_credentials.h
new file mode 100644
index 0000000..c6d1c4f
--- /dev/null
+++ b/third_party/grpc/include/grpc++/security/server_credentials.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SECURITY_SERVER_CREDENTIALS_H
+#define GRPCXX_SECURITY_SERVER_CREDENTIALS_H
+
+#include <grpcpp/security/server_credentials.h>
+
+#endif  // GRPCXX_SECURITY_SERVER_CREDENTIALS_H
diff --git a/third_party/grpc/include/grpc++/server.h b/third_party/grpc/include/grpc++/server.h
new file mode 100644
index 0000000..086c24c
--- /dev/null
+++ b/third_party/grpc/include/grpc++/server.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SERVER_H
+#define GRPCXX_SERVER_H
+
+#include <grpcpp/server.h>
+
+#endif  // GRPCXX_SERVER_H
diff --git a/third_party/grpc/include/grpc++/server_builder.h b/third_party/grpc/include/grpc++/server_builder.h
new file mode 100644
index 0000000..2c6dab4
--- /dev/null
+++ b/third_party/grpc/include/grpc++/server_builder.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SERVER_BUILDER_H
+#define GRPCXX_SERVER_BUILDER_H
+
+#include <grpcpp/server_builder.h>
+
+#endif  // GRPCXX_SERVER_BUILDER_H
diff --git a/third_party/grpc/include/grpc++/server_context.h b/third_party/grpc/include/grpc++/server_context.h
new file mode 100644
index 0000000..672ccdc
--- /dev/null
+++ b/third_party/grpc/include/grpc++/server_context.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SERVER_CONTEXT_H
+#define GRPCXX_SERVER_CONTEXT_H
+
+#include <grpcpp/server_context.h>
+
+#endif  // GRPCXX_SERVER_CONTEXT_H
diff --git a/third_party/grpc/include/grpc++/server_posix.h b/third_party/grpc/include/grpc++/server_posix.h
new file mode 100644
index 0000000..d2866d9
--- /dev/null
+++ b/third_party/grpc/include/grpc++/server_posix.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SERVER_POSIX_H
+#define GRPCXX_SERVER_POSIX_H
+
+#include <grpcpp/server_posix.h>
+
+#endif  // GRPCXX_SERVER_POSIX_H
diff --git a/third_party/grpc/include/grpc++/support/async_stream.h b/third_party/grpc/include/grpc++/support/async_stream.h
new file mode 100644
index 0000000..9bb2b72
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/async_stream.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_ASYNC_STREAM_H
+#define GRPCXX_SUPPORT_ASYNC_STREAM_H
+
+#include <grpcpp/support/async_stream.h>
+
+#endif  // GRPCXX_SUPPORT_ASYNC_STREAM_H
diff --git a/third_party/grpc/include/grpc++/support/async_unary_call.h b/third_party/grpc/include/grpc++/support/async_unary_call.h
new file mode 100644
index 0000000..56fbf31
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/async_unary_call.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H
+#define GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H
+
+#include <grpcpp/support/async_unary_call.h>
+
+#endif  // GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H
diff --git a/third_party/grpc/include/grpc++/support/byte_buffer.h b/third_party/grpc/include/grpc++/support/byte_buffer.h
new file mode 100644
index 0000000..ec607ee
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/byte_buffer.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_BYTE_BUFFER_H
+#define GRPCXX_SUPPORT_BYTE_BUFFER_H
+
+#include <grpcpp/support/byte_buffer.h>
+
+#endif  // GRPCXX_SUPPORT_BYTE_BUFFER_H
diff --git a/third_party/grpc/include/grpc++/support/channel_arguments.h b/third_party/grpc/include/grpc++/support/channel_arguments.h
new file mode 100644
index 0000000..6d5300c
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/channel_arguments.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H
+#define GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H
+
+#include <grpcpp/support/channel_arguments.h>
+
+#endif  // GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H
diff --git a/third_party/grpc/include/grpc++/support/config.h b/third_party/grpc/include/grpc++/support/config.h
new file mode 100644
index 0000000..f8eee50
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/config.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_CONFIG_H
+#define GRPCXX_SUPPORT_CONFIG_H
+
+#include <grpcpp/support/config.h>
+
+#endif  // GRPCXX_SUPPORT_CONFIG_H
diff --git a/third_party/grpc/include/grpc++/support/error_details.h b/third_party/grpc/include/grpc++/support/error_details.h
new file mode 100644
index 0000000..7ace308
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/error_details.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_ERROR_DETAILS_H
+#define GRPCXX_SUPPORT_ERROR_DETAILS_H
+
+#include <grpcpp/support/error_details.h>
+
+#endif  // GRPCXX_SUPPORT_ERROR_DETAILS_H
diff --git a/third_party/grpc/include/grpc++/support/slice.h b/third_party/grpc/include/grpc++/support/slice.h
new file mode 100644
index 0000000..b02b1a9
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/slice.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_SLICE_H
+#define GRPCXX_SUPPORT_SLICE_H
+
+#include <grpcpp/support/slice.h>
+
+#endif  // GRPCXX_SUPPORT_SLICE_H
diff --git a/third_party/grpc/include/grpc++/support/status.h b/third_party/grpc/include/grpc++/support/status.h
new file mode 100644
index 0000000..e58a18b
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/status.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_STATUS_H
+#define GRPCXX_SUPPORT_STATUS_H
+
+#include <grpcpp/support/status.h>
+
+#endif  // GRPCXX_SUPPORT_STATUS_H
diff --git a/third_party/grpc/include/grpc++/support/status_code_enum.h b/third_party/grpc/include/grpc++/support/status_code_enum.h
new file mode 100644
index 0000000..c278add
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/status_code_enum.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_STATUS_CODE_ENUM_H
+#define GRPCXX_SUPPORT_STATUS_CODE_ENUM_H
+
+#include <grpcpp/support/status_code_enum.h>
+
+#endif  // GRPCXX_SUPPORT_STATUS_CODE_ENUM_H
diff --git a/third_party/grpc/include/grpc++/support/string_ref.h b/third_party/grpc/include/grpc++/support/string_ref.h
new file mode 100644
index 0000000..49de6da
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/string_ref.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_STRING_REF_H
+#define GRPCXX_SUPPORT_STRING_REF_H
+
+#include <grpcpp/support/string_ref.h>
+
+#endif  // GRPCXX_SUPPORT_STRING_REF_H
diff --git a/third_party/grpc/include/grpc++/support/stub_options.h b/third_party/grpc/include/grpc++/support/stub_options.h
new file mode 100644
index 0000000..a712ce8
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/stub_options.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_STUB_OPTIONS_H
+#define GRPCXX_SUPPORT_STUB_OPTIONS_H
+
+#include <grpcpp/support/stub_options.h>
+
+#endif  // GRPCXX_SUPPORT_STUB_OPTIONS_H
diff --git a/third_party/grpc/include/grpc++/support/sync_stream.h b/third_party/grpc/include/grpc++/support/sync_stream.h
new file mode 100644
index 0000000..c118df9
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/sync_stream.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_SYNC_STREAM_H
+#define GRPCXX_SUPPORT_SYNC_STREAM_H
+
+#include <grpcpp/support/sync_stream.h>
+
+#endif  // GRPCXX_SUPPORT_SYNC_STREAM_H
diff --git a/third_party/grpc/include/grpc++/support/time.h b/third_party/grpc/include/grpc++/support/time.h
new file mode 100644
index 0000000..d356b91
--- /dev/null
+++ b/third_party/grpc/include/grpc++/support/time.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_SUPPORT_TIME_H
+#define GRPCXX_SUPPORT_TIME_H
+
+#include <grpcpp/support/time.h>
+
+#endif  // GRPCXX_SUPPORT_TIME_H
diff --git a/third_party/grpc/include/grpc++/test/mock_stream.h b/third_party/grpc/include/grpc++/test/mock_stream.h
new file mode 100644
index 0000000..a29345b
--- /dev/null
+++ b/third_party/grpc/include/grpc++/test/mock_stream.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_TEST_MOCK_STREAM_H
+#define GRPCXX_TEST_MOCK_STREAM_H
+
+#include <grpcpp/test/mock_stream.h>
+
+#endif  // GRPCXX_TEST_MOCK_STREAM_H
diff --git a/third_party/grpc/include/grpc++/test/server_context_test_spouse.h b/third_party/grpc/include/grpc++/test/server_context_test_spouse.h
new file mode 100644
index 0000000..48a4838
--- /dev/null
+++ b/third_party/grpc/include/grpc++/test/server_context_test_spouse.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the
+// headers in include/grpcpp instead. This header exists only for backwards
+// compatibility.
+
+#ifndef GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
+#define GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
+
+#include <grpcpp/test/server_context_test_spouse.h>
+
+#endif  // GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
diff --git a/third_party/grpc/include/grpc/byte_buffer.h b/third_party/grpc/include/grpc/byte_buffer.h
new file mode 100644
index 0000000..ee740f4
--- /dev/null
+++ b/third_party/grpc/include/grpc/byte_buffer.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_BYTE_BUFFER_H
+#define GRPC_BYTE_BUFFER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/byte_buffer.h>
+#include <grpc/slice_buffer.h>
+
+#endif /* GRPC_BYTE_BUFFER_H */
diff --git a/third_party/grpc/include/grpc/byte_buffer_reader.h b/third_party/grpc/include/grpc/byte_buffer_reader.h
new file mode 100644
index 0000000..15e06ca
--- /dev/null
+++ b/third_party/grpc/include/grpc/byte_buffer_reader.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_BYTE_BUFFER_READER_H
+#define GRPC_BYTE_BUFFER_READER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+
+#endif /* GRPC_BYTE_BUFFER_READER_H */
diff --git a/third_party/grpc/include/grpc/census.h b/third_party/grpc/include/grpc/census.h
new file mode 100644
index 0000000..4894f1c
--- /dev/null
+++ b/third_party/grpc/include/grpc/census.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CENSUS_H
+#define GRPC_CENSUS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+  A Census Context is a handle used by Census to represent the current tracing
+  and stats collection information. Contexts should be propagated across RPC's
+  (this is the responsibility of the local RPC system). */
+typedef struct census_context census_context;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CENSUS_H */
diff --git a/third_party/grpc/include/grpc/compression.h b/third_party/grpc/include/grpc/compression.h
new file mode 100644
index 0000000..a4f6a8f
--- /dev/null
+++ b/third_party/grpc/include/grpc/compression.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_COMPRESSION_H
+#define GRPC_COMPRESSION_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <stdlib.h>
+
+#include <grpc/impl/codegen/compression_types.h>
+#include <grpc/slice.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Return if an algorithm is message compression algorithm. */
+GRPCAPI int grpc_compression_algorithm_is_message(
+    grpc_compression_algorithm algorithm);
+
+/** Return if an algorithm is stream compression algorithm. */
+GRPCAPI int grpc_compression_algorithm_is_stream(
+    grpc_compression_algorithm algorithm);
+
+/** Parses the \a slice as a grpc_compression_algorithm instance and updating \a
+ * algorithm. Returns 1 upon success, 0 otherwise. */
+GRPCAPI int grpc_compression_algorithm_parse(
+    grpc_slice value, grpc_compression_algorithm* algorithm);
+
+/** Updates \a name with the encoding name corresponding to a valid \a
+ * algorithm. Note that \a name is statically allocated and must *not* be freed.
+ * Returns 1 upon success, 0 otherwise. */
+GRPCAPI int grpc_compression_algorithm_name(
+    grpc_compression_algorithm algorithm, const char** name);
+
+/** Returns the compression algorithm corresponding to \a level for the
+ * compression algorithms encoded in the \a accepted_encodings bitset.*/
+GRPCAPI grpc_compression_algorithm grpc_compression_algorithm_for_level(
+    grpc_compression_level level, uint32_t accepted_encodings);
+
+GRPCAPI void grpc_compression_options_init(grpc_compression_options* opts);
+
+/** Mark \a algorithm as enabled in \a opts. */
+GRPCAPI void grpc_compression_options_enable_algorithm(
+    grpc_compression_options* opts, grpc_compression_algorithm algorithm);
+
+/** Mark \a algorithm as disabled in \a opts. */
+GRPCAPI void grpc_compression_options_disable_algorithm(
+    grpc_compression_options* opts, grpc_compression_algorithm algorithm);
+
+/** Returns true if \a algorithm is marked as enabled in \a opts. */
+GRPCAPI int grpc_compression_options_is_algorithm_enabled(
+    const grpc_compression_options* opts, grpc_compression_algorithm algorithm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_COMPRESSION_H */
diff --git a/third_party/grpc/include/grpc/fork.h b/third_party/grpc/include/grpc/fork.h
new file mode 100644
index 0000000..26f9df9
--- /dev/null
+++ b/third_party/grpc/include/grpc/fork.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_FORK_H
+#define GRPC_FORK_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/fork.h>
+
+#endif /* GRPC_FORK_H */
diff --git a/third_party/grpc/include/grpc/grpc.h b/third_party/grpc/include/grpc/grpc.h
new file mode 100644
index 0000000..fec7f52
--- /dev/null
+++ b/third_party/grpc/include/grpc/grpc.h
@@ -0,0 +1,533 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_GRPC_H
+#define GRPC_GRPC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/status.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/impl/codegen/connectivity_state.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/propagation_bits.h>
+#include <grpc/slice.h>
+#include <grpc/support/time.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \mainpage GRPC Core
+ *
+ * The GRPC Core library is a low-level library designed to be wrapped by higher
+ * level libraries. The top-level API is provided in grpc.h. Security related
+ * functionality lives in grpc_security.h.
+ */
+
+GRPCAPI void grpc_metadata_array_init(grpc_metadata_array* array);
+GRPCAPI void grpc_metadata_array_destroy(grpc_metadata_array* array);
+
+GRPCAPI void grpc_call_details_init(grpc_call_details* details);
+GRPCAPI void grpc_call_details_destroy(grpc_call_details* details);
+
+/** Registers a plugin to be initialized and destroyed with the library.
+
+    The \a init and \a destroy functions will be invoked as part of
+    \a grpc_init() and \a grpc_shutdown(), respectively.
+    Note that these functions can be invoked an arbitrary number of times
+    (and hence so will \a init and \a destroy).
+    It is safe to pass NULL to either argument. Plugins are destroyed in
+    the reverse order they were initialized. */
+GRPCAPI void grpc_register_plugin(void (*init)(void), void (*destroy)(void));
+
+/** Initialize the grpc library.
+
+    After it's called, a matching invocation to grpc_shutdown() is expected.
+
+    It is not safe to call any other grpc functions before calling this.
+    (To avoid overhead, little checking is done, and some things may work. We
+    do not warrant that they will continue to do so in future revisions of this
+    library). */
+GRPCAPI void grpc_init(void);
+
+/** Shut down the grpc library.
+
+    Before it's called, there should haven been a matching invocation to
+    grpc_init().
+
+    No memory is used by grpc after this call returns, nor are any instructions
+    executing within the grpc library.
+    Prior to calling, all application owned grpc objects must have been
+    destroyed. */
+GRPCAPI void grpc_shutdown(void);
+
+/** EXPERIMENTAL. Returns 1 if the grpc library has been initialized.
+    TODO(ericgribkoff) Decide if this should be promoted to non-experimental as
+    part of stabilizing the fork support API, as tracked in
+    https://github.com/grpc/grpc/issues/15334 */
+GRPCAPI int grpc_is_initialized(void);
+
+/** 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);
+
+/** Returns the completion queue factory based on the attributes. MAY return a
+    NULL if no factory can be found */
+GRPCAPI const grpc_completion_queue_factory*
+grpc_completion_queue_factory_lookup(
+    const grpc_completion_queue_attributes* attributes);
+
+/** Helper function to create a completion queue with grpc_cq_completion_type
+    of GRPC_CQ_NEXT and grpc_cq_polling_type of GRPC_CQ_DEFAULT_POLLING */
+GRPCAPI grpc_completion_queue* grpc_completion_queue_create_for_next(
+    void* reserved);
+
+/** Helper function to create a completion queue with grpc_cq_completion_type
+    of GRPC_CQ_PLUCK and grpc_cq_polling_type of GRPC_CQ_DEFAULT_POLLING */
+GRPCAPI grpc_completion_queue* grpc_completion_queue_create_for_pluck(
+    void* reserved);
+
+/** Helper function to create a completion queue with grpc_cq_completion_type
+    of GRPC_CQ_CALLBACK and grpc_cq_polling_type of GRPC_CQ_DEFAULT_POLLING.
+    This function is experimental. */
+GRPCAPI grpc_completion_queue* grpc_completion_queue_create_for_callback(
+    grpc_experimental_completion_queue_functor* shutdown_callback,
+    void* reserved);
+
+/** Create a completion queue */
+GRPCAPI grpc_completion_queue* grpc_completion_queue_create(
+    const grpc_completion_queue_factory* factory,
+    const grpc_completion_queue_attributes* attributes, void* reserved);
+
+/** Blocks until an event is available, the completion queue is being shut down,
+    or deadline is reached.
+
+    Returns a grpc_event with type GRPC_QUEUE_TIMEOUT on timeout,
+    otherwise a grpc_event describing the event that occurred.
+
+    Callers must not call grpc_completion_queue_next and
+    grpc_completion_queue_pluck simultaneously on the same completion queue. */
+GRPCAPI grpc_event grpc_completion_queue_next(grpc_completion_queue* cq,
+                                              gpr_timespec deadline,
+                                              void* reserved);
+
+/** Blocks until an event with tag 'tag' is available, the completion queue is
+    being shutdown or deadline is reached.
+
+    Returns a grpc_event with type GRPC_QUEUE_TIMEOUT on timeout,
+    otherwise a grpc_event describing the event that occurred.
+
+    Callers must not call grpc_completion_queue_next and
+    grpc_completion_queue_pluck simultaneously on the same completion queue.
+
+    Completion queues support a maximum of GRPC_MAX_COMPLETION_QUEUE_PLUCKERS
+    concurrently executing plucks at any time. */
+GRPCAPI grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq,
+                                               void* tag, gpr_timespec deadline,
+                                               void* reserved);
+
+/** Maximum number of outstanding grpc_completion_queue_pluck executions per
+    completion queue */
+#define GRPC_MAX_COMPLETION_QUEUE_PLUCKERS 6
+
+/** Begin destruction of a completion queue. Once all possible events are
+    drained then grpc_completion_queue_next will start to produce
+    GRPC_QUEUE_SHUTDOWN events only. At that point it's safe to call
+    grpc_completion_queue_destroy.
+
+    After calling this function applications should ensure that no
+    NEW work is added to be published on this completion queue. */
+GRPCAPI void grpc_completion_queue_shutdown(grpc_completion_queue* cq);
+
+/** Destroy a completion queue. The caller must ensure that the queue is
+    drained and no threads are executing grpc_completion_queue_next */
+GRPCAPI void grpc_completion_queue_destroy(grpc_completion_queue* cq);
+
+/*********** EXPERIMENTAL API ************/
+/** Initializes a thread local cache for \a cq.
+ * grpc_flush_cq_tls_cache() MUST be called on the same thread,
+ * with the same cq.
+ */
+GRPCAPI void grpc_completion_queue_thread_local_cache_init(
+    grpc_completion_queue* cq);
+
+/*********** EXPERIMENTAL API ************/
+/** Flushes the thread local cache for \a cq.
+ * Returns 1 if there was contents in the cache.  If there was an event
+ * in \a cq tls cache, its tag is placed in tag, and ok is set to the
+ * event success.
+ */
+GRPCAPI int grpc_completion_queue_thread_local_cache_flush(
+    grpc_completion_queue* cq, void** tag, int* ok);
+
+/** Check the connectivity state of a channel. */
+GRPCAPI grpc_connectivity_state grpc_channel_check_connectivity_state(
+    grpc_channel* channel, int try_to_connect);
+
+/** Number of active "external connectivity state watchers" attached to a
+ * channel.
+ * Useful for testing. **/
+GRPCAPI int grpc_channel_num_external_connectivity_watchers(
+    grpc_channel* channel);
+
+/** Watch for a change in connectivity state.
+    Once the channel connectivity state is different from last_observed_state,
+    tag will be enqueued on cq with success=1.
+    If deadline expires BEFORE the state is changed, tag will be enqueued on cq
+    with success=0. */
+GRPCAPI void grpc_channel_watch_connectivity_state(
+    grpc_channel* channel, grpc_connectivity_state last_observed_state,
+    gpr_timespec deadline, grpc_completion_queue* cq, void* tag);
+
+/** Check whether a grpc channel supports connectivity watcher */
+GRPCAPI int grpc_channel_support_connectivity_watcher(grpc_channel* channel);
+
+/** Create a call given a grpc_channel, in order to call 'method'. All
+    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,
+    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, grpc_slice method,
+    const grpc_slice* host, gpr_timespec deadline, void* reserved);
+
+/** Ping the channels peer (load balanced channels will select one sub-channel
+    to ping); if the channel is not connected, posts a failed. */
+GRPCAPI void grpc_channel_ping(grpc_channel* channel, grpc_completion_queue* cq,
+                               void* tag, void* reserved);
+
+/** Pre-register a method/host pair on a channel. */
+GRPCAPI void* grpc_channel_register_call(grpc_channel* channel,
+                                         const char* method, const char* host,
+                                         void* reserved);
+
+/** 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,
+    gpr_timespec deadline, void* reserved);
+
+/** Allocate memory in the grpc_call arena: this memory is automatically
+    discarded at call completion */
+GRPCAPI void* grpc_call_arena_alloc(grpc_call* call, size_t size);
+
+/** Start a batch of operations defined in the array ops; when complete, post a
+    completion of type 'tag' to the completion queue bound to the call.
+    The order of ops specified in the batch has no significance.
+    Only one operation of each type can be active at once in any given
+    batch.
+    If a call to grpc_call_start_batch returns GRPC_CALL_OK you must call
+    grpc_completion_queue_next or grpc_completion_queue_pluck on the completion
+    queue associated with 'call' for work to be performed. If a call to
+    grpc_call_start_batch returns any value other than GRPC_CALL_OK it is
+    guaranteed that no state associated with 'call' is changed and it is not
+    appropriate to call grpc_completion_queue_next or
+    grpc_completion_queue_pluck consequent to the failed grpc_call_start_batch
+    call.
+    If a call to grpc_call_start_batch with an empty batch returns
+    GRPC_CALL_OK, the tag is put in the completion queue immediately.
+    THREAD SAFETY: access to grpc_call_start_batch in multi-threaded environment
+    needs to be synchronized. As an optimization, you may synchronize batches
+    containing just send operations independently from batches containing just
+    receive operations. Access to grpc_call_start_batch with an empty batch is
+    thread-compatible. */
+GRPCAPI grpc_call_error grpc_call_start_batch(grpc_call* call,
+                                              const grpc_op* ops, size_t nops,
+                                              void* tag, void* reserved);
+
+/** Returns a newly allocated string representing the endpoint to which this
+    call is communicating with. The string is in the uri format accepted by
+    grpc_channel_create.
+    The returned string should be disposed of with gpr_free().
+
+    WARNING: this value is never authenticated or subject to any security
+    related code. It must not be used for any authentication related
+    functionality. Instead, use grpc_auth_context. */
+GRPCAPI char* grpc_call_get_peer(grpc_call* call);
+
+struct census_context;
+
+/** Set census context for a call; Must be called before first call to
+   grpc_call_start_batch(). */
+GRPCAPI void grpc_census_call_set_context(grpc_call* call,
+                                          struct census_context* context);
+
+/** Retrieve the calls current census context. */
+GRPCAPI struct census_context* grpc_census_call_get_context(grpc_call* call);
+
+/** Return a newly allocated string representing the target a channel was
+    created for. */
+GRPCAPI char* grpc_channel_get_target(grpc_channel* channel);
+
+/** Request info about the channel.
+    \a channel_info indicates what information is being requested and
+    how that information will be returned.
+    \a channel_info is owned by the caller. */
+GRPCAPI void grpc_channel_get_info(grpc_channel* channel,
+                                   const grpc_channel_info* channel_info);
+
+/** EXPERIMENTAL.  Resets the channel's connect backoff.
+    TODO(roth): When we see whether this proves useful, either promote
+    to non-experimental or remove it. */
+GRPCAPI void grpc_channel_reset_connect_backoff(grpc_channel* channel);
+
+/** Create a client channel to 'target'. Additional channel level configuration
+    MAY be provided by grpc_channel_args, though the expectation is that most
+    clients will want to simply pass NULL. The user data in 'args' need only
+    live through the invocation of this function. However, if any args of the
+    'pointer' type are passed, then the referenced vtable must be maintained
+    by the caller until grpc_channel_destroy terminates. See grpc_channel_args
+    definition for more on this. */
+GRPCAPI grpc_channel* grpc_insecure_channel_create(
+    const char* target, const grpc_channel_args* args, void* reserved);
+
+/** Create a lame client: this client fails every operation attempted on it. */
+GRPCAPI grpc_channel* grpc_lame_client_channel_create(
+    const char* target, grpc_status_code error_code, const char* error_message);
+
+/** Close and destroy a grpc channel */
+GRPCAPI void grpc_channel_destroy(grpc_channel* channel);
+
+/** Error handling for grpc_call
+   Most grpc_call functions return a grpc_error. If the error is not GRPC_OK
+   then the operation failed due to some unsatisfied precondition.
+   If a grpc_call fails, it's guaranteed that no change to the call state
+   has been made. */
+
+/** Called by clients to cancel an RPC on the server.
+    Can be called multiple times, from any thread.
+    THREAD-SAFETY grpc_call_cancel and grpc_call_cancel_with_status
+    are thread-safe, and can be called at any point before grpc_call_unref
+    is called.*/
+GRPCAPI grpc_call_error grpc_call_cancel(grpc_call* call, void* reserved);
+
+/** Called by clients to cancel an RPC on the server.
+    Can be called multiple times, from any thread.
+    If a status has not been received for the call, set it to the status code
+    and description passed in.
+    Importantly, this function does not send status nor description to the
+    remote endpoint.
+    Note that \a description doesn't need be a static string.
+    It doesn't need to be alive after the call to
+    grpc_call_cancel_with_status completes.
+    */
+GRPCAPI grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
+                                                     grpc_status_code status,
+                                                     const char* description,
+                                                     void* reserved);
+
+/** Ref a call.
+    THREAD SAFETY: grpc_call_ref is thread-compatible */
+GRPCAPI void grpc_call_ref(grpc_call* call);
+
+/** Unref a call.
+    THREAD SAFETY: grpc_call_unref is thread-compatible */
+GRPCAPI void grpc_call_unref(grpc_call* call);
+
+/** Request notification of a new call.
+    Once a call is received, a notification tagged with \a tag_new is added to
+    \a cq_for_notification. \a call, \a details and \a request_metadata are
+    updated with the appropriate call information. \a cq_bound_to_call is bound
+    to \a call, and batch operation notifications for that call will be posted
+    to \a cq_bound_to_call.
+    Note that \a cq_for_notification must have been registered to the server via
+    \a grpc_server_register_completion_queue. */
+GRPCAPI grpc_call_error grpc_server_request_call(
+    grpc_server* server, grpc_call** call, grpc_call_details* details,
+    grpc_metadata_array* request_metadata,
+    grpc_completion_queue* cq_bound_to_call,
+    grpc_completion_queue* cq_for_notification, void* tag_new);
+
+/** How to handle payloads for a registered method */
+typedef enum {
+  /** Don't try to read the payload */
+  GRPC_SRM_PAYLOAD_NONE,
+  /** Read the initial payload as a byte buffer */
+  GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER
+} grpc_server_register_method_payload_handling;
+
+/** Registers a method in the server.
+    Methods to this (host, method) pair will not be reported by
+    grpc_server_request_call, but instead be reported by
+    grpc_server_request_registered_call when passed the appropriate
+    registered_method (as returned by this function).
+    Must be called before grpc_server_start.
+    Returns NULL on failure. */
+GRPCAPI void* grpc_server_register_method(
+    grpc_server* server, const char* method, const char* host,
+    grpc_server_register_method_payload_handling payload_handling,
+    uint32_t flags);
+
+/** Request notification of a new pre-registered call. 'cq_for_notification'
+    must have been registered to the server via
+    grpc_server_register_completion_queue. */
+GRPCAPI grpc_call_error grpc_server_request_registered_call(
+    grpc_server* server, void* registered_method, grpc_call** call,
+    gpr_timespec* deadline, grpc_metadata_array* request_metadata,
+    grpc_byte_buffer** optional_payload,
+    grpc_completion_queue* cq_bound_to_call,
+    grpc_completion_queue* cq_for_notification, void* tag_new);
+
+/** Create a server. Additional configuration for each incoming channel can
+    be specified with args. If no additional configuration is needed, args can
+    be NULL. The user data in 'args' need only live through the invocation of
+    this function. However, if any args of the 'pointer' type are passed, then
+    the referenced vtable must be maintained by the caller until
+    grpc_server_destroy terminates. See grpc_channel_args definition for more
+    on this. */
+GRPCAPI grpc_server* grpc_server_create(const grpc_channel_args* args,
+                                        void* reserved);
+
+/** Register a completion queue with the server. Must be done for any
+    notification completion queue that is passed to grpc_server_request_*_call
+    and to grpc_server_shutdown_and_notify. Must be performed prior to
+    grpc_server_start. */
+GRPCAPI void grpc_server_register_completion_queue(grpc_server* server,
+                                                   grpc_completion_queue* cq,
+                                                   void* reserved);
+
+/** Add a HTTP2 over plaintext over tcp listener.
+    Returns bound port number on success, 0 on failure.
+    REQUIRES: server not started */
+GRPCAPI int grpc_server_add_insecure_http2_port(grpc_server* server,
+                                                const char* addr);
+
+/** Start a server - tells all listeners to start listening */
+GRPCAPI void grpc_server_start(grpc_server* server);
+
+/** Begin shutting down a server.
+    After completion, no new calls or connections will be admitted.
+    Existing calls will be allowed to complete.
+    Send a GRPC_OP_COMPLETE event when there are no more calls being serviced.
+    Shutdown is idempotent, and all tags will be notified at once if multiple
+    grpc_server_shutdown_and_notify calls are made. 'cq' must have been
+    registered to this server via grpc_server_register_completion_queue. */
+GRPCAPI void grpc_server_shutdown_and_notify(grpc_server* server,
+                                             grpc_completion_queue* cq,
+                                             void* tag);
+
+/** Cancel all in-progress calls.
+    Only usable after shutdown. */
+GRPCAPI void grpc_server_cancel_all_calls(grpc_server* server);
+
+/** Destroy a server.
+    Shutdown must have completed beforehand (i.e. all tags generated by
+    grpc_server_shutdown_and_notify must have been received, and at least
+    one call to grpc_server_shutdown_and_notify must have been made). */
+GRPCAPI void grpc_server_destroy(grpc_server* server);
+
+/** Enable or disable a tracer.
+
+    Tracers (usually controlled by the environment variable GRPC_TRACE)
+    allow printf-style debugging on GRPC internals, and are useful for
+    tracking down problems in the field.
+
+    Use of this function is not strictly thread-safe, but the
+    thread-safety issues raised by it should not be of concern. */
+GRPCAPI int grpc_tracer_set_enabled(const char* name, int enabled);
+
+/** Check whether a metadata key is legal (will be accepted by core) */
+GRPCAPI int grpc_header_key_is_legal(grpc_slice slice);
+
+/** Check whether a non-binary metadata value is legal (will be accepted by
+    core) */
+GRPCAPI int grpc_header_nonbin_value_is_legal(grpc_slice slice);
+
+/** Check whether a metadata key corresponds to a binary value */
+GRPCAPI int grpc_is_binary_header(grpc_slice slice);
+
+/** Convert grpc_call_error values to a string */
+GRPCAPI const char* grpc_call_error_to_string(grpc_call_error error);
+
+/** Create a buffer pool */
+GRPCAPI grpc_resource_quota* grpc_resource_quota_create(const char* trace_name);
+
+/** Add a reference to a buffer pool */
+GRPCAPI void grpc_resource_quota_ref(grpc_resource_quota* resource_quota);
+
+/** Drop a reference to a buffer pool */
+GRPCAPI void grpc_resource_quota_unref(grpc_resource_quota* resource_quota);
+
+/** Update the size of a buffer pool */
+GRPCAPI void grpc_resource_quota_resize(grpc_resource_quota* resource_quota,
+                                        size_t new_size);
+
+/** Update the size of the maximum number of threads allowed */
+GRPCAPI void grpc_resource_quota_set_max_threads(
+    grpc_resource_quota* resource_quota, int new_max_threads);
+
+/** Fetch a vtable for a grpc_channel_arg that points to a grpc_resource_quota
+ */
+GRPCAPI const grpc_arg_pointer_vtable* grpc_resource_quota_arg_vtable(void);
+
+/************* CHANNELZ API *************/
+/** Channelz is under active development. The following APIs will see some
+    churn as the feature is implemented. This comment will be removed once
+    channelz is officially supported, and these APIs become stable. For now
+    you may track the progress by following this github issue:
+    https://github.com/grpc/grpc/issues/15340
+
+    the following APIs return allocated JSON strings that match the response
+    objects from the channelz proto, found here:
+    https://github.com/grpc/grpc/blob/master/src/proto/grpc/channelz/channelz.proto.
+
+    For easy conversion to protobuf, The JSON is formatted according to:
+    https://developers.google.com/protocol-buffers/docs/proto3#json. */
+
+/* Gets all root channels (i.e. channels the application has directly
+   created). This does not include subchannels nor non-top level channels.
+   The returned string is allocated and must be freed by the application. */
+GRPCAPI char* grpc_channelz_get_top_channels(intptr_t start_channel_id);
+
+/* Gets all servers that exist in the process. */
+GRPCAPI char* grpc_channelz_get_servers(intptr_t start_server_id);
+
+/* Returns a single Server, or else a NOT_FOUND code. */
+GRPCAPI char* grpc_channelz_get_server(intptr_t server_id);
+
+/* Gets all server sockets that exist in the server. */
+GRPCAPI char* grpc_channelz_get_server_sockets(intptr_t server_id,
+                                               intptr_t start_socket_id,
+                                               intptr_t max_results);
+
+/* Returns a single Channel, or else a NOT_FOUND code. The returned string
+   is allocated and must be freed by the application. */
+GRPCAPI char* grpc_channelz_get_channel(intptr_t channel_id);
+
+/* Returns a single Subchannel, or else a NOT_FOUND code. The returned string
+   is allocated and must be freed by the application. */
+GRPCAPI char* grpc_channelz_get_subchannel(intptr_t subchannel_id);
+
+/* Returns a single Socket, or else a NOT_FOUND code. The returned string
+   is allocated and must be freed by the application. */
+GRPCAPI char* grpc_channelz_get_socket(intptr_t socket_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_GRPC_H */
diff --git a/third_party/grpc/include/grpc/grpc_cronet.h b/third_party/grpc/include/grpc/grpc_cronet.h
new file mode 100644
index 0000000..289cfcd
--- /dev/null
+++ b/third_party/grpc/include/grpc/grpc_cronet.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_GRPC_CRONET_H
+#define GRPC_GRPC_CRONET_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+GRPCAPI grpc_channel* grpc_cronet_secure_channel_create(
+    void* engine, const char* target, const grpc_channel_args* args,
+    void* reserved);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_GRPC_CRONET_H */
diff --git a/third_party/grpc/include/grpc/grpc_posix.h b/third_party/grpc/include/grpc/grpc_posix.h
new file mode 100644
index 0000000..fbce5e1
--- /dev/null
+++ b/third_party/grpc/include/grpc/grpc_posix.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_GRPC_POSIX_H
+#define GRPC_GRPC_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \mainpage GRPC Core POSIX
+ *
+ * The GRPC Core POSIX library provides some POSIX-specific low-level
+ * functionality on top of GRPC Core.
+ */
+
+/** Create a client channel to 'target' using file descriptor 'fd'. The 'target'
+    argument will be used to indicate the name for this channel. See the comment
+    for grpc_insecure_channel_create for description of 'args' argument. */
+GRPCAPI grpc_channel* grpc_insecure_channel_create_from_fd(
+    const char* target, int fd, const grpc_channel_args* args);
+
+/** 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. 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,
+                                                      void* reserved, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_GRPC_POSIX_H */
diff --git a/third_party/grpc/include/grpc/grpc_security.h b/third_party/grpc/include/grpc/grpc_security.h
new file mode 100644
index 0000000..de90971
--- /dev/null
+++ b/third_party/grpc/include/grpc/grpc_security.h
@@ -0,0 +1,616 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_GRPC_SECURITY_H
+#define GRPC_GRPC_SECURITY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security_constants.h>
+#include <grpc/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** --- Authentication Context. --- */
+
+typedef struct grpc_auth_context grpc_auth_context;
+
+typedef struct grpc_auth_property_iterator {
+  const grpc_auth_context* ctx;
+  size_t index;
+  const char* name;
+} grpc_auth_property_iterator;
+
+/** value, if not NULL, is guaranteed to be NULL terminated. */
+typedef struct grpc_auth_property {
+  char* name;
+  char* value;
+  size_t value_length;
+} grpc_auth_property;
+
+/** Returns NULL when the iterator is at the end. */
+GRPCAPI const grpc_auth_property* grpc_auth_property_iterator_next(
+    grpc_auth_property_iterator* it);
+
+/** Iterates over the auth context. */
+GRPCAPI grpc_auth_property_iterator
+grpc_auth_context_property_iterator(const grpc_auth_context* ctx);
+
+/** Gets the peer identity. Returns an empty iterator (first _next will return
+   NULL) if the peer is not authenticated. */
+GRPCAPI grpc_auth_property_iterator
+grpc_auth_context_peer_identity(const grpc_auth_context* ctx);
+
+/** Finds a property in the context. May return an empty iterator (first _next
+   will return NULL) if no property with this name was found in the context. */
+GRPCAPI grpc_auth_property_iterator grpc_auth_context_find_properties_by_name(
+    const grpc_auth_context* ctx, const char* name);
+
+/** Gets the name of the property that indicates the peer identity. Will return
+   NULL if the peer is not authenticated. */
+GRPCAPI const char* grpc_auth_context_peer_identity_property_name(
+    const grpc_auth_context* ctx);
+
+/** Returns 1 if the peer is authenticated, 0 otherwise. */
+GRPCAPI int grpc_auth_context_peer_is_authenticated(
+    const grpc_auth_context* ctx);
+
+/** Gets the auth context from the call. Caller needs to call
+   grpc_auth_context_release on the returned context. */
+GRPCAPI grpc_auth_context* grpc_call_auth_context(grpc_call* call);
+
+/** Releases the auth context returned from grpc_call_auth_context. */
+GRPCAPI void grpc_auth_context_release(grpc_auth_context* context);
+
+/** --
+   The following auth context methods should only be called by a server metadata
+   processor to set properties extracted from auth metadata.
+   -- */
+
+/** Add a property. */
+GRPCAPI void grpc_auth_context_add_property(grpc_auth_context* ctx,
+                                            const char* name, const char* value,
+                                            size_t value_length);
+
+/** Add a C string property. */
+GRPCAPI void grpc_auth_context_add_cstring_property(grpc_auth_context* ctx,
+                                                    const char* name,
+                                                    const char* value);
+
+/** Sets the property name. Returns 1 if successful or 0 in case of failure
+   (which means that no property with this name exists). */
+GRPCAPI int grpc_auth_context_set_peer_identity_property_name(
+    grpc_auth_context* ctx, const char* name);
+
+/** --- SSL Session Cache. ---
+
+    A SSL session cache object represents a way to cache client sessions
+    between connections. Only ticket-based resumption is supported. */
+
+typedef struct grpc_ssl_session_cache grpc_ssl_session_cache;
+
+/** Create LRU cache for client-side SSL sessions with the given capacity.
+    If capacity is < 1, a default capacity is used instead. */
+GRPCAPI grpc_ssl_session_cache* grpc_ssl_session_cache_create_lru(
+    size_t capacity);
+
+/** Destroy SSL session cache. */
+GRPCAPI void grpc_ssl_session_cache_destroy(grpc_ssl_session_cache* cache);
+
+/** Create a channel arg with the given cache object. */
+GRPCAPI grpc_arg
+grpc_ssl_session_cache_create_channel_arg(grpc_ssl_session_cache* cache);
+
+/** --- grpc_channel_credentials object. ---
+
+   A channel credentials object represents a way to authenticate a client on a
+   channel.  */
+
+typedef struct grpc_channel_credentials grpc_channel_credentials;
+
+/** Releases a channel credentials object.
+   The creator of the credentials object is responsible for its release. */
+GRPCAPI void grpc_channel_credentials_release(grpc_channel_credentials* creds);
+
+/** Creates default credentials to connect to a google gRPC service.
+   WARNING: Do NOT use this credentials to connect to a non-google service as
+   this could result in an oauth2 token leak. */
+GRPCAPI grpc_channel_credentials* grpc_google_default_credentials_create(void);
+
+/** Callback for getting the SSL roots override from the application.
+   In case of success, *pem_roots_certs must be set to a NULL terminated string
+   containing the list of PEM encoded root certificates. The ownership is passed
+   to the core and freed (laster by the core) with gpr_free.
+   If this function fails and GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment is
+   set to a valid path, it will override the roots specified this func */
+typedef grpc_ssl_roots_override_result (*grpc_ssl_roots_override_callback)(
+    char** pem_root_certs);
+
+/** Setup a callback to override the default TLS/SSL roots.
+   This function is not thread-safe and must be called at initialization time
+   before any ssl credentials are created to have the desired side effect.
+   If GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment is set to a valid path, the
+   callback will not be called. */
+GRPCAPI void grpc_set_ssl_roots_override_callback(
+    grpc_ssl_roots_override_callback cb);
+
+/** Object that holds a private key / certificate chain pair in PEM format. */
+typedef struct {
+  /** private_key is the NULL-terminated string containing the PEM encoding of
+     the client's private key. */
+  const char* private_key;
+
+  /** cert_chain is the NULL-terminated string containing the PEM encoding of
+     the client's certificate chain. */
+  const char* cert_chain;
+} grpc_ssl_pem_key_cert_pair;
+
+/** Object that holds additional peer-verification options on a secure
+   channel. */
+typedef struct {
+  /** If non-NULL this callback will be invoked with the expected
+     target_name, the peer's certificate (in PEM format), and whatever
+     userdata pointer is set below. If a non-zero value is returned by this
+     callback then it is treated as a verification failure. Invocation of
+     the callback is blocking, so any implementation should be light-weight.
+     */
+  int (*verify_peer_callback)(const char* target_name, const char* peer_pem,
+                              void* userdata);
+  /** Arbitrary userdata that will be passed as the last argument to
+     verify_peer_callback. */
+  void* verify_peer_callback_userdata;
+  /** A destruct callback that will be invoked when the channel is being
+     cleaned up. The userdata argument will be passed to it. The intent is
+     to perform any cleanup associated with that userdata. */
+  void (*verify_peer_destruct)(void* userdata);
+} verify_peer_options;
+
+/** Creates an SSL credentials object.
+   - pem_root_certs is the NULL-terminated string containing the PEM encoding
+     of the server root certificates. If this parameter is NULL, the
+     implementation will first try to dereference the file pointed by the
+     GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable, and if that fails,
+     try to get the roots set by grpc_override_ssl_default_roots. Eventually,
+     if all these fail, it will try to get the roots from a well-known place on
+     disk (in the grpc install directory).
+   - pem_key_cert_pair is a pointer on the object containing client's private
+     key and certificate chain. This parameter can be NULL if the client does
+     not have such a key/cert pair.
+   - verify_options is an optional verify_peer_options object which holds
+     additional options controlling how peer certificates are verified. For
+     example, you can supply a callback which receives the peer's certificate
+     with which you can do additional verification. Can be NULL, in which
+     case verification will retain default behavior. Any settings in
+     verify_options are copied during this call, so the verify_options
+     object can be released afterwards. */
+GRPCAPI grpc_channel_credentials* grpc_ssl_credentials_create(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
+    const verify_peer_options* verify_options, void* reserved);
+
+/** --- grpc_call_credentials object.
+
+   A call credentials object represents a way to authenticate on a particular
+   call. These credentials can be composed with a channel credentials object
+   so that they are sent with every call on this channel.  */
+
+typedef struct grpc_call_credentials grpc_call_credentials;
+
+/** Releases a call credentials object.
+   The creator of the credentials object is responsible for its release. */
+GRPCAPI void grpc_call_credentials_release(grpc_call_credentials* creds);
+
+/** Creates a composite channel credentials object. */
+GRPCAPI grpc_channel_credentials* grpc_composite_channel_credentials_create(
+    grpc_channel_credentials* channel_creds, grpc_call_credentials* call_creds,
+    void* reserved);
+
+/** Creates a composite call credentials object. */
+GRPCAPI grpc_call_credentials* grpc_composite_call_credentials_create(
+    grpc_call_credentials* creds1, grpc_call_credentials* creds2,
+    void* reserved);
+
+/** Creates a compute engine credentials object for connecting to Google.
+   WARNING: Do NOT use this credentials to connect to a non-google service as
+   this could result in an oauth2 token leak. */
+GRPCAPI grpc_call_credentials* grpc_google_compute_engine_credentials_create(
+    void* reserved);
+
+GRPCAPI gpr_timespec grpc_max_auth_token_lifetime(void);
+
+/** Creates a JWT credentials object. May return NULL if the input is invalid.
+   - json_key is the JSON key string containing the client's private key.
+   - token_lifetime is the lifetime of each Json Web Token (JWT) created with
+     this credentials.  It should not exceed grpc_max_auth_token_lifetime or
+     will be cropped to this value.  */
+GRPCAPI grpc_call_credentials*
+grpc_service_account_jwt_access_credentials_create(const char* json_key,
+                                                   gpr_timespec token_lifetime,
+                                                   void* reserved);
+
+/** Creates an Oauth2 Refresh Token credentials object for connecting to Google.
+   May return NULL if the input is invalid.
+   WARNING: Do NOT use this credentials to connect to a non-google service as
+   this could result in an oauth2 token leak.
+   - json_refresh_token is the JSON string containing the refresh token itself
+     along with a client_id and client_secret. */
+GRPCAPI grpc_call_credentials* grpc_google_refresh_token_credentials_create(
+    const char* json_refresh_token, void* reserved);
+
+/** Creates an Oauth2 Access Token credentials with an access token that was
+   aquired by an out of band mechanism. */
+GRPCAPI grpc_call_credentials* grpc_access_token_credentials_create(
+    const char* access_token, void* reserved);
+
+/** Creates an IAM credentials object for connecting to Google. */
+GRPCAPI grpc_call_credentials* grpc_google_iam_credentials_create(
+    const char* authorization_token, const char* authority_selector,
+    void* reserved);
+
+/** Callback function to be called by the metadata credentials plugin
+   implementation when the metadata is ready.
+   - user_data is the opaque pointer that was passed in the get_metadata method
+     of the grpc_metadata_credentials_plugin (see below).
+   - creds_md is an array of credentials metadata produced by the plugin. It
+     may be set to NULL in case of an error.
+   - num_creds_md is the number of items in the creds_md array.
+   - status must be GRPC_STATUS_OK in case of success or another specific error
+     code otherwise.
+   - error_details contains details about the error if any. In case of success
+     it should be NULL and will be otherwise ignored. */
+typedef void (*grpc_credentials_plugin_metadata_cb)(
+    void* user_data, const grpc_metadata* creds_md, size_t num_creds_md,
+    grpc_status_code status, const char* error_details);
+
+/** Context that can be used by metadata credentials plugin in order to create
+   auth related metadata. */
+typedef struct {
+  /** The fully qualifed service url. */
+  const char* service_url;
+
+  /** The method name of the RPC being called (not fully qualified).
+     The fully qualified method name can be built from the service_url:
+     full_qualified_method_name = ctx->service_url + '/' + ctx->method_name. */
+  const char* method_name;
+
+  /** The auth_context of the channel which gives the server's identity. */
+  const grpc_auth_context* channel_auth_context;
+
+  /** Reserved for future use. */
+  void* reserved;
+} grpc_auth_metadata_context;
+
+/** Maximum number of metadata entries returnable by a credentials plugin via
+    a synchronous return. */
+#define GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX 4
+
+/** grpc_metadata_credentials plugin is an API user provided structure used to
+   create grpc_credentials objects that can be set on a channel (composed) or
+   a call. See grpc_credentials_metadata_create_from_plugin below.
+   The grpc client stack will call the get_metadata method of the plugin for
+   every call in scope for the credentials created from it. */
+typedef struct {
+  /** The implementation of this method has to be non-blocking, but can
+     be performed synchronously or asynchronously.
+
+     If processing occurs synchronously, returns non-zero and populates
+     creds_md, num_creds_md, status, and error_details.  In this case,
+     the caller takes ownership of the entries in creds_md and of
+     error_details.  Note that if the plugin needs to return more than
+     GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX entries in creds_md, it must
+     return asynchronously.
+
+     If processing occurs asynchronously, returns zero and invokes \a cb
+     when processing is completed.  \a user_data will be passed as the
+     first parameter of the callback.  NOTE: \a cb MUST be invoked in a
+     different thread, not from the thread in which \a get_metadata() is
+     invoked.
+
+     \a context is the information that can be used by the plugin to create
+     auth metadata. */
+  int (*get_metadata)(
+      void* state, grpc_auth_metadata_context context,
+      grpc_credentials_plugin_metadata_cb cb, void* user_data,
+      grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+      size_t* num_creds_md, grpc_status_code* status,
+      const char** error_details);
+
+  /** Destroys the plugin state. */
+  void (*destroy)(void* state);
+
+  /** State that will be set as the first parameter of the methods above. */
+  void* state;
+
+  /** Type of credentials that this plugin is implementing. */
+  const char* type;
+} grpc_metadata_credentials_plugin;
+
+/** Creates a credentials object from a plugin. */
+GRPCAPI grpc_call_credentials* grpc_metadata_credentials_create_from_plugin(
+    grpc_metadata_credentials_plugin plugin, void* reserved);
+
+/** --- Secure channel creation. --- */
+
+/** Creates a secure channel using the passed-in credentials. Additional
+    channel level configuration MAY be provided by grpc_channel_args, though
+    the expectation is that most clients will want to simply pass NULL. The
+    user data in 'args' need only live through the invocation of this function.
+    However, if any args of the 'pointer' type are passed, then the referenced
+    vtable must be maintained by the caller until grpc_channel_destroy
+    terminates. See grpc_channel_args definition for more on this. */
+GRPCAPI grpc_channel* grpc_secure_channel_create(
+    grpc_channel_credentials* creds, const char* target,
+    const grpc_channel_args* args, void* reserved);
+
+/** --- grpc_server_credentials object. ---
+
+   A server credentials object represents a way to authenticate a server.  */
+
+typedef struct grpc_server_credentials grpc_server_credentials;
+
+/** Releases a server_credentials object.
+   The creator of the server_credentials object is responsible for its release.
+   */
+GRPCAPI void grpc_server_credentials_release(grpc_server_credentials* creds);
+
+/** Server certificate config object holds the server's public certificates and
+   associated private keys, as well as any CA certificates needed for client
+   certificate validation (if applicable). Create using
+   grpc_ssl_server_certificate_config_create(). */
+typedef struct grpc_ssl_server_certificate_config
+    grpc_ssl_server_certificate_config;
+
+/** Creates a grpc_ssl_server_certificate_config object.
+   - pem_roots_cert is the NULL-terminated string containing the PEM encoding of
+     the client root certificates. This parameter may be NULL if the server does
+     not want the client to be authenticated with SSL.
+   - pem_key_cert_pairs is an array private key / certificate chains of the
+     server. This parameter cannot be NULL.
+   - num_key_cert_pairs indicates the number of items in the private_key_files
+     and cert_chain_files parameters. It must be at least 1.
+   - It is the caller's responsibility to free this object via
+     grpc_ssl_server_certificate_config_destroy(). */
+GRPCAPI grpc_ssl_server_certificate_config*
+grpc_ssl_server_certificate_config_create(
+    const char* pem_root_certs,
+    const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs);
+
+/** Destroys a grpc_ssl_server_certificate_config object. */
+GRPCAPI void grpc_ssl_server_certificate_config_destroy(
+    grpc_ssl_server_certificate_config* config);
+
+/** Callback to retrieve updated SSL server certificates, private keys, and
+   trusted CAs (for client authentication).
+    - user_data parameter, if not NULL, contains opaque data to be used by the
+      callback.
+    - Use grpc_ssl_server_certificate_config_create to create the config.
+    - The caller assumes ownership of the config. */
+typedef grpc_ssl_certificate_config_reload_status (
+    *grpc_ssl_server_certificate_config_callback)(
+    void* user_data, grpc_ssl_server_certificate_config** config);
+
+/** Deprecated in favor of grpc_ssl_server_credentials_create_ex.
+   Creates an SSL server_credentials object.
+   - pem_roots_cert is the NULL-terminated string containing the PEM encoding of
+     the client root certificates. This parameter may be NULL if the server does
+     not want the client to be authenticated with SSL.
+   - pem_key_cert_pairs is an array private key / certificate chains of the
+     server. This parameter cannot be NULL.
+   - num_key_cert_pairs indicates the number of items in the private_key_files
+     and cert_chain_files parameters. It should be at least 1.
+   - force_client_auth, if set to non-zero will force the client to authenticate
+     with an SSL cert. Note that this option is ignored if pem_root_certs is
+     NULL. */
+GRPCAPI grpc_server_credentials* grpc_ssl_server_credentials_create(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs, int force_client_auth, void* reserved);
+
+/** Deprecated in favor of grpc_ssl_server_credentials_create_with_options.
+   Same as grpc_ssl_server_credentials_create method except uses
+   grpc_ssl_client_certificate_request_type enum to support more ways to
+   authenticate client cerificates.*/
+GRPCAPI grpc_server_credentials* grpc_ssl_server_credentials_create_ex(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs,
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    void* reserved);
+
+typedef struct grpc_ssl_server_credentials_options
+    grpc_ssl_server_credentials_options;
+
+/** Creates an options object using a certificate config. Use this method when
+   the certificates and keys of the SSL server will not change during the
+   server's lifetime.
+   - Takes ownership of the certificate_config parameter. */
+GRPCAPI grpc_ssl_server_credentials_options*
+grpc_ssl_server_credentials_create_options_using_config(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config* certificate_config);
+
+/** Creates an options object using a certificate config fetcher. Use this
+   method to reload the certificates and keys of the SSL server without
+   interrupting the operation of the server. Initial certificate config will be
+   fetched during server initialization.
+   - user_data parameter, if not NULL, contains opaque data which will be passed
+     to the fetcher (see definition of
+     grpc_ssl_server_certificate_config_callback). */
+GRPCAPI grpc_ssl_server_credentials_options*
+grpc_ssl_server_credentials_create_options_using_config_fetcher(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config_callback cb, void* user_data);
+
+/** Destroys a grpc_ssl_server_credentials_options object. */
+GRPCAPI void grpc_ssl_server_credentials_options_destroy(
+    grpc_ssl_server_credentials_options* options);
+
+/** Creates an SSL server_credentials object using the provided options struct.
+    - Takes ownership of the options parameter. */
+GRPCAPI grpc_server_credentials*
+grpc_ssl_server_credentials_create_with_options(
+    grpc_ssl_server_credentials_options* options);
+
+/** --- Server-side secure ports. --- */
+
+/** Add a HTTP2 over an encrypted link over tcp listener.
+   Returns bound port number on success, 0 on failure.
+   REQUIRES: server not started */
+GRPCAPI int grpc_server_add_secure_http2_port(grpc_server* server,
+                                              const char* addr,
+                                              grpc_server_credentials* creds);
+
+/** --- Call specific credentials. --- */
+
+/** Sets a credentials to a call. Can only be called on the client side before
+   grpc_call_start_batch. */
+GRPCAPI grpc_call_error grpc_call_set_credentials(grpc_call* call,
+                                                  grpc_call_credentials* creds);
+
+/** --- Auth Metadata Processing --- */
+
+/** Callback function that is called when the metadata processing is done.
+   - Consumed metadata will be removed from the set of metadata available on the
+     call. consumed_md may be NULL if no metadata has been consumed.
+   - Response metadata will be set on the response. response_md may be NULL.
+   - status is GRPC_STATUS_OK for success or a specific status for an error.
+     Common error status for auth metadata processing is either
+     GRPC_STATUS_UNAUTHENTICATED in case of an authentication failure or
+     GRPC_STATUS PERMISSION_DENIED in case of an authorization failure.
+   - error_details gives details about the error. May be NULL. */
+typedef void (*grpc_process_auth_metadata_done_cb)(
+    void* user_data, const grpc_metadata* consumed_md, size_t num_consumed_md,
+    const grpc_metadata* response_md, size_t num_response_md,
+    grpc_status_code status, const char* error_details);
+
+/** Pluggable server-side metadata processor object. */
+typedef struct {
+  /** The context object is read/write: it contains the properties of the
+     channel peer and it is the job of the process function to augment it with
+     properties derived from the passed-in metadata.
+     The lifetime of these objects is guaranteed until cb is invoked. */
+  void (*process)(void* state, grpc_auth_context* context,
+                  const grpc_metadata* md, size_t num_md,
+                  grpc_process_auth_metadata_done_cb cb, void* user_data);
+  void (*destroy)(void* state);
+  void* state;
+} grpc_auth_metadata_processor;
+
+GRPCAPI void grpc_server_credentials_set_auth_metadata_processor(
+    grpc_server_credentials* creds, grpc_auth_metadata_processor processor);
+
+/** --- ALTS channel/server credentials --- **/
+
+/**
+ * Main interface for ALTS credentials options. The options will contain
+ * information that will be passed from grpc to TSI layer such as RPC protocol
+ * versions. ALTS client (channel) and server credentials will have their own
+ * implementation of this interface. The APIs listed in this header are
+ * thread-compatible. It is used for experimental purpose for now and subject
+ * to change.
+ */
+typedef struct grpc_alts_credentials_options grpc_alts_credentials_options;
+
+/**
+ * This method creates a grpc ALTS credentials client options instance.
+ * It is used for experimental purpose for now and subject to change.
+ */
+GRPCAPI grpc_alts_credentials_options*
+grpc_alts_credentials_client_options_create(void);
+
+/**
+ * This method creates a grpc ALTS credentials server options instance.
+ * It is used for experimental purpose for now and subject to change.
+ */
+GRPCAPI grpc_alts_credentials_options*
+grpc_alts_credentials_server_options_create(void);
+
+/**
+ * This method adds a target service account to grpc client's ALTS credentials
+ * options instance. It is used for experimental purpose for now and subject
+ * to change.
+ *
+ * - options: grpc ALTS credentials options instance.
+ * - service_account: service account of target endpoint.
+ */
+GRPCAPI void grpc_alts_credentials_client_options_add_target_service_account(
+    grpc_alts_credentials_options* options, const char* service_account);
+
+/**
+ * This method destroys a grpc_alts_credentials_options instance by
+ * de-allocating all of its occupied memory. It is used for experimental purpose
+ * for now and subject to change.
+ *
+ * - options: a grpc_alts_credentials_options instance that needs to be
+ *   destroyed.
+ */
+GRPCAPI void grpc_alts_credentials_options_destroy(
+    grpc_alts_credentials_options* options);
+
+/**
+ * This method creates an ALTS channel credential object. It is used for
+ * experimental purpose for now and subject to change.
+ *
+ * - options: grpc ALTS credentials options instance for client.
+ *
+ * It returns the created ALTS channel credential object.
+ */
+GRPCAPI grpc_channel_credentials* grpc_alts_credentials_create(
+    const grpc_alts_credentials_options* options);
+
+/**
+ * This method creates an ALTS server credential object. It is used for
+ * experimental purpose for now and subject to change.
+ *
+ * - options: grpc ALTS credentials options instance for server.
+ *
+ * It returns the created ALTS server credential object.
+ */
+GRPCAPI grpc_server_credentials* grpc_alts_server_credentials_create(
+    const grpc_alts_credentials_options* options);
+
+/** --- Local channel/server credentials --- **/
+
+/**
+ * This method creates a local channel credential object. It is used for
+ * experimental purpose for now and subject to change.
+ *
+ * - type: local connection type
+ *
+ * It returns the created local channel credential object.
+ */
+GRPCAPI grpc_channel_credentials* grpc_local_credentials_create(
+    grpc_local_connect_type type);
+
+/**
+ * This method creates a local server credential object. It is used for
+ * experimental purpose for now and subject to change.
+ *
+ * - type: local connection type
+ *
+ * It returns the created local server credential object.
+ */
+GRPCAPI grpc_server_credentials* grpc_local_server_credentials_create(
+    grpc_local_connect_type type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_GRPC_SECURITY_H */
diff --git a/third_party/grpc/include/grpc/grpc_security_constants.h b/third_party/grpc/include/grpc/grpc_security_constants.h
new file mode 100644
index 0000000..a082f67
--- /dev/null
+++ b/third_party/grpc/include/grpc/grpc_security_constants.h
@@ -0,0 +1,118 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_GRPC_SECURITY_CONSTANTS_H
+#define GRPC_GRPC_SECURITY_CONSTANTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME "transport_security_type"
+#define GRPC_SSL_TRANSPORT_SECURITY_TYPE "ssl"
+
+#define GRPC_X509_CN_PROPERTY_NAME "x509_common_name"
+#define GRPC_X509_SAN_PROPERTY_NAME "x509_subject_alternative_name"
+#define GRPC_X509_PEM_CERT_PROPERTY_NAME "x509_pem_cert"
+#define GRPC_SSL_SESSION_REUSED_PROPERTY "ssl_session_reused"
+
+/** Environment variable that points to the default SSL roots file. This file
+   must be a PEM encoded file with all the roots such as the one that can be
+   downloaded from https://pki.google.com/roots.pem.  */
+#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \
+  "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"
+
+/** Environment variable that points to the google default application
+   credentials json key or refresh token. Used in the
+   grpc_google_default_credentials_create function. */
+#define GRPC_GOOGLE_CREDENTIALS_ENV_VAR "GOOGLE_APPLICATION_CREDENTIALS"
+
+/** Results for the SSL roots override callback. */
+typedef enum {
+  GRPC_SSL_ROOTS_OVERRIDE_OK,
+  GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY, /** Do not try fallback options. */
+  GRPC_SSL_ROOTS_OVERRIDE_FAIL
+} grpc_ssl_roots_override_result;
+
+/** Callback results for dynamically loading a SSL certificate config. */
+typedef enum {
+  GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED,
+  GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW,
+  GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL
+} grpc_ssl_certificate_config_reload_status;
+
+typedef enum {
+  /** Server does not request client certificate.
+     The certificate presented by the client is not checked by the server at
+     all. (A client may present a self signed or signed certificate or not
+     present a certificate at all and any of those option would be accepted) */
+  GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+  /** Server requests client certificate but does not enforce that the client
+     presents a certificate.
+
+     If the client presents a certificate, the client authentication is left to
+     the application (the necessary metadata will be available to the
+     application via authentication context properties, see grpc_auth_context).
+
+     The client's key certificate pair must be valid for the SSL connection to
+     be established. */
+  GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+  /** Server requests client certificate but does not enforce that the client
+     presents a certificate.
+
+     If the client presents a certificate, the client authentication is done by
+     the gRPC framework. (For a successful connection the client needs to either
+     present a certificate that can be verified against the root certificate
+     configured by the server or not present a certificate at all)
+
+     The client's key certificate pair must be valid for the SSL connection to
+     be established. */
+  GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY,
+  /** Server requests client certificate and enforces that the client presents a
+     certificate.
+
+     If the client presents a certificate, the client authentication is left to
+     the application (the necessary metadata will be available to the
+     application via authentication context properties, see grpc_auth_context).
+
+     The client's key certificate pair must be valid for the SSL connection to
+     be established. */
+  GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+  /** Server requests client certificate and enforces that the client presents a
+     certificate.
+
+     The cerificate presented by the client is verified by the gRPC framework.
+     (For a successful connection the client needs to present a certificate that
+     can be verified against the root certificate configured by the server)
+
+     The client's key certificate pair must be valid for the SSL connection to
+     be established. */
+  GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+} grpc_ssl_client_certificate_request_type;
+
+/**
+ * Type of local connections for which local channel/server credentials will be
+ * applied. It supports UDS and local TCP connections.
+ */
+typedef enum { UDS = 0, LOCAL_TCP } grpc_local_connect_type;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_GRPC_SECURITY_CONSTANTS_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/atm.h b/third_party/grpc/include/grpc/impl/codegen/atm.h
new file mode 100644
index 0000000..00d83f0
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/atm.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_ATM_H
+#define GRPC_IMPL_CODEGEN_ATM_H
+
+/** This interface provides atomic operations and barriers.
+   It is internal to gpr support code and should not be used outside it.
+
+   If an operation with acquire semantics precedes another memory access by the
+   same thread, the operation will precede that other access as seen by other
+   threads.
+
+   If an operation with release semantics follows another memory access by the
+   same thread, the operation will follow that other access as seen by other
+   threads.
+
+   Routines with "acq" or "full" in the name have acquire semantics.  Routines
+   with "rel" or "full" in the name have release semantics.  Routines with
+   "no_barrier" in the name have neither acquire not release semantics.
+
+   The routines may be implemented as macros.
+
+   // Atomic operations act on an intergral_type gpr_atm that is guaranteed to
+   // be the same size as a pointer.
+   typedef intptr_t gpr_atm;
+
+   // A memory barrier, providing both acquire and release semantics, but not
+   // otherwise acting on memory.
+   void gpr_atm_full_barrier(void);
+
+   // Atomically return *p, with acquire semantics.
+   gpr_atm gpr_atm_acq_load(gpr_atm *p);
+   gpr_atm gpr_atm_no_barrier_load(gpr_atm *p);
+
+   // Atomically set *p = value, with release semantics.
+   void gpr_atm_rel_store(gpr_atm *p, gpr_atm value);
+
+   // Atomically add delta to *p, and return the old value of *p, with
+   // the barriers specified.
+   gpr_atm gpr_atm_no_barrier_fetch_add(gpr_atm *p, gpr_atm delta);
+   gpr_atm gpr_atm_full_fetch_add(gpr_atm *p, gpr_atm delta);
+
+   // Atomically, if *p==o, set *p=n and return non-zero otherwise return 0,
+   // with the barriers specified if the operation succeeds.
+   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);
+   int gpr_atm_full_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>
+
+#if defined(GPR_GCC_ATOMIC)
+#include <grpc/impl/codegen/atm_gcc_atomic.h>
+#elif defined(GPR_GCC_SYNC)
+#include <grpc/impl/codegen/atm_gcc_sync.h>
+#elif defined(GPR_WINDOWS_ATOMIC)
+#include <grpc/impl/codegen/atm_windows.h>
+#else
+#error could not determine platform for atm
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Adds \a delta to \a *value, clamping the result to the range specified
+    by \a min and \a max.  Returns the new value. */
+gpr_atm gpr_atm_no_barrier_clamped_add(gpr_atm* value, gpr_atm delta,
+                                       gpr_atm min, gpr_atm max);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_ATM_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/atm_gcc_atomic.h b/third_party/grpc/include/grpc/impl/codegen/atm_gcc_atomic.h
new file mode 100644
index 0000000..5879708
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/atm_gcc_atomic.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_ATM_GCC_ATOMIC_H
+#define GRPC_IMPL_CODEGEN_ATM_GCC_ATOMIC_H
+
+/* atm_platform.h for gcc and gcc-like compilers with the
+   __atomic_* interface.  */
+#include <grpc/impl/codegen/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef intptr_t gpr_atm;
+#define GPR_ATM_MAX INTPTR_MAX
+#define GPR_ATM_MIN INTPTR_MIN
+
+#ifdef GPR_LOW_LEVEL_COUNTERS
+extern gpr_atm gpr_counter_atm_cas;
+extern gpr_atm gpr_counter_atm_add;
+#define GPR_ATM_INC_COUNTER(counter) \
+  __atomic_fetch_add(&counter, 1, __ATOMIC_RELAXED)
+#define GPR_ATM_INC_CAS_THEN(blah) \
+  (GPR_ATM_INC_COUNTER(gpr_counter_atm_cas), blah)
+#define GPR_ATM_INC_ADD_THEN(blah) \
+  (GPR_ATM_INC_COUNTER(gpr_counter_atm_add), blah)
+#else
+#define GPR_ATM_INC_CAS_THEN(blah) blah
+#define GPR_ATM_INC_ADD_THEN(blah) blah
+#endif
+
+#define gpr_atm_full_barrier() (__atomic_thread_fence(__ATOMIC_SEQ_CST))
+
+#define gpr_atm_acq_load(p) (__atomic_load_n((p), __ATOMIC_ACQUIRE))
+#define gpr_atm_no_barrier_load(p) (__atomic_load_n((p), __ATOMIC_RELAXED))
+#define gpr_atm_rel_store(p, value) \
+  (__atomic_store_n((p), (intptr_t)(value), __ATOMIC_RELEASE))
+#define gpr_atm_no_barrier_store(p, value) \
+  (__atomic_store_n((p), (intptr_t)(value), __ATOMIC_RELAXED))
+
+#define gpr_atm_no_barrier_fetch_add(p, delta) \
+  GPR_ATM_INC_ADD_THEN(                        \
+      __atomic_fetch_add((p), (intptr_t)(delta), __ATOMIC_RELAXED))
+#define gpr_atm_full_fetch_add(p, delta) \
+  GPR_ATM_INC_ADD_THEN(                  \
+      __atomic_fetch_add((p), (intptr_t)(delta), __ATOMIC_ACQ_REL))
+
+static __inline int gpr_atm_no_barrier_cas(gpr_atm* p, gpr_atm o, gpr_atm n) {
+  return GPR_ATM_INC_CAS_THEN(__atomic_compare_exchange_n(
+      p, &o, n, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED));
+}
+
+static __inline int gpr_atm_acq_cas(gpr_atm* p, gpr_atm o, gpr_atm n) {
+  return GPR_ATM_INC_CAS_THEN(__atomic_compare_exchange_n(
+      p, &o, n, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED));
+}
+
+static __inline int gpr_atm_rel_cas(gpr_atm* p, gpr_atm o, gpr_atm n) {
+  return GPR_ATM_INC_CAS_THEN(__atomic_compare_exchange_n(
+      p, &o, n, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED));
+}
+
+static __inline int gpr_atm_full_cas(gpr_atm* p, gpr_atm o, gpr_atm n) {
+  return GPR_ATM_INC_CAS_THEN(__atomic_compare_exchange_n(
+      p, &o, n, 0, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED));
+}
+
+#define gpr_atm_full_xchg(p, n) \
+  GPR_ATM_INC_CAS_THEN(__atomic_exchange_n((p), (n), __ATOMIC_ACQ_REL))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_ATM_GCC_ATOMIC_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/atm_gcc_sync.h b/third_party/grpc/include/grpc/impl/codegen/atm_gcc_sync.h
new file mode 100644
index 0000000..728c3d5
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/atm_gcc_sync.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_ATM_GCC_SYNC_H
+#define GRPC_IMPL_CODEGEN_ATM_GCC_SYNC_H
+
+/* variant of atm_platform.h for gcc and gcc-like compiers with __sync_*
+   interface */
+#include <grpc/impl/codegen/port_platform.h>
+
+typedef intptr_t gpr_atm;
+#define GPR_ATM_MAX INTPTR_MAX
+#define GPR_ATM_MIN INTPTR_MIN
+#define GPR_ATM_INC_CAS_THEN(blah) blah
+#define GPR_ATM_INC_ADD_THEN(blah) blah
+
+#define GPR_ATM_COMPILE_BARRIER_() __asm__ __volatile__("" : : : "memory")
+
+#if defined(__i386) || defined(__x86_64__)
+/* All loads are acquire loads and all stores are release stores.  */
+#define GPR_ATM_LS_BARRIER_() GPR_ATM_COMPILE_BARRIER_()
+#else
+#define GPR_ATM_LS_BARRIER_() gpr_atm_full_barrier()
+#endif
+
+#define gpr_atm_full_barrier() (__sync_synchronize())
+
+static __inline gpr_atm gpr_atm_acq_load(const gpr_atm* p) {
+  gpr_atm value = *p;
+  GPR_ATM_LS_BARRIER_();
+  return value;
+}
+
+static __inline gpr_atm gpr_atm_no_barrier_load(const gpr_atm* p) {
+  gpr_atm value = *p;
+  GPR_ATM_COMPILE_BARRIER_();
+  return value;
+}
+
+static __inline void gpr_atm_rel_store(gpr_atm* p, gpr_atm value) {
+  GPR_ATM_LS_BARRIER_();
+  *p = value;
+}
+
+static __inline void gpr_atm_no_barrier_store(gpr_atm* p, gpr_atm value) {
+  GPR_ATM_COMPILE_BARRIER_();
+  *p = value;
+}
+
+#undef GPR_ATM_LS_BARRIER_
+#undef GPR_ATM_COMPILE_BARRIER_
+
+#define gpr_atm_no_barrier_fetch_add(p, delta) \
+  gpr_atm_full_fetch_add((p), (delta))
+#define gpr_atm_full_fetch_add(p, delta) (__sync_fetch_and_add((p), (delta)))
+
+#define gpr_atm_no_barrier_cas(p, o, n) gpr_atm_acq_cas((p), (o), (n))
+#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))
+#define gpr_atm_full_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/third_party/grpc/include/grpc/impl/codegen/atm_windows.h b/third_party/grpc/include/grpc/impl/codegen/atm_windows.h
new file mode 100644
index 0000000..c016b90
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/atm_windows.h
@@ -0,0 +1,128 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_ATM_WINDOWS_H
+#define GRPC_IMPL_CODEGEN_ATM_WINDOWS_H
+
+/** Win32 variant of atm_platform.h */
+#include <grpc/impl/codegen/port_platform.h>
+
+typedef intptr_t gpr_atm;
+#define GPR_ATM_MAX INTPTR_MAX
+#define GPR_ATM_MIN INTPTR_MIN
+#define GPR_ATM_INC_CAS_THEN(blah) blah
+#define GPR_ATM_INC_ADD_THEN(blah) blah
+
+#define gpr_atm_full_barrier MemoryBarrier
+
+static __inline gpr_atm gpr_atm_acq_load(const gpr_atm* p) {
+  gpr_atm result = *p;
+  gpr_atm_full_barrier();
+  return result;
+}
+
+static __inline gpr_atm gpr_atm_no_barrier_load(const gpr_atm* p) {
+  /* TODO(dklempner): Can we implement something better here? */
+  return gpr_atm_acq_load(p);
+}
+
+static __inline void gpr_atm_rel_store(gpr_atm* p, gpr_atm value) {
+  gpr_atm_full_barrier();
+  *p = value;
+}
+
+static __inline void gpr_atm_no_barrier_store(gpr_atm* p, gpr_atm value) {
+  /* TODO(ctiller): Can we implement something better here? */
+  gpr_atm_rel_store(p, value);
+}
+
+static __inline int gpr_atm_no_barrier_cas(gpr_atm* p, gpr_atm o, gpr_atm n) {
+/** InterlockedCompareExchangePointerNoFence() not available on vista or
+   windows7 */
+#ifdef GPR_ARCH_64
+  return o == (gpr_atm)InterlockedCompareExchangeAcquire64(
+                  (volatile LONGLONG*)p, (LONGLONG)n, (LONGLONG)o);
+#else
+  return o == (gpr_atm)InterlockedCompareExchangeAcquire((volatile LONG*)p,
+                                                         (LONG)n, (LONG)o);
+#endif
+}
+
+static __inline int gpr_atm_acq_cas(gpr_atm* p, gpr_atm o, gpr_atm n) {
+#ifdef GPR_ARCH_64
+  return o == (gpr_atm)InterlockedCompareExchangeAcquire64(
+                  (volatile LONGLONG*)p, (LONGLONG)n, (LONGLONG)o);
+#else
+  return o == (gpr_atm)InterlockedCompareExchangeAcquire((volatile LONG*)p,
+                                                         (LONG)n, (LONG)o);
+#endif
+}
+
+static __inline int gpr_atm_rel_cas(gpr_atm* p, gpr_atm o, gpr_atm n) {
+#ifdef GPR_ARCH_64
+  return o == (gpr_atm)InterlockedCompareExchangeRelease64(
+                  (volatile LONGLONG*)p, (LONGLONG)n, (LONGLONG)o);
+#else
+  return o == (gpr_atm)InterlockedCompareExchangeRelease((volatile LONG*)p,
+                                                         (LONG)n, (LONG)o);
+#endif
+}
+
+static __inline int gpr_atm_full_cas(gpr_atm* p, gpr_atm o, gpr_atm n) {
+#ifdef GPR_ARCH_64
+  return o == (gpr_atm)InterlockedCompareExchange64((volatile LONGLONG*)p,
+                                                    (LONGLONG)n, (LONGLONG)o);
+#else
+  return o == (gpr_atm)InterlockedCompareExchange((volatile LONG*)p, (LONG)n,
+                                                  (LONG)o);
+#endif
+}
+
+static __inline gpr_atm gpr_atm_no_barrier_fetch_add(gpr_atm* p,
+                                                     gpr_atm delta) {
+  /** Use the CAS operation to get pointer-sized fetch and add */
+  gpr_atm old;
+  do {
+    old = *p;
+  } while (!gpr_atm_no_barrier_cas(p, old, old + delta));
+  return old;
+}
+
+static __inline gpr_atm gpr_atm_full_fetch_add(gpr_atm* p, gpr_atm delta) {
+  /** Use a CAS operation to get pointer-sized fetch and add */
+  gpr_atm old;
+#ifdef GPR_ARCH_64
+  do {
+    old = *p;
+  } while (old != (gpr_atm)InterlockedCompareExchange64((volatile LONGLONG*)p,
+                                                        (LONGLONG)old + delta,
+                                                        (LONGLONG)old));
+#else
+  do {
+    old = *p;
+  } while (old != (gpr_atm)InterlockedCompareExchange(
+                      (volatile LONG*)p, (LONG)old + delta, (LONG)old));
+#endif
+  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/third_party/grpc/include/grpc/impl/codegen/byte_buffer.h b/third_party/grpc/include/grpc/impl/codegen/byte_buffer.h
new file mode 100644
index 0000000..774655e
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/byte_buffer.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_BYTE_BUFFER_H
+#define GRPC_IMPL_CODEGEN_BYTE_BUFFER_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.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(grpc_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(
+    grpc_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
+ * grpc_slice_unref on the result. */
+GRPCAPI int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
+                                         grpc_slice* slice);
+
+/** Merge all data from \a reader into single slice */
+GRPCAPI grpc_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/third_party/grpc/include/grpc/impl/codegen/byte_buffer_reader.h b/third_party/grpc/include/grpc/impl/codegen/byte_buffer_reader.h
new file mode 100644
index 0000000..e06e195
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/byte_buffer_reader.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_BYTE_BUFFER_READER_H
+#define GRPC_IMPL_CODEGEN_BYTE_BUFFER_READER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct grpc_byte_buffer;
+
+struct grpc_byte_buffer_reader {
+  struct grpc_byte_buffer* buffer_in;
+  struct grpc_byte_buffer* buffer_out;
+  /** Different current objects correspond to different types of byte buffers */
+  union grpc_byte_buffer_reader_current {
+    /** Index into a slice buffer's array of slices */
+    unsigned index;
+  } current;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_BYTE_BUFFER_READER_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/compression_types.h b/third_party/grpc/include/grpc/impl/codegen/compression_types.h
new file mode 100644
index 0000000..f778b00
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/compression_types.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_COMPRESSION_TYPES_H
+#define GRPC_IMPL_CODEGEN_COMPRESSION_TYPES_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** To be used as initial metadata key for the request of a concrete compression
+ * algorithm */
+#define GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY \
+  "grpc-internal-encoding-request"
+
+/** To be used in channel arguments.
+ *
+ * \addtogroup grpc_arg_keys
+ * \{ */
+/** Default compression algorithm for the channel.
+ * Its value is an int from the \a grpc_compression_algorithm enum. */
+#define GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM \
+  "grpc.default_compression_algorithm"
+/** Default compression level for the channel.
+ * Its value is an int from the \a grpc_compression_level enum. */
+#define GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL "grpc.default_compression_level"
+/** Compression algorithms supported by the channel.
+ * Its value is a bitset (an int). Bits correspond to algorithms in \a
+ * grpc_compression_algorithm. For example, its LSB corresponds to
+ * GRPC_COMPRESS_NONE, the next bit to GRPC_COMPRESS_DEFLATE, etc.
+ * Unset bits disable support for the algorithm. By default all algorithms are
+ * supported. It's not possible to disable GRPC_COMPRESS_NONE (the attempt will
+ * be ignored). */
+#define GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET \
+  "grpc.compression_enabled_algorithms_bitset"
+/** \} */
+
+/** The various compression algorithms supported by gRPC (not sorted by
+ * compression level) */
+typedef enum {
+  GRPC_COMPRESS_NONE = 0,
+  GRPC_COMPRESS_DEFLATE,
+  GRPC_COMPRESS_GZIP,
+  /* EXPERIMENTAL: Stream compression is currently experimental. */
+  GRPC_COMPRESS_STREAM_GZIP,
+  /* TODO(ctiller): snappy */
+  GRPC_COMPRESS_ALGORITHMS_COUNT
+} grpc_compression_algorithm;
+
+/** Compression levels allow a party with knowledge of its peer's accepted
+ * encodings to request compression in an abstract way. The level-algorithm
+ * mapping is performed internally and depends on the peer's supported
+ * compression algorithms. */
+typedef enum {
+  GRPC_COMPRESS_LEVEL_NONE = 0,
+  GRPC_COMPRESS_LEVEL_LOW,
+  GRPC_COMPRESS_LEVEL_MED,
+  GRPC_COMPRESS_LEVEL_HIGH,
+  GRPC_COMPRESS_LEVEL_COUNT
+} grpc_compression_level;
+
+typedef struct grpc_compression_options {
+  /** All algs are enabled by default. This option corresponds to the channel
+   * argument key behind \a GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET
+   */
+  uint32_t enabled_algorithms_bitset;
+
+  /** The default compression level. It'll be used in the absence of call
+   * specific settings. This option corresponds to the channel
+   * argument key behind \a GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL. If present,
+   * takes precedence over \a default_algorithm.
+   * TODO(dgq): currently only available for server channels. */
+  struct grpc_compression_options_default_level {
+    int is_set;
+    grpc_compression_level level;
+  } default_level;
+
+  /** The default message compression algorithm. It'll be used in the absence of
+   * call specific settings. This option corresponds to the channel argument key
+   * behind \a GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM. */
+  struct grpc_compression_options_default_algorithm {
+    int is_set;
+    grpc_compression_algorithm algorithm;
+  } default_algorithm;
+} grpc_compression_options;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_COMPRESSION_TYPES_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/connectivity_state.h b/third_party/grpc/include/grpc/impl/codegen/connectivity_state.h
new file mode 100644
index 0000000..b70dbef
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/connectivity_state.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_CONNECTIVITY_STATE_H
+#define GRPC_IMPL_CODEGEN_CONNECTIVITY_STATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Connectivity state of a channel. */
+typedef enum {
+  /** channel is idle */
+  GRPC_CHANNEL_IDLE,
+  /** channel is connecting */
+  GRPC_CHANNEL_CONNECTING,
+  /** channel is ready for work */
+  GRPC_CHANNEL_READY,
+  /** channel has seen a failure but expects to recover */
+  GRPC_CHANNEL_TRANSIENT_FAILURE,
+  /** channel has seen a failure that it cannot recover from */
+  GRPC_CHANNEL_SHUTDOWN
+} grpc_connectivity_state;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_CONNECTIVITY_STATE_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/fork.h b/third_party/grpc/include/grpc/impl/codegen/fork.h
new file mode 100644
index 0000000..555df34
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/fork.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_FORK_H
+#define GRPC_IMPL_CODEGEN_FORK_H
+
+/**
+ * gRPC applications should call this before calling fork().  There should be no
+ * active gRPC function calls between calling grpc_prefork() and
+ * grpc_postfork_parent()/grpc_postfork_child().
+ *
+ *
+ * Typical use:
+ * grpc_prefork();
+ * int pid = fork();
+ * if (pid) {
+ *  grpc_postfork_parent();
+ *  // Parent process..
+ * } else {
+ *  grpc_postfork_child();
+ *  // Child process...
+ * }
+ */
+
+void grpc_prefork(void);
+
+void grpc_postfork_parent(void);
+
+void grpc_postfork_child(void);
+
+void grpc_fork_handlers_auto_register(void);
+
+#endif /* GRPC_IMPL_CODEGEN_FORK_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/gpr_slice.h b/third_party/grpc/include/grpc/impl/codegen/gpr_slice.h
new file mode 100644
index 0000000..89fa72d
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/gpr_slice.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+#ifndef GRPC_IMPL_CODEGEN_GPR_SLICE_H
+#define GRPC_IMPL_CODEGEN_GPR_SLICE_H
+
+/** WARNING: Please do not use this header. This was added as a temporary
+ * measure to not break some of the external projects that depend on
+ * gpr_slice_* functions. We are actively working on moving all the
+ * gpr_slice_* references to grpc_slice_* and this file will be removed
+ */
+
+/* TODO (sreek) - Allowed by default but will be very soon turned off */
+#define GRPC_ALLOW_GPR_SLICE_FUNCTIONS 1
+
+#ifdef GRPC_ALLOW_GPR_SLICE_FUNCTIONS
+
+#define gpr_slice_refcount grpc_slice_refcount
+#define gpr_slice grpc_slice
+#define gpr_slice_buffer grpc_slice_buffer
+
+#define gpr_slice_ref grpc_slice_ref
+#define gpr_slice_unref grpc_slice_unref
+#define gpr_slice_new grpc_slice_new
+#define gpr_slice_new_with_user_data grpc_slice_new_with_user_data
+#define gpr_slice_new_with_len grpc_slice_new_with_len
+#define gpr_slice_malloc grpc_slice_malloc
+#define gpr_slice_from_copied_string grpc_slice_from_copied_string
+#define gpr_slice_from_copied_buffer grpc_slice_from_copied_buffer
+#define gpr_slice_from_static_string grpc_slice_from_static_string
+#define gpr_slice_sub grpc_slice_sub
+#define gpr_slice_sub_no_ref grpc_slice_sub_no_ref
+#define gpr_slice_split_tail grpc_slice_split_tail
+#define gpr_slice_split_head grpc_slice_split_head
+#define gpr_slice_cmp grpc_slice_cmp
+#define gpr_slice_str_cmp grpc_slice_str_cmp
+
+#define gpr_slice_buffer grpc_slice_buffer
+#define gpr_slice_buffer_init grpc_slice_buffer_init
+#define gpr_slice_buffer_destroy grpc_slice_buffer_destroy
+#define gpr_slice_buffer_add grpc_slice_buffer_add
+#define gpr_slice_buffer_add_indexed grpc_slice_buffer_add_indexed
+#define gpr_slice_buffer_addn grpc_slice_buffer_addn
+#define gpr_slice_buffer_tiny_add grpc_slice_buffer_tiny_add
+#define gpr_slice_buffer_pop grpc_slice_buffer_pop
+#define gpr_slice_buffer_reset_and_unref grpc_slice_buffer_reset_and_unref
+#define gpr_slice_buffer_swap grpc_slice_buffer_swap
+#define gpr_slice_buffer_move_into grpc_slice_buffer_move_into
+#define gpr_slice_buffer_trim_end grpc_slice_buffer_trim_end
+#define gpr_slice_buffer_move_first grpc_slice_buffer_move_first
+#define gpr_slice_buffer_take_first grpc_slice_buffer_take_first
+
+#endif /* GRPC_ALLOW_GPR_SLICE_FUNCTIONS */
+
+#endif /* GRPC_IMPL_CODEGEN_GPR_SLICE_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/gpr_types.h b/third_party/grpc/include/grpc/impl/codegen/gpr_types.h
new file mode 100644
index 0000000..d7bb545
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/gpr_types.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_GPR_TYPES_H
+#define GRPC_IMPL_CODEGEN_GPR_TYPES_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <stddef.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;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_GPR_TYPES_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/grpc_types.h b/third_party/grpc/include/grpc/impl/codegen/grpc_types.h
new file mode 100644
index 0000000..a9fb279
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/grpc_types.h
@@ -0,0 +1,716 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_GRPC_TYPES_H
+#define GRPC_IMPL_CODEGEN_GRPC_TYPES_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/impl/codegen/compression_types.h>
+#include <grpc/impl/codegen/gpr_types.h>
+#include <grpc/impl/codegen/slice.h>
+#include <grpc/impl/codegen/status.h>
+
+#include <stddef.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 grpc_byte_buffer_data {
+    struct /* internal */ {
+      void* reserved[8];
+    } reserved;
+    struct grpc_compressed_buffer {
+      grpc_compression_algorithm compression;
+      grpc_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;
+
+/** An alarm associated with a completion queue. */
+typedef struct grpc_alarm grpc_alarm;
+
+/** The Channel interface allows creation of Call objects. */
+typedef struct grpc_channel grpc_channel;
+
+/** A server listens to some port and responds to request calls */
+typedef struct grpc_server grpc_server;
+
+/** A Call represents an RPC. When created, it is in a configuration state
+    allowing properties to be set until it is invoked. After invoke, the Call
+    can have messages written to it and read from it. */
+typedef struct grpc_call grpc_call;
+
+/** The Socket Mutator interface allows changes on socket options */
+typedef struct grpc_socket_mutator grpc_socket_mutator;
+
+/** The Socket Factory interface creates and binds sockets */
+typedef struct grpc_socket_factory grpc_socket_factory;
+
+/** Type specifier for grpc_arg */
+typedef enum {
+  GRPC_ARG_STRING,
+  GRPC_ARG_INTEGER,
+  GRPC_ARG_POINTER
+} grpc_arg_type;
+
+typedef struct grpc_arg_pointer_vtable {
+  void* (*copy)(void* p);
+  void (*destroy)(void* p);
+  int (*cmp)(void* p, void* q);
+} grpc_arg_pointer_vtable;
+
+/** A single argument... each argument has a key and a value
+
+    A note on naming keys:
+      Keys are namespaced into groups, usually grouped by library, and are
+      keys for module XYZ are named XYZ.key1, XYZ.key2, etc. Module names must
+      be restricted to the regex [A-Za-z][_A-Za-z0-9]{,15}.
+      Key names must be restricted to the regex [A-Za-z][_A-Za-z0-9]{,47}.
+
+    GRPC core library keys are prefixed by grpc.
+
+    Library authors are strongly encouraged to \#define symbolic constants for
+    their keys so that it's possible to change them in the future. */
+typedef struct {
+  grpc_arg_type type;
+  char* key;
+  union grpc_arg_value {
+    char* string;
+    int integer;
+    struct grpc_arg_pointer {
+      void* p;
+      const grpc_arg_pointer_vtable* vtable;
+    } pointer;
+  } value;
+} grpc_arg;
+
+/** An array of arguments that can be passed around.
+
+    Used to set optional channel-level configuration.
+    These configuration options are modelled as key-value pairs as defined
+    by grpc_arg; keys are strings to allow easy backwards-compatible extension
+    by arbitrary parties. All evaluation is performed at channel creation
+    time (i.e. the keys and values in this structure need only live through the
+    creation invocation).
+
+    However, if one of the args has grpc_arg_type==GRPC_ARG_POINTER, then the
+    grpc_arg_pointer_vtable must live until the channel args are done being
+    used by core (i.e. when the object for use with which they were passed
+    is destroyed).
+
+    See the description of the \ref grpc_arg_keys "available args" for more
+    details. */
+typedef struct {
+  size_t num_args;
+  grpc_arg* args;
+} grpc_channel_args;
+
+/** \defgroup grpc_arg_keys
+ * Channel argument keys.
+ * \{
+ */
+/** If non-zero, enable census for tracing and stats collection. */
+#define GRPC_ARG_ENABLE_CENSUS "grpc.census"
+/** If non-zero, enable load reporting. */
+#define GRPC_ARG_ENABLE_LOAD_REPORTING "grpc.loadreporting"
+/** Request that optional features default to off (regardless of what they
+    usually default to) - to enable tight control over what gets enabled */
+#define GRPC_ARG_MINIMAL_STACK "grpc.minimal_stack"
+/** 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.
+    -1 means unlimited. */
+#define GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH "grpc.max_receive_message_length"
+/** \deprecated For backward compatibility.
+ * Use GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH instead. */
+#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"
+/** Maximum time that a channel may have no outstanding rpcs. Int valued,
+    milliseconds. INT_MAX means unlimited. */
+#define GRPC_ARG_MAX_CONNECTION_IDLE_MS "grpc.max_connection_idle_ms"
+/** Maximum time that a channel may exist. Int valued, milliseconds.
+ * INT_MAX means unlimited. */
+#define GRPC_ARG_MAX_CONNECTION_AGE_MS "grpc.max_connection_age_ms"
+/** Grace period after the chennel reaches its max age. Int valued,
+   milliseconds. INT_MAX means unlimited. */
+#define GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS "grpc.max_connection_age_grace_ms"
+/** Enable/disable support for per-message compression. Defaults to 1, unless
+    GRPC_ARG_MINIMAL_STACK is enabled, in which case it defaults to 0. */
+#define GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION "grpc.per_message_compression"
+/** Enable/disable support for deadline checking. Defaults to 1, unless
+    GRPC_ARG_MINIMAL_STACK is enabled, in which case it defaults to 0 */
+#define GRPC_ARG_ENABLE_DEADLINE_CHECKS "grpc.enable_deadline_checking"
+/** Initial stream ID for http2 transports. Int valued. */
+#define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \
+  "grpc.http2.initial_sequence_number"
+/** Amount to read ahead on individual streams. Defaults to 64kb, larger
+    values can help throughput on high-latency connections.
+    NOTE: at some point we'd like to auto-tune this, and this parameter
+    will become a no-op. Int valued, bytes. */
+#define GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES "grpc.http2.lookahead_bytes"
+/** How much memory to use for hpack decoding. Int valued, bytes. */
+#define GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER \
+  "grpc.http2.hpack_table_size.decoder"
+/** 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"
+/** Should BDP probing be performed? */
+#define GRPC_ARG_HTTP2_BDP_PROBE "grpc.http2.bdp_probe"
+/** Minimum time between sending successive ping frames without receiving any
+    data frame, Int valued, milliseconds. */
+#define GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS \
+  "grpc.http2.min_time_between_pings_ms"
+/** Minimum allowed time between a server receiving successive ping frames
+   without sending any data frame. Int valued, milliseconds */
+#define GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS \
+  "grpc.http2.min_ping_interval_without_data_ms"
+/** Channel arg to override the http2 :scheme header */
+#define GRPC_ARG_HTTP2_SCHEME "grpc.http2_scheme"
+/** How many pings can we send before needing to send a data frame or header
+    frame? (0 indicates that an infinite number of pings can be sent without
+    sending a data frame or header frame) */
+#define GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA \
+  "grpc.http2.max_pings_without_data"
+/** How many misbehaving pings the server can bear before sending goaway and
+    closing the transport? (0 indicates that the server can bear an infinite
+    number of misbehaving pings) */
+#define GRPC_ARG_HTTP2_MAX_PING_STRIKES "grpc.http2.max_ping_strikes"
+/** How much data are we willing to queue up per stream if
+    GRPC_WRITE_BUFFER_HINT is set? This is an upper bound */
+#define GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE "grpc.http2.write_buffer_size"
+/** Should we allow receipt of true-binary data on http2 connections?
+    Defaults to on (1) */
+#define GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY "grpc.http2.true_binary"
+/** After a duration of this time the client/server pings its peer to see if the
+    transport is still alive. Int valued, milliseconds. */
+#define GRPC_ARG_KEEPALIVE_TIME_MS "grpc.keepalive_time_ms"
+/** After waiting for a duration of this time, if the keepalive ping sender does
+    not receive the ping ack, it will close the transport. Int valued,
+    milliseconds. */
+#define GRPC_ARG_KEEPALIVE_TIMEOUT_MS "grpc.keepalive_timeout_ms"
+/** Is it permissible to send keepalive pings without any outstanding streams.
+    Int valued, 0(false)/1(true). */
+#define GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS \
+  "grpc.keepalive_permit_without_calls"
+/** Default authority to pass if none specified on call construction. A string.
+ * */
+#define GRPC_ARG_DEFAULT_AUTHORITY "grpc.default_authority"
+/** Primary user agent: goes at the start of the user-agent metadata
+    sent on each request. A string. */
+#define GRPC_ARG_PRIMARY_USER_AGENT_STRING "grpc.primary_user_agent"
+/** Secondary user agent: goes at the end of the user-agent metadata
+    sent on each request. A string. */
+#define GRPC_ARG_SECONDARY_USER_AGENT_STRING "grpc.secondary_user_agent"
+/** The minimum time between subsequent connection attempts, in ms */
+#define GRPC_ARG_MIN_RECONNECT_BACKOFF_MS "grpc.min_reconnect_backoff_ms"
+/** 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"
+/** Minimum amount of time between DNS resolutions, in ms */
+#define GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS \
+  "grpc.dns_min_time_between_resolutions_ms"
+/** The timeout used on servers for finishing handshaking on an incoming
+    connection.  Defaults to 120 seconds. */
+#define GRPC_ARG_SERVER_HANDSHAKE_TIMEOUT_MS "grpc.server_handshake_timeout_ms"
+/** This *should* be used for testing only.
+    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. If this argument is not specified, the name used
+    for SSL host name checking will be the target parameter (assuming that the
+    secure channel is an SSL channel). If this parameter is specified and the
+    underlying is not an SSL channel, it will just be ignored. */
+#define GRPC_SSL_TARGET_NAME_OVERRIDE_ARG "grpc.ssl_target_name_override"
+/** If non-zero, a pointer to a session cache (a pointer of type
+    grpc_ssl_session_cache*). (use grpc_ssl_session_cache_arg_vtable() to fetch
+    an appropriate pointer arg vtable) */
+#define GRPC_SSL_SESSION_CACHE_ARG "grpc.ssl_session_cache"
+/** Maximum metadata size, in bytes. Note this limit applies to the max sum of
+    all metadata key-value entries in a batch of headers. */
+#define GRPC_ARG_MAX_METADATA_SIZE "grpc.max_metadata_size"
+/** If non-zero, allow the use of SO_REUSEPORT if it's available (default 1) */
+#define GRPC_ARG_ALLOW_REUSEPORT "grpc.so_reuseport"
+/** If non-zero, a pointer to a buffer pool (a pointer of type
+ * grpc_resource_quota*). (use grpc_resource_quota_arg_vtable() to fetch an
+ * appropriate pointer arg vtable) */
+#define GRPC_ARG_RESOURCE_QUOTA "grpc.resource_quota"
+/** If non-zero, expand wildcard addresses to a list of local addresses. */
+#define GRPC_ARG_EXPAND_WILDCARD_ADDRS "grpc.expand_wildcard_addrs"
+/** Service config data in JSON form.
+    This value will be ignored if the name resolver returns a service config. */
+#define GRPC_ARG_SERVICE_CONFIG "grpc.service_config"
+/** Disable looking up the service config via the name resolver. */
+#define GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION \
+  "grpc.service_config_disable_resolution"
+/** LB policy name. */
+#define GRPC_ARG_LB_POLICY_NAME "grpc.lb_policy_name"
+/** The grpc_socket_mutator instance that set the socket options. A pointer. */
+#define GRPC_ARG_SOCKET_MUTATOR "grpc.socket_mutator"
+/** The grpc_socket_factory instance to create and bind sockets. A pointer. */
+#define GRPC_ARG_SOCKET_FACTORY "grpc.socket_factory"
+/** The maximum amount of memory used by trace events per channel trace node.
+ * Once the maximum is reached, subsequent events will evict the oldest events
+ * from the buffer. The unit for this knob is bytes. Setting it to zero causes
+ * channel tracing to be disabled. */
+#define GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE \
+  "grpc.max_channel_trace_event_memory_per_node"
+/** If non-zero, gRPC library will track stats and information at at per channel
+ * level. Disabling channelz naturally disables channel tracing. The default
+ * is for channelz to be enabled. */
+#define GRPC_ARG_ENABLE_CHANNELZ "grpc.enable_channelz"
+/** If non-zero, Cronet transport will coalesce packets to fewer frames
+ * when possible. */
+#define GRPC_ARG_USE_CRONET_PACKET_COALESCING \
+  "grpc.use_cronet_packet_coalescing"
+/** Channel arg (integer) setting how large a slice to try and read from the
+   wire each time recvmsg (or equivalent) is called **/
+#define GRPC_ARG_TCP_READ_CHUNK_SIZE "grpc.experimental.tcp_read_chunk_size"
+/** Note this is not a "channel arg" key. This is the default slice size to use
+ * when trying to read from the wire if the GRPC_ARG_TCP_READ_CHUNK_SIZE
+ * channel arg is unspecified. */
+#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192
+#define GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE \
+  "grpc.experimental.tcp_min_read_chunk_size"
+#define GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE \
+  "grpc.experimental.tcp_max_read_chunk_size"
+/* Timeout in milliseconds to use for calls to the grpclb load balancer.
+   If 0 or unset, the balancer calls will have no deadline. */
+#define GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS "grpc.grpclb_call_timeout_ms"
+/* Timeout in milliseconds to wait for the serverlist from the grpclb load
+   balancer before using fallback backend addresses from the resolver.
+   If 0, fallback will never be used. Default value is 10000. */
+#define GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS "grpc.grpclb_fallback_timeout_ms"
+/** If non-zero, grpc server's cronet compression workaround will be enabled */
+#define GRPC_ARG_WORKAROUND_CRONET_COMPRESSION \
+  "grpc.workaround.cronet_compression"
+/** String defining the optimization target for a channel.
+    Can be: "latency"    - attempt to minimize latency at the cost of throughput
+            "blend"      - try to balance latency and throughput
+            "throughput" - attempt to maximize throughput at the expense of
+                           latency
+    Defaults to "blend". In the current implementation "blend" is equivalent to
+    "latency". */
+#define GRPC_ARG_OPTIMIZATION_TARGET "grpc.optimization_target"
+/** If set to zero, disables retry behavior. Otherwise, transparent retries
+    are enabled for all RPCs, and configurable retries are enabled when they
+    are configured via the service config. For details, see:
+      https://github.com/grpc/proposal/blob/master/A6-client-retries.md
+ */
+#define GRPC_ARG_ENABLE_RETRIES "grpc.enable_retries"
+/** Per-RPC retry buffer size, in bytes. Default is 256 KiB. */
+#define GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE "grpc.per_rpc_retry_buffer_size"
+/** Channel arg that carries the bridged objective c object for custom metrics
+ * logging filter. */
+#define GRPC_ARG_MOBILE_LOG_CONTEXT "grpc.mobile_log_context"
+/** If non-zero, client authority filter is disabled for the channel */
+#define GRPC_ARG_DISABLE_CLIENT_AUTHORITY_FILTER \
+  "grpc.disable_client_authority_filter"
+/** If set to zero, disables use of http proxies. Enabled by default. */
+#define GRPC_ARG_ENABLE_HTTP_PROXY "grpc.enable_http_proxy"
+/** If set to non zero, surfaces the user agent string to the server. User
+    agent is surfaced by default. */
+#define GRPC_ARG_SURFACE_USER_AGENT "grpc.surface_user_agent"
+/** If set, inhibits health checking (which may be enabled via the
+ *  service config.) */
+#define GRPC_ARG_INHIBIT_HEALTH_CHECKING "grpc.inhibit_health_checking"
+/** If set, determines the number of milliseconds that the c-ares based
+ * DNS resolver will wait on queries before cancelling them. The default value
+ * is 10000. Setting this to "0" will disable c-ares query timeouts
+ * entirely. */
+#define GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS "grpc.dns_ares_query_timeout"
+/** \} */
+
+/** Result of a grpc call. If the caller satisfies the prerequisites of a
+    particular operation, the grpc_call_error returned will be GRPC_CALL_OK.
+    Receiving any other value listed here is an indication of a bug in the
+    caller. */
+typedef enum grpc_call_error {
+  /** everything went ok */
+  GRPC_CALL_OK = 0,
+  /** something failed, we don't know what */
+  GRPC_CALL_ERROR,
+  /** this method is not available on the server */
+  GRPC_CALL_ERROR_NOT_ON_SERVER,
+  /** this method is not available on the client */
+  GRPC_CALL_ERROR_NOT_ON_CLIENT,
+  /** this method must be called before server_accept */
+  GRPC_CALL_ERROR_ALREADY_ACCEPTED,
+  /** this method must be called before invoke */
+  GRPC_CALL_ERROR_ALREADY_INVOKED,
+  /** this method must be called after invoke */
+  GRPC_CALL_ERROR_NOT_INVOKED,
+  /** this call is already finished
+      (writes_done or write_status has already been called) */
+  GRPC_CALL_ERROR_ALREADY_FINISHED,
+  /** there is already an outstanding read/write operation on the call */
+  GRPC_CALL_ERROR_TOO_MANY_OPERATIONS,
+  /** the flags value was illegal for this call */
+  GRPC_CALL_ERROR_INVALID_FLAGS,
+  /** invalid metadata was passed to this call */
+  GRPC_CALL_ERROR_INVALID_METADATA,
+  /** invalid message was passed to this call */
+  GRPC_CALL_ERROR_INVALID_MESSAGE,
+  /** completion queue for notification has not been registered
+   * with the server */
+  GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE,
+  /** this batch of operations leads to more operations than allowed */
+  GRPC_CALL_ERROR_BATCH_TOO_BIG,
+  /** payload type requested is not the type registered */
+  GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH,
+  /** completion queue has been shutdown */
+  GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN
+} grpc_call_error;
+
+/** Default send/receive message size limits in bytes. -1 for unlimited. */
+/** TODO(roth) Make this match the default receive limit after next release */
+#define GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH -1
+#define GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH (4 * 1024 * 1024)
+
+/** Write Flags: */
+/** Hint that the write may be buffered and need not go out on the wire
+    immediately. GRPC is free to buffer the message until the next non-buffered
+    write, or until writes_done, but it need not buffer completely or at all. */
+#define GRPC_WRITE_BUFFER_HINT (0x00000001u)
+/** Force compression to be disabled for a particular write
+    (start_write/add_metadata). Illegal on invoke/accept. */
+#define GRPC_WRITE_NO_COMPRESS (0x00000002u)
+/** Force this message to be written to the socket before completing it */
+#define GRPC_WRITE_THROUGH (0x00000004u)
+/** Mask of all valid flags. */
+#define GRPC_WRITE_USED_MASK \
+  (GRPC_WRITE_BUFFER_HINT | GRPC_WRITE_NO_COMPRESS | GRPC_WRITE_THROUGH)
+
+/** Initial metadata flags */
+/** Signal that the call is idempotent */
+#define GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST (0x00000010u)
+/** Signal that the call should not return UNAVAILABLE before it has started */
+#define GRPC_INITIAL_METADATA_WAIT_FOR_READY (0x00000020u)
+/** Signal that the call is cacheable. GRPC is free to use GET verb */
+#define GRPC_INITIAL_METADATA_CACHEABLE_REQUEST (0x00000040u)
+/** Signal that GRPC_INITIAL_METADATA_WAIT_FOR_READY was explicitly set
+    by the calling application. */
+#define GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET (0x00000080u)
+/** Signal that the initial metadata should be corked */
+#define GRPC_INITIAL_METADATA_CORKED (0x00000100u)
+
+/** Mask of all valid flags */
+#define GRPC_INITIAL_METADATA_USED_MASK                  \
+  (GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST |            \
+   GRPC_INITIAL_METADATA_WAIT_FOR_READY |                \
+   GRPC_INITIAL_METADATA_CACHEABLE_REQUEST |             \
+   GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET | \
+   GRPC_INITIAL_METADATA_CORKED | GRPC_WRITE_THROUGH)
+
+/** A single metadata element */
+typedef struct grpc_metadata {
+  /** the key, value values are expected to line up with grpc_mdelem: if
+     changing them, update metadata.h at the same time. */
+  grpc_slice key;
+  grpc_slice value;
+
+  uint32_t flags;
+
+  /** The following fields are reserved for grpc internal use.
+      There is no need to initialize them, and they will be set to garbage
+      during calls to grpc. */
+  struct /* internal */ {
+    void* obfuscated[4];
+  } internal_data;
+} grpc_metadata;
+
+/** The type of completion (for grpc_event) */
+typedef enum grpc_completion_type {
+  /** Shutting down */
+  GRPC_QUEUE_SHUTDOWN,
+  /** No event before timeout */
+  GRPC_QUEUE_TIMEOUT,
+  /** Operation completion */
+  GRPC_OP_COMPLETE
+} grpc_completion_type;
+
+/** The result of an operation.
+
+    Returned by a completion queue when the operation started with tag. */
+typedef struct grpc_event {
+  /** The type of the completion. */
+  grpc_completion_type type;
+  /** If the grpc_completion_type is GRPC_OP_COMPLETE, this field indicates
+      whether the operation was successful or not; 0 in case of failure and
+      non-zero in case of success.
+      If grpc_completion_type is GRPC_QUEUE_SHUTDOWN or GRPC_QUEUE_TIMEOUT, this
+      field is guaranteed to be 0 */
+  int success;
+  /** The tag passed to grpc_call_start_batch etc to start this operation.
+      Only GRPC_OP_COMPLETE has a tag. */
+  void* tag;
+} grpc_event;
+
+typedef struct {
+  size_t count;
+  size_t capacity;
+  grpc_metadata* metadata;
+} grpc_metadata_array;
+
+typedef struct {
+  grpc_slice method;
+  grpc_slice host;
+  gpr_timespec deadline;
+  uint32_t flags;
+  void* reserved;
+} grpc_call_details;
+
+typedef enum {
+  /** Send initial metadata: one and only one instance MUST be sent for each
+      call, unless the call was cancelled - in which case this can be skipped.
+      This op completes after all bytes of metadata have been accepted by
+      outgoing flow control. */
+  GRPC_OP_SEND_INITIAL_METADATA = 0,
+  /** Send a message: 0 or more of these operations can occur for each call.
+      This op completes after all bytes for the message have been accepted by
+      outgoing flow control. */
+  GRPC_OP_SEND_MESSAGE,
+  /** Send a close from the client: one and only one instance MUST be sent from
+      the client, unless the call was cancelled - in which case this can be
+      skipped. This op completes after all bytes for the call
+      (including the close) have passed outgoing flow control. */
+  GRPC_OP_SEND_CLOSE_FROM_CLIENT,
+  /** Send status from the server: one and only one instance MUST be sent from
+      the server unless the call was cancelled - in which case this can be
+      skipped. This op completes after all bytes for the call
+      (including the status) have passed outgoing flow control. */
+  GRPC_OP_SEND_STATUS_FROM_SERVER,
+  /** Receive initial metadata: one and only one MUST be made on the client,
+      must not be made on the server.
+      This op completes after all initial metadata has been read from the
+      peer. */
+  GRPC_OP_RECV_INITIAL_METADATA,
+  /** Receive a message: 0 or more of these operations can occur for each call.
+      This op completes after all bytes of the received message have been
+      read, or after a half-close has been received on this call. */
+  GRPC_OP_RECV_MESSAGE,
+  /** Receive status on the client: one and only one must be made on the client.
+      This operation always succeeds, meaning ops paired with this operation
+      will also appear to succeed, even though they may not have. In that case
+      the status will indicate some failure.
+      This op completes after all activity on the call has completed. */
+  GRPC_OP_RECV_STATUS_ON_CLIENT,
+  /** Receive close on the server: one and only one must be made on the
+      server. This op completes after the close has been received by the
+      server. This operation always succeeds, meaning ops paired with
+      this operation will also appear to succeed, even though they may not
+      have. */
+  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 {
+  /** Operation type, as defined by grpc_op_type */
+  grpc_op_type op;
+  /** Write flags bitset for grpc_begin_messages */
+  uint32_t flags;
+  /** Reserved for future usage */
+  void* reserved;
+  union grpc_op_data {
+    /** Reserved for future usage */
+    struct /* internal */ {
+      void* reserved[8];
+    } reserved;
+    struct grpc_op_send_initial_metadata {
+      size_t count;
+      grpc_metadata* metadata;
+      /** If \a is_set, \a compression_level will be used for the call.
+       * Otherwise, \a compression_level won't be considered */
+      struct grpc_op_send_initial_metadata_maybe_compression_level {
+        uint8_t is_set;
+        grpc_compression_level level;
+      } maybe_compression_level;
+    } send_initial_metadata;
+    struct grpc_op_send_message {
+      /** This op takes ownership of the slices in send_message.  After
+       * a call completes, the contents of send_message are not guaranteed
+       * and likely empty.  The original owner should still call
+       * grpc_byte_buffer_destroy() on this object however.
+       */
+      struct grpc_byte_buffer* send_message;
+    } send_message;
+    struct grpc_op_send_status_from_server {
+      size_t trailing_metadata_count;
+      grpc_metadata* trailing_metadata;
+      grpc_status_code status;
+      /** optional: set to NULL if no details need sending, non-NULL if they do
+       * pointer will not be retained past the start_batch call
+       */
+      grpc_slice* status_details;
+    } send_status_from_server;
+    /** ownership of the array is with the caller, but ownership of the elements
+        stays with the call object (ie key, value members are owned by the call
+        object, recv_initial_metadata->array is owned by the caller).
+        After the operation completes, call grpc_metadata_array_destroy on this
+        value, or reuse it in a future op. */
+    struct grpc_op_recv_initial_metadata {
+      grpc_metadata_array* recv_initial_metadata;
+    } recv_initial_metadata;
+    /** 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.
+        The returned byte buffer will be NULL if trailing metadata was
+        received instead of a message.
+       */
+    struct grpc_op_recv_message {
+      struct grpc_byte_buffer** recv_message;
+    } recv_message;
+    struct grpc_op_recv_status_on_client {
+      /** ownership of the array is with the caller, but ownership of the
+          elements stays with the call object (ie key, value members are owned
+          by the call object, trailing_metadata->array is owned by the caller).
+          After the operation completes, call grpc_metadata_array_destroy on
+          this value, or reuse it in a future op. */
+      grpc_metadata_array* trailing_metadata;
+      grpc_status_code* status;
+      grpc_slice* status_details;
+      /** If this is not nullptr, it will be populated with the full fidelity
+       * error string for debugging purposes. The application is responsible
+       * for freeing the data by using gpr_free(). */
+      const char** error_string;
+    } recv_status_on_client;
+    struct grpc_op_recv_close_on_server {
+      /** out argument, set to 1 if the call failed in any way (seen as a
+          cancellation on the server), or 0 if the call succeeded */
+      int* cancelled;
+    } recv_close_on_server;
+  } data;
+} grpc_op;
+
+/** Information requested from the channel. */
+typedef struct {
+  /** If non-NULL, will be set to point to a string indicating the LB
+   * policy name.  Caller takes ownership. */
+  char** lb_policy_name;
+  /** If non-NULL, will be set to point to a string containing the
+   * service config used by the channel in JSON form. */
+  char** service_config_json;
+} grpc_channel_info;
+
+typedef struct grpc_resource_quota grpc_resource_quota;
+
+/** Completion queues internally MAY maintain a set of file descriptors in a
+    structure called 'pollset'. This enum specifies if a completion queue has an
+    associated pollset and any restrictions on the type of file descriptors that
+    can be present in the pollset.
+
+    I/O progress can only be made when grpc_completion_queue_next() or
+    grpc_completion_queue_pluck() are called on the completion queue (unless the
+    grpc_cq_polling_type is GRPC_CQ_NON_POLLING) and hence it is very important
+    to actively call these APIs */
+typedef enum {
+  /** The completion queue will have an associated pollset and there is no
+      restriction on the type of file descriptors the pollset may contain */
+  GRPC_CQ_DEFAULT_POLLING,
+
+  /** Similar to GRPC_CQ_DEFAULT_POLLING except that the completion queues will
+      not contain any 'listening file descriptors' (i.e file descriptors used to
+      listen to incoming channels) */
+  GRPC_CQ_NON_LISTENING,
+
+  /** The completion queue will not have an associated pollset. Note that
+      grpc_completion_queue_next() or grpc_completion_queue_pluck() MUST still
+      be called to pop events from the completion queue; it is not required to
+      call them actively to make I/O progress */
+  GRPC_CQ_NON_POLLING
+} grpc_cq_polling_type;
+
+/** Specifies the type of APIs to use to pop events from the completion queue */
+typedef enum {
+  /** Events are popped out by calling grpc_completion_queue_next() API ONLY */
+  GRPC_CQ_NEXT,
+
+  /** Events are popped out by calling grpc_completion_queue_pluck() API ONLY*/
+  GRPC_CQ_PLUCK,
+
+  /** EXPERIMENTAL: Events trigger a callback specified as the tag */
+  GRPC_CQ_CALLBACK
+} grpc_cq_completion_type;
+
+/** EXPERIMENTAL: Specifies an interface class to be used as a tag
+    for callback-based completion queues. This can be used directly,
+    as the first element of a struct in C, or as a base class in C++.
+    Its "run" value should be assigned to some non-member function, such as
+    a static method. */
+typedef struct grpc_experimental_completion_queue_functor {
+  /** The run member specifies a function that will be called when this
+      tag is extracted from the completion queue. Its arguments will be a
+      pointer to this functor and a boolean that indicates whether the
+      operation succeeded (non-zero) or failed (zero) */
+  void (*functor_run)(struct grpc_experimental_completion_queue_functor*, int);
+} grpc_experimental_completion_queue_functor;
+
+/* The upgrade to version 2 is currently experimental. */
+
+#define GRPC_CQ_CURRENT_VERSION 2
+#define GRPC_CQ_VERSION_MINIMUM_FOR_CALLBACKABLE 2
+typedef struct grpc_completion_queue_attributes {
+  /** The version number of this structure. More fields might be added to this
+     structure in future. */
+  int version; /** Set to GRPC_CQ_CURRENT_VERSION */
+
+  grpc_cq_completion_type cq_completion_type;
+
+  grpc_cq_polling_type cq_polling_type;
+
+  /* END OF VERSION 1 CQ ATTRIBUTES */
+
+  /* EXPERIMENTAL: START OF VERSION 2 CQ ATTRIBUTES */
+  /** When creating a callbackable CQ, pass in a functor to get invoked when
+   * shutdown is complete */
+  grpc_experimental_completion_queue_functor* cq_shutdown_cb;
+
+  /* END OF VERSION 2 CQ ATTRIBUTES */
+} grpc_completion_queue_attributes;
+
+/** The completion queue factory structure is opaque to the callers of grpc */
+typedef struct grpc_completion_queue_factory grpc_completion_queue_factory;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_GRPC_TYPES_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/log.h b/third_party/grpc/include/grpc/impl/codegen/log.h
new file mode 100644
index 0000000..ad7f024
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/log.h
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_LOG_H
+#define GRPC_IMPL_CODEGEN_LOG_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <stdarg.h>
+#include <stdlib.h> /* for abort() */
+
+#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 */
+GPRAPI 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, ...) GPR_PRINT_FORMAT_CHECK(4, 5);
+
+GPRAPI int gpr_should_log(gpr_log_severity severity);
+
+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(void);
+
+/** Log overrides: applications can use this API to intercept logging calls
+   and use their own implementations */
+
+struct gpr_log_func_args {
+  const char* file;
+  int line;
+  gpr_log_severity severity;
+  const char* message;
+};
+
+typedef struct gpr_log_func_args 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 (GPR_UNLIKELY(!(x))) {                         \
+      gpr_log(GPR_ERROR, "assertion failed: %s", #x); \
+      abort();                                        \
+    }                                                 \
+  } while (0)
+
+#ifndef NDEBUG
+#define GPR_DEBUG_ASSERT(x) GPR_ASSERT(x)
+#else
+#define GPR_DEBUG_ASSERT(x)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_LOG_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/port_platform.h b/third_party/grpc/include/grpc/impl/codegen/port_platform.h
new file mode 100644
index 0000000..031c0c3
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/port_platform.h
@@ -0,0 +1,568 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_PORT_PLATFORM_H
+#define GRPC_IMPL_CODEGEN_PORT_PLATFORM_H
+
+/*
+ * Define GPR_BACKWARDS_COMPATIBILITY_MODE to try harder to be ABI
+ * compatible with older platforms (currently only on Linux)
+ * Causes:
+ *  - some libc calls to be gotten via dlsym
+ *  - some syscalls to be made directly
+ */
+
+/* Get windows.h included everywhere (we need it) */
+#if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32)
+#ifndef WIN32_LEAN_AND_MEAN
+#define GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED
+#define WIN32_LEAN_AND_MEAN
+#endif /* WIN32_LEAN_AND_MEAN */
+
+#ifndef NOMINMAX
+#define GRPC_NOMINMX_WAS_NOT_DEFINED
+#define NOMINMAX
+#endif /* NOMINMAX */
+
+#ifndef _WIN32_WINNT
+#error \
+    "Please compile grpc with _WIN32_WINNT of at least 0x600 (aka Windows Vista)"
+#else /* !defined(_WIN32_WINNT) */
+#if (_WIN32_WINNT < 0x0600)
+#error \
+    "Please compile grpc with _WIN32_WINNT of at least 0x600 (aka Windows Vista)"
+#endif /* _WIN32_WINNT < 0x0600 */
+#endif /* defined(_WIN32_WINNT) */
+
+#include <windows.h>
+
+#ifdef GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED
+#undef GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED
+#undef WIN32_LEAN_AND_MEAN
+#endif /* GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED */
+
+#ifdef GRPC_NOMINMAX_WAS_NOT_DEFINED
+#undef GRPC_NOMINMAX_WAS_NOT_DEFINED
+#undef NOMINMAX
+#endif /* GRPC_WIN32_LEAN_AND_MEAN_WAS_NOT_DEFINED */
+#endif /* defined(_WIN64) || defined(WIN64) || defined(_WIN32) || \
+          defined(WIN32) */
+
+/* Override this file with one for your platform if you need to redefine
+   things.  */
+
+#if !defined(GPR_NO_AUTODETECT_PLATFORM)
+#if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32)
+#if defined(_WIN64) || defined(WIN64)
+#define GPR_ARCH_64 1
+#else
+#define GPR_ARCH_32 1
+#endif
+#define GPR_PLATFORM_STRING "windows"
+#define GPR_WINDOWS 1
+#define GPR_WINDOWS_SUBPROCESS 1
+#define GPR_WINDOWS_ENV
+#ifdef __MSYS__
+#define GPR_GETPID_IN_UNISTD_H 1
+#define GPR_MSYS_TMPFILE
+#define GPR_POSIX_LOG
+#define GPR_POSIX_STRING
+#define GPR_POSIX_TIME
+#else
+#define GPR_GETPID_IN_PROCESS_H 1
+#define GPR_WINDOWS_TMPFILE
+#define GPR_WINDOWS_LOG
+#define GPR_WINDOWS_CRASH_HANDLER 1
+#define GPR_WINDOWS_STRING
+#define GPR_WINDOWS_TIME
+#endif
+#ifdef __GNUC__
+#define GPR_GCC_ATOMIC 1
+#define GPR_GCC_TLS 1
+#else
+#define GPR_WINDOWS_ATOMIC 1
+#define GPR_MSVC_TLS 1
+#endif
+#elif defined(GPR_MANYLINUX1)
+// TODO(atash): manylinux1 is just another __linux__ but with ancient
+// libraries; it should be integrated with the `__linux__` definitions below.
+#define GPR_PLATFORM_STRING "manylinux"
+#define GPR_POSIX_CRASH_HANDLER 1
+#define GPR_CPU_POSIX 1
+#define GPR_GCC_ATOMIC 1
+#define GPR_GCC_TLS 1
+#define GPR_LINUX 1
+#define GPR_LINUX_LOG 1
+#define GPR_SUPPORT_CHANNELS_FROM_FD 1
+#define GPR_LINUX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#elif defined(ANDROID) || defined(__ANDROID__)
+#define GPR_PLATFORM_STRING "android"
+#define GPR_ANDROID 1
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#define GPR_CPU_POSIX 1
+#define GPR_GCC_SYNC 1
+#define GPR_GCC_TLS 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_ANDROID_LOG 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+#define GPR_SUPPORT_CHANNELS_FROM_FD 1
+#elif defined(__linux__)
+#define GPR_PLATFORM_STRING "linux"
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <features.h>
+#define GPR_CPU_LINUX 1
+#define GPR_GCC_ATOMIC 1
+#define GPR_GCC_TLS 1
+#define GPR_LINUX 1
+#define GPR_LINUX_LOG
+#define GPR_SUPPORT_CHANNELS_FROM_FD 1
+#define GPR_LINUX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#ifdef __GLIBC__
+#define GPR_POSIX_CRASH_HANDLER 1
+#define GPR_LINUX_PTHREAD_NAME 1
+#include <linux/version.h>
+#else /* musl libc */
+#define GPR_MUSL_LIBC_COMPAT 1
+#endif
+#elif defined(__APPLE__)
+#include <Availability.h>
+#include <TargetConditionals.h>
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+#if TARGET_OS_IPHONE
+#define GPR_PLATFORM_STRING "ios"
+#define GPR_CPU_IPHONE 1
+#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
+/* TODO(vjpai): there is a reported issue in bazel build for Mac where __thread
+   in a header is currently not working (bazelbuild/bazel#4341). Remove
+   the following conditional and use GPR_GCC_TLS when that is fixed */
+#ifndef GRPC_BAZEL_BUILD
+#define GPR_GCC_TLS 1
+#else /* GRPC_BAZEL_BUILD */
+#define GPR_PTHREAD_TLS 1
+#endif /* GRPC_BAZEL_BUILD */
+#define GPR_APPLE_PTHREAD_NAME 1
+#endif
+#else /* __MAC_OS_X_VERSION_MIN_REQUIRED */
+#define GPR_CPU_POSIX 1
+/* TODO(vjpai): Remove the following conditional and use only GPR_GCC_TLS
+   when bazelbuild/bazel#4341 is fixed */
+#ifndef GRPC_BAZEL_BUILD
+#define GPR_GCC_TLS 1
+#else /* GRPC_BAZEL_BUILD */
+#define GPR_PTHREAD_TLS 1
+#endif /* GRPC_BAZEL_BUILD */
+#endif
+#define GPR_POSIX_CRASH_HANDLER 1
+#endif
+#define GPR_APPLE 1
+#define GPR_GCC_ATOMIC 1
+#define GPR_POSIX_LOG 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+/* TODO(mxyan): Remove when CFStream becomes default */
+#ifndef GRPC_CFSTREAM
+#define GPR_SUPPORT_CHANNELS_FROM_FD 1
+#endif
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#elif defined(__FreeBSD__)
+#define GPR_PLATFORM_STRING "freebsd"
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+#define GPR_FREEBSD 1
+#define GPR_CPU_POSIX 1
+#define GPR_GCC_ATOMIC 1
+#define GPR_GCC_TLS 1
+#define GPR_POSIX_LOG 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+#define GPR_SUPPORT_CHANNELS_FROM_FD 1
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#elif defined(__OpenBSD__)
+#define GPR_PLATFORM_STRING "openbsd"
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+#define GPR_OPENBSD 1
+#define GPR_CPU_POSIX 1
+#define GPR_GCC_ATOMIC 1
+#define GPR_GCC_TLS 1
+#define GPR_POSIX_LOG 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+#define GPR_SUPPORT_CHANNELS_FROM_FD 1
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#elif defined(__sun) && defined(__SVR4)
+#define GPR_PLATFORM_STRING "solaris"
+#define GPR_SOLARIS 1
+#define GPR_CPU_POSIX 1
+#define GPR_GCC_ATOMIC 1
+#define GPR_GCC_TLS 1
+#define GPR_POSIX_LOG 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#elif defined(_AIX)
+#define GPR_PLATFORM_STRING "aix"
+#ifndef _ALL_SOURCE
+#define _ALL_SOURCE
+#endif
+#define GPR_AIX 1
+#define GPR_CPU_POSIX 1
+#define GPR_GCC_ATOMIC 1
+#define GPR_GCC_TLS 1
+#define GPR_POSIX_LOG 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#elif defined(__native_client__)
+#define GPR_PLATFORM_STRING "nacl"
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#define GPR_NACL 1
+#define GPR_CPU_POSIX 1
+#define GPR_GCC_ATOMIC 1
+#define GPR_GCC_TLS 1
+#define GPR_POSIX_LOG 1
+#define GPR_POSIX_ENV 1
+#define GPR_POSIX_TMPFILE 1
+#define GPR_POSIX_STRING 1
+#define GPR_POSIX_SUBPROCESS 1
+#define GPR_POSIX_SYNC 1
+#define GPR_POSIX_TIME 1
+#define GPR_GETPID_IN_UNISTD_H 1
+#ifdef _LP64
+#define GPR_ARCH_64 1
+#else /* _LP64 */
+#define GPR_ARCH_32 1
+#endif /* _LP64 */
+#else
+#error "Could not auto-detect platform"
+#endif
+#endif /* GPR_NO_AUTODETECT_PLATFORM */
+
+/*
+ *  There are platforms for which TLS should not be used even though the
+ * compiler makes it seem like it's supported (Android NDK < r12b for example).
+ * This is primarily because of linker problems and toolchain misconfiguration:
+ * TLS isn't supported until NDK r12b per
+ * https://developer.android.com/ndk/downloads/revision_history.html
+ * TLS also does not work with Android NDK if GCC is being used as the compiler
+ * instead of Clang.
+ * Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in
+ * <android/ndk-version.h>. For NDK < r16, users should define these macros,
+ * e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. */
+#if defined(__ANDROID__) && defined(GPR_GCC_TLS)
+#if __has_include(<android/ndk-version.h>)
+#include <android/ndk-version.h>
+#endif /* __has_include(<android/ndk-version.h>) */
+#if (defined(__clang__) && defined(__NDK_MAJOR__) && defined(__NDK_MINOR__) && \
+     ((__NDK_MAJOR__ < 12) ||                                                  \
+      ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))) ||                      \
+    (defined(__GNUC__) && !defined(__clang__))
+#undef GPR_GCC_TLS
+#define GPR_PTHREAD_TLS 1
+#endif
+#endif /*defined(__ANDROID__) && defined(GPR_GCC_TLS) */
+
+#if defined(__has_include)
+#if __has_include(<atomic>)
+#define GRPC_HAS_CXX11_ATOMIC
+#endif /* __has_include(<atomic>) */
+#endif /* defined(__has_include) */
+
+#ifndef GPR_PLATFORM_STRING
+#warning "GPR_PLATFORM_STRING not auto-detected"
+#define GPR_PLATFORM_STRING "unknown"
+#endif
+
+#ifdef GPR_GCOV
+#undef GPR_FORBID_UNREACHABLE_CODE
+#define GPR_FORBID_UNREACHABLE_CODE 1
+#endif
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1700
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif /* _MSC_VER < 1700 */
+#else
+#include <stdint.h>
+#endif /* _MSC_VER */
+
+/* Cache line alignment */
+#ifndef GPR_CACHELINE_SIZE_LOG
+#if defined(__i386__) || defined(__x86_64__)
+#define GPR_CACHELINE_SIZE_LOG 6
+#endif
+#ifndef GPR_CACHELINE_SIZE_LOG
+/* A reasonable default guess. Note that overestimates tend to waste more
+   space, while underestimates tend to waste more time. */
+#define GPR_CACHELINE_SIZE_LOG 6
+#endif /* GPR_CACHELINE_SIZE_LOG */
+#endif /* GPR_CACHELINE_SIZE_LOG */
+
+#define GPR_CACHELINE_SIZE (1 << GPR_CACHELINE_SIZE_LOG)
+
+/* scrub GCC_ATOMIC if it's not available on this compiler */
+#if defined(GPR_GCC_ATOMIC) && !defined(__ATOMIC_RELAXED)
+#undef GPR_GCC_ATOMIC
+#define GPR_GCC_SYNC 1
+#endif
+
+/* Validate platform combinations */
+#if defined(GPR_GCC_ATOMIC) + defined(GPR_GCC_SYNC) + \
+        defined(GPR_WINDOWS_ATOMIC) !=                \
+    1
+#error Must define exactly one of GPR_GCC_ATOMIC, GPR_GCC_SYNC, GPR_WINDOWS_ATOMIC
+#endif
+
+#if defined(GPR_ARCH_32) + defined(GPR_ARCH_64) != 1
+#error Must define exactly one of GPR_ARCH_32, GPR_ARCH_64
+#endif
+
+#if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WINDOWS) + \
+        defined(GPR_CPU_IPHONE) + defined(GPR_CPU_CUSTOM) !=                 \
+    1
+#error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WINDOWS, GPR_CPU_IPHONE, GPR_CPU_CUSTOM
+#endif
+
+#if defined(GPR_MSVC_TLS) + defined(GPR_GCC_TLS) + defined(GPR_PTHREAD_TLS) + \
+        defined(GPR_CUSTOM_TLS) !=                                            \
+    1
+#error Must define exactly one of GPR_MSVC_TLS, GPR_GCC_TLS, GPR_PTHREAD_TLS, GPR_CUSTOM_TLS
+#endif
+
+/* maximum alignment needed for any type on this platform, rounded up to a
+   power of two */
+#define GPR_MAX_ALIGNMENT 16
+
+#ifndef GRPC_ARES
+#define GRPC_ARES 1
+#endif
+
+#ifndef GRPC_MUST_USE_RESULT
+#if defined(__GNUC__) && !defined(__MINGW32__)
+#define GRPC_MUST_USE_RESULT __attribute__((warn_unused_result))
+#define GPR_ALIGN_STRUCT(n) __attribute__((aligned(n)))
+#else
+#define GRPC_MUST_USE_RESULT
+#define GPR_ALIGN_STRUCT(n)
+#endif
+#endif
+
+#ifndef GRPC_UNUSED
+#if defined(__GNUC__) && !defined(__MINGW32__)
+#define GRPC_UNUSED __attribute__((unused))
+#else
+#define GRPC_UNUSED
+#endif
+#endif
+
+#ifndef GPR_PRINT_FORMAT_CHECK
+#ifdef __GNUC__
+#define GPR_PRINT_FORMAT_CHECK(FORMAT_STR, ARGS) \
+  __attribute__((format(printf, FORMAT_STR, ARGS)))
+#else
+#define GPR_PRINT_FORMAT_CHECK(FORMAT_STR, ARGS)
+#endif
+#endif /* GPR_PRINT_FORMAT_CHECK */
+
+#if GPR_FORBID_UNREACHABLE_CODE
+#define GPR_UNREACHABLE_CODE(STATEMENT)
+#else
+#define GPR_UNREACHABLE_CODE(STATEMENT)             \
+  do {                                              \
+    gpr_log(GPR_ERROR, "Should never reach here."); \
+    abort();                                        \
+    STATEMENT;                                      \
+  } while (0)
+#endif /* GPR_FORBID_UNREACHABLE_CODE */
+
+#ifndef GPRAPI
+#define GPRAPI
+#endif
+
+#ifndef GRPCAPI
+#define GRPCAPI GPRAPI
+#endif
+
+#ifndef CENSUSAPI
+#define CENSUSAPI GRPCAPI
+#endif
+
+#ifndef GPR_ATTRIBUTE_NO_TSAN /* (1) */
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#define GPR_ATTRIBUTE_NO_TSAN __attribute__((no_sanitize("thread")))
+#endif                        /* __has_feature(thread_sanitizer) */
+#endif                        /* defined(__has_feature) */
+#ifndef GPR_ATTRIBUTE_NO_TSAN /* (2) */
+#define GPR_ATTRIBUTE_NO_TSAN
+#endif /* GPR_ATTRIBUTE_NO_TSAN (2) */
+#endif /* GPR_ATTRIBUTE_NO_TSAN (1) */
+
+/* GRPC_TSAN_ENABLED will be defined, when compiled with thread sanitizer. */
+#if defined(__SANITIZE_THREAD__)
+#define GRPC_TSAN_ENABLED
+#elif defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#define GRPC_TSAN_ENABLED
+#endif
+#endif
+
+/* GRPC_ALLOW_EXCEPTIONS should be 0 or 1 if exceptions are allowed or not */
+#ifndef GRPC_ALLOW_EXCEPTIONS
+/* If not already set, set to 1 on Windows (style guide standard) but to
+ * 0 on non-Windows platforms unless the compiler defines __EXCEPTIONS */
+#ifdef GPR_WINDOWS
+#define GRPC_ALLOW_EXCEPTIONS 1
+#else /* GPR_WINDOWS */
+#ifdef __EXCEPTIONS
+#define GRPC_ALLOW_EXCEPTIONS 1
+#else /* __EXCEPTIONS */
+#define GRPC_ALLOW_EXCEPTIONS 0
+#endif /* __EXCEPTIONS */
+#endif /* __GPR_WINDOWS */
+#endif /* GRPC_ALLOW_EXCEPTIONS */
+
+/* Use GPR_LIKELY only in cases where you are sure that a certain outcome is the
+ * most likely. Ideally, also collect performance numbers to justify the claim.
+ */
+#ifdef __GNUC__
+#define GPR_LIKELY(x) __builtin_expect((x), 1)
+#define GPR_UNLIKELY(x) __builtin_expect((x), 0)
+#else /* __GNUC__ */
+#define GPR_LIKELY(x) (x)
+#define GPR_UNLIKELY(x) (x)
+#endif /* __GNUC__ */
+
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_PORT_PLATFORM_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/propagation_bits.h b/third_party/grpc/include/grpc/impl/codegen/propagation_bits.h
new file mode 100644
index 0000000..824bdbd
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/propagation_bits.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_PROPAGATION_BITS_H
+#define GRPC_IMPL_CODEGEN_PROPAGATION_BITS_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Propagation bits: this can be bitwise or-ed to form propagation_mask for
+ * grpc_call */
+/** Propagate deadline */
+#define GRPC_PROPAGATE_DEADLINE ((uint32_t)1)
+/** Propagate census context */
+#define GRPC_PROPAGATE_CENSUS_STATS_CONTEXT ((uint32_t)2)
+#define GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT ((uint32_t)4)
+/** Propagate cancellation */
+#define GRPC_PROPAGATE_CANCELLATION ((uint32_t)8)
+
+/** Default propagation mask: clients of the core API are encouraged to encode
+   deltas from this in their implementations... ie write:
+   GRPC_PROPAGATE_DEFAULTS & ~GRPC_PROPAGATE_DEADLINE to disable deadline
+   propagation. Doing so gives flexibility in the future to define new
+   propagation types that are default inherited or not. */
+#define GRPC_PROPAGATE_DEFAULTS                                                \
+  ((uint32_t)((                                                                \
+      0xffff | GRPC_PROPAGATE_DEADLINE | GRPC_PROPAGATE_CENSUS_STATS_CONTEXT | \
+      GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT | GRPC_PROPAGATE_CANCELLATION)))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_PROPAGATION_BITS_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/slice.h b/third_party/grpc/include/grpc/impl/codegen/slice.h
new file mode 100644
index 0000000..90dbfd3
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/slice.h
@@ -0,0 +1,147 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_SLICE_H
+#define GRPC_IMPL_CODEGEN_SLICE_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <stddef.h>
+
+#include <grpc/impl/codegen/gpr_slice.h>
+
+typedef struct grpc_slice grpc_slice;
+
+/** 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?) */
+
+typedef struct grpc_slice_refcount_vtable {
+  void (*ref)(void*);
+  void (*unref)(void*);
+  int (*eq)(grpc_slice a, grpc_slice b);
+  uint32_t (*hash)(grpc_slice slice);
+} grpc_slice_refcount_vtable;
+
+/** Reference count container for grpc_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 grpc_slice_malloc,
+   grpc_slice_new, or grpc_slice_new_with_len instead. */
+typedef struct grpc_slice_refcount {
+  const grpc_slice_refcount_vtable* vtable;
+  /** If a subset of this slice is taken, use this pointer for the refcount.
+     Typically points back to the refcount itself, however iterning
+     implementations can use this to avoid a verification step on each hash
+     or equality check */
+  struct grpc_slice_refcount* sub_refcount;
+} grpc_slice_refcount;
+
+/* Inlined half of grpc_slice is allowed to expand the size of the overall type
+   by this many bytes */
+#define GRPC_SLICE_INLINE_EXTRA_SIZE sizeof(void*)
+
+#define GRPC_SLICE_INLINED_SIZE \
+  (sizeof(size_t) + sizeof(uint8_t*) - 1 + GRPC_SLICE_INLINE_EXTRA_SIZE)
+
+/** A grpc_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 grpc_slice_new() and grp_slice_unref()).
+   Multiple grpc_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. */
+struct grpc_slice {
+  struct grpc_slice_refcount* refcount;
+  union grpc_slice_data {
+    struct grpc_slice_refcounted {
+      uint8_t* bytes;
+      size_t length;
+    } refcounted;
+    struct grpc_slice_inlined {
+      uint8_t length;
+      uint8_t bytes[GRPC_SLICE_INLINED_SIZE];
+    } inlined;
+  } data;
+};
+
+#define GRPC_SLICE_BUFFER_INLINE_ELEMENTS 8
+
+/** Represents an expandable array of slices, to be interpreted as a
+   single item. */
+typedef struct grpc_slice_buffer {
+  /** This is for internal use only. External users (i.e any code outside grpc
+   * core) MUST NOT use this field */
+  grpc_slice* base_slices;
+
+  /** slices in the array (Points to the first valid grpc_slice in the array) */
+  grpc_slice* slices;
+  /** the number of slices in the array */
+  size_t count;
+  /** the number of slices allocated in the array. External users (i.e any code
+   * outside grpc core) MUST NOT use this field */
+  size_t capacity;
+  /** the combined length of all slices in the array */
+  size_t length;
+  /** inlined elements to avoid allocations */
+  grpc_slice inlined[GRPC_SLICE_BUFFER_INLINE_ELEMENTS];
+} grpc_slice_buffer;
+
+#define GRPC_SLICE_START_PTR(slice)                 \
+  ((slice).refcount ? (slice).data.refcounted.bytes \
+                    : (slice).data.inlined.bytes)
+#define GRPC_SLICE_LENGTH(slice)                     \
+  ((slice).refcount ? (slice).data.refcounted.length \
+                    : (slice).data.inlined.length)
+#define GRPC_SLICE_SET_LENGTH(slice, newlen)                              \
+  ((slice).refcount ? ((slice).data.refcounted.length = (size_t)(newlen)) \
+                    : ((slice).data.inlined.length = (uint8_t)(newlen)))
+#define GRPC_SLICE_END_PTR(slice) \
+  GRPC_SLICE_START_PTR(slice) + GRPC_SLICE_LENGTH(slice)
+#define GRPC_SLICE_IS_EMPTY(slice) (GRPC_SLICE_LENGTH(slice) == 0)
+
+#ifdef GRPC_ALLOW_GPR_SLICE_FUNCTIONS
+
+/* Duplicate GPR_* definitions */
+#define GPR_SLICE_START_PTR(slice)                  \
+  ((slice).refcount ? (slice).data.refcounted.bytes \
+                    : (slice).data.inlined.bytes)
+#define GPR_SLICE_LENGTH(slice)                      \
+  ((slice).refcount ? (slice).data.refcounted.length \
+                    : (slice).data.inlined.length)
+#define GPR_SLICE_SET_LENGTH(slice, newlen)                               \
+  ((slice).refcount ? ((slice).data.refcounted.length = (size_t)(newlen)) \
+                    : ((slice).data.inlined.length = (uint8_t)(newlen)))
+#define GPR_SLICE_END_PTR(slice) \
+  GRPC_SLICE_START_PTR(slice) + GRPC_SLICE_LENGTH(slice)
+#define GPR_SLICE_IS_EMPTY(slice) (GRPC_SLICE_LENGTH(slice) == 0)
+
+#endif /* GRPC_ALLOW_GPR_SLICE_FUNCTIONS */
+
+#endif /* GRPC_IMPL_CODEGEN_SLICE_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/status.h b/third_party/grpc/include/grpc/impl/codegen/status.h
new file mode 100644
index 0000000..9bc3dc9
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/status.h
@@ -0,0 +1,153 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_STATUS_H
+#define GRPC_IMPL_CODEGEN_STATUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  /** Not an error; returned on success */
+  GRPC_STATUS_OK = 0,
+
+  /** The operation was cancelled (typically by the caller). */
+  GRPC_STATUS_CANCELLED = 1,
+
+  /** Unknown error.  An example of where this error may be returned is
+     if a Status value received from another address space belongs to
+     an error-space that is not known in this address space.  Also
+     errors raised by APIs that do not return enough error information
+     may be converted to this error. */
+  GRPC_STATUS_UNKNOWN = 2,
+
+  /** Client specified an invalid argument.  Note that this differs
+     from FAILED_PRECONDITION.  INVALID_ARGUMENT indicates arguments
+     that are problematic regardless of the state of the system
+     (e.g., a malformed file name). */
+  GRPC_STATUS_INVALID_ARGUMENT = 3,
+
+  /** Deadline expired before operation could complete.  For operations
+     that change the state of the system, this error may be returned
+     even if the operation has completed successfully.  For example, a
+     successful response from a server could have been delayed long
+     enough for the deadline to expire. */
+  GRPC_STATUS_DEADLINE_EXCEEDED = 4,
+
+  /** Some requested entity (e.g., file or directory) was not found. */
+  GRPC_STATUS_NOT_FOUND = 5,
+
+  /** Some entity that we attempted to create (e.g., file or directory)
+     already exists. */
+  GRPC_STATUS_ALREADY_EXISTS = 6,
+
+  /** The caller does not have permission to execute the specified
+     operation.  PERMISSION_DENIED must not be used for rejections
+     caused by exhausting some resource (use RESOURCE_EXHAUSTED
+     instead for those errors).  PERMISSION_DENIED must not be
+     used if the caller can not be identified (use UNAUTHENTICATED
+     instead for those errors). */
+  GRPC_STATUS_PERMISSION_DENIED = 7,
+
+  /** The request does not have valid authentication credentials for the
+     operation. */
+  GRPC_STATUS_UNAUTHENTICATED = 16,
+
+  /** Some resource has been exhausted, perhaps a per-user quota, or
+     perhaps the entire file system is out of space. */
+  GRPC_STATUS_RESOURCE_EXHAUSTED = 8,
+
+  /** Operation was rejected because the system is not in a state
+     required for the operation's execution.  For example, directory
+     to be deleted may be non-empty, an rmdir operation is applied to
+     a non-directory, etc.
+
+     A litmus test that may help a service implementor in deciding
+     between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
+      (a) Use UNAVAILABLE if the client can retry just the failing call.
+      (b) Use ABORTED if the client should retry at a higher-level
+          (e.g., restarting a read-modify-write sequence).
+      (c) Use FAILED_PRECONDITION if the client should not retry until
+          the system state has been explicitly fixed.  E.g., if an "rmdir"
+          fails because the directory is non-empty, FAILED_PRECONDITION
+          should be returned since the client should not retry unless
+          they have first fixed up the directory by deleting files from it.
+      (d) Use FAILED_PRECONDITION if the client performs conditional
+          REST Get/Update/Delete on a resource and the resource on the
+          server does not match the condition. E.g., conflicting
+          read-modify-write on the same resource. */
+  GRPC_STATUS_FAILED_PRECONDITION = 9,
+
+  /** The operation was aborted, typically due to a concurrency issue
+     like sequencer check failures, transaction aborts, etc.
+
+     See litmus test above for deciding between FAILED_PRECONDITION,
+     ABORTED, and UNAVAILABLE. */
+  GRPC_STATUS_ABORTED = 10,
+
+  /** Operation was attempted past the valid range.  E.g., seeking or
+     reading past end of file.
+
+     Unlike INVALID_ARGUMENT, this error indicates a problem that may
+     be fixed if the system state changes. For example, a 32-bit file
+     system will generate INVALID_ARGUMENT if asked to read at an
+     offset that is not in the range [0,2^32-1], but it will generate
+     OUT_OF_RANGE if asked to read from an offset past the current
+     file size.
+
+     There is a fair bit of overlap between FAILED_PRECONDITION and
+     OUT_OF_RANGE.  We recommend using OUT_OF_RANGE (the more specific
+     error) when it applies so that callers who are iterating through
+     a space can easily look for an OUT_OF_RANGE error to detect when
+     they are done. */
+  GRPC_STATUS_OUT_OF_RANGE = 11,
+
+  /** Operation is not implemented or not supported/enabled in this service. */
+  GRPC_STATUS_UNIMPLEMENTED = 12,
+
+  /** Internal errors.  Means some invariants expected by underlying
+     system has been broken.  If you see one of these errors,
+     something is very broken. */
+  GRPC_STATUS_INTERNAL = 13,
+
+  /** The service is currently unavailable.  This is a most likely a
+     transient condition and may be corrected by retrying with
+     a backoff.
+
+     WARNING: Although data MIGHT not have been transmitted when this
+     status occurs, there is NOT A GUARANTEE that the server has not seen
+     anything. So in general it is unsafe to retry on this status code
+     if the call is non-idempotent.
+
+     See litmus test above for deciding between FAILED_PRECONDITION,
+     ABORTED, and UNAVAILABLE. */
+  GRPC_STATUS_UNAVAILABLE = 14,
+
+  /** Unrecoverable data loss or corruption. */
+  GRPC_STATUS_DATA_LOSS = 15,
+
+  /** Force users to include a default branch: */
+  GRPC_STATUS__DO_NOT_USE = -1
+} grpc_status_code;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_STATUS_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/sync.h b/third_party/grpc/include/grpc/impl/codegen/sync.h
new file mode 100644
index 0000000..3df68c6
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/sync.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_SYNC_H
+#define GRPC_IMPL_CODEGEN_SYNC_H
+/** Synchronization primitives for GPR.
+
+   The type  gpr_mu              provides a non-reentrant mutex (lock).
+
+   The type  gpr_cv              provides a condition variable.
+
+   The type  gpr_once            provides for one-time initialization.
+
+   The type gpr_event            provides one-time-setting, reading, and
+                                 waiting of a void*, with memory barriers.
+
+   The type gpr_refcount         provides an object reference counter,
+                                 with memory barriers suitable to control
+                                 object lifetimes.
+
+   The type gpr_stats_counter    provides an atomic statistics counter. It
+                                 provides no memory barriers.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Platform-specific type declarations of gpr_mu and gpr_cv.   */
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/impl/codegen/sync_generic.h>
+
+#if defined(GPR_POSIX_SYNC)
+#include <grpc/impl/codegen/sync_posix.h>
+#elif defined(GPR_WINDOWS)
+#include <grpc/impl/codegen/sync_windows.h>
+#elif defined(GPR_CUSTOM_SYNC)
+#include <grpc/impl/codegen/sync_custom.h>
+#else
+#error Unable to determine platform for sync
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_SYNC_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_custom.h b/third_party/grpc/include/grpc/impl/codegen/sync_custom.h
new file mode 100644
index 0000000..69b1bf6
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/sync_custom.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_SYNC_CUSTOM_H
+#define GRPC_IMPL_CODEGEN_SYNC_CUSTOM_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/impl/codegen/sync_generic.h>
+
+/* Users defining GPR_CUSTOM_SYNC need to define the following macros. */
+
+#ifdef GPR_CUSTOM_SYNC
+
+typedef GPR_CUSTOM_MU_TYPE gpr_mu;
+typedef GPR_CUSTOM_CV_TYPE gpr_cv;
+typedef GPR_CUSTOM_ONCE_TYPE gpr_once;
+
+#define GPR_ONCE_INIT GPR_CUSTOM_ONCE_INIT
+
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_SYNC_CUSTOM_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_generic.h b/third_party/grpc/include/grpc/impl/codegen/sync_generic.h
new file mode 100644
index 0000000..d64db58
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/sync_generic.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_SYNC_GENERIC_H
+#define GRPC_IMPL_CODEGEN_SYNC_GENERIC_H
+/* Generic type defintions for gpr_sync. */
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/impl/codegen/atm.h>
+
+/* gpr_event */
+typedef struct {
+  gpr_atm state;
+} gpr_event;
+
+#define GPR_EVENT_INIT \
+  { 0 }
+
+/* gpr_refcount */
+typedef struct {
+  gpr_atm count;
+} gpr_refcount;
+
+/* gpr_stats_counter */
+typedef struct {
+  gpr_atm value;
+} gpr_stats_counter;
+
+#define GPR_STATS_INIT \
+  { 0 }
+
+#endif /* GRPC_IMPL_CODEGEN_SYNC_GENERIC_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_posix.h b/third_party/grpc/include/grpc/impl/codegen/sync_posix.h
new file mode 100644
index 0000000..d927046
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/sync_posix.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_SYNC_POSIX_H
+#define GRPC_IMPL_CODEGEN_SYNC_POSIX_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/impl/codegen/sync_generic.h>
+
+#include <pthread.h>
+
+typedef pthread_mutex_t gpr_mu;
+typedef pthread_cond_t gpr_cv;
+typedef pthread_once_t gpr_once;
+
+#define GPR_ONCE_INIT PTHREAD_ONCE_INIT
+
+#endif /* GRPC_IMPL_CODEGEN_SYNC_POSIX_H */
diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_windows.h b/third_party/grpc/include/grpc/impl/codegen/sync_windows.h
new file mode 100644
index 0000000..ba5d5ae
--- /dev/null
+++ b/third_party/grpc/include/grpc/impl/codegen/sync_windows.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_SYNC_WINDOWS_H
+#define GRPC_IMPL_CODEGEN_SYNC_WINDOWS_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/impl/codegen/sync_generic.h>
+
+typedef struct {
+  CRITICAL_SECTION cs; /* Not an SRWLock until Vista is unsupported */
+  int locked;
+} gpr_mu;
+
+typedef CONDITION_VARIABLE gpr_cv;
+
+typedef INIT_ONCE gpr_once;
+#define GPR_ONCE_INIT INIT_ONCE_STATIC_INIT
+
+#endif /* GRPC_IMPL_CODEGEN_SYNC_WINDOWS_H */
diff --git a/third_party/grpc/include/grpc/load_reporting.h b/third_party/grpc/include/grpc/load_reporting.h
new file mode 100644
index 0000000..55f50ea
--- /dev/null
+++ b/third_party/grpc/include/grpc/load_reporting.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_LOAD_REPORTING_H
+#define GRPC_LOAD_REPORTING_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Metadata key for the gRPC LB load balancer token.
+ *
+ * The value corresponding to this key is an opaque token that is given to the
+ * frontend as part of each pick; the frontend sends this token to the backend
+ * in each request it sends when using that pick. The token is used by the
+ * backend to verify the request and to allow the backend to report load to the
+ * gRPC LB system. */
+#define GRPC_LB_TOKEN_MD_KEY "lb-token"
+
+/** Metadata key for gRPC LB cost reporting.
+ *
+ * The value corresponding to this key is an opaque binary blob reported by the
+ * backend as part of its trailing metadata containing cost information for the
+ * call. */
+#define GRPC_LB_COST_MD_KEY "lb-cost-bin"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_LOAD_REPORTING_H */
diff --git a/third_party/grpc/include/grpc/module.modulemap b/third_party/grpc/include/grpc/module.modulemap
new file mode 100644
index 0000000..9612a05
--- /dev/null
+++ b/third_party/grpc/include/grpc/module.modulemap
@@ -0,0 +1,76 @@
+
+framework module grpc {
+  umbrella header "grpc.h"
+
+  header "support/alloc.h"
+  header "support/atm.h"
+  header "support/cpu.h"
+  header "support/log.h"
+  header "support/log_windows.h"
+  header "support/port_platform.h"
+  header "support/string_util.h"
+  header "support/sync.h"
+  header "support/sync_generic.h"
+  header "support/thd_id.h"
+  header "support/time.h"
+  header "impl/codegen/atm.h"
+  header "impl/codegen/fork.h"
+  header "impl/codegen/gpr_slice.h"
+  header "impl/codegen/gpr_types.h"
+  header "impl/codegen/log.h"
+  header "impl/codegen/port_platform.h"
+  header "impl/codegen/sync.h"
+  header "impl/codegen/sync_generic.h"
+  header "impl/codegen/byte_buffer.h"
+  header "impl/codegen/byte_buffer_reader.h"
+  header "impl/codegen/compression_types.h"
+  header "impl/codegen/connectivity_state.h"
+  header "impl/codegen/grpc_types.h"
+  header "impl/codegen/propagation_bits.h"
+  header "impl/codegen/slice.h"
+  header "impl/codegen/status.h"
+  header "impl/codegen/atm.h"
+  header "impl/codegen/fork.h"
+  header "impl/codegen/gpr_slice.h"
+  header "impl/codegen/gpr_types.h"
+  header "impl/codegen/log.h"
+  header "impl/codegen/port_platform.h"
+  header "impl/codegen/sync.h"
+  header "impl/codegen/sync_generic.h"
+  header "grpc_security.h"
+  header "byte_buffer.h"
+  header "byte_buffer_reader.h"
+  header "compression.h"
+  header "fork.h"
+  header "grpc.h"
+  header "grpc_posix.h"
+  header "grpc_security_constants.h"
+  header "load_reporting.h"
+  header "slice.h"
+  header "slice_buffer.h"
+  header "status.h"
+  header "support/workaround_list.h"
+  header "census.h"
+
+  textual header "support/atm_gcc_atomic.h"
+  textual header "support/atm_gcc_sync.h"
+  textual header "support/atm_windows.h"
+  textual header "support/sync_custom.h"
+  textual header "support/sync_posix.h"
+  textual header "support/sync_windows.h"
+  textual header "impl/codegen/atm_gcc_atomic.h"
+  textual header "impl/codegen/atm_gcc_sync.h"
+  textual header "impl/codegen/atm_windows.h"
+  textual header "impl/codegen/sync_custom.h"
+  textual header "impl/codegen/sync_posix.h"
+  textual header "impl/codegen/sync_windows.h"
+  textual header "impl/codegen/atm_gcc_atomic.h"
+  textual header "impl/codegen/atm_gcc_sync.h"
+  textual header "impl/codegen/atm_windows.h"
+  textual header "impl/codegen/sync_custom.h"
+  textual header "impl/codegen/sync_posix.h"
+  textual header "impl/codegen/sync_windows.h"
+
+  export *
+  module * { export * }
+}
diff --git a/third_party/grpc/include/grpc/slice.h b/third_party/grpc/include/grpc/slice.h
new file mode 100644
index 0000000..ce48292
--- /dev/null
+++ b/third_party/grpc/include/grpc/slice.h
@@ -0,0 +1,172 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SLICE_H
+#define GRPC_SLICE_H
+
+#include <grpc/support/port_platform.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 grpc_slice grpc_slice_ref(grpc_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 grpc_slice_new(start,
+   len, dest) where dest!=NULL , then (*dest)(start) is called, else if s is
+   ultimately derived from a call to grpc_slice_new_with_len(start, len, dest)
+   where dest!=NULL , then (*dest)(start, len).  Requires s initialized.  */
+GPRAPI void grpc_slice_unref(grpc_slice s);
+
+/** Copy slice - create a new slice that contains the same data as s */
+GPRAPI grpc_slice grpc_slice_copy(grpc_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 grpc_slice grpc_slice_new(void* p, size_t len, void (*destroy)(void*));
+
+/** Equivalent to grpc_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 grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
+                                                void (*destroy)(void*),
+                                                void* user_data);
+
+/** Equivalent to grpc_slice_new, but with a two argument destroy function that
+   also takes the slice length. */
+GPRAPI grpc_slice grpc_slice_new_with_len(void* p, size_t len,
+                                          void (*destroy)(void*, size_t));
+
+/** Equivalent to grpc_slice_new(malloc(len), len, free), but saves one malloc()
+   call.
+   Aborts if malloc() fails. */
+GPRAPI grpc_slice grpc_slice_malloc(size_t length);
+GPRAPI grpc_slice grpc_slice_malloc_large(size_t length);
+
+#define GRPC_SLICE_MALLOC(len) grpc_slice_malloc(len)
+
+/** Intern a slice:
+
+   The return value for two invocations of this function with  the same sequence
+   of bytes is a slice which points to the same memory. */
+GPRAPI grpc_slice grpc_slice_intern(grpc_slice slice);
+
+/** Create a slice by copying a string.
+   Does not preserve null terminators.
+   Equivalent to:
+     size_t len = strlen(source);
+     grpc_slice slice = grpc_slice_malloc(len);
+     memcpy(slice->data, source, len); */
+GPRAPI grpc_slice grpc_slice_from_copied_string(const char* source);
+
+/** Create a slice by copying a buffer.
+   Equivalent to:
+     grpc_slice slice = grpc_slice_malloc(len);
+     memcpy(slice->data, source, len); */
+GPRAPI grpc_slice grpc_slice_from_copied_buffer(const char* source, size_t len);
+
+/** Create a slice pointing to constant memory */
+GPRAPI grpc_slice grpc_slice_from_static_string(const char* source);
+
+/** Create a slice pointing to constant memory */
+GPRAPI grpc_slice grpc_slice_from_static_buffer(const void* source, size_t len);
+
+/** Return a result slice derived from s, which shares a ref count with \a s,
+   where result.data==s.data+begin, and result.length==end-begin. The ref count
+   of \a s is increased by one. Do not assign result back to \a s.
+   Requires s initialized, begin <= end, begin <= s.length, and
+   end <= source->length. */
+GPRAPI grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end);
+
+/** The same as grpc_slice_sub, but without altering the ref count */
+GPRAPI grpc_slice grpc_slice_sub_no_ref(grpc_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 grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split);
+
+typedef enum {
+  GRPC_SLICE_REF_TAIL = 1,
+  GRPC_SLICE_REF_HEAD = 2,
+  GRPC_SLICE_REF_BOTH = 1 + 2
+} grpc_slice_ref_whom;
+
+/** The same as grpc_slice_split_tail, but with an option to skip altering
+ * refcounts (grpc_slice_split_tail_maybe_ref(..., true) is equivalent to
+ * grpc_slice_split_tail(...)) */
+GPRAPI grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice* s, size_t split,
+                                                  grpc_slice_ref_whom ref_whom);
+
+/** 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 grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split);
+
+GPRAPI grpc_slice grpc_empty_slice(void);
+
+GPRAPI uint32_t grpc_slice_default_hash_impl(grpc_slice s);
+GPRAPI int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b);
+
+GPRAPI int grpc_slice_eq(grpc_slice a, grpc_slice b);
+
+/** 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 grpc_slice_cmp(grpc_slice a, grpc_slice b);
+GPRAPI int grpc_slice_str_cmp(grpc_slice a, const char* b);
+
+/** return non-zero if the first blen bytes of a are equal to b */
+GPRAPI int grpc_slice_buf_start_eq(grpc_slice a, const void* b, size_t blen);
+
+/** return the index of the last instance of \a c in \a s, or -1 if not found */
+GPRAPI int grpc_slice_rchr(grpc_slice s, char c);
+GPRAPI int grpc_slice_chr(grpc_slice s, char c);
+
+/** return the index of the first occurance of \a needle in \a haystack, or -1
+   if it's not found */
+GPRAPI int grpc_slice_slice(grpc_slice haystack, grpc_slice needle);
+
+GPRAPI uint32_t grpc_slice_hash(grpc_slice s);
+
+/** Do two slices point at the same memory, with the same length
+   If a or b is inlined, actually compares data */
+GPRAPI int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b);
+
+/** Return a slice pointing to newly allocated memory that has the same contents
+ * as \a s */
+GPRAPI grpc_slice grpc_slice_dup(grpc_slice a);
+
+/** Return a copy of slice as a C string. Offers no protection against embedded
+   NULL's. Returned string must be freed with gpr_free. */
+GPRAPI char* grpc_slice_to_c_string(grpc_slice s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_SLICE_H */
diff --git a/third_party/grpc/include/grpc/slice_buffer.h b/third_party/grpc/include/grpc/slice_buffer.h
new file mode 100644
index 0000000..3260019
--- /dev/null
+++ b/third_party/grpc/include/grpc/slice_buffer.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SLICE_BUFFER_H
+#define GRPC_SLICE_BUFFER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** initialize a slice buffer */
+GPRAPI void grpc_slice_buffer_init(grpc_slice_buffer* sb);
+/** destroy a slice buffer - unrefs any held elements */
+GPRAPI void grpc_slice_buffer_destroy(grpc_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 grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_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 grpc_slice_buffer_add_indexed(grpc_slice_buffer* sb,
+                                            grpc_slice slice);
+GPRAPI void grpc_slice_buffer_addn(grpc_slice_buffer* sb, grpc_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* grpc_slice_buffer_tiny_add(grpc_slice_buffer* sb, size_t len);
+/** pop the last buffer, but don't unref it */
+GPRAPI void grpc_slice_buffer_pop(grpc_slice_buffer* sb);
+/** clear a slice buffer, unref all elements */
+GPRAPI void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb);
+/** swap the contents of two slice buffers */
+GPRAPI void grpc_slice_buffer_swap(grpc_slice_buffer* a, grpc_slice_buffer* b);
+/** move all of the elements of src into dst */
+GPRAPI void grpc_slice_buffer_move_into(grpc_slice_buffer* src,
+                                        grpc_slice_buffer* dst);
+/** remove n bytes from the end of a slice buffer */
+GPRAPI void grpc_slice_buffer_trim_end(grpc_slice_buffer* src, size_t n,
+                                       grpc_slice_buffer* garbage);
+/** move the first n bytes of src into dst */
+GPRAPI void grpc_slice_buffer_move_first(grpc_slice_buffer* src, size_t n,
+                                         grpc_slice_buffer* dst);
+/** move the first n bytes of src into dst without adding references */
+GPRAPI void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer* src,
+                                                size_t n,
+                                                grpc_slice_buffer* dst);
+/** move the first n bytes of src into dst (copying them) */
+GPRAPI void grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer* src,
+                                                     size_t n, void* dst);
+/** take the first slice in the slice buffer */
+GPRAPI grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer* src);
+/** undo the above with (a possibly different) \a slice */
+GPRAPI void grpc_slice_buffer_undo_take_first(grpc_slice_buffer* src,
+                                              grpc_slice slice);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_SLICE_BUFFER_H */
diff --git a/third_party/grpc/include/grpc/status.h b/third_party/grpc/include/grpc/status.h
new file mode 100644
index 0000000..ecb9668
--- /dev/null
+++ b/third_party/grpc/include/grpc/status.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_STATUS_H
+#define GRPC_STATUS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/status.h>
+
+#endif /* GRPC_STATUS_H */
diff --git a/third_party/grpc/include/grpc/support/alloc.h b/third_party/grpc/include/grpc/support/alloc.h
new file mode 100644
index 0000000..8bd940b
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/alloc.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_ALLOC_H
+#define GRPC_SUPPORT_ALLOC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct gpr_allocation_functions {
+  void* (*malloc_fn)(size_t size);
+  void* (*zalloc_fn)(size_t size); /** if NULL, uses malloc_fn then memset */
+  void* (*realloc_fn)(void* ptr, size_t size);
+  void (*free_fn)(void* ptr);
+} gpr_allocation_functions;
+
+/** malloc.
+ * If size==0, always returns NULL. Otherwise this function never returns NULL.
+ * The pointer returned is suitably aligned for any kind of variable it could
+ * contain.
+ */
+GPRAPI void* gpr_malloc(size_t size);
+/** like malloc, but zero all bytes before returning them */
+GPRAPI void* gpr_zalloc(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 alignment, which
+ * must be a power of 2. */
+GPRAPI void* gpr_malloc_aligned(size_t size, size_t alignment);
+/** 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.
+ * The functions.free_fn implementation must be a no-op for NULL input. */
+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(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_SUPPORT_ALLOC_H */
diff --git a/third_party/grpc/include/grpc/support/atm.h b/third_party/grpc/include/grpc/support/atm.h
new file mode 100644
index 0000000..073b0a6
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/atm.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_ATM_H
+#define GRPC_SUPPORT_ATM_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/atm.h>
+
+#endif /* GRPC_SUPPORT_ATM_H */
diff --git a/third_party/grpc/include/grpc/support/atm_gcc_atomic.h b/third_party/grpc/include/grpc/support/atm_gcc_atomic.h
new file mode 100644
index 0000000..ae603db
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/atm_gcc_atomic.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_ATM_GCC_ATOMIC_H
+#define GRPC_SUPPORT_ATM_GCC_ATOMIC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/atm_gcc_atomic.h>
+
+#endif /* GRPC_SUPPORT_ATM_GCC_ATOMIC_H */
diff --git a/third_party/grpc/include/grpc/support/atm_gcc_sync.h b/third_party/grpc/include/grpc/support/atm_gcc_sync.h
new file mode 100644
index 0000000..6f51fdb
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/atm_gcc_sync.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_ATM_GCC_SYNC_H
+#define GRPC_SUPPORT_ATM_GCC_SYNC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/atm_gcc_sync.h>
+
+#endif /* GRPC_SUPPORT_ATM_GCC_SYNC_H */
diff --git a/third_party/grpc/include/grpc/support/atm_windows.h b/third_party/grpc/include/grpc/support/atm_windows.h
new file mode 100644
index 0000000..36955e4
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/atm_windows.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_ATM_WINDOWS_H
+#define GRPC_SUPPORT_ATM_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/atm_windows.h>
+
+#endif /* GRPC_SUPPORT_ATM_WINDOWS_H */
diff --git a/third_party/grpc/include/grpc/support/cpu.h b/third_party/grpc/include/grpc/support/cpu.h
new file mode 100644
index 0000000..f0e898e
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/cpu.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_CPU_H
+#define GRPC_SUPPORT_CPU_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Interface providing CPU information for currently running system */
+
+/** Return the number of CPU cores on the current system. Will return 0 if
+   the information is not available. */
+GPRAPI unsigned gpr_cpu_num_cores(void);
+
+/** Return the CPU on which the current thread is executing; N.B. This should
+   be considered advisory only - it is possible that the thread is switched
+   to a different CPU at any time. Returns a value in range
+   [0, gpr_cpu_num_cores() - 1] */
+GPRAPI unsigned gpr_cpu_current_cpu(void);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif /* GRPC_SUPPORT_CPU_H */
diff --git a/third_party/grpc/include/grpc/support/log.h b/third_party/grpc/include/grpc/support/log.h
new file mode 100644
index 0000000..8d8742b
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/log.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_LOG_H
+#define GRPC_SUPPORT_LOG_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/log.h>
+
+#endif /* GRPC_SUPPORT_LOG_H */
diff --git a/third_party/grpc/include/grpc/support/log_windows.h b/third_party/grpc/include/grpc/support/log_windows.h
new file mode 100644
index 0000000..e833f9d
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/log_windows.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_LOG_WINDOWS_H
+#define GRPC_SUPPORT_LOG_WINDOWS_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Returns a string allocated with gpr_malloc that contains a UTF-8
+ * formatted error message, corresponding to the error messageid.
+ * Use in conjunction with GetLastError() et al.
+ */
+GPRAPI char* gpr_format_message(int messageid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_SUPPORT_LOG_WINDOWS_H */
diff --git a/third_party/grpc/include/grpc/support/port_platform.h b/third_party/grpc/include/grpc/support/port_platform.h
new file mode 100644
index 0000000..26025dc
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/port_platform.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_PORT_PLATFORM_H
+#define GRPC_SUPPORT_PORT_PLATFORM_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#endif /* GRPC_SUPPORT_PORT_PLATFORM_H */
diff --git a/third_party/grpc/include/grpc/support/string_util.h b/third_party/grpc/include/grpc/support/string_util.h
new file mode 100644
index 0000000..2679160
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/string_util.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_STRING_UTIL_H
+#define GRPC_SUPPORT_STRING_UTIL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/gpr_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** String utility functions */
+
+/** Returns a copy of src that can be passed to gpr_free().
+   If allocation fails or if src is NULL, returns NULL. */
+GPRAPI char* gpr_strdup(const char* src);
+
+/** printf to a newly-allocated string.  The set of supported formats may vary
+   between platforms.
+
+   On success, returns the number of bytes printed (excluding the final '\0'),
+   and *strp points to a string which must later be destroyed with gpr_free().
+
+   On error, returns -1 and sets *strp to NULL. If the format string is bad,
+   the result is undefined. */
+GPRAPI int gpr_asprintf(char** strp, const char* format, ...)
+    GPR_PRINT_FORMAT_CHECK(2, 3);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_SUPPORT_STRING_UTIL_H */
diff --git a/third_party/grpc/include/grpc/support/sync.h b/third_party/grpc/include/grpc/support/sync.h
new file mode 100644
index 0000000..da820de
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/sync.h
@@ -0,0 +1,282 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_SYNC_H
+#define GRPC_SUPPORT_SYNC_H
+
+#include <grpc/support/port_platform.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);
+
+/** Return non-zero iff the reference count of *r is one, and thus is owned
+   by exactly one object. */
+GPRAPI int gpr_ref_is_unique(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
+}  // extern "C"
+#endif
+
+#endif /* GRPC_SUPPORT_SYNC_H */
diff --git a/third_party/grpc/include/grpc/support/sync_custom.h b/third_party/grpc/include/grpc/support/sync_custom.h
new file mode 100644
index 0000000..27cf0e0
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/sync_custom.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_SYNC_CUSTOM_H
+#define GRPC_SUPPORT_SYNC_CUSTOM_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/sync_custom.h>
+
+#endif /* GRPC_SUPPORT_SYNC_CUSTOM_H */
diff --git a/third_party/grpc/include/grpc/support/sync_generic.h b/third_party/grpc/include/grpc/support/sync_generic.h
new file mode 100644
index 0000000..93028c4
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/sync_generic.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_SYNC_GENERIC_H
+#define GRPC_SUPPORT_SYNC_GENERIC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/sync_generic.h>
+
+#endif /* GRPC_SUPPORT_SYNC_GENERIC_H */
diff --git a/third_party/grpc/include/grpc/support/sync_posix.h b/third_party/grpc/include/grpc/support/sync_posix.h
new file mode 100644
index 0000000..3dce7ee
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/sync_posix.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_SYNC_POSIX_H
+#define GRPC_SUPPORT_SYNC_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/sync_posix.h>
+
+#endif /* GRPC_SUPPORT_SYNC_POSIX_H */
diff --git a/third_party/grpc/include/grpc/support/sync_windows.h b/third_party/grpc/include/grpc/support/sync_windows.h
new file mode 100644
index 0000000..a493c86
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/sync_windows.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_SYNC_WINDOWS_H
+#define GRPC_SUPPORT_SYNC_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/sync_windows.h>
+
+#endif /* GRPC_SUPPORT_SYNC_WINDOWS_H */
diff --git a/third_party/grpc/include/grpc/support/thd_id.h b/third_party/grpc/include/grpc/support/thd_id.h
new file mode 100644
index 0000000..e9b2b7e
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/thd_id.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_THD_ID_H
+#define GRPC_SUPPORT_THD_ID_H
+/** Thread ID interface for GPR.
+
+   Used by some wrapped languages for logging purposes.
+
+   Types
+        gpr_thd_id        a unique opaque identifier for a thread.
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uintptr_t gpr_thd_id;
+
+/** Returns the identifier of the current thread. */
+GPRAPI gpr_thd_id gpr_thd_currentid(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_SUPPORT_THD_ID_H */
diff --git a/third_party/grpc/include/grpc/support/time.h b/third_party/grpc/include/grpc/support/time.h
new file mode 100644
index 0000000..550ffc2
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/time.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_TIME_H
+#define GRPC_SUPPORT_TIME_H
+
+#include <grpc/support/port_platform.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/third_party/grpc/include/grpc/support/workaround_list.h b/third_party/grpc/include/grpc/support/workaround_list.h
new file mode 100644
index 0000000..e28dfa1
--- /dev/null
+++ b/third_party/grpc/include/grpc/support/workaround_list.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SUPPORT_WORKAROUND_LIST_H
+#define GRPC_SUPPORT_WORKAROUND_LIST_H
+
+/* The list of IDs of server workarounds currently maintained by gRPC. For
+ * explanation and detailed descriptions of workarounds, see
+ * /doc/workarounds.md
+ */
+typedef enum {
+  GRPC_WORKAROUND_ID_CRONET_COMPRESSION = 0,
+  GRPC_MAX_WORKAROUND_ID
+} grpc_workaround_list;
+
+#endif /* GRPC_SUPPORT_WORKAROUND_LIST_H */
diff --git a/third_party/grpc/include/grpcpp/alarm.h b/third_party/grpc/include/grpcpp/alarm.h
new file mode 100644
index 0000000..2343c11
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/alarm.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_ALARM_H
+#define GRPCPP_ALARM_H
+
+#include <grpcpp/alarm_impl.h>
+
+namespace grpc {
+
+typedef ::grpc_impl::Alarm Alarm;
+}
+
+#endif  // GRPCPP_ALARM_H
diff --git a/third_party/grpc/include/grpcpp/alarm_impl.h b/third_party/grpc/include/grpcpp/alarm_impl.h
new file mode 100644
index 0000000..7844e7c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/alarm_impl.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/// An Alarm posts the user provided tag to its associated completion queue upon
+/// expiry or cancellation.
+#ifndef GRPCPP_ALARM_IMPL_H
+#define GRPCPP_ALARM_IMPL_H
+
+#include <functional>
+
+#include <grpc/grpc.h>
+#include <grpcpp/impl/codegen/completion_queue.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/time.h>
+#include <grpcpp/impl/grpc_library.h>
+
+namespace grpc_impl {
+
+/// A thin wrapper around \a grpc_alarm (see / \a / src/core/surface/alarm.h).
+class Alarm : private ::grpc::GrpcLibraryCodegen {
+ public:
+  /// Create an unset completion queue alarm
+  Alarm();
+
+  /// Destroy the given completion queue alarm, cancelling it in the process.
+  ~Alarm();
+
+  /// DEPRECATED: Create and set a completion queue alarm instance associated to
+  /// \a cq.
+  /// This form is deprecated because it is inherently racy.
+  /// \internal We rely on the presence of \a cq for grpc initialization. If \a
+  /// cq were ever to be removed, a reference to a static
+  /// internal::GrpcLibraryInitializer instance would need to be introduced
+  /// here. \endinternal.
+  template <typename T>
+  Alarm(::grpc::CompletionQueue* cq, const T& deadline, void* tag) : Alarm() {
+    SetInternal(cq, ::grpc::TimePoint<T>(deadline).raw_time(), tag);
+  }
+
+  /// Trigger an alarm instance on completion queue \a cq at the specified time.
+  /// Once the alarm expires (at \a deadline) or it's cancelled (see \a Cancel),
+  /// an event with tag \a tag will be added to \a cq. If the alarm expired, the
+  /// event's success bit will be true, false otherwise (ie, upon cancellation).
+  template <typename T>
+  void Set(::grpc::CompletionQueue* cq, const T& deadline, void* tag) {
+    SetInternal(cq, ::grpc::TimePoint<T>(deadline).raw_time(), tag);
+  }
+
+  /// Alarms aren't copyable.
+  Alarm(const Alarm&) = delete;
+  Alarm& operator=(const Alarm&) = delete;
+
+  /// Alarms are movable.
+  Alarm(Alarm&& rhs) : alarm_(rhs.alarm_) { rhs.alarm_ = nullptr; }
+  Alarm& operator=(Alarm&& rhs) {
+    alarm_ = rhs.alarm_;
+    rhs.alarm_ = nullptr;
+    return *this;
+  }
+
+  /// Cancel a completion queue alarm. Calling this function over an alarm that
+  /// has already fired has no effect.
+  void Cancel();
+
+  /// NOTE: class experimental_type is not part of the public API of this class
+  /// TODO(vjpai): Move these contents to the public API of Alarm when
+  ///              they are no longer experimental
+  class experimental_type {
+   public:
+    explicit experimental_type(Alarm* alarm) : alarm_(alarm) {}
+
+    /// Set an alarm to invoke callback \a f. The argument to the callback
+    /// states whether the alarm expired at \a deadline (true) or was cancelled
+    /// (false)
+    template <typename T>
+    void Set(const T& deadline, std::function<void(bool)> f) {
+      alarm_->SetInternal(::grpc::TimePoint<T>(deadline).raw_time(),
+                          std::move(f));
+    }
+
+   private:
+    Alarm* alarm_;
+  };
+
+  /// NOTE: The function experimental() is not stable public API. It is a view
+  /// to the experimental components of this class. It may be changed or removed
+  /// at any time.
+  experimental_type experimental() { return experimental_type(this); }
+
+ private:
+  void SetInternal(::grpc::CompletionQueue* cq, gpr_timespec deadline,
+                   void* tag);
+  void SetInternal(gpr_timespec deadline, std::function<void(bool)> f);
+
+  ::grpc::internal::CompletionQueueTag* alarm_;
+};
+
+}  // namespace grpc_impl
+
+#endif  // GRPCPP_ALARM_IMPL_H
diff --git a/third_party/grpc/include/grpcpp/channel.h b/third_party/grpc/include/grpcpp/channel.h
new file mode 100644
index 0000000..ee83396
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/channel.h
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_CHANNEL_H
+#define GRPCPP_CHANNEL_H
+
+#include <memory>
+#include <mutex>
+
+#include <grpc/grpc.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+struct grpc_channel;
+
+namespace grpc {
+
+namespace experimental {
+/// Resets the channel's connection backoff.
+/// TODO(roth): Once we see whether this proves useful, either create a gRFC
+/// and change this to be a method of the Channel class, or remove it.
+void ChannelResetConnectionBackoff(Channel* channel);
+}  // namespace experimental
+
+/// Channels represent a connection to an endpoint. Created by \a CreateChannel.
+class Channel final : public ChannelInterface,
+                      public internal::CallHook,
+                      public std::enable_shared_from_this<Channel>,
+                      private GrpcLibraryCodegen {
+ public:
+  ~Channel();
+
+  /// Get the current channel state. If the channel is in IDLE and
+  /// \a try_to_connect is set to true, try to connect.
+  grpc_connectivity_state GetState(bool try_to_connect) override;
+
+  /// Returns the LB policy name, or the empty string if not yet available.
+  grpc::string GetLoadBalancingPolicyName() const;
+
+  /// Returns the service config in JSON form, or the empty string if
+  /// not available.
+  grpc::string GetServiceConfigJSON() const;
+
+ private:
+  template <class InputMessage, class OutputMessage>
+  friend class internal::BlockingUnaryCallImpl;
+  friend void experimental::ChannelResetConnectionBackoff(Channel* channel);
+  friend std::shared_ptr<Channel> CreateChannelInternal(
+      const grpc::string& host, grpc_channel* c_channel,
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators);
+  friend class internal::InterceptedChannel;
+  Channel(const grpc::string& host, grpc_channel* c_channel,
+          std::vector<
+              std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+              interceptor_creators);
+
+  internal::Call CreateCall(const internal::RpcMethod& method,
+                            ClientContext* context,
+                            CompletionQueue* cq) override;
+  void PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                        internal::Call* call) override;
+  void* RegisterMethod(const char* method) override;
+
+  void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
+                               gpr_timespec deadline, CompletionQueue* cq,
+                               void* tag) override;
+  bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
+                              gpr_timespec deadline) override;
+
+  CompletionQueue* CallbackCQ() override;
+
+  internal::Call CreateCallInternal(const internal::RpcMethod& method,
+                                    ClientContext* context, CompletionQueue* cq,
+                                    size_t interceptor_pos) override;
+
+  const grpc::string host_;
+  grpc_channel* const c_channel_;  // owned
+
+  // mu_ protects callback_cq_ (the per-channel callbackable completion queue)
+  std::mutex mu_;
+
+  // callback_cq_ references the callbackable completion queue associated
+  // with this channel (if any). It is set on the first call to CallbackCQ().
+  // It is _not owned_ by the channel; ownership belongs with its internal
+  // shutdown callback tag (invoked when the CQ is fully shutdown).
+  CompletionQueue* callback_cq_ = nullptr;
+
+  std::vector<std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+      interceptor_creators_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_CHANNEL_H
diff --git a/third_party/grpc/include/grpcpp/client_context.h b/third_party/grpc/include/grpcpp/client_context.h
new file mode 100644
index 0000000..1994fcc
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/client_context.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/// A ClientContext allows the person implementing a service client to:
+///
+/// - Add custom metadata key-value pairs that will propagated to the server
+/// side.
+/// - Control call settings such as compression and authentication.
+/// - Initial and trailing metadata coming from the server.
+/// - Get performance metrics (ie, census).
+///
+/// Context settings are only relevant to the call they are invoked with, that
+/// is to say, they aren't sticky. Some of these settings, such as the
+/// compression options, can be made persistent at channel construction time
+/// (see \a grpc::CreateCustomChannel).
+///
+/// \warning ClientContext instances should \em not be reused across rpcs.
+
+#ifndef GRPCPP_CLIENT_CONTEXT_H
+#define GRPCPP_CLIENT_CONTEXT_H
+
+#include <grpcpp/impl/codegen/client_context.h>
+
+#endif  // GRPCPP_CLIENT_CONTEXT_H
diff --git a/third_party/grpc/include/grpcpp/completion_queue.h b/third_party/grpc/include/grpcpp/completion_queue.h
new file mode 100644
index 0000000..123b277
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/completion_queue.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_COMPLETION_QUEUE_H
+#define GRPCPP_COMPLETION_QUEUE_H
+
+#include <grpcpp/impl/codegen/completion_queue.h>
+
+#endif  // GRPCPP_COMPLETION_QUEUE_H
diff --git a/third_party/grpc/include/grpcpp/create_channel.h b/third_party/grpc/include/grpcpp/create_channel.h
new file mode 100644
index 0000000..e8a2a70
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/create_channel.h
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_CREATE_CHANNEL_H
+#define GRPCPP_CREATE_CHANNEL_H
+
+#include <memory>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+
+/// Create a new \a Channel pointing to \a target.
+///
+/// \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 (one on which all operations
+/// fail) is returned.
+std::shared_ptr<Channel> CreateChannel(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds);
+
+/// Create a new \em custom \a Channel pointing to \a target.
+///
+/// \warning For advanced use and testing ONLY. Override default channel
+/// arguments only if necessary.
+///
+/// \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 (one on which all operations
+/// fail) is returned.
+/// \param args Options for channel creation.
+std::shared_ptr<Channel> CreateCustomChannel(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds,
+    const ChannelArguments& args);
+
+namespace experimental {
+/// Create a new \em custom \a Channel pointing to \a target with \a
+/// interceptors being invoked per call.
+///
+/// \warning For advanced use and testing ONLY. Override default channel
+/// arguments only if necessary.
+///
+/// \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 (one on which all operations
+/// fail) is returned.
+/// \param args Options for channel creation.
+std::shared_ptr<Channel> CreateCustomChannelWithInterceptors(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds,
+    const ChannelArguments& args,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators);
+}  // namespace experimental
+}  // namespace grpc
+
+#endif  // GRPCPP_CREATE_CHANNEL_H
diff --git a/third_party/grpc/include/grpcpp/create_channel_posix.h b/third_party/grpc/include/grpcpp/create_channel_posix.h
new file mode 100644
index 0000000..8085140
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/create_channel_posix.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_CREATE_CHANNEL_POSIX_H
+#define GRPCPP_CREATE_CHANNEL_POSIX_H
+
+#include <memory>
+
+#include <grpc/support/port_platform.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/support/channel_arguments.h>
+
+namespace grpc {
+
+#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
+
+/// Create a new \a Channel communicating over the given file descriptor.
+///
+/// \param target The name of the target.
+/// \param fd The file descriptor representing a socket.
+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);
+
+namespace experimental {
+
+/// 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.
+/// \param interceptor_creators Vector of interceptor factory objects.
+std::shared_ptr<Channel> CreateCustomInsecureChannelWithInterceptorsFromFd(
+    const grpc::string& target, int fd, const ChannelArguments& args,
+    std::unique_ptr<std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>>
+        interceptor_creators);
+
+}  // namespace experimental
+
+#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
+
+}  // namespace grpc
+
+#endif  // GRPCPP_CREATE_CHANNEL_POSIX_H
diff --git a/third_party/grpc/include/grpcpp/ext/channelz_service_plugin.h b/third_party/grpc/include/grpcpp/ext/channelz_service_plugin.h
new file mode 100644
index 0000000..af3192d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/ext/channelz_service_plugin.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_EXT_CHANNELZ_SERVICE_PLUGIN_H
+#define GRPCPP_EXT_CHANNELZ_SERVICE_PLUGIN_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/impl/server_initializer.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+namespace channelz {
+namespace experimental {
+
+/// Add channelz server plugin to \a ServerBuilder. This function should
+/// be called at static initialization time. This service is experimental
+/// for now. Track progress in https://github.com/grpc/grpc/issues/15988.
+void InitChannelzService();
+
+}  // namespace experimental
+}  // namespace channelz
+}  // namespace grpc
+
+#endif  // GRPCPP_EXT_CHANNELZ_SERVICE_PLUGIN_H
diff --git a/third_party/grpc/include/grpcpp/ext/health_check_service_server_builder_option.h b/third_party/grpc/include/grpcpp/ext/health_check_service_server_builder_option.h
new file mode 100644
index 0000000..dd43b05
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/ext/health_check_service_server_builder_option.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
+#define GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
+
+#include <memory>
+
+#include <grpcpp/health_check_service_interface.h>
+#include <grpcpp/impl/server_builder_option.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+
+class HealthCheckServiceServerBuilderOption : public ServerBuilderOption {
+ public:
+  /// The ownership of \a hc will be taken and transferred to the grpc server.
+  /// To explicitly disable default service, pass in a nullptr.
+  explicit HealthCheckServiceServerBuilderOption(
+      std::unique_ptr<HealthCheckServiceInterface> hc);
+  ~HealthCheckServiceServerBuilderOption() override {}
+  void UpdateArguments(ChannelArguments* args) override;
+  void UpdatePlugins(
+      std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) override;
+
+ private:
+  std::unique_ptr<HealthCheckServiceInterface> hc_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H
diff --git a/third_party/grpc/include/grpcpp/ext/proto_server_reflection_plugin.h b/third_party/grpc/include/grpcpp/ext/proto_server_reflection_plugin.h
new file mode 100644
index 0000000..1cfdc1b
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/ext/proto_server_reflection_plugin.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
+#define GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
+
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+class ServerInitializer;
+class ProtoServerReflection;
+}  // namespace grpc
+
+namespace grpc {
+namespace reflection {
+
+class ProtoServerReflectionPlugin : public ::grpc::ServerBuilderPlugin {
+ public:
+  ProtoServerReflectionPlugin();
+  ::grpc::string name() override;
+  void InitServer(::grpc::ServerInitializer* si) override;
+  void Finish(::grpc::ServerInitializer* si) override;
+  void ChangeArguments(const ::grpc::string& name, void* value) override;
+  bool has_async_methods() const override;
+  bool has_sync_methods() const override;
+
+ private:
+  std::shared_ptr<grpc::ProtoServerReflection> reflection_service_;
+};
+
+/// Add proto reflection plugin to \a ServerBuilder.
+/// This function should be called at the static initialization time.
+void InitProtoReflectionServerBuilderPlugin();
+
+}  // namespace reflection
+}  // namespace grpc
+
+#endif  // GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H
diff --git a/third_party/grpc/include/grpcpp/ext/server_load_reporting.h b/third_party/grpc/include/grpcpp/ext/server_load_reporting.h
new file mode 100644
index 0000000..939569c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/ext/server_load_reporting.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_EXT_SERVER_LOAD_REPORTING_H
+#define GRPCPP_EXT_SERVER_LOAD_REPORTING_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/load_reporting.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/server_builder_option.h>
+
+namespace grpc {
+namespace load_reporter {
+namespace experimental {
+
+// The ServerBuilderOption to enable server-side load reporting feature. To
+// enable the feature, please make sure the binary builds with the
+// grpcpp_server_load_reporting library and set this option in the
+// ServerBuilder.
+class LoadReportingServiceServerBuilderOption : public ServerBuilderOption {
+ public:
+  void UpdateArguments(::grpc::ChannelArguments* args) override;
+  void UpdatePlugins(std::vector<std::unique_ptr<::grpc::ServerBuilderPlugin>>*
+                         plugins) override;
+};
+
+// Adds the load reporting cost with \a cost_name and \a cost_value in the
+// trailing metadata of the server context.
+void AddLoadReportingCost(grpc::ServerContext* ctx,
+                          const grpc::string& cost_name, double cost_value);
+
+}  // namespace experimental
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPCPP_EXT_SERVER_LOAD_REPORTING_H
diff --git a/third_party/grpc/include/grpcpp/generic/async_generic_service.h b/third_party/grpc/include/grpcpp/generic/async_generic_service.h
new file mode 100644
index 0000000..2c67edc
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/generic/async_generic_service.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H
+#define GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H
+
+#include <grpcpp/impl/codegen/async_generic_service.h>
+
+#endif  // GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H
diff --git a/third_party/grpc/include/grpcpp/generic/generic_stub.h b/third_party/grpc/include/grpcpp/generic/generic_stub.h
new file mode 100644
index 0000000..eb01418
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/generic/generic_stub.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_GENERIC_GENERIC_STUB_H
+#define GRPCPP_GENERIC_GENERIC_STUB_H
+
+#include <functional>
+
+#include <grpcpp/support/async_stream.h>
+#include <grpcpp/support/async_unary_call.h>
+#include <grpcpp/support/byte_buffer.h>
+#include <grpcpp/support/client_callback.h>
+#include <grpcpp/support/status.h>
+
+namespace grpc {
+
+class CompletionQueue;
+typedef ClientAsyncReaderWriter<ByteBuffer, ByteBuffer>
+    GenericClientAsyncReaderWriter;
+typedef ClientAsyncResponseReader<ByteBuffer> GenericClientAsyncResponseReader;
+
+/// Generic stubs provide a type-unsafe interface to call gRPC methods
+/// by name.
+class GenericStub final {
+ public:
+  explicit GenericStub(std::shared_ptr<ChannelInterface> channel)
+      : channel_(channel) {}
+
+  /// Setup a call to a named method \a method using \a context, but don't
+  /// start it. Let it be started explicitly with StartCall and a tag.
+  /// The return value only indicates whether or not registration of the call
+  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
+  std::unique_ptr<GenericClientAsyncReaderWriter> PrepareCall(
+      ClientContext* context, const grpc::string& method, CompletionQueue* cq);
+
+  /// Setup a unary call to a named method \a method using \a context, and don't
+  /// start it. Let it be started explicitly with StartCall.
+  /// The return value only indicates whether or not registration of the call
+  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
+  std::unique_ptr<GenericClientAsyncResponseReader> PrepareUnaryCall(
+      ClientContext* context, const grpc::string& method,
+      const ByteBuffer& request, CompletionQueue* cq);
+
+  /// DEPRECATED for multi-threaded use
+  /// Begin a call to a named method \a method using \a context.
+  /// A tag \a tag will be delivered to \a cq when the call has been started
+  /// (i.e, initial metadata has been sent).
+  /// The return value only indicates whether or not registration of the call
+  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
+  std::unique_ptr<GenericClientAsyncReaderWriter> Call(
+      ClientContext* context, const grpc::string& method, CompletionQueue* cq,
+      void* tag);
+
+  /// NOTE: class experimental_type is not part of the public API of this class
+  /// TODO(vjpai): Move these contents to the public API of GenericStub when
+  ///              they are no longer experimental
+  class experimental_type {
+   public:
+    explicit experimental_type(GenericStub* stub) : stub_(stub) {}
+
+    void UnaryCall(ClientContext* context, const grpc::string& method,
+                   const ByteBuffer* request, ByteBuffer* response,
+                   std::function<void(Status)> on_completion);
+
+    void PrepareBidiStreamingCall(
+        ClientContext* context, const grpc::string& method,
+        experimental::ClientBidiReactor<ByteBuffer, ByteBuffer>* reactor);
+
+   private:
+    GenericStub* stub_;
+  };
+
+  /// NOTE: The function experimental() is not stable public API. It is a view
+  /// to the experimental components of this class. It may be changed or removed
+  /// at any time.
+  experimental_type experimental() { return experimental_type(this); }
+
+ private:
+  std::shared_ptr<ChannelInterface> channel_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_GENERIC_GENERIC_STUB_H
diff --git a/third_party/grpc/include/grpcpp/grpcpp.h b/third_party/grpc/include/grpcpp/grpcpp.h
new file mode 100644
index 0000000..aa8a242
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/grpcpp.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/// \mainpage gRPC C++ API
+///
+/// The gRPC C++ API mainly consists of the following classes:
+/// <br>
+/// - grpc::Channel, which represents the connection to an endpoint. See [the
+/// gRPC Concepts page](https://grpc.io/docs/guides/concepts.html) for more
+/// details. Channels are created by the factory function grpc::CreateChannel.
+///
+/// - grpc::CompletionQueue, the producer-consumer queue used for all
+/// asynchronous communication with the gRPC runtime.
+///
+/// - grpc::ClientContext and grpc::ServerContext, where optional configuration
+/// for an RPC can be set, such as setting custom metadata to be conveyed to the
+/// peer, compression settings, authentication, etc.
+///
+/// - grpc::Server, representing a gRPC server, created by grpc::ServerBuilder.
+///
+/// Streaming calls are handled with the streaming classes in
+/// \ref sync_stream.h and
+/// \ref async_stream.h.
+///
+/// Refer to the
+/// [examples](https://github.com/grpc/grpc/blob/master/examples/cpp)
+/// for code putting these pieces into play.
+
+#ifndef GRPCPP_GRPCPP_H
+#define GRPCPP_GRPCPP_H
+
+// Pragma for http://include-what-you-use.org/ tool, tells that following
+// headers are not private for grpcpp.h and are part of its interface.
+// IWYU pragma: begin_exports
+#include <grpc/grpc.h>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/create_channel_posix.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/server_posix.h>
+// IWYU pragma: end_exports
+
+namespace grpc {
+/// Return gRPC library version.
+grpc::string Version();
+}  // namespace grpc
+
+#endif  // GRPCPP_GRPCPP_H
diff --git a/third_party/grpc/include/grpcpp/health_check_service_interface.h b/third_party/grpc/include/grpcpp/health_check_service_interface.h
new file mode 100644
index 0000000..dfd4c39
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/health_check_service_interface.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H
+#define GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H
+
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+
+const char kHealthCheckServiceInterfaceArg[] =
+    "grpc.health_check_service_interface";
+
+/// The gRPC server uses this interface to expose the health checking service
+/// without depending on protobuf.
+class HealthCheckServiceInterface {
+ public:
+  virtual ~HealthCheckServiceInterface() {}
+
+  /// Set or change the serving status of the given \a service_name.
+  virtual void SetServingStatus(const grpc::string& service_name,
+                                bool serving) = 0;
+  /// Apply to all registered service names.
+  virtual void SetServingStatus(bool serving) = 0;
+
+  /// Set all registered service names to not serving and prevent future
+  /// state changes.
+  virtual void Shutdown() {}
+};
+
+/// Enable/disable the default health checking service. This applies to all C++
+/// servers created afterwards. For each server, user can override the default
+/// with a HealthCheckServiceServerBuilderOption.
+/// NOT thread safe.
+void EnableDefaultHealthCheckService(bool enable);
+
+/// Returns whether the default health checking service is enabled.
+/// NOT thread safe.
+bool DefaultHealthCheckServiceEnabled();
+
+}  // namespace grpc
+
+#endif  // GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H
diff --git a/third_party/grpc/include/grpcpp/impl/README.md b/third_party/grpc/include/grpcpp/impl/README.md
new file mode 100644
index 0000000..612150c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/README.md
@@ -0,0 +1,4 @@
+**The APIs in this directory are not stable!**
+
+This directory contains header files that need to be installed but are not part
+of the public API. Users should not use these headers directly.
diff --git a/third_party/grpc/include/grpcpp/impl/call.h b/third_party/grpc/include/grpcpp/impl/call.h
new file mode 100644
index 0000000..a6b1312
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/call.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CALL_H
+#define GRPCPP_IMPL_CALL_H
+
+#include <grpcpp/impl/codegen/call.h>
+
+#endif  // GRPCPP_IMPL_CALL_H
diff --git a/third_party/grpc/include/grpcpp/impl/channel_argument_option.h b/third_party/grpc/include/grpcpp/impl/channel_argument_option.h
new file mode 100644
index 0000000..0c48824
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/channel_argument_option.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H
+#define GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H
+
+#include <map>
+#include <memory>
+
+#include <grpcpp/impl/server_builder_option.h>
+#include <grpcpp/support/channel_arguments.h>
+
+namespace grpc {
+
+std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
+    const grpc::string& name, const grpc::string& value);
+std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
+    const grpc::string& name, int value);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H
diff --git a/third_party/grpc/include/grpcpp/impl/client_unary_call.h b/third_party/grpc/include/grpcpp/impl/client_unary_call.h
new file mode 100644
index 0000000..378482c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/client_unary_call.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CLIENT_UNARY_CALL_H
+#define GRPCPP_IMPL_CLIENT_UNARY_CALL_H
+
+#include <grpcpp/impl/codegen/client_unary_call.h>
+
+#endif  // GRPCPP_IMPL_CLIENT_UNARY_CALL_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/async_generic_service.h b/third_party/grpc/include/grpcpp/impl/codegen/async_generic_service.h
new file mode 100644
index 0000000..2a0e1b4
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/async_generic_service.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H
+#define GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H
+
+#include <grpcpp/impl/codegen/async_stream.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+
+struct grpc_server;
+
+namespace grpc {
+
+typedef ServerAsyncReaderWriter<ByteBuffer, ByteBuffer>
+    GenericServerAsyncReaderWriter;
+typedef ServerAsyncResponseWriter<ByteBuffer> GenericServerAsyncResponseWriter;
+typedef ServerAsyncReader<ByteBuffer, ByteBuffer> GenericServerAsyncReader;
+typedef ServerAsyncWriter<ByteBuffer> GenericServerAsyncWriter;
+
+class GenericServerContext final : public ServerContext {
+ public:
+  const grpc::string& method() const { return method_; }
+  const grpc::string& host() const { return host_; }
+
+ private:
+  friend class Server;
+  friend class ServerInterface;
+
+  grpc::string method_;
+  grpc::string host_;
+};
+
+// A generic service at the server side accepts all RPC methods and hosts. It is
+// typically used in proxies. The generic service can be registered to a server
+// which also has other services.
+// Sample usage:
+//   ServerBuilder builder;
+//   auto cq = builder.AddCompletionQueue();
+//   AsyncGenericService generic_service;
+//   builder.RegisterAsyncGenericService(&generic_service);
+//   auto server = builder.BuildAndStart();
+//
+//   // request a new call
+//   GenericServerContext context;
+//   GenericServerAsyncReaderWriter stream;
+//   generic_service.RequestCall(&context, &stream, cq.get(), cq.get(), tag);
+//
+// When tag is retrieved from cq->Next(), context.method() can be used to look
+// at the method and the RPC can be handled accordingly.
+class AsyncGenericService final {
+ public:
+  AsyncGenericService() : server_(nullptr) {}
+
+  void RequestCall(GenericServerContext* ctx,
+                   GenericServerAsyncReaderWriter* reader_writer,
+                   CompletionQueue* call_cq,
+                   ServerCompletionQueue* notification_cq, void* tag);
+
+ private:
+  friend class Server;
+  Server* server_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/async_stream.h b/third_party/grpc/include/grpcpp/impl/codegen/async_stream.h
new file mode 100644
index 0000000..bfb2df4
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/async_stream.h
@@ -0,0 +1,1134 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H
+#define GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class CompletionQueue;
+
+namespace internal {
+/// Common interface for all client side asynchronous streaming.
+class ClientAsyncStreamingInterface {
+ public:
+  virtual ~ClientAsyncStreamingInterface() {}
+
+  /// Start the call that was set up by the constructor, but only if the
+  /// constructor was invoked through the "Prepare" API which doesn't actually
+  /// start the call
+  virtual void StartCall(void* tag) = 0;
+
+  /// Request notification of the reading of the initial metadata. Completion
+  /// will be notified by \a tag on the associated completion queue.
+  /// This call is optional, but if it is used, it cannot be used concurrently
+  /// with or after the \a AsyncReaderInterface::Read method.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  virtual void ReadInitialMetadata(void* tag) = 0;
+
+  /// Indicate that the stream is to be finished and request notification for
+  /// when the call has been ended.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// It is appropriate to call this method when both:
+  ///   * the client side has no more message to send
+  ///     (this can be declared implicitly by calling this method, or
+  ///     explicitly through an earlier call to the <i>WritesDone</i> method
+  ///     of the class in use, e.g. \a ClientAsyncWriterInterface::WritesDone or
+  ///     \a ClientAsyncReaderWriterInterface::WritesDone).
+  ///   * there are no more messages to be received from the server (this can
+  ///     be known implicitly by the calling code, or explicitly from an
+  ///     earlier call to \a AsyncReaderInterface::Read that yielded a failed
+  ///     result, e.g. cq->Next(&read_tag, &ok) filled in 'ok' with 'false').
+  ///
+  /// The tag will be returned when either:
+  /// - all incoming messages have been read and the server has returned
+  ///   a status.
+  /// - the server has returned a non-OK status.
+  /// - the call failed for some reason and the library generated a
+  ///   status.
+  ///
+  /// Note that implementations of this method attempt to receive initial
+  /// metadata from the server if initial metadata hasn't yet been received.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[out] status To be updated with the operation status.
+  virtual void Finish(Status* status, void* tag) = 0;
+};
+
+/// An interface that yields a sequence of messages of type \a R.
+template <class R>
+class AsyncReaderInterface {
+ public:
+  virtual ~AsyncReaderInterface() {}
+
+  /// Read a message of type \a R into \a msg. Completion will be notified by \a
+  /// tag on the associated completion queue.
+  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
+  /// should not be called concurrently with other streaming APIs
+  /// on the same stream. It is not meaningful to call it concurrently
+  /// with another \a AsyncReaderInterface::Read on the same stream since reads
+  /// on the same stream are delivered in order.
+  ///
+  /// \param[out] msg Where to eventually store the read message.
+  /// \param[in] tag The tag identifying the operation.
+  ///
+  /// Side effect: note that this method attempt to receive initial metadata for
+  /// a stream if it hasn't yet been received.
+  virtual void Read(R* msg, void* tag) = 0;
+};
+
+/// An interface that can be fed a sequence of messages of type \a W.
+template <class W>
+class AsyncWriterInterface {
+ public:
+  virtual ~AsyncWriterInterface() {}
+
+  /// Request the writing of \a msg with identifying tag \a tag.
+  ///
+  /// Only one write may be outstanding at any given time. This means that
+  /// after calling Write, one must wait to receive \a tag from the completion
+  /// queue BEFORE calling Write again.
+  /// This is thread-safe with respect to \a AsyncReaderInterface::Read
+  ///
+  /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to
+  /// to deallocate once Write returns.
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] tag The tag identifying the operation.
+  virtual void Write(const W& msg, void* tag) = 0;
+
+  /// Request the writing of \a msg using WriteOptions \a options with
+  /// identifying tag \a tag.
+  ///
+  /// Only one write may be outstanding at any given time. This means that
+  /// after calling Write, one must wait to receive \a tag from the completion
+  /// queue BEFORE calling Write again.
+  /// WriteOptions \a options is used to set the write options of this message.
+  /// This is thread-safe with respect to \a AsyncReaderInterface::Read
+  ///
+  /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to
+  /// to deallocate once Write returns.
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  /// \param[in] tag The tag identifying the operation.
+  virtual void Write(const W& msg, WriteOptions options, void* tag) = 0;
+
+  /// Request the writing of \a msg and coalesce it with the writing
+  /// of trailing metadata, using WriteOptions \a options with
+  /// identifying tag \a tag.
+  ///
+  /// For client, WriteLast is equivalent of performing Write and
+  /// WritesDone in a single step.
+  /// For server, WriteLast buffers the \a msg. The writing of \a msg is held
+  /// until Finish is called, where \a msg and trailing metadata are coalesced
+  /// and write is initiated. Note that WriteLast can only buffer \a msg up to
+  /// the flow control window size. If \a msg size is larger than the window
+  /// size, it will be sent on wire without buffering.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to
+  /// to deallocate once Write returns.
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  /// \param[in] tag The tag identifying the operation.
+  void WriteLast(const W& msg, WriteOptions options, void* tag) {
+    Write(msg, options.set_last_message(), tag);
+  }
+};
+
+}  // namespace internal
+
+template <class R>
+class ClientAsyncReaderInterface
+    : public internal::ClientAsyncStreamingInterface,
+      public internal::AsyncReaderInterface<R> {};
+
+namespace internal {
+template <class R>
+class ClientAsyncReaderFactory {
+ public:
+  /// Create a stream object.
+  /// Write the first request out if \a start is set.
+  /// \a tag will be notified on \a cq when the call has been started and
+  /// \a request has been written out. If \a start is not set, \a tag must be
+  /// nullptr and the actual call must be initiated by StartCall
+  /// Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  template <class W>
+  static ClientAsyncReader<R>* Create(ChannelInterface* channel,
+                                      CompletionQueue* cq,
+                                      const ::grpc::internal::RpcMethod& method,
+                                      ClientContext* context, const W& request,
+                                      bool start, void* tag) {
+    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
+    return new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientAsyncReader<R>)))
+        ClientAsyncReader<R>(call, context, request, start, tag);
+  }
+};
+}  // namespace internal
+
+/// Async client-side API for doing server-streaming RPCs,
+/// where the incoming message stream coming from the server has
+/// messages of type \a R.
+template <class R>
+class ClientAsyncReader final : public ClientAsyncReaderInterface<R> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientAsyncReader));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  void StartCall(void* tag) override {
+    assert(!started_);
+    started_ = true;
+    StartCallInternal(tag);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata
+  /// method for semantics.
+  ///
+  /// Side effect:
+  ///   - upon receiving initial metadata from the server,
+  ///     the \a ClientContext associated with this call is updated, and the
+  ///     calling code can access the received metadata through the
+  ///     \a ClientContext.
+  void ReadInitialMetadata(void* tag) override {
+    assert(started_);
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.RecvInitialMetadata(context_);
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Read(R* msg, void* tag) override {
+    assert(started_);
+    read_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      read_ops_.RecvInitialMetadata(context_);
+    }
+    read_ops_.RecvMessage(msg);
+    call_.PerformOps(&read_ops_);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata received from the server.
+  void Finish(Status* status, void* tag) override {
+    assert(started_);
+    finish_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      finish_ops_.RecvInitialMetadata(context_);
+    }
+    finish_ops_.ClientRecvStatus(context_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  friend class internal::ClientAsyncReaderFactory<R>;
+  template <class W>
+  ClientAsyncReader(::grpc::internal::Call call, ClientContext* context,
+                    const W& request, bool start, void* tag)
+      : context_(context), call_(call), started_(start) {
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(init_ops_.SendMessage(request).ok());
+    init_ops_.ClientSendClose();
+    if (start) {
+      StartCallInternal(tag);
+    } else {
+      assert(tag == nullptr);
+    }
+  }
+
+  void StartCallInternal(void* tag) {
+    init_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                  context_->initial_metadata_flags());
+    init_ops_.set_output_tag(tag);
+    call_.PerformOps(&init_ops_);
+  }
+
+  ClientContext* context_;
+  ::grpc::internal::Call call_;
+  bool started_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpClientSendClose>
+      init_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpRecvMessage<R>>
+      read_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_ops_;
+};
+
+/// Common interface for client side asynchronous writing.
+template <class W>
+class ClientAsyncWriterInterface
+    : public internal::ClientAsyncStreamingInterface,
+      public internal::AsyncWriterInterface<W> {
+ public:
+  /// Signal the client is done with the writes (half-close the client stream).
+  /// Thread-safe with respect to \a AsyncReaderInterface::Read
+  ///
+  /// \param[in] tag The tag identifying the operation.
+  virtual void WritesDone(void* tag) = 0;
+};
+
+namespace internal {
+template <class W>
+class ClientAsyncWriterFactory {
+ public:
+  /// Create a stream object.
+  /// Start the RPC if \a start is set
+  /// \a tag will be notified on \a cq when the call has been started (i.e.
+  /// intitial metadata sent) and \a request has been written out.
+  /// If \a start is not set, \a tag must be nullptr and the actual call
+  /// must be initiated by StartCall
+  /// Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  /// \a response will be filled in with the single expected response
+  /// message from the server upon a successful call to the \a Finish
+  /// method of this instance.
+  template <class R>
+  static ClientAsyncWriter<W>* Create(ChannelInterface* channel,
+                                      CompletionQueue* cq,
+                                      const ::grpc::internal::RpcMethod& method,
+                                      ClientContext* context, R* response,
+                                      bool start, void* tag) {
+    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
+    return new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientAsyncWriter<W>)))
+        ClientAsyncWriter<W>(call, context, response, start, tag);
+  }
+};
+}  // namespace internal
+
+/// Async API on the client side for doing client-streaming RPCs,
+/// where the outgoing message stream going to the server contains
+/// messages of type \a W.
+template <class W>
+class ClientAsyncWriter final : public ClientAsyncWriterInterface<W> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientAsyncWriter));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  void StartCall(void* tag) override {
+    assert(!started_);
+    started_ = true;
+    StartCallInternal(tag);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method for
+  /// semantics.
+  ///
+  /// Side effect:
+  ///   - upon receiving initial metadata from the server, the \a ClientContext
+  ///     associated with this call is updated, and the calling code can access
+  ///     the received metadata through the \a ClientContext.
+  void ReadInitialMetadata(void* tag) override {
+    assert(started_);
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.RecvInitialMetadata(context_);
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Write(const W& msg, void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void Write(const W& msg, WriteOptions options, void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      write_ops_.ClientSendClose();
+    }
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void WritesDone(void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    write_ops_.ClientSendClose();
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata received from the server.
+  ///   - attempts to fill in the \a response parameter passed to this class's
+  ///     constructor with the server's response message.
+  void Finish(Status* status, void* tag) override {
+    assert(started_);
+    finish_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      finish_ops_.RecvInitialMetadata(context_);
+    }
+    finish_ops_.ClientRecvStatus(context_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  friend class internal::ClientAsyncWriterFactory<W>;
+  template <class R>
+  ClientAsyncWriter(::grpc::internal::Call call, ClientContext* context,
+                    R* response, bool start, void* tag)
+      : context_(context), call_(call), started_(start) {
+    finish_ops_.RecvMessage(response);
+    finish_ops_.AllowNoMessage();
+    if (start) {
+      StartCallInternal(tag);
+    } else {
+      assert(tag == nullptr);
+    }
+  }
+
+  void StartCallInternal(void* tag) {
+    write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                   context_->initial_metadata_flags());
+    // if corked bit is set in context, we just keep the initial metadata
+    // buffered up to coalesce with later message send. No op is performed.
+    if (!context_->initial_metadata_corked_) {
+      write_ops_.set_output_tag(tag);
+      call_.PerformOps(&write_ops_);
+    }
+  }
+
+  ClientContext* context_;
+  ::grpc::internal::Call call_;
+  bool started_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpClientSendClose>
+      write_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpGenericRecvMessage,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_ops_;
+};
+
+/// Async client-side interface for bi-directional streaming,
+/// where the client-to-server message stream has messages of type \a W,
+/// and the server-to-client message stream has messages of type \a R.
+template <class W, class R>
+class ClientAsyncReaderWriterInterface
+    : public internal::ClientAsyncStreamingInterface,
+      public internal::AsyncWriterInterface<W>,
+      public internal::AsyncReaderInterface<R> {
+ public:
+  /// Signal the client is done with the writes (half-close the client stream).
+  /// Thread-safe with respect to \a AsyncReaderInterface::Read
+  ///
+  /// \param[in] tag The tag identifying the operation.
+  virtual void WritesDone(void* tag) = 0;
+};
+
+namespace internal {
+template <class W, class R>
+class ClientAsyncReaderWriterFactory {
+ public:
+  /// Create a stream object.
+  /// Start the RPC request if \a start is set.
+  /// \a tag will be notified on \a cq when the call has been started (i.e.
+  /// intitial metadata sent). If \a start is not set, \a tag must be
+  /// nullptr and the actual call must be initiated by StartCall
+  /// Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  static ClientAsyncReaderWriter<W, R>* Create(
+      ChannelInterface* channel, CompletionQueue* cq,
+      const ::grpc::internal::RpcMethod& method, ClientContext* context,
+      bool start, void* tag) {
+    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
+
+    return new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientAsyncReaderWriter<W, R>)))
+        ClientAsyncReaderWriter<W, R>(call, context, start, tag);
+  }
+};
+}  // namespace internal
+
+/// Async client-side interface for bi-directional streaming,
+/// where the outgoing message stream going to the server
+/// has messages of type \a W,  and the incoming message stream coming
+/// from the server has messages of type \a R.
+template <class W, class R>
+class ClientAsyncReaderWriter final
+    : public ClientAsyncReaderWriterInterface<W, R> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientAsyncReaderWriter));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  void StartCall(void* tag) override {
+    assert(!started_);
+    started_ = true;
+    StartCallInternal(tag);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method
+  /// for semantics of this method.
+  ///
+  /// Side effect:
+  ///   - upon receiving initial metadata from the server, the \a ClientContext
+  ///     is updated with it, and then the receiving initial metadata can
+  ///     be accessed through this \a ClientContext.
+  void ReadInitialMetadata(void* tag) override {
+    assert(started_);
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.RecvInitialMetadata(context_);
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Read(R* msg, void* tag) override {
+    assert(started_);
+    read_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      read_ops_.RecvInitialMetadata(context_);
+    }
+    read_ops_.RecvMessage(msg);
+    call_.PerformOps(&read_ops_);
+  }
+
+  void Write(const W& msg, void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void Write(const W& msg, WriteOptions options, void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      write_ops_.ClientSendClose();
+    }
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void WritesDone(void* tag) override {
+    assert(started_);
+    write_ops_.set_output_tag(tag);
+    write_ops_.ClientSendClose();
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
+  /// Side effect
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata sent from the server.
+  void Finish(Status* status, void* tag) override {
+    assert(started_);
+    finish_ops_.set_output_tag(tag);
+    if (!context_->initial_metadata_received_) {
+      finish_ops_.RecvInitialMetadata(context_);
+    }
+    finish_ops_.ClientRecvStatus(context_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  friend class internal::ClientAsyncReaderWriterFactory<W, R>;
+  ClientAsyncReaderWriter(::grpc::internal::Call call, ClientContext* context,
+                          bool start, void* tag)
+      : context_(context), call_(call), started_(start) {
+    if (start) {
+      StartCallInternal(tag);
+    } else {
+      assert(tag == nullptr);
+    }
+  }
+
+  void StartCallInternal(void* tag) {
+    write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                   context_->initial_metadata_flags());
+    // if corked bit is set in context, we just keep the initial metadata
+    // buffered up to coalesce with later message send. No op is performed.
+    if (!context_->initial_metadata_corked_) {
+      write_ops_.set_output_tag(tag);
+      call_.PerformOps(&write_ops_);
+    }
+  }
+
+  ClientContext* context_;
+  ::grpc::internal::Call call_;
+  bool started_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpRecvMessage<R>>
+      read_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpClientSendClose>
+      write_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_ops_;
+};
+
+template <class W, class R>
+class ServerAsyncReaderInterface
+    : public internal::ServerAsyncStreamingInterface,
+      public internal::AsyncReaderInterface<R> {
+ public:
+  /// Indicate that the stream is to be finished with a certain status code
+  /// and also send out \a msg response to the client.
+  /// Request notification for when the server has sent the response and the
+  /// appropriate signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// It is appropriate to call this method when:
+  ///   * all messages from the client have been received (either known
+  ///     implictly, or explicitly because a previous
+  ///     \a AsyncReaderInterface::Read operation with a non-ok result,
+  ///     e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false').
+  ///
+  /// This operation will end when the server has finished sending out initial
+  /// metadata (if not sent already), response message, and status, or if
+  /// some failure occurred when trying to do so.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a msg or \a status, so it
+  /// is safe to to deallocate once Finish returns.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of this call.
+  /// \param[in] msg To be sent to the client as the response for this call.
+  virtual void Finish(const W& msg, const Status& status, void* tag) = 0;
+
+  /// Indicate that the stream is to be finished with a certain
+  /// non-OK status code.
+  /// Request notification for when the server has sent the appropriate
+  /// signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// This call is meant to end the call with some error, and can be called at
+  /// any point that the server would like to "fail" the call (though note
+  /// this shouldn't be called concurrently with any other "sending" call, like
+  /// \a AsyncWriterInterface::Write).
+  ///
+  /// This operation will end when the server has finished sending out initial
+  /// metadata (if not sent already), and status, or if some failure occurred
+  /// when trying to do so.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a status, so it is safe to
+  /// to deallocate once FinishWithError returns.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of this call.
+  ///     - Note: \a status must have a non-OK code.
+  virtual void FinishWithError(const Status& status, void* tag) = 0;
+};
+
+/// Async server-side API for doing client-streaming RPCs,
+/// where the incoming message stream from the client has messages of type \a R,
+/// and the single response message sent from the server is type \a W.
+template <class W, class R>
+class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
+ public:
+  explicit ServerAsyncReader(ServerContext* ctx)
+      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
+
+  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - The initial metadata that will be sent to the client from this op will
+  ///     be taken from the \a ServerContext associated with the call.
+  void SendInitialMetadata(void* tag) override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                  ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Read(R* msg, void* tag) override {
+    read_ops_.set_output_tag(tag);
+    read_ops_.RecvMessage(msg);
+    call_.PerformOps(&read_ops_);
+  }
+
+  /// See the \a ServerAsyncReaderInterface.Read method for semantics
+  ///
+  /// Side effect:
+  ///   - also sends initial metadata if not alreay sent.
+  ///   - uses the \a ServerContext associated with this call to send possible
+  ///     initial and trailing metadata.
+  ///
+  /// Note: \a msg is not sent if \a status has a non-OK code.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
+  /// is safe to to deallocate once Finish returns.
+  void Finish(const W& msg, const Status& status, void* tag) override {
+    finish_ops_.set_output_tag(tag);
+    if (!ctx_->sent_initial_metadata_) {
+      finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    // The response is dropped if the status is not OK.
+    if (status.ok()) {
+      finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_,
+                                   finish_ops_.SendMessage(msg));
+    } else {
+      finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
+    }
+    call_.PerformOps(&finish_ops_);
+  }
+
+  /// See the \a ServerAsyncReaderInterface.Read method for semantics
+  ///
+  /// Side effect:
+  ///   - also sends initial metadata if not alreay sent.
+  ///   - uses the \a ServerContext associated with this call to send possible
+  ///     initial and trailing metadata.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a status, so it is safe to
+  /// to deallocate once FinishWithError returns.
+  void FinishWithError(const Status& status, void* tag) override {
+    GPR_CODEGEN_ASSERT(!status.ok());
+    finish_ops_.set_output_tag(tag);
+    if (!ctx_->sent_initial_metadata_) {
+      finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
+
+  ::grpc::internal::Call call_;
+  ServerContext* ctx_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage<R>> read_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpServerSendStatus>
+      finish_ops_;
+};
+
+template <class W>
+class ServerAsyncWriterInterface
+    : public internal::ServerAsyncStreamingInterface,
+      public internal::AsyncWriterInterface<W> {
+ public:
+  /// Indicate that the stream is to be finished with a certain status code.
+  /// Request notification for when the server has sent the appropriate
+  /// signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// It is appropriate to call this method when either:
+  ///   * all messages from the client have been received (either known
+  ///     implictly, or explicitly because a previous \a
+  ///     AsyncReaderInterface::Read operation with a non-ok
+  ///     result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'.
+  ///   * it is desired to end the call early with some non-OK status code.
+  ///
+  /// This operation will end when the server has finished sending out initial
+  /// metadata (if not sent already), response message, and status, or if
+  /// some failure occurred when trying to do so.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a status, so it is safe to
+  /// to deallocate once Finish returns.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of this call.
+  virtual void Finish(const Status& status, void* tag) = 0;
+
+  /// Request the writing of \a msg and coalesce it with trailing metadata which
+  /// contains \a status, using WriteOptions options with
+  /// identifying tag \a tag.
+  ///
+  /// WriteAndFinish is equivalent of performing WriteLast and Finish
+  /// in a single step.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
+  /// is safe to to deallocate once WriteAndFinish returns.
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  /// \param[in] status The Status that server returns to client.
+  /// \param[in] tag The tag identifying the operation.
+  virtual void WriteAndFinish(const W& msg, WriteOptions options,
+                              const Status& status, void* tag) = 0;
+};
+
+/// Async server-side API for doing server streaming RPCs,
+/// where the outgoing message stream from the server has messages of type \a W.
+template <class W>
+class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
+ public:
+  explicit ServerAsyncWriter(ServerContext* ctx)
+      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
+
+  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - The initial metadata that will be sent to the client from this op will
+  ///     be taken from the \a ServerContext associated with the call.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  void SendInitialMetadata(void* tag) override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                  ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Write(const W& msg, void* tag) override {
+    write_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&write_ops_);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void Write(const W& msg, WriteOptions options, void* tag) override {
+    write_ops_.set_output_tag(tag);
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+    }
+
+    EnsureInitialMetadataSent(&write_ops_);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ServerAsyncWriterInterface.WriteAndFinish method for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - the \a ServerContext associated with this call is used
+  ///     for sending trailing (and initial) metadata to the client.
+  ///
+  /// Note: \a status must have an OK code.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
+  /// is safe to to deallocate once WriteAndFinish returns.
+  void WriteAndFinish(const W& msg, WriteOptions options, const Status& status,
+                      void* tag) override {
+    write_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&write_ops_);
+    options.set_buffer_hint();
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ServerAsyncWriterInterface.Finish method for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - the \a ServerContext associated with this call is used for sending
+  ///     trailing (and initial if not already sent) metadata to the client.
+  ///
+  /// Note: there are no restrictions are the code of
+  /// \a status,it may be non-OK
+  ///
+  /// gRPC doesn't take ownership or a reference to \a status, so it is safe to
+  /// to deallocate once Finish returns.
+  void Finish(const Status& status, void* tag) override {
+    finish_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&finish_ops_);
+    finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
+
+  template <class T>
+  void EnsureInitialMetadataSent(T* ops) {
+    if (!ctx_->sent_initial_metadata_) {
+      ops->SendInitialMetadata(&ctx_->initial_metadata_,
+                               ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ops->set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+  }
+
+  ::grpc::internal::Call call_;
+  ServerContext* ctx_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpServerSendStatus>
+      write_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpServerSendStatus>
+      finish_ops_;
+};
+
+/// Server-side interface for asynchronous bi-directional streaming.
+template <class W, class R>
+class ServerAsyncReaderWriterInterface
+    : public internal::ServerAsyncStreamingInterface,
+      public internal::AsyncWriterInterface<W>,
+      public internal::AsyncReaderInterface<R> {
+ public:
+  /// Indicate that the stream is to be finished with a certain status code.
+  /// Request notification for when the server has sent the appropriate
+  /// signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// It is appropriate to call this method when either:
+  ///   * all messages from the client have been received (either known
+  ///     implictly, or explicitly because a previous \a
+  ///     AsyncReaderInterface::Read operation
+  ///     with a non-ok result (e.g., cq->Next(&read_tag, &ok) filled in 'ok'
+  ///     with 'false'.
+  ///   * it is desired to end the call early with some non-OK status code.
+  ///
+  /// This operation will end when the server has finished sending out initial
+  /// metadata (if not sent already), response message, and status, or if some
+  /// failure occurred when trying to do so.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a status, so it is safe to
+  /// to deallocate once Finish returns.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of this call.
+  virtual void Finish(const Status& status, void* tag) = 0;
+
+  /// Request the writing of \a msg and coalesce it with trailing metadata which
+  /// contains \a status, using WriteOptions options with
+  /// identifying tag \a tag.
+  ///
+  /// WriteAndFinish is equivalent of performing WriteLast and Finish in a
+  /// single step.
+  ///
+  /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
+  /// is safe to to deallocate once WriteAndFinish returns.
+  ///
+  /// \param[in] msg The message to be written.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  /// \param[in] status The Status that server returns to client.
+  /// \param[in] tag The tag identifying the operation.
+  virtual void WriteAndFinish(const W& msg, WriteOptions options,
+                              const Status& status, void* tag) = 0;
+};
+
+/// Async server-side API for doing bidirectional streaming RPCs,
+/// where the incoming message stream coming from the client has messages of
+/// type \a R, and the outgoing message stream coming from the server has
+/// messages of type \a W.
+template <class W, class R>
+class ServerAsyncReaderWriter final
+    : public ServerAsyncReaderWriterInterface<W, R> {
+ public:
+  explicit ServerAsyncReaderWriter(ServerContext* ctx)
+      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
+
+  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - The initial metadata that will be sent to the client from this op will
+  ///     be taken from the \a ServerContext associated with the call.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  void SendInitialMetadata(void* tag) override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    meta_ops_.set_output_tag(tag);
+    meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                  ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_ops_.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_.PerformOps(&meta_ops_);
+  }
+
+  void Read(R* msg, void* tag) override {
+    read_ops_.set_output_tag(tag);
+    read_ops_.RecvMessage(msg);
+    call_.PerformOps(&read_ops_);
+  }
+
+  void Write(const W& msg, void* tag) override {
+    write_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&write_ops_);
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  void Write(const W& msg, WriteOptions options, void* tag) override {
+    write_ops_.set_output_tag(tag);
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+    }
+    EnsureInitialMetadataSent(&write_ops_);
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ServerAsyncReaderWriterInterface.WriteAndFinish
+  /// method for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - the \a ServerContext associated with this call is used
+  ///     for sending trailing (and initial) metadata to the client.
+  ///
+  /// Note: \a status must have an OK code.
+  //
+  /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
+  /// is safe to to deallocate once WriteAndFinish returns.
+  void WriteAndFinish(const W& msg, WriteOptions options, const Status& status,
+                      void* tag) override {
+    write_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&write_ops_);
+    options.set_buffer_hint();
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok());
+    write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
+    call_.PerformOps(&write_ops_);
+  }
+
+  /// See the \a ServerAsyncReaderWriterInterface.Finish method for semantics.
+  ///
+  /// Implicit input parameter:
+  ///   - the \a ServerContext associated with this call is used for sending
+  ///     trailing (and initial if not already sent) metadata to the client.
+  ///
+  /// Note: there are no restrictions are the code of \a status,
+  /// it may be non-OK
+  //
+  /// gRPC doesn't take ownership or a reference to \a status, so it is safe to
+  /// to deallocate once Finish returns.
+  void Finish(const Status& status, void* tag) override {
+    finish_ops_.set_output_tag(tag);
+    EnsureInitialMetadataSent(&finish_ops_);
+
+    finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
+    call_.PerformOps(&finish_ops_);
+  }
+
+ private:
+  friend class ::grpc::Server;
+
+  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
+
+  template <class T>
+  void EnsureInitialMetadataSent(T* ops) {
+    if (!ctx_->sent_initial_metadata_) {
+      ops->SendInitialMetadata(&ctx_->initial_metadata_,
+                               ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ops->set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+  }
+
+  ::grpc::internal::Call call_;
+  ServerContext* ctx_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+      meta_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage<R>> read_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpServerSendStatus>
+      write_ops_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpServerSendStatus>
+      finish_ops_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/async_unary_call.h b/third_party/grpc/include/grpcpp/impl/codegen/async_unary_call.h
new file mode 100644
index 0000000..89dcb12
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/async_unary_call.h
@@ -0,0 +1,317 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
+#define GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
+
+#include <assert.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/client_context.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class CompletionQueue;
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+/// An interface relevant for async client side unary RPCs (which send
+/// one request message to a server and receive one response message).
+template <class R>
+class ClientAsyncResponseReaderInterface {
+ public:
+  virtual ~ClientAsyncResponseReaderInterface() {}
+
+  /// Start the call that was set up by the constructor, but only if the
+  /// constructor was invoked through the "Prepare" API which doesn't actually
+  /// start the call
+  virtual void StartCall() = 0;
+
+  /// Request notification of the reading of initial metadata. Completion
+  /// will be notified by \a tag on the associated completion queue.
+  /// This call is optional, but if it is used, it cannot be used concurrently
+  /// with or after the \a Finish method.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  virtual void ReadInitialMetadata(void* tag) = 0;
+
+  /// Request to receive the server's response \a msg and final \a status for
+  /// the call, and to notify \a tag on this call's completion queue when
+  /// finished.
+  ///
+  /// This function will return when either:
+  /// - when the server's response message and status have been received.
+  /// - when the server has returned a non-OK status (no message expected in
+  ///   this case).
+  /// - when the call failed for some reason and the library generated a
+  ///   non-OK status.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[out] status To be updated with the operation status.
+  /// \param[out] msg To be filled in with the server's response message.
+  virtual void Finish(R* msg, Status* status, void* tag) = 0;
+};
+
+namespace internal {
+template <class R>
+class ClientAsyncResponseReaderFactory {
+ public:
+  /// Start a call and write the request out if \a start is set.
+  /// \a tag will be notified on \a cq when the call has been started (i.e.
+  /// intitial metadata sent) and \a request has been written out.
+  /// If \a start is not set, the actual call must be initiated by StartCall
+  /// Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  template <class W>
+  static ClientAsyncResponseReader<R>* Create(
+      ChannelInterface* channel, CompletionQueue* cq,
+      const ::grpc::internal::RpcMethod& method, ClientContext* context,
+      const W& request, bool start) {
+    ::grpc::internal::Call call = channel->CreateCall(method, context, cq);
+    return new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientAsyncResponseReader<R>)))
+        ClientAsyncResponseReader<R>(call, context, request, start);
+  }
+};
+}  // namespace internal
+
+/// Async API for client-side unary RPCs, where the message response
+/// received from the server is of type \a R.
+template <class R>
+class ClientAsyncResponseReader final
+    : public ClientAsyncResponseReaderInterface<R> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientAsyncResponseReader));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  void StartCall() override {
+    assert(!started_);
+    started_ = true;
+    StartCallInternal();
+  }
+
+  /// See \a ClientAsyncResponseReaderInterface::ReadInitialMetadata for
+  /// semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata sent from the server.
+  void ReadInitialMetadata(void* tag) override {
+    assert(started_);
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    single_buf.set_output_tag(tag);
+    single_buf.RecvInitialMetadata(context_);
+    call_.PerformOps(&single_buf);
+    initial_metadata_read_ = true;
+  }
+
+  /// See \a ClientAysncResponseReaderInterface::Finish for semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible initial and trailing metadata sent from the server.
+  void Finish(R* msg, Status* status, void* tag) override {
+    assert(started_);
+    if (initial_metadata_read_) {
+      finish_buf.set_output_tag(tag);
+      finish_buf.RecvMessage(msg);
+      finish_buf.AllowNoMessage();
+      finish_buf.ClientRecvStatus(context_, status);
+      call_.PerformOps(&finish_buf);
+    } else {
+      single_buf.set_output_tag(tag);
+      single_buf.RecvInitialMetadata(context_);
+      single_buf.RecvMessage(msg);
+      single_buf.AllowNoMessage();
+      single_buf.ClientRecvStatus(context_, status);
+      call_.PerformOps(&single_buf);
+    }
+  }
+
+ private:
+  friend class internal::ClientAsyncResponseReaderFactory<R>;
+  ClientContext* const context_;
+  ::grpc::internal::Call call_;
+  bool started_;
+  bool initial_metadata_read_ = false;
+
+  template <class W>
+  ClientAsyncResponseReader(::grpc::internal::Call call, ClientContext* context,
+                            const W& request, bool start)
+      : context_(context), call_(call), started_(start) {
+    // Bind the metadata at time of StartCallInternal but set up the rest here
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(single_buf.SendMessage(request).ok());
+    single_buf.ClientSendClose();
+    if (start) StartCallInternal();
+  }
+
+  void StartCallInternal() {
+    single_buf.SendInitialMetadata(&context_->send_initial_metadata_,
+                                   context_->initial_metadata_flags());
+  }
+
+  // disable operator new
+  static void* operator new(std::size_t size);
+  static void* operator new(std::size_t size, void* p) { return p; }
+
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpClientSendClose,
+                              ::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpRecvMessage<R>,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      single_buf;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage<R>,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_buf;
+};
+
+/// Async server-side API for handling unary calls, where the single
+/// response message sent to the client is of type \a W.
+template <class W>
+class ServerAsyncResponseWriter final
+    : public internal::ServerAsyncStreamingInterface {
+ public:
+  explicit ServerAsyncResponseWriter(ServerContext* ctx)
+      : call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
+
+  /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
+  ///
+  /// Side effect:
+  ///   The initial metadata that will be sent to the client from this op will
+  ///   be taken from the \a ServerContext associated with the call.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  void SendInitialMetadata(void* tag) override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    meta_buf_.set_output_tag(tag);
+    meta_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                  ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      meta_buf_.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_.PerformOps(&meta_buf_);
+  }
+
+  /// Indicate that the stream is to be finished and request notification
+  /// when the server has sent the appropriate signals to the client to
+  /// end the call. Should not be used concurrently with other operations.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of the call.
+  /// \param[in] msg Message to be sent to the client.
+  ///
+  /// Side effect:
+  ///   - also sends initial metadata if not already sent (using the
+  ///     \a ServerContext associated with this call).
+  ///
+  /// Note: if \a status has a non-OK code, then \a msg will not be sent,
+  /// and the client will receive only the status with possible trailing
+  /// metadata.
+  void Finish(const W& msg, const Status& status, void* tag) {
+    finish_buf_.set_output_tag(tag);
+    finish_buf_.set_core_cq_tag(&finish_buf_);
+    if (!ctx_->sent_initial_metadata_) {
+      finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_buf_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    // The response is dropped if the status is not OK.
+    if (status.ok()) {
+      finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_,
+                                   finish_buf_.SendMessage(msg));
+    } else {
+      finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status);
+    }
+    call_.PerformOps(&finish_buf_);
+  }
+
+  /// Indicate that the stream is to be finished with a non-OK status,
+  /// and request notification for when the server has finished sending the
+  /// appropriate signals to the client to end the call.
+  /// Should not be used concurrently with other operations.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  /// \param[in] status To be sent to the client as the result of the call.
+  ///   - Note: \a status must have a non-OK code.
+  ///
+  /// Side effect:
+  ///   - also sends initial metadata if not already sent (using the
+  ///     \a ServerContext associated with this call).
+  void FinishWithError(const Status& status, void* tag) {
+    GPR_CODEGEN_ASSERT(!status.ok());
+    finish_buf_.set_output_tag(tag);
+    if (!ctx_->sent_initial_metadata_) {
+      finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                      ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        finish_buf_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status);
+    call_.PerformOps(&finish_buf_);
+  }
+
+ private:
+  void BindCall(::grpc::internal::Call* call) override { call_ = *call; }
+
+  ::grpc::internal::Call call_;
+  ServerContext* ctx_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+      meta_buf_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                              ::grpc::internal::CallOpSendMessage,
+                              ::grpc::internal::CallOpServerSendStatus>
+      finish_buf_;
+};
+
+}  // namespace grpc
+
+namespace std {
+template <class R>
+class default_delete<grpc::ClientAsyncResponseReader<R>> {
+ public:
+  void operator()(void* p) {}
+};
+template <class R>
+class default_delete<grpc::ClientAsyncResponseReaderInterface<R>> {
+ public:
+  void operator()(void* p) {}
+};
+}  // namespace std
+
+#endif  // GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/byte_buffer.h b/third_party/grpc/include/grpcpp/impl/codegen/byte_buffer.h
new file mode 100644
index 0000000..a77e36d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/byte_buffer.h
@@ -0,0 +1,224 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H
+#define GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H
+
+#include <grpc/impl/codegen/byte_buffer.h>
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/status.h>
+
+#include <vector>
+
+namespace grpc {
+
+class ServerInterface;
+class ByteBuffer;
+class ServerInterface;
+
+namespace internal {
+class CallOpSendMessage;
+template <class R>
+class CallOpRecvMessage;
+class CallOpGenericRecvMessage;
+class MethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+template <class RequestType, class ResponseType>
+class CallbackUnaryHandler;
+template <class RequestType, class ResponseType>
+class CallbackServerStreamingHandler;
+template <StatusCode code>
+class ErrorMethodHandler;
+template <class R>
+class DeserializeFuncType;
+class GrpcByteBufferPeer;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+
+}  // namespace internal
+/// A sequence of bytes.
+class ByteBuffer final {
+ public:
+  /// Constuct an empty buffer.
+  ByteBuffer() : buffer_(nullptr) {}
+
+  /// Construct buffer from \a slices, of which there are \a nslices.
+  ByteBuffer(const Slice* slices, size_t nslices) {
+    // The following assertions check that the representation of a grpc::Slice
+    // is identical to that of a grpc_slice:  it has a grpc_slice field, and
+    // nothing else.
+    static_assert(std::is_same<decltype(slices[0].slice_), grpc_slice>::value,
+                  "Slice must have same representation as grpc_slice");
+    static_assert(sizeof(Slice) == sizeof(grpc_slice),
+                  "Slice must have same representation as grpc_slice");
+    // The following assertions check that the representation of a ByteBuffer is
+    // identical to grpc_byte_buffer*:  it has a grpc_byte_buffer* field,
+    // and nothing else.
+    static_assert(std::is_same<decltype(buffer_), grpc_byte_buffer*>::value,
+                  "ByteBuffer must have same representation as "
+                  "grpc_byte_buffer*");
+    static_assert(sizeof(ByteBuffer) == sizeof(grpc_byte_buffer*),
+                  "ByteBuffer must have same representation as "
+                  "grpc_byte_buffer*");
+    // 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_ = g_core_codegen_interface->grpc_raw_byte_buffer_create(
+        reinterpret_cast<grpc_slice*>(const_cast<Slice*>(slices)), nslices);
+  }
+
+  /// Constuct a byte buffer by referencing elements of existing buffer
+  /// \a buf. Wrapper of core function grpc_byte_buffer_copy . This is not
+  /// a deep copy; it is just a referencing. As a result, its performance is
+  /// size-independent.
+  ByteBuffer(const ByteBuffer& buf);
+
+  ~ByteBuffer() {
+    if (buffer_) {
+      g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_);
+    }
+  }
+
+  /// Wrapper of core function grpc_byte_buffer_copy . This is not
+  /// a deep copy; it is just a referencing. As a result, its performance is
+  /// size-independent.
+  ByteBuffer& operator=(const ByteBuffer&);
+
+  /// Dump (read) the buffer contents into \a slices.
+  Status Dump(std::vector<Slice>* slices) const;
+
+  /// Remove all data.
+  void Clear() {
+    if (buffer_) {
+      g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_);
+      buffer_ = nullptr;
+    }
+  }
+
+  /// Make a duplicate copy of the internals of this byte
+  /// buffer so that we have our own owned version of it.
+  /// bbuf.Duplicate(); is equivalent to bbuf=bbuf; but is actually readable.
+  /// This is not a deep copy; it is a referencing and its performance
+  /// is size-independent.
+  void Duplicate() {
+    buffer_ = g_core_codegen_interface->grpc_byte_buffer_copy(buffer_);
+  }
+
+  /// Forget underlying byte buffer without destroying
+  /// Use this only for un-owned byte buffers
+  void Release() { buffer_ = nullptr; }
+
+  /// Buffer size in bytes.
+  size_t Length() const {
+    return buffer_ == nullptr
+               ? 0
+               : g_core_codegen_interface->grpc_byte_buffer_length(buffer_);
+  }
+
+  /// Swap the state of *this and *other.
+  void Swap(ByteBuffer* other) {
+    grpc_byte_buffer* tmp = other->buffer_;
+    other->buffer_ = buffer_;
+    buffer_ = tmp;
+  }
+
+  /// Is this ByteBuffer valid?
+  bool Valid() const { return (buffer_ != nullptr); }
+
+ private:
+  friend class SerializationTraits<ByteBuffer, void>;
+  friend class ServerInterface;
+  friend class internal::CallOpSendMessage;
+  template <class R>
+  friend class internal::CallOpRecvMessage;
+  friend class internal::CallOpGenericRecvMessage;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ServerStreamingHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class internal::RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class internal::ServerStreamingHandler;
+  template <class RequestType, class ResponseType>
+  friend class internal::CallbackUnaryHandler;
+  template <class RequestType, class ResponseType>
+  friend class ::grpc::internal::CallbackServerStreamingHandler;
+  template <StatusCode code>
+  friend class internal::ErrorMethodHandler;
+  template <class R>
+  friend class internal::DeserializeFuncType;
+  friend class ProtoBufferReader;
+  friend class ProtoBufferWriter;
+  friend class internal::GrpcByteBufferPeer;
+
+  grpc_byte_buffer* buffer_;
+
+  // takes ownership
+  void set_buffer(grpc_byte_buffer* buf) {
+    if (buffer_) {
+      Clear();
+    }
+    buffer_ = buf;
+  }
+
+  grpc_byte_buffer* c_buffer() { return buffer_; }
+  grpc_byte_buffer** c_buffer_ptr() { return &buffer_; }
+
+  class ByteBufferPointer {
+   public:
+    ByteBufferPointer(const ByteBuffer* b)
+        : bbuf_(const_cast<ByteBuffer*>(b)) {}
+    operator ByteBuffer*() { return bbuf_; }
+    operator grpc_byte_buffer*() { return bbuf_->buffer_; }
+    operator grpc_byte_buffer**() { return &bbuf_->buffer_; }
+
+   private:
+    ByteBuffer* bbuf_;
+  };
+  ByteBufferPointer bbuf_ptr() const { return ByteBufferPointer(this); }
+};
+
+template <>
+class SerializationTraits<ByteBuffer, void> {
+ public:
+  static Status Deserialize(ByteBuffer* byte_buffer, ByteBuffer* dest) {
+    dest->set_buffer(byte_buffer->buffer_);
+    return Status::OK;
+  }
+  static Status Serialize(const ByteBuffer& source, ByteBuffer* buffer,
+                          bool* own_buffer) {
+    *buffer = source;
+    *own_buffer = true;
+    return Status::OK;
+  }
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/call.h b/third_party/grpc/include/grpcpp/impl/codegen/call.h
new file mode 100644
index 0000000..c040c30
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/call.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+#ifndef GRPCPP_IMPL_CODEGEN_CALL_H
+#define GRPCPP_IMPL_CODEGEN_CALL_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/call_hook.h>
+
+namespace grpc {
+class CompletionQueue;
+
+namespace experimental {
+class ClientRpcInfo;
+class ServerRpcInfo;
+}  // namespace experimental
+namespace internal {
+class CallHook;
+class CallOpSetInterface;
+
+/// Straightforward wrapping of the C call object
+class Call final {
+ public:
+  Call()
+      : call_hook_(nullptr),
+        cq_(nullptr),
+        call_(nullptr),
+        max_receive_message_size_(-1) {}
+  /** call is owned by the caller */
+  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
+      : call_hook_(call_hook),
+        cq_(cq),
+        call_(call),
+        max_receive_message_size_(-1) {}
+
+  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
+       experimental::ClientRpcInfo* rpc_info)
+      : call_hook_(call_hook),
+        cq_(cq),
+        call_(call),
+        max_receive_message_size_(-1),
+        client_rpc_info_(rpc_info) {}
+
+  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
+       int max_receive_message_size, experimental::ServerRpcInfo* rpc_info)
+      : call_hook_(call_hook),
+        cq_(cq),
+        call_(call),
+        max_receive_message_size_(max_receive_message_size),
+        server_rpc_info_(rpc_info) {}
+
+  void PerformOps(CallOpSetInterface* ops) {
+    call_hook_->PerformOpsOnCall(ops, this);
+  }
+
+  grpc_call* call() const { return call_; }
+  CompletionQueue* cq() const { return cq_; }
+
+  int max_receive_message_size() const { return max_receive_message_size_; }
+
+  experimental::ClientRpcInfo* client_rpc_info() const {
+    return client_rpc_info_;
+  }
+
+  experimental::ServerRpcInfo* server_rpc_info() const {
+    return server_rpc_info_;
+  }
+
+ private:
+  CallHook* call_hook_;
+  CompletionQueue* cq_;
+  grpc_call* call_;
+  int max_receive_message_size_;
+  experimental::ClientRpcInfo* client_rpc_info_ = nullptr;
+  experimental::ServerRpcInfo* server_rpc_info_ = nullptr;
+};
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CALL_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/call_hook.h b/third_party/grpc/include/grpcpp/impl/codegen/call_hook.h
new file mode 100644
index 0000000..4f7d370
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/call_hook.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CALL_HOOK_H
+#define GRPCPP_IMPL_CODEGEN_CALL_HOOK_H
+
+namespace grpc {
+
+namespace internal {
+class CallOpSetInterface;
+class Call;
+
+/// This is an interface that Channel and Server implement to allow them to hook
+/// performing ops.
+class CallHook {
+ public:
+  virtual ~CallHook() {}
+  virtual void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) = 0;
+};
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CALL_HOOK_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/call_op_set.h b/third_party/grpc/include/grpcpp/impl/codegen/call_op_set.h
new file mode 100644
index 0000000..b2100c6
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/call_op_set.h
@@ -0,0 +1,923 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
+#define GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
+
+#include <assert.h>
+#include <array>
+#include <cstring>
+#include <functional>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_hook.h>
+#include <grpcpp/impl/codegen/call_op_set_interface.h>
+#include <grpcpp/impl/codegen/client_context.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/intercepted_channel.h>
+#include <grpcpp/impl/codegen/interceptor_common.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+#include <grpc/impl/codegen/atm.h>
+#include <grpc/impl/codegen/compression_types.h>
+#include <grpc/impl/codegen/grpc_types.h>
+
+namespace grpc {
+
+class CompletionQueue;
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+namespace internal {
+class Call;
+class CallHook;
+
+// TODO(yangg) if the map is changed before we send, the pointers will be a
+// mess. Make sure it does not happen.
+inline grpc_metadata* FillMetadataArray(
+    const std::multimap<grpc::string, grpc::string>& metadata,
+    size_t* metadata_count, const grpc::string& optional_error_details) {
+  *metadata_count = metadata.size() + (optional_error_details.empty() ? 0 : 1);
+  if (*metadata_count == 0) {
+    return nullptr;
+  }
+  grpc_metadata* metadata_array =
+      (grpc_metadata*)(g_core_codegen_interface->gpr_malloc(
+          (*metadata_count) * sizeof(grpc_metadata)));
+  size_t i = 0;
+  for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) {
+    metadata_array[i].key = SliceReferencingString(iter->first);
+    metadata_array[i].value = SliceReferencingString(iter->second);
+  }
+  if (!optional_error_details.empty()) {
+    metadata_array[i].key =
+        g_core_codegen_interface->grpc_slice_from_static_buffer(
+            kBinaryErrorDetailsKey, sizeof(kBinaryErrorDetailsKey) - 1);
+    metadata_array[i].value = SliceReferencingString(optional_error_details);
+  }
+  return metadata_array;
+}
+}  // namespace internal
+
+/// Per-message write options.
+class WriteOptions {
+ public:
+  WriteOptions() : flags_(0), last_message_(false) {}
+  WriteOptions(const WriteOptions& other)
+      : flags_(other.flags_), last_message_(other.last_message_) {}
+
+  /// Clear all flags.
+  inline void Clear() { flags_ = 0; }
+
+  /// Returns raw flags bitset.
+  inline uint32_t flags() const { return flags_; }
+
+  /// Sets flag for the disabling of compression for the next message write.
+  ///
+  /// \sa GRPC_WRITE_NO_COMPRESS
+  inline WriteOptions& set_no_compression() {
+    SetBit(GRPC_WRITE_NO_COMPRESS);
+    return *this;
+  }
+
+  /// Clears flag for the disabling of compression for the next message write.
+  ///
+  /// \sa GRPC_WRITE_NO_COMPRESS
+  inline WriteOptions& clear_no_compression() {
+    ClearBit(GRPC_WRITE_NO_COMPRESS);
+    return *this;
+  }
+
+  /// Get value for the flag indicating whether compression for the next
+  /// message write is forcefully disabled.
+  ///
+  /// \sa GRPC_WRITE_NO_COMPRESS
+  inline bool get_no_compression() const {
+    return GetBit(GRPC_WRITE_NO_COMPRESS);
+  }
+
+  /// Sets flag indicating that the write may be buffered and need not go out on
+  /// the wire immediately.
+  ///
+  /// \sa GRPC_WRITE_BUFFER_HINT
+  inline WriteOptions& set_buffer_hint() {
+    SetBit(GRPC_WRITE_BUFFER_HINT);
+    return *this;
+  }
+
+  /// Clears flag indicating that the write may be buffered and need not go out
+  /// on the wire immediately.
+  ///
+  /// \sa GRPC_WRITE_BUFFER_HINT
+  inline WriteOptions& clear_buffer_hint() {
+    ClearBit(GRPC_WRITE_BUFFER_HINT);
+    return *this;
+  }
+
+  /// Get value for the flag indicating that the write may be buffered and need
+  /// not go out on the wire immediately.
+  ///
+  /// \sa GRPC_WRITE_BUFFER_HINT
+  inline bool get_buffer_hint() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
+
+  /// corked bit: aliases set_buffer_hint currently, with the intent that
+  /// set_buffer_hint will be removed in the future
+  inline WriteOptions& set_corked() {
+    SetBit(GRPC_WRITE_BUFFER_HINT);
+    return *this;
+  }
+
+  inline WriteOptions& clear_corked() {
+    ClearBit(GRPC_WRITE_BUFFER_HINT);
+    return *this;
+  }
+
+  inline bool is_corked() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
+
+  /// last-message bit: indicates this is the last message in a stream
+  /// client-side:  makes Write the equivalent of performing Write, WritesDone
+  /// in a single step
+  /// server-side:  hold the Write until the service handler returns (sync api)
+  /// or until Finish is called (async api)
+  inline WriteOptions& set_last_message() {
+    last_message_ = true;
+    return *this;
+  }
+
+  /// Clears flag indicating that this is the last message in a stream,
+  /// disabling coalescing.
+  inline WriteOptions& clear_last_message() {
+    last_message_ = false;
+    return *this;
+  }
+
+  /// Guarantee that all bytes have been written to the socket before completing
+  /// this write (usually writes are completed when they pass flow control).
+  inline WriteOptions& set_write_through() {
+    SetBit(GRPC_WRITE_THROUGH);
+    return *this;
+  }
+
+  inline bool is_write_through() const { return GetBit(GRPC_WRITE_THROUGH); }
+
+  /// Get value for the flag indicating that this is the last message, and
+  /// should be coalesced with trailing metadata.
+  ///
+  /// \sa GRPC_WRITE_LAST_MESSAGE
+  bool is_last_message() const { return last_message_; }
+
+  WriteOptions& operator=(const WriteOptions& rhs) {
+    flags_ = rhs.flags_;
+    return *this;
+  }
+
+ private:
+  void SetBit(const uint32_t mask) { flags_ |= mask; }
+
+  void ClearBit(const uint32_t mask) { flags_ &= ~mask; }
+
+  bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; }
+
+  uint32_t flags_;
+  bool last_message_;
+};
+
+namespace internal {
+
+/// Default argument for CallOpSet. I is unused by the class, but can be
+/// used for generating multiple names for the same thing.
+template <int I>
+class CallNoOp {
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {}
+  void FinishOp(bool* status) {}
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {}
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {}
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {}
+};
+
+class CallOpSendInitialMetadata {
+ public:
+  CallOpSendInitialMetadata() : send_(false) {
+    maybe_compression_level_.is_set = false;
+  }
+
+  void SendInitialMetadata(std::multimap<grpc::string, grpc::string>* metadata,
+                           uint32_t flags) {
+    maybe_compression_level_.is_set = false;
+    send_ = true;
+    flags_ = flags;
+    metadata_map_ = metadata;
+  }
+
+  void set_compression_level(grpc_compression_level level) {
+    maybe_compression_level_.is_set = true;
+    maybe_compression_level_.level = level;
+  }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!send_ || hijacked_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_SEND_INITIAL_METADATA;
+    op->flags = flags_;
+    op->reserved = NULL;
+    initial_metadata_ =
+        FillMetadataArray(*metadata_map_, &initial_metadata_count_, "");
+    op->data.send_initial_metadata.count = initial_metadata_count_;
+    op->data.send_initial_metadata.metadata = initial_metadata_;
+    op->data.send_initial_metadata.maybe_compression_level.is_set =
+        maybe_compression_level_.is_set;
+    if (maybe_compression_level_.is_set) {
+      op->data.send_initial_metadata.maybe_compression_level.level =
+          maybe_compression_level_.level;
+    }
+  }
+  void FinishOp(bool* status) {
+    if (!send_ || hijacked_) return;
+    g_core_codegen_interface->gpr_free(initial_metadata_);
+    send_ = false;
+  }
+
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    if (!send_) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA);
+    interceptor_methods->SetSendInitialMetadata(metadata_map_);
+  }
+
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {}
+
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {
+    hijacked_ = true;
+  }
+
+  bool hijacked_ = false;
+  bool send_;
+  uint32_t flags_;
+  size_t initial_metadata_count_;
+  std::multimap<grpc::string, grpc::string>* metadata_map_;
+  grpc_metadata* initial_metadata_;
+  struct {
+    bool is_set;
+    grpc_compression_level level;
+  } maybe_compression_level_;
+};
+
+class CallOpSendMessage {
+ public:
+  CallOpSendMessage() : send_buf_() {}
+
+  /// Send \a message using \a options for the write. The \a options are cleared
+  /// after use.
+  template <class M>
+  Status SendMessage(const M& message,
+                     WriteOptions options) GRPC_MUST_USE_RESULT;
+
+  template <class M>
+  Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!send_buf_.Valid() || hijacked_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_SEND_MESSAGE;
+    op->flags = write_options_.flags();
+    op->reserved = NULL;
+    op->data.send_message.send_message = send_buf_.c_buffer();
+    // Flags are per-message: clear them after use.
+    write_options_.Clear();
+  }
+  void FinishOp(bool* status) { send_buf_.Clear(); }
+
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    if (!send_buf_.Valid()) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::PRE_SEND_MESSAGE);
+    interceptor_methods->SetSendMessage(&send_buf_);
+  }
+
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    // The contents of the SendMessage value that was previously set
+    // has had its references stolen by core's operations
+    interceptor_methods->SetSendMessage(nullptr);
+  }
+
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {
+    hijacked_ = true;
+  }
+
+ private:
+  bool hijacked_ = false;
+  ByteBuffer send_buf_;
+  WriteOptions write_options_;
+};
+
+template <class M>
+Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) {
+  write_options_ = options;
+  bool own_buf;
+  // TODO(vjpai): Remove the void below when possible
+  // The void in the template parameter below should not be needed
+  // (since it should be implicit) but is needed due to an observed
+  // difference in behavior between clang and gcc for certain internal users
+  Status result = SerializationTraits<M, void>::Serialize(
+      message, send_buf_.bbuf_ptr(), &own_buf);
+  if (!own_buf) {
+    send_buf_.Duplicate();
+  }
+  return result;
+}
+
+template <class M>
+Status CallOpSendMessage::SendMessage(const M& message) {
+  return SendMessage(message, WriteOptions());
+}
+
+template <class R>
+class CallOpRecvMessage {
+ public:
+  CallOpRecvMessage()
+      : got_message(false),
+        message_(nullptr),
+        allow_not_getting_message_(false) {}
+
+  void RecvMessage(R* message) { message_ = message; }
+
+  // Do not change status if no message is received.
+  void AllowNoMessage() { allow_not_getting_message_ = true; }
+
+  bool got_message;
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (message_ == nullptr || hijacked_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_RECV_MESSAGE;
+    op->flags = 0;
+    op->reserved = NULL;
+    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
+  }
+
+  void FinishOp(bool* status) {
+    if (message_ == nullptr || hijacked_) return;
+    if (recv_buf_.Valid()) {
+      if (*status) {
+        got_message = *status =
+            SerializationTraits<R>::Deserialize(recv_buf_.bbuf_ptr(), message_)
+                .ok();
+        recv_buf_.Release();
+      } else {
+        got_message = false;
+        recv_buf_.Clear();
+      }
+    } else {
+      got_message = false;
+      if (!allow_not_getting_message_) {
+        *status = false;
+      }
+    }
+    message_ = nullptr;
+  }
+
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    interceptor_methods->SetRecvMessage(message_);
+  }
+
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    if (!got_message) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+  }
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {
+    hijacked_ = true;
+    if (message_ == nullptr) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::PRE_RECV_MESSAGE);
+    got_message = true;
+  }
+
+ private:
+  R* message_;
+  ByteBuffer recv_buf_;
+  bool allow_not_getting_message_;
+  bool hijacked_ = false;
+};
+
+class DeserializeFunc {
+ public:
+  virtual Status Deserialize(ByteBuffer* buf) = 0;
+  virtual ~DeserializeFunc() {}
+};
+
+template <class R>
+class DeserializeFuncType final : public DeserializeFunc {
+ public:
+  DeserializeFuncType(R* message) : message_(message) {}
+  Status Deserialize(ByteBuffer* buf) override {
+    return SerializationTraits<R>::Deserialize(buf->bbuf_ptr(), message_);
+  }
+
+  ~DeserializeFuncType() override {}
+
+ private:
+  R* message_;  // Not a managed pointer because management is external to this
+};
+
+class CallOpGenericRecvMessage {
+ public:
+  CallOpGenericRecvMessage()
+      : got_message(false), allow_not_getting_message_(false) {}
+
+  template <class R>
+  void RecvMessage(R* message) {
+    // Use an explicit base class pointer to avoid resolution error in the
+    // following unique_ptr::reset for some old implementations.
+    DeserializeFunc* func = new DeserializeFuncType<R>(message);
+    deserialize_.reset(func);
+    message_ = message;
+  }
+
+  // Do not change status if no message is received.
+  void AllowNoMessage() { allow_not_getting_message_ = true; }
+
+  bool got_message;
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!deserialize_ || hijacked_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_RECV_MESSAGE;
+    op->flags = 0;
+    op->reserved = NULL;
+    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
+  }
+
+  void FinishOp(bool* status) {
+    if (!deserialize_ || hijacked_) return;
+    if (recv_buf_.Valid()) {
+      if (*status) {
+        got_message = true;
+        *status = deserialize_->Deserialize(&recv_buf_).ok();
+        recv_buf_.Release();
+      } else {
+        got_message = false;
+        recv_buf_.Clear();
+      }
+    } else {
+      got_message = false;
+      if (!allow_not_getting_message_) {
+        *status = false;
+      }
+    }
+    deserialize_.reset();
+  }
+
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    interceptor_methods->SetRecvMessage(message_);
+  }
+
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    if (!got_message) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+  }
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {
+    hijacked_ = true;
+    if (!deserialize_) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::PRE_RECV_MESSAGE);
+  }
+
+ private:
+  void* message_;
+  bool hijacked_ = false;
+  std::unique_ptr<DeserializeFunc> deserialize_;
+  ByteBuffer recv_buf_;
+  bool allow_not_getting_message_;
+};
+
+class CallOpClientSendClose {
+ public:
+  CallOpClientSendClose() : send_(false) {}
+
+  void ClientSendClose() { send_ = true; }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!send_ || hijacked_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
+    op->flags = 0;
+    op->reserved = NULL;
+  }
+  void FinishOp(bool* status) { send_ = false; }
+
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    if (!send_) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::PRE_SEND_CLOSE);
+  }
+
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {}
+
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {
+    hijacked_ = true;
+  }
+
+ private:
+  bool hijacked_ = false;
+  bool send_;
+};
+
+class CallOpServerSendStatus {
+ public:
+  CallOpServerSendStatus() : send_status_available_(false) {}
+
+  void ServerSendStatus(
+      std::multimap<grpc::string, grpc::string>* trailing_metadata,
+      const Status& status) {
+    send_error_details_ = status.error_details();
+    metadata_map_ = trailing_metadata;
+    send_status_available_ = true;
+    send_status_code_ = static_cast<grpc_status_code>(status.error_code());
+    send_error_message_ = status.error_message();
+  }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (!send_status_available_ || hijacked_) return;
+    trailing_metadata_ = FillMetadataArray(
+        *metadata_map_, &trailing_metadata_count_, send_error_details_);
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
+    op->data.send_status_from_server.trailing_metadata_count =
+        trailing_metadata_count_;
+    op->data.send_status_from_server.trailing_metadata = trailing_metadata_;
+    op->data.send_status_from_server.status = send_status_code_;
+    error_message_slice_ = SliceReferencingString(send_error_message_);
+    op->data.send_status_from_server.status_details =
+        send_error_message_.empty() ? nullptr : &error_message_slice_;
+    op->flags = 0;
+    op->reserved = NULL;
+  }
+
+  void FinishOp(bool* status) {
+    if (!send_status_available_ || hijacked_) return;
+    g_core_codegen_interface->gpr_free(trailing_metadata_);
+    send_status_available_ = false;
+  }
+
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    if (!send_status_available_) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::PRE_SEND_STATUS);
+    interceptor_methods->SetSendTrailingMetadata(metadata_map_);
+    interceptor_methods->SetSendStatus(&send_status_code_, &send_error_details_,
+                                       &send_error_message_);
+  }
+
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {}
+
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {
+    hijacked_ = true;
+  }
+
+ private:
+  bool hijacked_ = false;
+  bool send_status_available_;
+  grpc_status_code send_status_code_;
+  grpc::string send_error_details_;
+  grpc::string send_error_message_;
+  size_t trailing_metadata_count_;
+  std::multimap<grpc::string, grpc::string>* metadata_map_;
+  grpc_metadata* trailing_metadata_;
+  grpc_slice error_message_slice_;
+};
+
+class CallOpRecvInitialMetadata {
+ public:
+  CallOpRecvInitialMetadata() : metadata_map_(nullptr) {}
+
+  void RecvInitialMetadata(ClientContext* context) {
+    context->initial_metadata_received_ = true;
+    metadata_map_ = &context->recv_initial_metadata_;
+  }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (metadata_map_ == nullptr || hijacked_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_RECV_INITIAL_METADATA;
+    op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr();
+    op->flags = 0;
+    op->reserved = NULL;
+  }
+
+  void FinishOp(bool* status) {
+    if (metadata_map_ == nullptr || hijacked_) return;
+  }
+
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    interceptor_methods->SetRecvInitialMetadata(metadata_map_);
+  }
+
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    if (metadata_map_ == nullptr) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+    metadata_map_ = nullptr;
+  }
+
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {
+    hijacked_ = true;
+    if (metadata_map_ == nullptr) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::PRE_RECV_INITIAL_METADATA);
+  }
+
+ private:
+  bool hijacked_ = false;
+  MetadataMap* metadata_map_;
+};
+
+class CallOpClientRecvStatus {
+ public:
+  CallOpClientRecvStatus()
+      : recv_status_(nullptr), debug_error_string_(nullptr) {}
+
+  void ClientRecvStatus(ClientContext* context, Status* status) {
+    client_context_ = context;
+    metadata_map_ = &client_context_->trailing_metadata_;
+    recv_status_ = status;
+    error_message_ = g_core_codegen_interface->grpc_empty_slice();
+  }
+
+ protected:
+  void AddOp(grpc_op* ops, size_t* nops) {
+    if (recv_status_ == nullptr || hijacked_) return;
+    grpc_op* op = &ops[(*nops)++];
+    op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+    op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr();
+    op->data.recv_status_on_client.status = &status_code_;
+    op->data.recv_status_on_client.status_details = &error_message_;
+    op->data.recv_status_on_client.error_string = &debug_error_string_;
+    op->flags = 0;
+    op->reserved = NULL;
+  }
+
+  void FinishOp(bool* status) {
+    if (recv_status_ == nullptr || hijacked_) return;
+    grpc::string binary_error_details = metadata_map_->GetBinaryErrorDetails();
+    *recv_status_ =
+        Status(static_cast<StatusCode>(status_code_),
+               GRPC_SLICE_IS_EMPTY(error_message_)
+                   ? grpc::string()
+                   : grpc::string(GRPC_SLICE_START_PTR(error_message_),
+                                  GRPC_SLICE_END_PTR(error_message_)),
+               binary_error_details);
+    client_context_->set_debug_error_string(
+        debug_error_string_ != nullptr ? debug_error_string_ : "");
+    g_core_codegen_interface->grpc_slice_unref(error_message_);
+    if (debug_error_string_ != nullptr) {
+      g_core_codegen_interface->gpr_free((void*)debug_error_string_);
+    }
+  }
+
+  void SetInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    interceptor_methods->SetRecvStatus(recv_status_);
+    interceptor_methods->SetRecvTrailingMetadata(metadata_map_);
+  }
+
+  void SetFinishInterceptionHookPoint(
+      InterceptorBatchMethodsImpl* interceptor_methods) {
+    if (recv_status_ == nullptr) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::POST_RECV_STATUS);
+    recv_status_ = nullptr;
+  }
+
+  void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {
+    hijacked_ = true;
+    if (recv_status_ == nullptr) return;
+    interceptor_methods->AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::PRE_RECV_STATUS);
+  }
+
+ private:
+  bool hijacked_ = false;
+  ClientContext* client_context_;
+  MetadataMap* metadata_map_;
+  Status* recv_status_;
+  const char* debug_error_string_;
+  grpc_status_code status_code_;
+  grpc_slice error_message_;
+};
+
+template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>,
+          class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>,
+          class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>>
+class CallOpSet;
+
+/// Primary implementation of CallOpSetInterface.
+/// Since we cannot use variadic templates, we declare slots up to
+/// the maximum count of ops we'll need in a set. We leverage the
+/// empty base class optimization to slim this class (especially
+/// when there are many unused slots used). To avoid duplicate base classes,
+/// the template parmeter for CallNoOp is varied by argument position.
+template <class Op1, class Op2, class Op3, class Op4, class Op5, class Op6>
+class CallOpSet : public CallOpSetInterface,
+                  public Op1,
+                  public Op2,
+                  public Op3,
+                  public Op4,
+                  public Op5,
+                  public Op6 {
+ public:
+  CallOpSet() : core_cq_tag_(this), return_tag_(this) {}
+  // The copy constructor and assignment operator reset the value of
+  // core_cq_tag_, return_tag_, done_intercepting_ and interceptor_methods_
+  // since those are only meaningful on a specific object, not across objects.
+  CallOpSet(const CallOpSet& other)
+      : core_cq_tag_(this),
+        return_tag_(this),
+        call_(other.call_),
+        done_intercepting_(false),
+        interceptor_methods_(InterceptorBatchMethodsImpl()) {}
+
+  CallOpSet& operator=(const CallOpSet& other) {
+    core_cq_tag_ = this;
+    return_tag_ = this;
+    call_ = other.call_;
+    done_intercepting_ = false;
+    interceptor_methods_ = InterceptorBatchMethodsImpl();
+    return *this;
+  }
+
+  void FillOps(Call* call) override {
+    done_intercepting_ = false;
+    g_core_codegen_interface->grpc_call_ref(call->call());
+    call_ =
+        *call;  // It's fine to create a copy of call since it's just pointers
+
+    if (RunInterceptors()) {
+      ContinueFillOpsAfterInterception();
+    } else {
+      // After the interceptors are run, ContinueFillOpsAfterInterception will
+      // be run
+    }
+  }
+
+  bool FinalizeResult(void** tag, bool* status) override {
+    if (done_intercepting_) {
+      // We have already finished intercepting and filling in the results. This
+      // round trip from the core needed to be made because interceptors were
+      // run
+      *tag = return_tag_;
+      *status = saved_status_;
+      g_core_codegen_interface->grpc_call_unref(call_.call());
+      return true;
+    }
+
+    this->Op1::FinishOp(status);
+    this->Op2::FinishOp(status);
+    this->Op3::FinishOp(status);
+    this->Op4::FinishOp(status);
+    this->Op5::FinishOp(status);
+    this->Op6::FinishOp(status);
+    saved_status_ = *status;
+    if (RunInterceptorsPostRecv()) {
+      *tag = return_tag_;
+      g_core_codegen_interface->grpc_call_unref(call_.call());
+      return true;
+    }
+    // Interceptors are going to be run, so we can't return the tag just yet.
+    // After the interceptors are run, ContinueFinalizeResultAfterInterception
+    return false;
+  }
+
+  void set_output_tag(void* return_tag) { return_tag_ = return_tag; }
+
+  void* core_cq_tag() override { return core_cq_tag_; }
+
+  /// set_core_cq_tag is used to provide a different core CQ tag than "this".
+  /// This is used for callback-based tags, where the core tag is the core
+  /// callback function. It does not change the use or behavior of any other
+  /// function (such as FinalizeResult)
+  void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; }
+
+  // This will be called while interceptors are run if the RPC is a hijacked
+  // RPC. This should set hijacking state for each of the ops.
+  void SetHijackingState() override {
+    this->Op1::SetHijackingState(&interceptor_methods_);
+    this->Op2::SetHijackingState(&interceptor_methods_);
+    this->Op3::SetHijackingState(&interceptor_methods_);
+    this->Op4::SetHijackingState(&interceptor_methods_);
+    this->Op5::SetHijackingState(&interceptor_methods_);
+    this->Op6::SetHijackingState(&interceptor_methods_);
+  }
+
+  // Should be called after interceptors are done running
+  void ContinueFillOpsAfterInterception() override {
+    static const size_t MAX_OPS = 6;
+    grpc_op ops[MAX_OPS];
+    size_t nops = 0;
+    this->Op1::AddOp(ops, &nops);
+    this->Op2::AddOp(ops, &nops);
+    this->Op3::AddOp(ops, &nops);
+    this->Op4::AddOp(ops, &nops);
+    this->Op5::AddOp(ops, &nops);
+    this->Op6::AddOp(ops, &nops);
+    GPR_CODEGEN_ASSERT(GRPC_CALL_OK ==
+                       g_core_codegen_interface->grpc_call_start_batch(
+                           call_.call(), ops, nops, core_cq_tag(), nullptr));
+  }
+
+  // Should be called after interceptors are done running on the finalize result
+  // path
+  void ContinueFinalizeResultAfterInterception() override {
+    done_intercepting_ = true;
+    GPR_CODEGEN_ASSERT(GRPC_CALL_OK ==
+                       g_core_codegen_interface->grpc_call_start_batch(
+                           call_.call(), nullptr, 0, core_cq_tag(), nullptr));
+  }
+
+ private:
+  // Returns true if no interceptors need to be run
+  bool RunInterceptors() {
+    interceptor_methods_.ClearState();
+    interceptor_methods_.SetCallOpSetInterface(this);
+    interceptor_methods_.SetCall(&call_);
+    this->Op1::SetInterceptionHookPoint(&interceptor_methods_);
+    this->Op2::SetInterceptionHookPoint(&interceptor_methods_);
+    this->Op3::SetInterceptionHookPoint(&interceptor_methods_);
+    this->Op4::SetInterceptionHookPoint(&interceptor_methods_);
+    this->Op5::SetInterceptionHookPoint(&interceptor_methods_);
+    this->Op6::SetInterceptionHookPoint(&interceptor_methods_);
+    return interceptor_methods_.RunInterceptors();
+  }
+  // Returns true if no interceptors need to be run
+  bool RunInterceptorsPostRecv() {
+    // Call and OpSet had already been set on the set state.
+    // SetReverse also clears previously set hook points
+    interceptor_methods_.SetReverse();
+    this->Op1::SetFinishInterceptionHookPoint(&interceptor_methods_);
+    this->Op2::SetFinishInterceptionHookPoint(&interceptor_methods_);
+    this->Op3::SetFinishInterceptionHookPoint(&interceptor_methods_);
+    this->Op4::SetFinishInterceptionHookPoint(&interceptor_methods_);
+    this->Op5::SetFinishInterceptionHookPoint(&interceptor_methods_);
+    this->Op6::SetFinishInterceptionHookPoint(&interceptor_methods_);
+    return interceptor_methods_.RunInterceptors();
+  }
+
+  void* core_cq_tag_;
+  void* return_tag_;
+  Call call_;
+  bool done_intercepting_ = false;
+  InterceptorBatchMethodsImpl interceptor_methods_;
+  bool saved_status_;
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/call_op_set_interface.h b/third_party/grpc/include/grpcpp/impl/codegen/call_op_set_interface.h
new file mode 100644
index 0000000..3b74566
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/call_op_set_interface.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H
+#define GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H
+
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+
+namespace grpc {
+namespace internal {
+
+class Call;
+
+/// An abstract collection of call ops, used to generate the
+/// grpc_call_op structure to pass down to the lower layers,
+/// and as it is-a CompletionQueueTag, also massages the final
+/// completion into the correct form for consumption in the C++
+/// API.
+class CallOpSetInterface : public CompletionQueueTag {
+ public:
+  /// Fills in grpc_op, starting from ops[*nops] and moving
+  /// upwards.
+  virtual void FillOps(internal::Call* call) = 0;
+
+  /// Get the tag to be used at the core completion queue. Generally, the
+  /// value of core_cq_tag will be "this". However, it can be overridden if we
+  /// want core to process the tag differently (e.g., as a core callback)
+  virtual void* core_cq_tag() = 0;
+
+  // This will be called while interceptors are run if the RPC is a hijacked
+  // RPC. This should set hijacking state for each of the ops.
+  virtual void SetHijackingState() = 0;
+
+  // Should be called after interceptors are done running
+  virtual void ContinueFillOpsAfterInterception() = 0;
+
+  // Should be called after interceptors are done running on the finalize result
+  // path
+  virtual void ContinueFinalizeResultAfterInterception() = 0;
+};
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/callback_common.h b/third_party/grpc/include/grpcpp/impl/codegen/callback_common.h
new file mode 100644
index 0000000..a3c8c41
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/callback_common.h
@@ -0,0 +1,217 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H
+#define GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H
+
+#include <functional>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+namespace internal {
+
+/// An exception-safe way of invoking a user-specified callback function
+// TODO(vjpai): decide whether it is better for this to take a const lvalue
+//              parameter or an rvalue parameter, or if it even matters
+template <class Func, class... Args>
+void CatchingCallback(Func&& func, Args&&... args) {
+#if GRPC_ALLOW_EXCEPTIONS
+  try {
+    func(std::forward<Args>(args)...);
+  } catch (...) {
+    // nothing to return or change here, just don't crash the library
+  }
+#else   // GRPC_ALLOW_EXCEPTIONS
+  func(std::forward<Args>(args)...);
+#endif  // GRPC_ALLOW_EXCEPTIONS
+}
+
+template <class ReturnType, class Func, class... Args>
+ReturnType* CatchingReactorCreator(Func&& func, Args&&... args) {
+#if GRPC_ALLOW_EXCEPTIONS
+  try {
+    return func(std::forward<Args>(args)...);
+  } catch (...) {
+    // fail the RPC, don't crash the library
+    return nullptr;
+  }
+#else   // GRPC_ALLOW_EXCEPTIONS
+  return func(std::forward<Args>(args)...);
+#endif  // GRPC_ALLOW_EXCEPTIONS
+}
+
+// The contract on these tags is that they are single-shot. They must be
+// constructed and then fired at exactly one point. There is no expectation
+// that they can be reused without reconstruction.
+
+class CallbackWithStatusTag
+    : public grpc_experimental_completion_queue_functor {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(CallbackWithStatusTag));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  CallbackWithStatusTag(grpc_call* call, std::function<void(Status)> f,
+                        CompletionQueueTag* ops)
+      : call_(call), func_(std::move(f)), ops_(ops) {
+    g_core_codegen_interface->grpc_call_ref(call);
+    functor_run = &CallbackWithStatusTag::StaticRun;
+  }
+  ~CallbackWithStatusTag() {}
+  Status* status_ptr() { return &status_; }
+
+  // force_run can not be performed on a tag if operations using this tag
+  // have been sent to PerformOpsOnCall. It is intended for error conditions
+  // that are detected before the operations are internally processed.
+  void force_run(Status s) {
+    status_ = std::move(s);
+    Run(true);
+  }
+
+ private:
+  grpc_call* call_;
+  std::function<void(Status)> func_;
+  CompletionQueueTag* ops_;
+  Status status_;
+
+  static void StaticRun(grpc_experimental_completion_queue_functor* cb,
+                        int ok) {
+    static_cast<CallbackWithStatusTag*>(cb)->Run(static_cast<bool>(ok));
+  }
+  void Run(bool ok) {
+    void* ignored = ops_;
+
+    if (!ops_->FinalizeResult(&ignored, &ok)) {
+      // The tag was swallowed
+      return;
+    }
+    GPR_CODEGEN_ASSERT(ignored == ops_);
+
+    // Last use of func_ or status_, so ok to move them out
+    auto func = std::move(func_);
+    auto status = std::move(status_);
+    func_ = nullptr;     // reset to clear this out for sure
+    status_ = Status();  // reset to clear this out for sure
+    CatchingCallback(std::move(func), std::move(status));
+    g_core_codegen_interface->grpc_call_unref(call_);
+  }
+};
+
+/// CallbackWithSuccessTag can be reused multiple times, and will be used in
+/// this fashion for streaming operations. As a result, it shouldn't clear
+/// anything up until its destructor
+class CallbackWithSuccessTag
+    : public grpc_experimental_completion_queue_functor {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(CallbackWithSuccessTag));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  CallbackWithSuccessTag() : call_(nullptr) {}
+
+  CallbackWithSuccessTag(grpc_call* call, std::function<void(bool)> f,
+                         CompletionQueueTag* ops) {
+    Set(call, f, ops);
+  }
+
+  CallbackWithSuccessTag(const CallbackWithSuccessTag&) = delete;
+  CallbackWithSuccessTag& operator=(const CallbackWithSuccessTag&) = delete;
+
+  ~CallbackWithSuccessTag() { Clear(); }
+
+  // Set can only be called on a default-constructed or Clear'ed tag.
+  // It should never be called on a tag that was constructed with arguments
+  // or on a tag that has been Set before unless the tag has been cleared.
+  void Set(grpc_call* call, std::function<void(bool)> f,
+           CompletionQueueTag* ops) {
+    GPR_CODEGEN_ASSERT(call_ == nullptr);
+    g_core_codegen_interface->grpc_call_ref(call);
+    call_ = call;
+    func_ = std::move(f);
+    ops_ = ops;
+    functor_run = &CallbackWithSuccessTag::StaticRun;
+  }
+
+  void Clear() {
+    if (call_ != nullptr) {
+      grpc_call* call = call_;
+      call_ = nullptr;
+      func_ = nullptr;
+      g_core_codegen_interface->grpc_call_unref(call);
+    }
+  }
+
+  CompletionQueueTag* ops() { return ops_; }
+
+  // force_run can not be performed on a tag if operations using this tag
+  // have been sent to PerformOpsOnCall. It is intended for error conditions
+  // that are detected before the operations are internally processed.
+  void force_run(bool ok) { Run(ok); }
+
+  /// check if this tag is currently set
+  operator bool() const { return call_ != nullptr; }
+
+ private:
+  grpc_call* call_;
+  std::function<void(bool)> func_;
+  CompletionQueueTag* ops_;
+
+  static void StaticRun(grpc_experimental_completion_queue_functor* cb,
+                        int ok) {
+    static_cast<CallbackWithSuccessTag*>(cb)->Run(static_cast<bool>(ok));
+  }
+  void Run(bool ok) {
+    void* ignored = ops_;
+    // Allow a "false" return value from FinalizeResult to silence the
+    // callback, just as it silences a CQ tag in the async cases
+    auto* ops = ops_;
+    bool do_callback = ops_->FinalizeResult(&ignored, &ok);
+    GPR_CODEGEN_ASSERT(ignored == ops);
+
+    if (do_callback) {
+      CatchingCallback(func_, ok);
+    }
+  }
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/channel_interface.h b/third_party/grpc/include/grpcpp/impl/codegen/channel_interface.h
new file mode 100644
index 0000000..5353f5f
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/channel_interface.h
@@ -0,0 +1,164 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H
+#define GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H
+
+#include <grpc/impl/codegen/connectivity_state.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/time.h>
+
+namespace grpc {
+class ChannelInterface;
+class ClientContext;
+class CompletionQueue;
+
+template <class R>
+class ClientReader;
+template <class W>
+class ClientWriter;
+template <class W, class R>
+class ClientReaderWriter;
+
+namespace internal {
+class Call;
+class CallOpSetInterface;
+class RpcMethod;
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl;
+template <class InputMessage, class OutputMessage>
+class CallbackUnaryCallImpl;
+template <class R>
+class ClientAsyncReaderFactory;
+template <class W>
+class ClientAsyncWriterFactory;
+template <class W, class R>
+class ClientAsyncReaderWriterFactory;
+template <class R>
+class ClientAsyncResponseReaderFactory;
+template <class W, class R>
+class ClientCallbackReaderWriterFactory;
+template <class R>
+class ClientCallbackReaderFactory;
+template <class W>
+class ClientCallbackWriterFactory;
+class InterceptedChannel;
+}  // namespace internal
+
+/// Codegen interface for \a grpc::Channel.
+class ChannelInterface {
+ public:
+  virtual ~ChannelInterface() {}
+  /// Get the current channel state. If the channel is in IDLE and
+  /// \a try_to_connect is set to true, try to connect.
+  virtual grpc_connectivity_state GetState(bool try_to_connect) = 0;
+
+  /// Return the \a tag on \a cq when the channel state is changed or \a
+  /// deadline expires. \a GetState needs to called to get the current state.
+  template <typename T>
+  void NotifyOnStateChange(grpc_connectivity_state last_observed, T deadline,
+                           CompletionQueue* cq, void* tag) {
+    TimePoint<T> deadline_tp(deadline);
+    NotifyOnStateChangeImpl(last_observed, deadline_tp.raw_time(), cq, tag);
+  }
+
+  /// Blocking wait for channel state change or \a deadline expiration.
+  /// \a GetState needs to called to get the current state.
+  template <typename T>
+  bool WaitForStateChange(grpc_connectivity_state last_observed, T deadline) {
+    TimePoint<T> deadline_tp(deadline);
+    return WaitForStateChangeImpl(last_observed, deadline_tp.raw_time());
+  }
+
+  /// Wait for this channel to be connected
+  template <typename T>
+  bool WaitForConnected(T deadline) {
+    grpc_connectivity_state state;
+    while ((state = GetState(true)) != GRPC_CHANNEL_READY) {
+      if (!WaitForStateChange(state, deadline)) return false;
+    }
+    return true;
+  }
+
+ private:
+  template <class R>
+  friend class ::grpc::ClientReader;
+  template <class W>
+  friend class ::grpc::ClientWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientReaderWriter;
+  template <class R>
+  friend class ::grpc::internal::ClientAsyncReaderFactory;
+  template <class W>
+  friend class ::grpc::internal::ClientAsyncWriterFactory;
+  template <class W, class R>
+  friend class ::grpc::internal::ClientAsyncReaderWriterFactory;
+  template <class R>
+  friend class ::grpc::internal::ClientAsyncResponseReaderFactory;
+  template <class W, class R>
+  friend class ::grpc::internal::ClientCallbackReaderWriterFactory;
+  template <class R>
+  friend class ::grpc::internal::ClientCallbackReaderFactory;
+  template <class W>
+  friend class ::grpc::internal::ClientCallbackWriterFactory;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::BlockingUnaryCallImpl;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::CallbackUnaryCallImpl;
+  friend class ::grpc::internal::RpcMethod;
+  friend class ::grpc::internal::InterceptedChannel;
+  virtual internal::Call CreateCall(const internal::RpcMethod& method,
+                                    ClientContext* context,
+                                    CompletionQueue* cq) = 0;
+  virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                                internal::Call* call) = 0;
+  virtual void* RegisterMethod(const char* method) = 0;
+  virtual void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
+                                       gpr_timespec deadline,
+                                       CompletionQueue* cq, void* tag) = 0;
+  virtual bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
+                                      gpr_timespec deadline) = 0;
+
+  // EXPERIMENTAL
+  // This is needed to keep codegen_test_minimal happy. InterceptedChannel needs
+  // to make use of this but can't directly call Channel's implementation
+  // because of the test.
+  // Returns an empty Call object (rather than being pure) since this is a new
+  // method and adding a new pure method to an interface would be a breaking
+  // change (even though this is private and non-API)
+  virtual internal::Call CreateCallInternal(const internal::RpcMethod& method,
+                                            ClientContext* context,
+                                            CompletionQueue* cq,
+                                            size_t interceptor_pos) {
+    return internal::Call();
+  }
+
+  // EXPERIMENTAL
+  // A method to get the callbackable completion queue associated with this
+  // channel. If the return value is nullptr, this channel doesn't support
+  // callback operations.
+  // TODO(vjpai): Consider a better default like using a global CQ
+  // Returns nullptr (rather than being pure) since this is a post-1.0 method
+  // and adding a new pure method to an interface would be a breaking change
+  // (even though this is private and non-API)
+  virtual CompletionQueue* CallbackCQ() { return nullptr; }
+};
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/client_callback.h b/third_party/grpc/include/grpcpp/impl/codegen/client_callback.h
new file mode 100644
index 0000000..66cf9b7
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/client_callback.h
@@ -0,0 +1,750 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H
+#define GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H
+
+#include <functional>
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_op_set.h>
+#include <grpcpp/impl/codegen/callback_common.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class Channel;
+class ClientContext;
+class CompletionQueue;
+
+namespace internal {
+class RpcMethod;
+
+/// Perform a callback-based unary call
+/// TODO(vjpai): Combine as much as possible with the blocking unary call code
+template <class InputMessage, class OutputMessage>
+void CallbackUnaryCall(ChannelInterface* channel, const RpcMethod& method,
+                       ClientContext* context, const InputMessage* request,
+                       OutputMessage* result,
+                       std::function<void(Status)> on_completion) {
+  CallbackUnaryCallImpl<InputMessage, OutputMessage> x(
+      channel, method, context, request, result, on_completion);
+}
+
+template <class InputMessage, class OutputMessage>
+class CallbackUnaryCallImpl {
+ public:
+  CallbackUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method,
+                        ClientContext* context, const InputMessage* request,
+                        OutputMessage* result,
+                        std::function<void(Status)> on_completion) {
+    CompletionQueue* cq = channel->CallbackCQ();
+    GPR_CODEGEN_ASSERT(cq != nullptr);
+    Call call(channel->CreateCall(method, context, cq));
+
+    using FullCallOpSet =
+        CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+                  CallOpRecvInitialMetadata, CallOpRecvMessage<OutputMessage>,
+                  CallOpClientSendClose, CallOpClientRecvStatus>;
+
+    auto* ops = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(FullCallOpSet))) FullCallOpSet;
+
+    auto* tag = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(CallbackWithStatusTag)))
+        CallbackWithStatusTag(call.call(), on_completion, ops);
+
+    // TODO(vjpai): Unify code with sync API as much as possible
+    Status s = ops->SendMessage(*request);
+    if (!s.ok()) {
+      tag->force_run(s);
+      return;
+    }
+    ops->SendInitialMetadata(&context->send_initial_metadata_,
+                             context->initial_metadata_flags());
+    ops->RecvInitialMetadata(context);
+    ops->RecvMessage(result);
+    ops->AllowNoMessage();
+    ops->ClientSendClose();
+    ops->ClientRecvStatus(context, tag->status_ptr());
+    ops->set_core_cq_tag(tag);
+    call.PerformOps(ops);
+  }
+};
+}  // namespace internal
+
+namespace experimental {
+
+// Forward declarations
+template <class Request, class Response>
+class ClientBidiReactor;
+template <class Response>
+class ClientReadReactor;
+template <class Request>
+class ClientWriteReactor;
+
+// NOTE: The streaming objects are not actually implemented in the public API.
+//       These interfaces are provided for mocking only. Typical applications
+//       will interact exclusively with the reactors that they define.
+template <class Request, class Response>
+class ClientCallbackReaderWriter {
+ public:
+  virtual ~ClientCallbackReaderWriter() {}
+  virtual void StartCall() = 0;
+  virtual void Write(const Request* req, WriteOptions options) = 0;
+  virtual void WritesDone() = 0;
+  virtual void Read(Response* resp) = 0;
+
+ protected:
+  void BindReactor(ClientBidiReactor<Request, Response>* reactor) {
+    reactor->BindStream(this);
+  }
+};
+
+template <class Response>
+class ClientCallbackReader {
+ public:
+  virtual ~ClientCallbackReader() {}
+  virtual void StartCall() = 0;
+  virtual void Read(Response* resp) = 0;
+
+ protected:
+  void BindReactor(ClientReadReactor<Response>* reactor) {
+    reactor->BindReader(this);
+  }
+};
+
+template <class Request>
+class ClientCallbackWriter {
+ public:
+  virtual ~ClientCallbackWriter() {}
+  virtual void StartCall() = 0;
+  void Write(const Request* req) { Write(req, WriteOptions()); }
+  virtual void Write(const Request* req, WriteOptions options) = 0;
+  void WriteLast(const Request* req, WriteOptions options) {
+    Write(req, options.set_last_message());
+  }
+  virtual void WritesDone() = 0;
+
+ protected:
+  void BindReactor(ClientWriteReactor<Request>* reactor) {
+    reactor->BindWriter(this);
+  }
+};
+
+// The user must implement this reactor interface with reactions to each event
+// type that gets called by the library. An empty reaction is provided by
+// default
+template <class Request, class Response>
+class ClientBidiReactor {
+ public:
+  virtual ~ClientBidiReactor() {}
+  virtual void OnDone(const Status& s) {}
+  virtual void OnReadInitialMetadataDone(bool ok) {}
+  virtual void OnReadDone(bool ok) {}
+  virtual void OnWriteDone(bool ok) {}
+  virtual void OnWritesDoneDone(bool ok) {}
+
+  void StartCall() { stream_->StartCall(); }
+  void StartRead(Response* resp) { stream_->Read(resp); }
+  void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); }
+  void StartWrite(const Request* req, WriteOptions options) {
+    stream_->Write(req, std::move(options));
+  }
+  void StartWriteLast(const Request* req, WriteOptions options) {
+    StartWrite(req, std::move(options.set_last_message()));
+  }
+  void StartWritesDone() { stream_->WritesDone(); }
+
+ private:
+  friend class ClientCallbackReaderWriter<Request, Response>;
+  void BindStream(ClientCallbackReaderWriter<Request, Response>* stream) {
+    stream_ = stream;
+  }
+  ClientCallbackReaderWriter<Request, Response>* stream_;
+};
+
+template <class Response>
+class ClientReadReactor {
+ public:
+  virtual ~ClientReadReactor() {}
+  virtual void OnDone(const Status& s) {}
+  virtual void OnReadInitialMetadataDone(bool ok) {}
+  virtual void OnReadDone(bool ok) {}
+
+  void StartCall() { reader_->StartCall(); }
+  void StartRead(Response* resp) { reader_->Read(resp); }
+
+ private:
+  friend class ClientCallbackReader<Response>;
+  void BindReader(ClientCallbackReader<Response>* reader) { reader_ = reader; }
+  ClientCallbackReader<Response>* reader_;
+};
+
+template <class Request>
+class ClientWriteReactor {
+ public:
+  virtual ~ClientWriteReactor() {}
+  virtual void OnDone(const Status& s) {}
+  virtual void OnReadInitialMetadataDone(bool ok) {}
+  virtual void OnWriteDone(bool ok) {}
+  virtual void OnWritesDoneDone(bool ok) {}
+
+  void StartCall() { writer_->StartCall(); }
+  void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); }
+  void StartWrite(const Request* req, WriteOptions options) {
+    writer_->Write(req, std::move(options));
+  }
+  void StartWriteLast(const Request* req, WriteOptions options) {
+    StartWrite(req, std::move(options.set_last_message()));
+  }
+  void StartWritesDone() { writer_->WritesDone(); }
+
+ private:
+  friend class ClientCallbackWriter<Request>;
+  void BindWriter(ClientCallbackWriter<Request>* writer) { writer_ = writer; }
+  ClientCallbackWriter<Request>* writer_;
+};
+
+}  // namespace experimental
+
+namespace internal {
+
+// Forward declare factory classes for friendship
+template <class Request, class Response>
+class ClientCallbackReaderWriterFactory;
+template <class Response>
+class ClientCallbackReaderFactory;
+template <class Request>
+class ClientCallbackWriterFactory;
+
+template <class Request, class Response>
+class ClientCallbackReaderWriterImpl
+    : public ::grpc::experimental::ClientCallbackReaderWriter<Request,
+                                                              Response> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientCallbackReaderWriterImpl));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  void MaybeFinish() {
+    if (--callbacks_outstanding_ == 0) {
+      Status s = std::move(finish_status_);
+      auto* reactor = reactor_;
+      auto* call = call_.call();
+      this->~ClientCallbackReaderWriterImpl();
+      g_core_codegen_interface->grpc_call_unref(call);
+      reactor->OnDone(s);
+    }
+  }
+
+  void StartCall() override {
+    // This call initiates two batches, plus any backlog, each with a callback
+    // 1. Send initial metadata (unless corked) + recv initial metadata
+    // 2. Any read backlog
+    // 3. Recv trailing metadata, on_completion callback
+    // 4. Any write backlog
+    // 5. See if the call can finish (if other callbacks were triggered already)
+    started_ = true;
+
+    start_tag_.Set(call_.call(),
+                   [this](bool ok) {
+                     reactor_->OnReadInitialMetadataDone(ok);
+                     MaybeFinish();
+                   },
+                   &start_ops_);
+    if (!start_corked_) {
+      start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                     context_->initial_metadata_flags());
+    }
+    start_ops_.RecvInitialMetadata(context_);
+    start_ops_.set_core_cq_tag(&start_tag_);
+    call_.PerformOps(&start_ops_);
+
+    // Also set up the read and write tags so that they don't have to be set up
+    // each time
+    write_tag_.Set(call_.call(),
+                   [this](bool ok) {
+                     reactor_->OnWriteDone(ok);
+                     MaybeFinish();
+                   },
+                   &write_ops_);
+    write_ops_.set_core_cq_tag(&write_tag_);
+
+    read_tag_.Set(call_.call(),
+                  [this](bool ok) {
+                    reactor_->OnReadDone(ok);
+                    MaybeFinish();
+                  },
+                  &read_ops_);
+    read_ops_.set_core_cq_tag(&read_tag_);
+    if (read_ops_at_start_) {
+      call_.PerformOps(&read_ops_);
+    }
+
+    finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); },
+                    &finish_ops_);
+    finish_ops_.ClientRecvStatus(context_, &finish_status_);
+    finish_ops_.set_core_cq_tag(&finish_tag_);
+    call_.PerformOps(&finish_ops_);
+
+    if (write_ops_at_start_) {
+      call_.PerformOps(&write_ops_);
+    }
+
+    if (writes_done_ops_at_start_) {
+      call_.PerformOps(&writes_done_ops_);
+    }
+    MaybeFinish();
+  }
+
+  void Read(Response* msg) override {
+    read_ops_.RecvMessage(msg);
+    callbacks_outstanding_++;
+    if (started_) {
+      call_.PerformOps(&read_ops_);
+    } else {
+      read_ops_at_start_ = true;
+    }
+  }
+
+  void Write(const Request* msg, WriteOptions options) override {
+    if (start_corked_) {
+      write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                     context_->initial_metadata_flags());
+      start_corked_ = false;
+    }
+    // TODO(vjpai): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*msg).ok());
+
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      write_ops_.ClientSendClose();
+    }
+    callbacks_outstanding_++;
+    if (started_) {
+      call_.PerformOps(&write_ops_);
+    } else {
+      write_ops_at_start_ = true;
+    }
+  }
+  void WritesDone() override {
+    if (start_corked_) {
+      writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                           context_->initial_metadata_flags());
+      start_corked_ = false;
+    }
+    writes_done_ops_.ClientSendClose();
+    writes_done_tag_.Set(call_.call(),
+                         [this](bool ok) {
+                           reactor_->OnWritesDoneDone(ok);
+                           MaybeFinish();
+                         },
+                         &writes_done_ops_);
+    writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
+    callbacks_outstanding_++;
+    if (started_) {
+      call_.PerformOps(&writes_done_ops_);
+    } else {
+      writes_done_ops_at_start_ = true;
+    }
+  }
+
+ private:
+  friend class ClientCallbackReaderWriterFactory<Request, Response>;
+
+  ClientCallbackReaderWriterImpl(
+      Call call, ClientContext* context,
+      ::grpc::experimental::ClientBidiReactor<Request, Response>* reactor)
+      : context_(context),
+        call_(call),
+        reactor_(reactor),
+        start_corked_(context_->initial_metadata_corked_) {
+    this->BindReactor(reactor);
+  }
+
+  ClientContext* context_;
+  Call call_;
+  ::grpc::experimental::ClientBidiReactor<Request, Response>* reactor_;
+
+  CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_;
+  CallbackWithSuccessTag start_tag_;
+  bool start_corked_;
+
+  CallOpSet<CallOpClientRecvStatus> finish_ops_;
+  CallbackWithSuccessTag finish_tag_;
+  Status finish_status_;
+
+  CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose>
+      write_ops_;
+  CallbackWithSuccessTag write_tag_;
+  bool write_ops_at_start_{false};
+
+  CallOpSet<CallOpSendInitialMetadata, CallOpClientSendClose> writes_done_ops_;
+  CallbackWithSuccessTag writes_done_tag_;
+  bool writes_done_ops_at_start_{false};
+
+  CallOpSet<CallOpRecvMessage<Response>> read_ops_;
+  CallbackWithSuccessTag read_tag_;
+  bool read_ops_at_start_{false};
+
+  // Minimum of 3 callbacks to pre-register for StartCall, start, and finish
+  std::atomic_int callbacks_outstanding_{3};
+  bool started_{false};
+};
+
+template <class Request, class Response>
+class ClientCallbackReaderWriterFactory {
+ public:
+  static void Create(
+      ChannelInterface* channel, const ::grpc::internal::RpcMethod& method,
+      ClientContext* context,
+      ::grpc::experimental::ClientBidiReactor<Request, Response>* reactor) {
+    Call call = channel->CreateCall(method, context, channel->CallbackCQ());
+
+    g_core_codegen_interface->grpc_call_ref(call.call());
+    new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientCallbackReaderWriterImpl<Request, Response>)))
+        ClientCallbackReaderWriterImpl<Request, Response>(call, context,
+                                                          reactor);
+  }
+};
+
+template <class Response>
+class ClientCallbackReaderImpl
+    : public ::grpc::experimental::ClientCallbackReader<Response> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientCallbackReaderImpl));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  void MaybeFinish() {
+    if (--callbacks_outstanding_ == 0) {
+      Status s = std::move(finish_status_);
+      auto* reactor = reactor_;
+      auto* call = call_.call();
+      this->~ClientCallbackReaderImpl();
+      g_core_codegen_interface->grpc_call_unref(call);
+      reactor->OnDone(s);
+    }
+  }
+
+  void StartCall() override {
+    // This call initiates two batches, plus any backlog, each with a callback
+    // 1. Send initial metadata (unless corked) + recv initial metadata
+    // 2. Any backlog
+    // 3. Recv trailing metadata, on_completion callback
+    // 4. See if the call can finish (if other callbacks were triggered already)
+    started_ = true;
+
+    start_tag_.Set(call_.call(),
+                   [this](bool ok) {
+                     reactor_->OnReadInitialMetadataDone(ok);
+                     MaybeFinish();
+                   },
+                   &start_ops_);
+    start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                   context_->initial_metadata_flags());
+    start_ops_.RecvInitialMetadata(context_);
+    start_ops_.set_core_cq_tag(&start_tag_);
+    call_.PerformOps(&start_ops_);
+
+    // Also set up the read tag so it doesn't have to be set up each time
+    read_tag_.Set(call_.call(),
+                  [this](bool ok) {
+                    reactor_->OnReadDone(ok);
+                    MaybeFinish();
+                  },
+                  &read_ops_);
+    read_ops_.set_core_cq_tag(&read_tag_);
+    if (read_ops_at_start_) {
+      call_.PerformOps(&read_ops_);
+    }
+
+    finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); },
+                    &finish_ops_);
+    finish_ops_.ClientRecvStatus(context_, &finish_status_);
+    finish_ops_.set_core_cq_tag(&finish_tag_);
+    call_.PerformOps(&finish_ops_);
+
+    MaybeFinish();
+  }
+
+  void Read(Response* msg) override {
+    read_ops_.RecvMessage(msg);
+    callbacks_outstanding_++;
+    if (started_) {
+      call_.PerformOps(&read_ops_);
+    } else {
+      read_ops_at_start_ = true;
+    }
+  }
+
+ private:
+  friend class ClientCallbackReaderFactory<Response>;
+
+  template <class Request>
+  ClientCallbackReaderImpl(
+      Call call, ClientContext* context, Request* request,
+      ::grpc::experimental::ClientReadReactor<Response>* reactor)
+      : context_(context), call_(call), reactor_(reactor) {
+    this->BindReactor(reactor);
+    // TODO(vjpai): don't assert
+    GPR_CODEGEN_ASSERT(start_ops_.SendMessage(*request).ok());
+    start_ops_.ClientSendClose();
+  }
+
+  ClientContext* context_;
+  Call call_;
+  ::grpc::experimental::ClientReadReactor<Response>* reactor_;
+
+  CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose,
+            CallOpRecvInitialMetadata>
+      start_ops_;
+  CallbackWithSuccessTag start_tag_;
+
+  CallOpSet<CallOpClientRecvStatus> finish_ops_;
+  CallbackWithSuccessTag finish_tag_;
+  Status finish_status_;
+
+  CallOpSet<CallOpRecvMessage<Response>> read_ops_;
+  CallbackWithSuccessTag read_tag_;
+  bool read_ops_at_start_{false};
+
+  // Minimum of 3 callbacks to pre-register for StartCall, start, and finish
+  std::atomic_int callbacks_outstanding_{3};
+  bool started_{false};
+};
+
+template <class Response>
+class ClientCallbackReaderFactory {
+ public:
+  template <class Request>
+  static void Create(
+      ChannelInterface* channel, const ::grpc::internal::RpcMethod& method,
+      ClientContext* context, const Request* request,
+      ::grpc::experimental::ClientReadReactor<Response>* reactor) {
+    Call call = channel->CreateCall(method, context, channel->CallbackCQ());
+
+    g_core_codegen_interface->grpc_call_ref(call.call());
+    new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientCallbackReaderImpl<Response>)))
+        ClientCallbackReaderImpl<Response>(call, context, request, reactor);
+  }
+};
+
+template <class Request>
+class ClientCallbackWriterImpl
+    : public ::grpc::experimental::ClientCallbackWriter<Request> {
+ public:
+  // always allocated against a call arena, no memory free required
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(ClientCallbackWriterImpl));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  void MaybeFinish() {
+    if (--callbacks_outstanding_ == 0) {
+      Status s = std::move(finish_status_);
+      auto* reactor = reactor_;
+      auto* call = call_.call();
+      this->~ClientCallbackWriterImpl();
+      g_core_codegen_interface->grpc_call_unref(call);
+      reactor->OnDone(s);
+    }
+  }
+
+  void StartCall() override {
+    // This call initiates two batches, plus any backlog, each with a callback
+    // 1. Send initial metadata (unless corked) + recv initial metadata
+    // 2. Recv trailing metadata, on_completion callback
+    // 3. Any backlog
+    // 4. See if the call can finish (if other callbacks were triggered already)
+    started_ = true;
+
+    start_tag_.Set(call_.call(),
+                   [this](bool ok) {
+                     reactor_->OnReadInitialMetadataDone(ok);
+                     MaybeFinish();
+                   },
+                   &start_ops_);
+    if (!start_corked_) {
+      start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                     context_->initial_metadata_flags());
+    }
+    start_ops_.RecvInitialMetadata(context_);
+    start_ops_.set_core_cq_tag(&start_tag_);
+    call_.PerformOps(&start_ops_);
+
+    // Also set up the read and write tags so that they don't have to be set up
+    // each time
+    write_tag_.Set(call_.call(),
+                   [this](bool ok) {
+                     reactor_->OnWriteDone(ok);
+                     MaybeFinish();
+                   },
+                   &write_ops_);
+    write_ops_.set_core_cq_tag(&write_tag_);
+
+    finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); },
+                    &finish_ops_);
+    finish_ops_.ClientRecvStatus(context_, &finish_status_);
+    finish_ops_.set_core_cq_tag(&finish_tag_);
+    call_.PerformOps(&finish_ops_);
+
+    if (write_ops_at_start_) {
+      call_.PerformOps(&write_ops_);
+    }
+
+    if (writes_done_ops_at_start_) {
+      call_.PerformOps(&writes_done_ops_);
+    }
+
+    MaybeFinish();
+  }
+
+  void Write(const Request* msg, WriteOptions options) override {
+    if (start_corked_) {
+      write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                     context_->initial_metadata_flags());
+      start_corked_ = false;
+    }
+    // TODO(vjpai): don't assert
+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*msg).ok());
+
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      write_ops_.ClientSendClose();
+    }
+    callbacks_outstanding_++;
+    if (started_) {
+      call_.PerformOps(&write_ops_);
+    } else {
+      write_ops_at_start_ = true;
+    }
+  }
+  void WritesDone() override {
+    if (start_corked_) {
+      writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
+                                           context_->initial_metadata_flags());
+      start_corked_ = false;
+    }
+    writes_done_ops_.ClientSendClose();
+    writes_done_tag_.Set(call_.call(),
+                         [this](bool ok) {
+                           reactor_->OnWritesDoneDone(ok);
+                           MaybeFinish();
+                         },
+                         &writes_done_ops_);
+    writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
+    callbacks_outstanding_++;
+    if (started_) {
+      call_.PerformOps(&writes_done_ops_);
+    } else {
+      writes_done_ops_at_start_ = true;
+    }
+  }
+
+ private:
+  friend class ClientCallbackWriterFactory<Request>;
+
+  template <class Response>
+  ClientCallbackWriterImpl(
+      Call call, ClientContext* context, Response* response,
+      ::grpc::experimental::ClientWriteReactor<Request>* reactor)
+      : context_(context),
+        call_(call),
+        reactor_(reactor),
+        start_corked_(context_->initial_metadata_corked_) {
+    this->BindReactor(reactor);
+    finish_ops_.RecvMessage(response);
+    finish_ops_.AllowNoMessage();
+  }
+
+  ClientContext* context_;
+  Call call_;
+  ::grpc::experimental::ClientWriteReactor<Request>* reactor_;
+
+  CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_;
+  CallbackWithSuccessTag start_tag_;
+  bool start_corked_;
+
+  CallOpSet<CallOpGenericRecvMessage, CallOpClientRecvStatus> finish_ops_;
+  CallbackWithSuccessTag finish_tag_;
+  Status finish_status_;
+
+  CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose>
+      write_ops_;
+  CallbackWithSuccessTag write_tag_;
+  bool write_ops_at_start_{false};
+
+  CallOpSet<CallOpSendInitialMetadata, CallOpClientSendClose> writes_done_ops_;
+  CallbackWithSuccessTag writes_done_tag_;
+  bool writes_done_ops_at_start_{false};
+
+  // Minimum of 3 callbacks to pre-register for StartCall, start, and finish
+  std::atomic_int callbacks_outstanding_{3};
+  bool started_{false};
+};
+
+template <class Request>
+class ClientCallbackWriterFactory {
+ public:
+  template <class Response>
+  static void Create(
+      ChannelInterface* channel, const ::grpc::internal::RpcMethod& method,
+      ClientContext* context, Response* response,
+      ::grpc::experimental::ClientWriteReactor<Request>* reactor) {
+    Call call = channel->CreateCall(method, context, channel->CallbackCQ());
+
+    g_core_codegen_interface->grpc_call_ref(call.call());
+    new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call.call(), sizeof(ClientCallbackWriterImpl<Request>)))
+        ClientCallbackWriterImpl<Request>(call, context, response, reactor);
+  }
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/client_context.h b/third_party/grpc/include/grpcpp/impl/codegen/client_context.h
new file mode 100644
index 0000000..5946488
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/client_context.h
@@ -0,0 +1,485 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/// A ClientContext allows the person implementing a service client to:
+///
+/// - Add custom metadata key-value pairs that will propagated to the server
+/// side.
+/// - Control call settings such as compression and authentication.
+/// - Initial and trailing metadata coming from the server.
+/// - Get performance metrics (ie, census).
+///
+/// Context settings are only relevant to the call they are invoked with, that
+/// is to say, they aren't sticky. Some of these settings, such as the
+/// compression options, can be made persistent at channel construction time
+/// (see \a grpc::CreateCustomChannel).
+///
+/// \warning ClientContext instances should \em not be reused across rpcs.
+
+#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H
+#define GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <grpc/impl/codegen/compression_types.h>
+#include <grpc/impl/codegen/propagation_bits.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/create_auth_context.h>
+#include <grpcpp/impl/codegen/metadata_map.h>
+#include <grpcpp/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/codegen/security/auth_context.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+#include <grpcpp/impl/codegen/time.h>
+
+struct census_context;
+struct grpc_call;
+
+namespace grpc {
+
+class Channel;
+class ChannelInterface;
+class CompletionQueue;
+class CallCredentials;
+class ClientContext;
+
+namespace internal {
+class RpcMethod;
+class CallOpClientRecvStatus;
+class CallOpRecvInitialMetadata;
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl;
+template <class InputMessage, class OutputMessage>
+class CallbackUnaryCallImpl;
+template <class Request, class Response>
+class ClientCallbackReaderWriterImpl;
+template <class Response>
+class ClientCallbackReaderImpl;
+template <class Request>
+class ClientCallbackWriterImpl;
+}  // namespace internal
+
+template <class R>
+class ClientReader;
+template <class W>
+class ClientWriter;
+template <class W, class R>
+class ClientReaderWriter;
+template <class R>
+class ClientAsyncReader;
+template <class W>
+class ClientAsyncWriter;
+template <class W, class R>
+class ClientAsyncReaderWriter;
+template <class R>
+class ClientAsyncResponseReader;
+class ServerContext;
+
+/// Options for \a ClientContext::FromServerContext specifying which traits from
+/// the \a ServerContext to propagate (copy) from it into a new \a
+/// ClientContext.
+///
+/// \see ClientContext::FromServerContext
+class PropagationOptions {
+ public:
+  PropagationOptions() : propagate_(GRPC_PROPAGATE_DEFAULTS) {}
+
+  PropagationOptions& enable_deadline_propagation() {
+    propagate_ |= GRPC_PROPAGATE_DEADLINE;
+    return *this;
+  }
+
+  PropagationOptions& disable_deadline_propagation() {
+    propagate_ &= ~GRPC_PROPAGATE_DEADLINE;
+    return *this;
+  }
+
+  PropagationOptions& enable_census_stats_propagation() {
+    propagate_ |= GRPC_PROPAGATE_CENSUS_STATS_CONTEXT;
+    return *this;
+  }
+
+  PropagationOptions& disable_census_stats_propagation() {
+    propagate_ &= ~GRPC_PROPAGATE_CENSUS_STATS_CONTEXT;
+    return *this;
+  }
+
+  PropagationOptions& enable_census_tracing_propagation() {
+    propagate_ |= GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT;
+    return *this;
+  }
+
+  PropagationOptions& disable_census_tracing_propagation() {
+    propagate_ &= ~GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT;
+    return *this;
+  }
+
+  PropagationOptions& enable_cancellation_propagation() {
+    propagate_ |= GRPC_PROPAGATE_CANCELLATION;
+    return *this;
+  }
+
+  PropagationOptions& disable_cancellation_propagation() {
+    propagate_ &= ~GRPC_PROPAGATE_CANCELLATION;
+    return *this;
+  }
+
+  uint32_t c_bitmask() const { return propagate_; }
+
+ private:
+  uint32_t propagate_;
+};
+
+namespace testing {
+class InteropClientContextInspector;
+}  // namespace testing
+
+/// A ClientContext allows the person implementing a service client to:
+///
+/// - Add custom metadata key-value pairs that will propagated to the server
+///   side.
+/// - Control call settings such as compression and authentication.
+/// - Initial and trailing metadata coming from the server.
+/// - Get performance metrics (ie, census).
+///
+/// Context settings are only relevant to the call they are invoked with, that
+/// is to say, they aren't sticky. Some of these settings, such as the
+/// compression options, can be made persistent at channel construction time
+/// (see \a grpc::CreateCustomChannel).
+///
+/// \warning ClientContext instances should \em not be reused across rpcs.
+/// \warning The ClientContext instance used for creating an rpc must remain
+///          alive and valid for the lifetime of the rpc.
+class ClientContext {
+ public:
+  ClientContext();
+  ~ClientContext();
+
+  /// Create a new \a ClientContext as a child of an incoming server call,
+  /// according to \a options (\see PropagationOptions).
+  ///
+  /// \param server_context The source server context to use as the basis for
+  /// constructing the client context.
+  /// \param options The options controlling what to copy from the \a
+  /// server_context.
+  ///
+  /// \return A newly constructed \a ClientContext instance based on \a
+  /// server_context, with traits propagated (copied) according to \a options.
+  static std::unique_ptr<ClientContext> FromServerContext(
+      const ServerContext& server_context,
+      PropagationOptions options = PropagationOptions());
+
+  /// Add the (\a meta_key, \a meta_value) pair to the metadata associated with
+  /// a client call. These are made available at the server side by the \a
+  /// grpc::ServerContext::client_metadata() method.
+  ///
+  /// \warning This method should only be called before invoking the rpc.
+  ///
+  /// \param meta_key The metadata key. If \a meta_value is binary data, it must
+  /// end in "-bin".
+  /// \param meta_value The metadata value. If its value is binary, the key name
+  /// must end in "-bin".
+  ///
+  /// Metadata must conform to the following format:
+  /// Custom-Metadata -> Binary-Header / ASCII-Header
+  /// Binary-Header -> {Header-Name "-bin" } {binary value}
+  /// ASCII-Header -> Header-Name ASCII-Value
+  /// Header-Name -> 1*( %x30-39 / %x61-7A / "_" / "-" / ".") ; 0-9 a-z _ - .
+  /// ASCII-Value -> 1*( %x20-%x7E ) ; space and printable ASCII
+  void AddMetadata(const grpc::string& meta_key,
+                   const grpc::string& meta_value);
+
+  /// Return a collection of initial metadata key-value pairs. Note that keys
+  /// may happen more than once (ie, a \a std::multimap is returned).
+  ///
+  /// \warning This method should only be called after initial metadata has been
+  /// received. For streaming calls, see \a
+  /// ClientReaderInterface::WaitForInitialMetadata().
+  ///
+  /// \return A multimap of initial metadata key-value pairs from the server.
+  const std::multimap<grpc::string_ref, grpc::string_ref>&
+  GetServerInitialMetadata() const {
+    GPR_CODEGEN_ASSERT(initial_metadata_received_);
+    return *recv_initial_metadata_.map();
+  }
+
+  /// Return a collection of trailing metadata key-value pairs. Note that keys
+  /// may happen more than once (ie, a \a std::multimap is returned).
+  ///
+  /// \warning This method is only callable once the stream has finished.
+  ///
+  /// \return A multimap of metadata trailing key-value pairs from the server.
+  const std::multimap<grpc::string_ref, grpc::string_ref>&
+  GetServerTrailingMetadata() const {
+    // TODO(yangg) check finished
+    return *trailing_metadata_.map();
+  }
+
+  /// Set the deadline for the client call.
+  ///
+  /// \warning This method should only be called before invoking the rpc.
+  ///
+  /// \param deadline the deadline for the client call. Units are determined by
+  /// the type used. The deadline is an absolute (not relative) time.
+  template <typename T>
+  void set_deadline(const T& deadline) {
+    TimePoint<T> deadline_tp(deadline);
+    deadline_ = deadline_tp.raw_time();
+  }
+
+  /// EXPERIMENTAL: Indicate that this request is idempotent.
+  /// By default, RPCs are assumed to <i>not</i> be idempotent.
+  ///
+  /// If true, the gRPC library assumes that it's safe to initiate
+  /// this RPC multiple times.
+  void set_idempotent(bool idempotent) { idempotent_ = idempotent; }
+
+  /// EXPERIMENTAL: Set this request to be cacheable.
+  /// If set, grpc is free to use the HTTP GET verb for sending the request,
+  /// with the possibility of receiving a cached response.
+  void set_cacheable(bool cacheable) { cacheable_ = cacheable; }
+
+  /// EXPERIMENTAL: Trigger wait-for-ready or not on this request.
+  /// See https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md.
+  /// If set, if an RPC is made when a channel's connectivity state is
+  /// TRANSIENT_FAILURE or CONNECTING, the call will not "fail fast",
+  /// and the channel will wait until the channel is READY before making the
+  /// call.
+  void set_wait_for_ready(bool wait_for_ready) {
+    wait_for_ready_ = wait_for_ready;
+    wait_for_ready_explicitly_set_ = true;
+  }
+
+  /// DEPRECATED: Use set_wait_for_ready() instead.
+  void set_fail_fast(bool fail_fast) { set_wait_for_ready(!fail_fast); }
+
+  /// Return the deadline for the client call.
+  std::chrono::system_clock::time_point deadline() const {
+    return Timespec2Timepoint(deadline_);
+  }
+
+  /// Return a \a gpr_timespec representation of the client call's deadline.
+  gpr_timespec raw_deadline() const { return deadline_; }
+
+  /// Set the per call authority header (see
+  /// https://tools.ietf.org/html/rfc7540#section-8.1.2.3).
+  void set_authority(const grpc::string& authority) { authority_ = authority; }
+
+  /// Return the authentication context for this client call.
+  ///
+  /// \see grpc::AuthContext.
+  std::shared_ptr<const AuthContext> auth_context() const {
+    if (auth_context_.get() == nullptr) {
+      auth_context_ = CreateAuthContext(call_);
+    }
+    return auth_context_;
+  }
+
+  /// Set credentials for the client call.
+  ///
+  /// A credentials object encapsulates all the state needed by a client to
+  /// authenticate with a server and make various assertions, e.g., about the
+  /// client’s identity, role, or whether it is authorized to make a particular
+  /// call.
+  ///
+  /// \see  https://grpc.io/docs/guides/auth.html
+  void set_credentials(const std::shared_ptr<CallCredentials>& creds) {
+    creds_ = creds;
+  }
+
+  /// Return the compression algorithm the client call will request be used.
+  /// Note that the gRPC runtime may decide to ignore this request, for example,
+  /// due to resource constraints.
+  grpc_compression_algorithm compression_algorithm() const {
+    return compression_algorithm_;
+  }
+
+  /// Set \a algorithm to be 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);
+
+  /// Flag whether the initial metadata should be \a corked
+  ///
+  /// If \a corked is true, then the initial metadata will be coalesced with the
+  /// write of first message in the stream. As a result, any tag set for the
+  /// initial metadata operation (starting a client-streaming or bidi-streaming
+  /// RPC) will not actually be sent to the completion queue or delivered
+  /// via Next.
+  ///
+  /// \param corked The flag indicating whether the initial metadata is to be
+  /// corked or not.
+  void set_initial_metadata_corked(bool corked) {
+    initial_metadata_corked_ = corked;
+  }
+
+  /// Return the peer uri in a string.
+  ///
+  /// \warning This value is never authenticated or subject to any security
+  /// related code. It must not be used for any authentication related
+  /// functionality. Instead, use auth_context.
+  ///
+  /// \return The call's peer URI.
+  grpc::string peer() const;
+
+  /// Get and set census context.
+  void set_census_context(struct census_context* ccp) { census_context_ = ccp; }
+  struct census_context* census_context() const {
+    return census_context_;
+  }
+
+  /// Send a best-effort out-of-band cancel on the call associated with
+  /// this client context.  The call could be in any stage; e.g., if it is
+  /// already finished, it may still return success.
+  ///
+  /// There is no guarantee the call will be cancelled.
+  ///
+  /// Note that TryCancel() does not change any of the tags that are pending
+  /// on the completion queue. All pending tags will still be delivered
+  /// (though their ok result may reflect the effect of cancellation).
+  void TryCancel();
+
+  /// Global Callbacks
+  ///
+  /// Can be set exactly once per application to install hooks whenever
+  /// a client context is constructed and destructed.
+  class GlobalCallbacks {
+   public:
+    virtual ~GlobalCallbacks() {}
+    virtual void DefaultConstructor(ClientContext* context) = 0;
+    virtual void Destructor(ClientContext* context) = 0;
+  };
+  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_; }
+
+  /// EXPERIMENTAL debugging API
+  ///
+  /// if status is not ok() for an RPC, this will return a detailed string
+  /// of the gRPC Core error that led to the failure. It should not be relied
+  /// upon for anything other than gaining more debug data in failure cases.
+  grpc::string debug_error_string() const { return debug_error_string_; }
+
+ private:
+  // Disallow copy and assign.
+  ClientContext(const ClientContext&);
+  ClientContext& operator=(const ClientContext&);
+
+  friend class ::grpc::testing::InteropClientContextInspector;
+  friend class ::grpc::internal::CallOpClientRecvStatus;
+  friend class ::grpc::internal::CallOpRecvInitialMetadata;
+  friend class Channel;
+  template <class R>
+  friend class ::grpc::ClientReader;
+  template <class W>
+  friend class ::grpc::ClientWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientReaderWriter;
+  template <class R>
+  friend class ::grpc::ClientAsyncReader;
+  template <class W>
+  friend class ::grpc::ClientAsyncWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientAsyncReaderWriter;
+  template <class R>
+  friend class ::grpc::ClientAsyncResponseReader;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::BlockingUnaryCallImpl;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::CallbackUnaryCallImpl;
+  template <class Request, class Response>
+  friend class ::grpc::internal::ClientCallbackReaderWriterImpl;
+  template <class Response>
+  friend class ::grpc::internal::ClientCallbackReaderImpl;
+  template <class Request>
+  friend class ::grpc::internal::ClientCallbackWriterImpl;
+
+  // Used by friend class CallOpClientRecvStatus
+  void set_debug_error_string(const grpc::string& debug_error_string) {
+    debug_error_string_ = debug_error_string;
+  }
+
+  grpc_call* call() const { return call_; }
+  void set_call(grpc_call* call, const std::shared_ptr<Channel>& channel);
+
+  experimental::ClientRpcInfo* set_client_rpc_info(
+      const char* method, internal::RpcMethod::RpcType type,
+      grpc::ChannelInterface* channel,
+      const std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>&
+          creators,
+      size_t interceptor_pos) {
+    rpc_info_ = experimental::ClientRpcInfo(this, type, method, channel);
+    rpc_info_.RegisterInterceptors(creators, interceptor_pos);
+    return &rpc_info_;
+  }
+
+  uint32_t initial_metadata_flags() const {
+    return (idempotent_ ? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST : 0) |
+           (wait_for_ready_ ? GRPC_INITIAL_METADATA_WAIT_FOR_READY : 0) |
+           (cacheable_ ? GRPC_INITIAL_METADATA_CACHEABLE_REQUEST : 0) |
+           (wait_for_ready_explicitly_set_
+                ? GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET
+                : 0) |
+           (initial_metadata_corked_ ? GRPC_INITIAL_METADATA_CORKED : 0);
+  }
+
+  grpc::string authority() { return authority_; }
+
+  void SendCancelToInterceptors();
+
+  bool initial_metadata_received_;
+  bool wait_for_ready_;
+  bool wait_for_ready_explicitly_set_;
+  bool idempotent_;
+  bool cacheable_;
+  std::shared_ptr<Channel> channel_;
+  std::mutex mu_;
+  grpc_call* call_;
+  bool call_canceled_;
+  gpr_timespec deadline_;
+  grpc::string authority_;
+  std::shared_ptr<CallCredentials> creds_;
+  mutable std::shared_ptr<const AuthContext> auth_context_;
+  struct census_context* census_context_;
+  std::multimap<grpc::string, grpc::string> send_initial_metadata_;
+  mutable internal::MetadataMap recv_initial_metadata_;
+  mutable internal::MetadataMap trailing_metadata_;
+
+  grpc_call* propagate_from_call_;
+  PropagationOptions propagation_options_;
+
+  grpc_compression_algorithm compression_algorithm_;
+  bool initial_metadata_corked_;
+
+  grpc::string debug_error_string_;
+
+  experimental::ClientRpcInfo rpc_info_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/client_interceptor.h b/third_party/grpc/include/grpcpp/impl/codegen/client_interceptor.h
new file mode 100644
index 0000000..7dfe229
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/client_interceptor.h
@@ -0,0 +1,189 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H
+#define GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H
+
+#include <memory>
+#include <vector>
+
+#include <grpcpp/impl/codegen/interceptor.h>
+#include <grpcpp/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+namespace grpc {
+
+class ClientContext;
+class Channel;
+
+namespace internal {
+class InterceptorBatchMethodsImpl;
+}
+
+namespace experimental {
+class ClientRpcInfo;
+
+// A factory interface for creation of client interceptors. A vector of
+// factories can be provided at channel creation which will be used to create a
+// new vector of client interceptors per RPC. Client interceptor authors should
+// create a subclass of ClientInterceptorFactorInterface which creates objects
+// of their interceptors.
+class ClientInterceptorFactoryInterface {
+ public:
+  virtual ~ClientInterceptorFactoryInterface() {}
+  // Returns a pointer to an Interceptor object on successful creation, nullptr
+  // otherwise. If nullptr is returned, this server interceptor factory is
+  // ignored for the purposes of that RPC.
+  virtual Interceptor* CreateClientInterceptor(ClientRpcInfo* info) = 0;
+};
+}  // namespace experimental
+
+namespace internal {
+extern experimental::ClientInterceptorFactoryInterface*
+    g_global_client_interceptor_factory;
+}
+
+/// ClientRpcInfo represents the state of a particular RPC as it
+/// appears to an interceptor. It is created and owned by the library and
+/// passed to the CreateClientInterceptor method of the application's
+/// ClientInterceptorFactoryInterface implementation
+namespace experimental {
+class ClientRpcInfo {
+ public:
+  // TODO(yashykt): Stop default-constructing ClientRpcInfo and remove UNKNOWN
+  //                from the list of possible Types.
+  /// Type categorizes RPCs by unary or streaming type
+  enum class Type {
+    UNARY,
+    CLIENT_STREAMING,
+    SERVER_STREAMING,
+    BIDI_STREAMING,
+    UNKNOWN  // UNKNOWN is not API and will be removed later
+  };
+
+  ~ClientRpcInfo(){};
+
+  // Delete copy constructor but allow default move constructor
+  ClientRpcInfo(const ClientRpcInfo&) = delete;
+  ClientRpcInfo(ClientRpcInfo&&) = default;
+
+  // Getter methods
+
+  /// Return the fully-specified method name
+  const char* method() const { return method_; }
+
+  /// Return a pointer to the channel on which the RPC is being sent
+  ChannelInterface* channel() { return channel_; }
+
+  /// Return a pointer to the underlying ClientContext structure associated
+  /// with the RPC to support features that apply to it
+  grpc::ClientContext* client_context() { return ctx_; }
+
+  /// Return the type of the RPC (unary or a streaming flavor)
+  Type type() const { return type_; }
+
+ private:
+  static_assert(Type::UNARY ==
+                    static_cast<Type>(internal::RpcMethod::NORMAL_RPC),
+                "violated expectation about Type enum");
+  static_assert(Type::CLIENT_STREAMING ==
+                    static_cast<Type>(internal::RpcMethod::CLIENT_STREAMING),
+                "violated expectation about Type enum");
+  static_assert(Type::SERVER_STREAMING ==
+                    static_cast<Type>(internal::RpcMethod::SERVER_STREAMING),
+                "violated expectation about Type enum");
+  static_assert(Type::BIDI_STREAMING ==
+                    static_cast<Type>(internal::RpcMethod::BIDI_STREAMING),
+                "violated expectation about Type enum");
+
+  // Default constructor should only be used by ClientContext
+  ClientRpcInfo() = default;
+
+  // Constructor will only be called from ClientContext
+  ClientRpcInfo(grpc::ClientContext* ctx, internal::RpcMethod::RpcType type,
+                const char* method, grpc::ChannelInterface* channel)
+      : ctx_(ctx),
+        type_(static_cast<Type>(type)),
+        method_(method),
+        channel_(channel) {}
+
+  // Move assignment should only be used by ClientContext
+  // TODO(yashykt): Delete move assignment
+  ClientRpcInfo& operator=(ClientRpcInfo&&) = default;
+
+  // Runs interceptor at pos \a pos.
+  void RunInterceptor(
+      experimental::InterceptorBatchMethods* interceptor_methods, size_t pos) {
+    GPR_CODEGEN_ASSERT(pos < interceptors_.size());
+    interceptors_[pos]->Intercept(interceptor_methods);
+  }
+
+  void RegisterInterceptors(
+      const std::vector<std::unique_ptr<
+          experimental::ClientInterceptorFactoryInterface>>& creators,
+      size_t interceptor_pos) {
+    if (interceptor_pos > creators.size()) {
+      // No interceptors to register
+      return;
+    }
+    for (auto it = creators.begin() + interceptor_pos; it != creators.end();
+         ++it) {
+      auto* interceptor = (*it)->CreateClientInterceptor(this);
+      if (interceptor != nullptr) {
+        interceptors_.push_back(
+            std::unique_ptr<experimental::Interceptor>(interceptor));
+      }
+    }
+    if (internal::g_global_client_interceptor_factory != nullptr) {
+      interceptors_.push_back(std::unique_ptr<experimental::Interceptor>(
+          internal::g_global_client_interceptor_factory
+              ->CreateClientInterceptor(this)));
+    }
+  }
+
+  grpc::ClientContext* ctx_ = nullptr;
+  // TODO(yashykt): make type_ const once move-assignment is deleted
+  Type type_{Type::UNKNOWN};
+  const char* method_ = nullptr;
+  grpc::ChannelInterface* channel_ = nullptr;
+  std::vector<std::unique_ptr<experimental::Interceptor>> interceptors_;
+  bool hijacked_ = false;
+  size_t hijacked_interceptor_ = 0;
+
+  friend class internal::InterceptorBatchMethodsImpl;
+  friend class grpc::ClientContext;
+};
+
+// PLEASE DO NOT USE THIS. ALWAYS PREFER PER CHANNEL INTERCEPTORS OVER A GLOBAL
+// INTERCEPTOR. IF USAGE IS ABSOLUTELY NECESSARY, PLEASE READ THE SAFETY NOTES.
+// Registers a global client interceptor factory object, which is used for all
+// RPCs made in this process.  If the argument is nullptr, the global
+// interceptor factory is deregistered. The application is responsible for
+// maintaining the life of the object while gRPC operations are in progress. It
+// is unsafe to try to register/deregister if any gRPC operation is in progress.
+// For safety, it is in the best interests of the developer to register the
+// global interceptor factory once at the start of the process before any gRPC
+// operations have begun. Deregistration is optional since gRPC does not
+// maintain any references to the object.
+void RegisterGlobalClientInterceptorFactory(
+    ClientInterceptorFactoryInterface* factory);
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/client_unary_call.h b/third_party/grpc/include/grpcpp/impl/codegen/client_unary_call.h
new file mode 100644
index 0000000..5151839
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/client_unary_call.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
+#define GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class Channel;
+class ClientContext;
+class CompletionQueue;
+
+namespace internal {
+class RpcMethod;
+/// Wrapper that performs a blocking unary call
+template <class InputMessage, class OutputMessage>
+Status BlockingUnaryCall(ChannelInterface* channel, const RpcMethod& method,
+                         ClientContext* context, const InputMessage& request,
+                         OutputMessage* result) {
+  return BlockingUnaryCallImpl<InputMessage, OutputMessage>(
+             channel, method, context, request, result)
+      .status();
+}
+
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl {
+ public:
+  BlockingUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method,
+                        ClientContext* context, const InputMessage& request,
+                        OutputMessage* result) {
+    CompletionQueue cq(grpc_completion_queue_attributes{
+        GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING,
+        nullptr});  // Pluckable completion queue
+    Call call(channel->CreateCall(method, context, &cq));
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpRecvInitialMetadata, CallOpRecvMessage<OutputMessage>,
+              CallOpClientSendClose, CallOpClientRecvStatus>
+        ops;
+    status_ = ops.SendMessage(request);
+    if (!status_.ok()) {
+      return;
+    }
+    ops.SendInitialMetadata(&context->send_initial_metadata_,
+                            context->initial_metadata_flags());
+    ops.RecvInitialMetadata(context);
+    ops.RecvMessage(result);
+    ops.AllowNoMessage();
+    ops.ClientSendClose();
+    ops.ClientRecvStatus(context, &status_);
+    call.PerformOps(&ops);
+    cq.Pluck(&ops);
+    // Some of the ops might fail. If the ops fail in the core layer, status
+    // would reflect the error. But, if the ops fail in the C++ layer, the
+    // status would still be the same as the one returned by gRPC Core. This can
+    // happen if deserialization of the message fails.
+    // TODO(yashykt): If deserialization fails, but the status received is OK,
+    // then it might be a good idea to change the status to something better
+    // than StatusCode::UNIMPLEMENTED to reflect this.
+    if (!ops.got_message && status_.ok()) {
+      status_ = Status(StatusCode::UNIMPLEMENTED,
+                       "No message returned for unary request");
+    }
+  }
+  Status status() { return status_; }
+
+ private:
+  Status status_;
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/completion_queue.h b/third_party/grpc/include/grpcpp/impl/codegen/completion_queue.h
new file mode 100644
index 0000000..fb38788
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/completion_queue.h
@@ -0,0 +1,403 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/// A completion queue implements a concurrent producer-consumer queue, with
+/// two main API-exposed methods: \a Next and \a AsyncNext. These
+/// methods are the essential component of the gRPC C++ asynchronous API.
+/// There is also a \a Shutdown method to indicate that a given completion queue
+/// will no longer have regular events. This must be called before the
+/// completion queue is destroyed.
+/// All completion queue APIs are thread-safe and may be used concurrently with
+/// any other completion queue API invocation; it is acceptable to have
+/// multiple threads calling \a Next or \a AsyncNext on the same or different
+/// completion queues, or to call these methods concurrently with a \a Shutdown
+/// elsewhere.
+/// \remark{All other API calls on completion queue should be completed before
+/// a completion queue destructor is called.}
+#ifndef GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H
+#define GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H
+
+#include <grpc/impl/codegen/atm.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/time.h>
+
+struct grpc_completion_queue;
+
+namespace grpc {
+
+template <class R>
+class ClientReader;
+template <class W>
+class ClientWriter;
+template <class W, class R>
+class ClientReaderWriter;
+template <class R>
+class ServerReader;
+template <class W>
+class ServerWriter;
+namespace internal {
+template <class W, class R>
+class ServerReaderWriterBody;
+}  // namespace internal
+
+class Channel;
+class ChannelInterface;
+class ClientContext;
+class CompletionQueue;
+class Server;
+class ServerBuilder;
+class ServerContext;
+class ServerInterface;
+
+namespace internal {
+class CompletionQueueTag;
+class RpcMethod;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler;
+template <class Streamer, bool WriteNeeded>
+class TemplatedBidiStreamingHandler;
+template <StatusCode code>
+class ErrorMethodHandler;
+template <class InputMessage, class OutputMessage>
+class BlockingUnaryCallImpl;
+}  // namespace internal
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+/// A thin wrapper around \ref grpc_completion_queue (see \ref
+/// src/core/lib/surface/completion_queue.h).
+/// See \ref doc/cpp/perf_notes.md for notes on best practices for high
+/// performance servers.
+class CompletionQueue : private GrpcLibraryCodegen {
+ public:
+  /// Default constructor. Implicitly creates a \a grpc_completion_queue
+  /// instance.
+  CompletionQueue()
+      : CompletionQueue(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, GRPC_CQ_DEFAULT_POLLING,
+            nullptr}) {}
+
+  /// Wrap \a take, taking ownership of the instance.
+  ///
+  /// \param take The completion queue instance to wrap. Ownership is taken.
+  explicit CompletionQueue(grpc_completion_queue* take);
+
+  /// Destructor. Destroys the owned wrapped completion queue / instance.
+  ~CompletionQueue() {
+    g_core_codegen_interface->grpc_completion_queue_destroy(cq_);
+  }
+
+  /// Tri-state return for AsyncNext: SHUTDOWN, GOT_EVENT, TIMEOUT.
+  enum NextStatus {
+    SHUTDOWN,   ///< The completion queue has been shutdown and fully-drained
+    GOT_EVENT,  ///< Got a new event; \a tag will be filled in with its
+                ///< associated value; \a ok indicating its success.
+    TIMEOUT     ///< deadline was reached.
+  };
+
+  /// Read from the queue, blocking until an event is available or the queue is
+  /// shutting down.
+  ///
+  /// \param tag [out] Updated to point to the read event's tag.
+  /// \param ok [out] true if read a successful event, false otherwise.
+  ///
+  /// Note that each tag sent to the completion queue (through RPC operations
+  /// or alarms) will be delivered out of the completion queue by a call to
+  /// Next (or a related method), regardless of whether the operation succeeded
+  /// or not. Success here means that this operation completed in the normal
+  /// valid manner.
+  ///
+  /// Server-side RPC request: \a ok indicates that the RPC has indeed
+  /// been started. If it is false, the server has been Shutdown
+  /// before this particular call got matched to an incoming RPC.
+  ///
+  /// Client-side StartCall/RPC invocation: \a ok indicates that the RPC is
+  /// going to go to the wire. If it is false, it not going to the wire. This
+  /// would happen if the channel is either permanently broken or
+  /// transiently broken but with the fail-fast option. (Note that async unary
+  /// RPCs don't post a CQ tag at this point, nor do client-streaming
+  /// or bidi-streaming RPCs that have the initial metadata corked option set.)
+  ///
+  /// Client-side Write, Client-side WritesDone, Server-side Write,
+  /// Server-side Finish, Server-side SendInitialMetadata (which is
+  /// typically included in Write or Finish when not done explicitly):
+  /// \a ok means that the data/metadata/status/etc is going to go to the
+  /// wire. If it is false, it not going to the wire because the call
+  /// is already dead (i.e., canceled, deadline expired, other side
+  /// dropped the channel, etc).
+  ///
+  /// Client-side Read, Server-side Read, Client-side
+  /// RecvInitialMetadata (which is typically included in Read if not
+  /// done explicitly): \a ok indicates whether there is a valid message
+  /// that got read. If not, you know that there are certainly no more
+  /// messages that can ever be read from this stream. For the client-side
+  /// operations, this only happens because the call is dead. For the
+  /// server-sider operation, though, this could happen because the client
+  /// has done a WritesDone already.
+  ///
+  /// Client-side Finish: \a ok should always be true
+  ///
+  /// Server-side AsyncNotifyWhenDone: \a ok should always be true
+  ///
+  /// Alarm: \a ok is true if it expired, false if it was canceled
+  ///
+  /// \return true if got an event, false if the queue is fully drained and
+  ///         shut down.
+  bool Next(void** tag, bool* ok) {
+    return (AsyncNextInternal(tag, ok,
+                              g_core_codegen_interface->gpr_inf_future(
+                                  GPR_CLOCK_REALTIME)) != SHUTDOWN);
+  }
+
+  /// Read from the queue, blocking up to \a deadline (or the queue's shutdown).
+  /// Both \a tag and \a ok are updated upon success (if an event is available
+  /// within the \a deadline).  A \a tag points to an arbitrary location usually
+  /// employed to uniquely identify an event.
+  ///
+  /// \param tag [out] Upon sucess, updated to point to the event's tag.
+  /// \param ok [out] Upon sucess, true if a successful event, false otherwise
+  ///        See documentation for CompletionQueue::Next for explanation of ok
+  /// \param deadline [in] How long to block in wait for an event.
+  ///
+  /// \return The type of event read.
+  template <typename T>
+  NextStatus AsyncNext(void** tag, bool* ok, const T& deadline) {
+    TimePoint<T> deadline_tp(deadline);
+    return AsyncNextInternal(tag, ok, deadline_tp.raw_time());
+  }
+
+  /// EXPERIMENTAL
+  /// First executes \a F, then reads from the queue, blocking up to
+  /// \a deadline (or the queue's shutdown).
+  /// Both \a tag and \a ok are updated upon success (if an event is available
+  /// within the \a deadline).  A \a tag points to an arbitrary location usually
+  /// employed to uniquely identify an event.
+  ///
+  /// \param f [in] Function to execute before calling AsyncNext on this queue.
+  /// \param tag [out] Upon sucess, updated to point to the event's tag.
+  /// \param ok [out] Upon sucess, true if read a regular event, false
+  /// otherwise.
+  /// \param deadline [in] How long to block in wait for an event.
+  ///
+  /// \return The type of event read.
+  template <typename T, typename F>
+  NextStatus DoThenAsyncNext(F&& f, void** tag, bool* ok, const T& deadline) {
+    CompletionQueueTLSCache cache = CompletionQueueTLSCache(this);
+    f();
+    if (cache.Flush(tag, ok)) {
+      return GOT_EVENT;
+    } else {
+      return AsyncNext(tag, ok, deadline);
+    }
+  }
+
+  /// Request the shutdown of the queue.
+  ///
+  /// \warning This method must be called at some point if this completion queue
+  /// is accessed with Next or AsyncNext. \a Next will not return false
+  /// until this method has been called and all pending tags have been drained.
+  /// (Likewise for \a AsyncNext returning \a NextStatus::SHUTDOWN .)
+  /// Only once either one of these methods does that (that is, once the queue
+  /// has been \em drained) can an instance of this class be destroyed.
+  /// Also note that applications must ensure that no work is enqueued on this
+  /// completion queue after this method is called.
+  void Shutdown();
+
+  /// Returns a \em raw pointer to the underlying \a grpc_completion_queue
+  /// instance.
+  ///
+  /// \warning Remember that the returned instance is owned. No transfer of
+  /// owership is performed.
+  grpc_completion_queue* cq() { return cq_; }
+
+ protected:
+  /// Private constructor of CompletionQueue only visible to friend classes
+  CompletionQueue(const grpc_completion_queue_attributes& attributes) {
+    cq_ = g_core_codegen_interface->grpc_completion_queue_create(
+        g_core_codegen_interface->grpc_completion_queue_factory_lookup(
+            &attributes),
+        &attributes, NULL);
+    InitialAvalanching();  // reserve this for the future shutdown
+  }
+
+ private:
+  // Friend synchronous wrappers so that they can access Pluck(), which is
+  // a semi-private API geared towards the synchronous implementation.
+  template <class R>
+  friend class ::grpc::ClientReader;
+  template <class W>
+  friend class ::grpc::ClientWriter;
+  template <class W, class R>
+  friend class ::grpc::ClientReaderWriter;
+  template <class R>
+  friend class ::grpc::ServerReader;
+  template <class W>
+  friend class ::grpc::ServerWriter;
+  template <class W, class R>
+  friend class ::grpc::internal::ServerReaderWriterBody;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ClientStreamingHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ServerStreamingHandler;
+  template <class Streamer, bool WriteNeeded>
+  friend class ::grpc::internal::TemplatedBidiStreamingHandler;
+  template <StatusCode code>
+  friend class ::grpc::internal::ErrorMethodHandler;
+  friend class ::grpc::Server;
+  friend class ::grpc::ServerContext;
+  friend class ::grpc::ServerInterface;
+  template <class InputMessage, class OutputMessage>
+  friend class ::grpc::internal::BlockingUnaryCallImpl;
+
+  // Friends that need access to constructor for callback CQ
+  friend class ::grpc::Channel;
+
+  /// EXPERIMENTAL
+  /// Creates a Thread Local cache to store the first event
+  /// On this completion queue queued from this thread.  Once
+  /// initialized, it must be flushed on the same thread.
+  class CompletionQueueTLSCache {
+   public:
+    CompletionQueueTLSCache(CompletionQueue* cq);
+    ~CompletionQueueTLSCache();
+    bool Flush(void** tag, bool* ok);
+
+   private:
+    CompletionQueue* cq_;
+    bool flushed_;
+  };
+
+  NextStatus AsyncNextInternal(void** tag, bool* ok, gpr_timespec deadline);
+
+  /// Wraps \a grpc_completion_queue_pluck.
+  /// \warning Must not be mixed with calls to \a Next.
+  bool Pluck(internal::CompletionQueueTag* tag) {
+    auto deadline =
+        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME);
+    while (true) {
+      auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
+          cq_, tag, deadline, nullptr);
+      bool ok = ev.success != 0;
+      void* ignored = tag;
+      if (tag->FinalizeResult(&ignored, &ok)) {
+        GPR_CODEGEN_ASSERT(ignored == tag);
+        return ok;
+      }
+    }
+  }
+
+  /// Performs a single polling pluck on \a tag.
+  /// \warning Must not be mixed with calls to \a Next.
+  ///
+  /// TODO: sreek - This calls tag->FinalizeResult() even if the cq_ is already
+  /// shutdown. This is most likely a bug and if it is a bug, then change this
+  /// implementation to simple call the other TryPluck function with a zero
+  /// timeout. i.e:
+  ///      TryPluck(tag, gpr_time_0(GPR_CLOCK_REALTIME))
+  void TryPluck(internal::CompletionQueueTag* tag) {
+    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;
+    bool ok = ev.success != 0;
+    void* ignored = tag;
+    // the tag must be swallowed if using TryPluck
+    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
+  }
+
+  /// Performs a single polling pluck on \a tag. Calls tag->FinalizeResult if
+  /// the pluck() was successful and returned the tag.
+  ///
+  /// This exects tag->FinalizeResult (if called) to return 'false' i.e expects
+  /// that the tag is internal not something that is returned to the user.
+  void TryPluck(internal::CompletionQueueTag* tag, gpr_timespec deadline) {
+    auto ev = g_core_codegen_interface->grpc_completion_queue_pluck(
+        cq_, tag, deadline, nullptr);
+    if (ev.type == GRPC_QUEUE_TIMEOUT || ev.type == GRPC_QUEUE_SHUTDOWN) {
+      return;
+    }
+
+    bool ok = ev.success != 0;
+    void* ignored = tag;
+    GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok));
+  }
+
+  /// Manage state of avalanching operations : completion queue tags that
+  /// trigger other completion queue operations. The underlying core completion
+  /// queue should not really shutdown until all avalanching operations have
+  /// been finalized. Note that we maintain the requirement that an avalanche
+  /// registration must take place before CQ shutdown (which must be maintained
+  /// elsehwere)
+  void InitialAvalanching() {
+    gpr_atm_rel_store(&avalanches_in_flight_, static_cast<gpr_atm>(1));
+  }
+  void RegisterAvalanching() {
+    gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
+                                 static_cast<gpr_atm>(1));
+  }
+  void CompleteAvalanching();
+
+  grpc_completion_queue* cq_;  // owned
+
+  gpr_atm avalanches_in_flight_;
+};
+
+/// A specific type of completion queue used by the processing of notifications
+/// by servers. Instantiated by \a ServerBuilder.
+class ServerCompletionQueue : public CompletionQueue {
+ public:
+  bool IsFrequentlyPolled() { return polling_type_ != GRPC_CQ_NON_LISTENING; }
+
+ protected:
+  /// Default constructor
+  ServerCompletionQueue() : polling_type_(GRPC_CQ_DEFAULT_POLLING) {}
+
+ private:
+  /// \param completion_type indicates whether this is a NEXT or CALLBACK
+  /// completion queue.
+  /// \param polling_type Informs the GRPC library about the type of polling
+  /// allowed on this completion queue. See grpc_cq_polling_type's description
+  /// in grpc_types.h for more details.
+  /// \param shutdown_cb is the shutdown callback used for CALLBACK api queues
+  ServerCompletionQueue(grpc_cq_completion_type completion_type,
+                        grpc_cq_polling_type polling_type,
+                        grpc_experimental_completion_queue_functor* shutdown_cb)
+      : CompletionQueue(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, completion_type, polling_type,
+            shutdown_cb}),
+        polling_type_(polling_type) {}
+
+  grpc_cq_polling_type polling_type_;
+  friend class ServerBuilder;
+  friend class Server;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/completion_queue_tag.h b/third_party/grpc/include/grpcpp/impl/codegen/completion_queue_tag.h
new file mode 100644
index 0000000..304386a
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/completion_queue_tag.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
+#define GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
+
+namespace grpc {
+
+namespace internal {
+/// An interface allowing implementors to process and filter event tags.
+class CompletionQueueTag {
+ public:
+  virtual ~CompletionQueueTag() {}
+
+  /// FinalizeResult must be called before informing user code that the
+  /// operation bound to the underlying core completion queue tag has
+  /// completed. In practice, this means:
+  ///
+  ///   1. For the sync API - before returning from Pluck
+  ///   2. For the CQ-based async API - before returning from Next
+  ///   3. For the callback-based API - before invoking the user callback
+  ///
+  /// This is the method that translates from core-side tag/status to
+  /// C++ API-observable tag/status.
+  ///
+  /// The return value is the status of the operation (returning status is the
+  /// general behavior of this function). If this function returns false, the
+  /// tag is dropped and not returned from the completion queue: this concept is
+  /// for events that are observed at core but not requested by the user
+  /// application (e.g., server shutdown, for server unimplemented method
+  /// responses, or for cases where a server-side RPC doesn't have a completion
+  /// notification registered using AsyncNotifyWhenDone)
+  virtual bool FinalizeResult(void** tag, bool* status) = 0;
+};
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/config.h b/third_party/grpc/include/grpcpp/impl/codegen/config.h
new file mode 100644
index 0000000..37f0fd1f
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/config.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CONFIG_H
+#define GRPCPP_IMPL_CODEGEN_CONFIG_H
+
+#ifndef GRPC_CUSTOM_STRING
+#include <string>
+#define GRPC_CUSTOM_STRING std::string
+#endif
+
+/// The following macros are deprecated and appear only for users
+/// with PB files generated using gRPC 1.0.x plugins. They should
+/// not be used in new code
+#define GRPC_OVERRIDE override  // deprecated
+#define GRPC_FINAL final        // deprecated
+
+namespace grpc {
+
+typedef GRPC_CUSTOM_STRING string;
+
+using std::to_string;
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CONFIG_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/config_protobuf.h b/third_party/grpc/include/grpcpp/impl/codegen/config_protobuf.h
new file mode 100644
index 0000000..8c2e9e6
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/config_protobuf.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H
+#define GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H
+
+#define GRPC_OPEN_SOURCE_PROTO
+
+#ifndef GRPC_CUSTOM_PROTOBUF_INT64
+#include <google/protobuf/stubs/common.h>
+#define GRPC_CUSTOM_PROTOBUF_INT64 ::google::protobuf::int64
+#endif
+
+#ifndef GRPC_CUSTOM_MESSAGE
+#ifdef GRPC_USE_PROTO_LITE
+#include <google/protobuf/message_lite.h>
+#define GRPC_CUSTOM_MESSAGE ::google::protobuf::MessageLite
+#else
+#include <google/protobuf/message.h>
+#define GRPC_CUSTOM_MESSAGE ::google::protobuf::Message
+#endif
+#endif
+
+#ifndef GRPC_CUSTOM_DESCRIPTOR
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/descriptor.pb.h>
+#define GRPC_CUSTOM_DESCRIPTOR ::google::protobuf::Descriptor
+#define GRPC_CUSTOM_DESCRIPTORPOOL ::google::protobuf::DescriptorPool
+#define GRPC_CUSTOM_FIELDDESCRIPTOR ::google::protobuf::FieldDescriptor
+#define GRPC_CUSTOM_FILEDESCRIPTOR ::google::protobuf::FileDescriptor
+#define GRPC_CUSTOM_FILEDESCRIPTORPROTO ::google::protobuf::FileDescriptorProto
+#define GRPC_CUSTOM_METHODDESCRIPTOR ::google::protobuf::MethodDescriptor
+#define GRPC_CUSTOM_SERVICEDESCRIPTOR ::google::protobuf::ServiceDescriptor
+#define GRPC_CUSTOM_SOURCELOCATION ::google::protobuf::SourceLocation
+#endif
+
+#ifndef GRPC_CUSTOM_DESCRIPTORDATABASE
+#include <google/protobuf/descriptor_database.h>
+#define GRPC_CUSTOM_DESCRIPTORDATABASE ::google::protobuf::DescriptorDatabase
+#define GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE \
+  ::google::protobuf::SimpleDescriptorDatabase
+#endif
+
+#ifndef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#define GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM \
+  ::google::protobuf::io::ZeroCopyOutputStream
+#define GRPC_CUSTOM_ZEROCOPYINPUTSTREAM \
+  ::google::protobuf::io::ZeroCopyInputStream
+#define GRPC_CUSTOM_CODEDINPUTSTREAM ::google::protobuf::io::CodedInputStream
+#endif
+
+#ifndef GRPC_CUSTOM_JSONUTIL
+#include <google/protobuf/util/json_util.h>
+#define GRPC_CUSTOM_JSONUTIL ::google::protobuf::util
+#define GRPC_CUSTOM_UTIL_STATUS ::google::protobuf::util::Status
+#endif
+
+namespace grpc {
+namespace protobuf {
+
+typedef GRPC_CUSTOM_MESSAGE Message;
+typedef GRPC_CUSTOM_PROTOBUF_INT64 int64;
+
+typedef GRPC_CUSTOM_DESCRIPTOR Descriptor;
+typedef GRPC_CUSTOM_DESCRIPTORPOOL DescriptorPool;
+typedef GRPC_CUSTOM_DESCRIPTORDATABASE DescriptorDatabase;
+typedef GRPC_CUSTOM_FIELDDESCRIPTOR FieldDescriptor;
+typedef GRPC_CUSTOM_FILEDESCRIPTOR FileDescriptor;
+typedef GRPC_CUSTOM_FILEDESCRIPTORPROTO FileDescriptorProto;
+typedef GRPC_CUSTOM_METHODDESCRIPTOR MethodDescriptor;
+typedef GRPC_CUSTOM_SERVICEDESCRIPTOR ServiceDescriptor;
+typedef GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE SimpleDescriptorDatabase;
+typedef GRPC_CUSTOM_SOURCELOCATION SourceLocation;
+
+namespace util {
+typedef GRPC_CUSTOM_UTIL_STATUS Status;
+}  // namespace util
+
+namespace json = GRPC_CUSTOM_JSONUTIL;
+
+namespace io {
+typedef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM ZeroCopyOutputStream;
+typedef GRPC_CUSTOM_ZEROCOPYINPUTSTREAM ZeroCopyInputStream;
+typedef GRPC_CUSTOM_CODEDINPUTSTREAM CodedInputStream;
+}  // namespace io
+
+}  // namespace protobuf
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/core_codegen.h b/third_party/grpc/include/grpcpp/impl/codegen/core_codegen.h
new file mode 100644
index 0000000..6ef184d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/core_codegen.h
@@ -0,0 +1,123 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H
+#define GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H
+
+// This file should be compiled as part of grpcpp.
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+
+namespace grpc {
+
+/// Implementation of the core codegen interface.
+class CoreCodegen final : public CoreCodegenInterface {
+ private:
+  virtual const grpc_completion_queue_factory*
+  grpc_completion_queue_factory_lookup(
+      const grpc_completion_queue_attributes* attributes) override;
+  virtual grpc_completion_queue* grpc_completion_queue_create(
+      const grpc_completion_queue_factory* factory,
+      const grpc_completion_queue_attributes* attributes,
+      void* reserved) override;
+  grpc_completion_queue* grpc_completion_queue_create_for_next(
+      void* reserved) override;
+  grpc_completion_queue* grpc_completion_queue_create_for_pluck(
+      void* reserved) override;
+  void grpc_completion_queue_destroy(grpc_completion_queue* cq) override;
+  grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq, void* tag,
+                                         gpr_timespec deadline,
+                                         void* reserved) override;
+
+  void* gpr_malloc(size_t size) override;
+  void gpr_free(void* p) override;
+
+  void grpc_init() override;
+  void grpc_shutdown() override;
+
+  void gpr_mu_init(gpr_mu* mu) override;
+  void gpr_mu_destroy(gpr_mu* mu) override;
+  void gpr_mu_lock(gpr_mu* mu) override;
+  void gpr_mu_unlock(gpr_mu* mu) override;
+  void gpr_cv_init(gpr_cv* cv) override;
+  void gpr_cv_destroy(gpr_cv* cv) override;
+  int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) override;
+  void gpr_cv_signal(gpr_cv* cv) override;
+  void gpr_cv_broadcast(gpr_cv* cv) override;
+
+  grpc_call_error grpc_call_start_batch(grpc_call* call, const grpc_op* ops,
+                                        size_t nops, void* tag,
+                                        void* reserved) override;
+  grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
+                                               grpc_status_code status,
+                                               const char* description,
+                                               void* reserved) override;
+  void grpc_call_ref(grpc_call* call) override;
+  void grpc_call_unref(grpc_call* call) override;
+  virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) override;
+
+  grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) override;
+  void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) override;
+  size_t grpc_byte_buffer_length(grpc_byte_buffer* bb) override;
+
+  int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
+                                   grpc_byte_buffer* buffer) override;
+  void grpc_byte_buffer_reader_destroy(
+      grpc_byte_buffer_reader* reader) override;
+  int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
+                                   grpc_slice* slice) override;
+
+  grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice,
+                                                size_t nslices) override;
+  grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
+                                           void (*destroy)(void*),
+                                           void* user_data) override;
+  grpc_slice grpc_slice_new_with_len(void* p, size_t len,
+                                     void (*destroy)(void*, size_t)) override;
+  grpc_slice grpc_empty_slice() override;
+  grpc_slice grpc_slice_malloc(size_t length) override;
+  void grpc_slice_unref(grpc_slice slice) override;
+  grpc_slice grpc_slice_ref(grpc_slice slice) override;
+  grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) override;
+  grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) override;
+  grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end) override;
+  void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_slice slice) override;
+  void grpc_slice_buffer_pop(grpc_slice_buffer* sb) override;
+  grpc_slice grpc_slice_from_static_buffer(const void* buffer,
+                                           size_t length) override;
+  grpc_slice grpc_slice_from_copied_buffer(const void* buffer,
+                                           size_t length) override;
+  void grpc_metadata_array_init(grpc_metadata_array* array) override;
+  void grpc_metadata_array_destroy(grpc_metadata_array* array) override;
+
+  gpr_timespec gpr_inf_future(gpr_clock_type type) override;
+  gpr_timespec gpr_time_0(gpr_clock_type type) override;
+
+  virtual const Status& ok() override;
+  virtual const Status& cancelled() override;
+
+  void assert_fail(const char* failed_assertion, const char* file,
+                   int line) override;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/core_codegen_interface.h b/third_party/grpc/include/grpcpp/impl/codegen/core_codegen_interface.h
new file mode 100644
index 0000000..20a5b33
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/core_codegen_interface.h
@@ -0,0 +1,159 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
+#define GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
+
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/sync.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+/// Interface between the codegen library and the minimal subset of core
+/// features required by the generated code.
+///
+/// All undocumented methods are simply forwarding the call to their namesakes.
+/// Please refer to their corresponding documentation for details.
+///
+/// \warning This interface should be considered internal and private.
+class CoreCodegenInterface {
+ public:
+  virtual ~CoreCodegenInterface() = default;
+
+  /// Upon a failed assertion, log the error.
+  virtual void assert_fail(const char* failed_assertion, const char* file,
+                           int line) = 0;
+
+  virtual const grpc_completion_queue_factory*
+  grpc_completion_queue_factory_lookup(
+      const grpc_completion_queue_attributes* attributes) = 0;
+  virtual grpc_completion_queue* grpc_completion_queue_create(
+      const grpc_completion_queue_factory* factory,
+      const grpc_completion_queue_attributes* attributes, void* reserved) = 0;
+  virtual grpc_completion_queue* grpc_completion_queue_create_for_next(
+      void* reserved) = 0;
+  virtual grpc_completion_queue* grpc_completion_queue_create_for_pluck(
+      void* reserved) = 0;
+  virtual void grpc_completion_queue_destroy(grpc_completion_queue* cq) = 0;
+  virtual grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq,
+                                                 void* tag,
+                                                 gpr_timespec deadline,
+                                                 void* reserved) = 0;
+
+  virtual void* gpr_malloc(size_t size) = 0;
+  virtual void gpr_free(void* p) = 0;
+
+  // These are only to be used to fix edge cases involving grpc_init and
+  // grpc_shutdown. Calling grpc_init from the codegen interface before
+  // the real grpc_init is called will cause a crash, so if you use this
+  // function, ensure that it is not the first call to grpc_init.
+  virtual void grpc_init() = 0;
+  virtual void grpc_shutdown() = 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 grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) = 0;
+  virtual void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) = 0;
+  virtual size_t grpc_byte_buffer_length(grpc_byte_buffer* bb)
+      GRPC_MUST_USE_RESULT = 0;
+
+  virtual int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
+                                           grpc_byte_buffer* buffer)
+      GRPC_MUST_USE_RESULT = 0;
+  virtual void grpc_byte_buffer_reader_destroy(
+      grpc_byte_buffer_reader* reader) = 0;
+  virtual int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
+                                           grpc_slice* slice) = 0;
+
+  virtual grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice,
+                                                        size_t nslices) = 0;
+  virtual grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
+                                                   void (*destroy)(void*),
+                                                   void* user_data) = 0;
+  virtual grpc_slice grpc_slice_new_with_len(void* p, size_t len,
+                                             void (*destroy)(void*,
+                                                             size_t)) = 0;
+  virtual grpc_call_error grpc_call_start_batch(grpc_call* call,
+                                                const grpc_op* ops, size_t nops,
+                                                void* tag, void* reserved) = 0;
+  virtual grpc_call_error grpc_call_cancel_with_status(grpc_call* call,
+                                                       grpc_status_code status,
+                                                       const char* description,
+                                                       void* reserved) = 0;
+  virtual void grpc_call_ref(grpc_call* call) = 0;
+  virtual void grpc_call_unref(grpc_call* call) = 0;
+  virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) = 0;
+  virtual grpc_slice grpc_empty_slice() = 0;
+  virtual grpc_slice grpc_slice_malloc(size_t length) = 0;
+  virtual void grpc_slice_unref(grpc_slice slice) = 0;
+  virtual grpc_slice grpc_slice_ref(grpc_slice slice) = 0;
+  virtual grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) = 0;
+  virtual grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) = 0;
+  virtual grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end) = 0;
+  virtual void grpc_slice_buffer_add(grpc_slice_buffer* sb,
+                                     grpc_slice slice) = 0;
+  virtual void grpc_slice_buffer_pop(grpc_slice_buffer* sb) = 0;
+  virtual grpc_slice grpc_slice_from_static_buffer(const void* buffer,
+                                                   size_t length) = 0;
+  virtual grpc_slice grpc_slice_from_copied_buffer(const void* buffer,
+                                                   size_t length) = 0;
+
+  virtual void grpc_metadata_array_init(grpc_metadata_array* array) = 0;
+  virtual void grpc_metadata_array_destroy(grpc_metadata_array* array) = 0;
+
+  virtual const Status& ok() = 0;
+  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;
+
+/// Codegen specific version of \a GPR_ASSERT.
+#define GPR_CODEGEN_ASSERT(x)                                              \
+  do {                                                                     \
+    if (!(x)) {                                                            \
+      grpc::g_core_codegen_interface->assert_fail(#x, __FILE__, __LINE__); \
+    }                                                                      \
+  } while (0)
+
+/// Codegen specific version of \a GPR_DEBUG_ASSERT.
+#ifndef NDEBUG
+#define GPR_CODEGEN_DEBUG_ASSERT(x) GPR_CODEGEN_ASSERT(x)
+#else
+#define GPR_CODEGEN_DEBUG_ASSERT(x) \
+  do {                              \
+  } while (0)
+#endif
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/create_auth_context.h b/third_party/grpc/include/grpcpp/impl/codegen/create_auth_context.h
new file mode 100644
index 0000000..cb6095c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/create_auth_context.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
+#define GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
+
+#include <memory>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/security/auth_context.h>
+
+namespace grpc {
+
+std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/grpc_library.h b/third_party/grpc/include/grpcpp/impl/codegen/grpc_library.h
new file mode 100644
index 0000000..17c904d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/grpc_library.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H
+#define GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H
+
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+
+namespace grpc {
+
+class GrpcLibraryInterface {
+ public:
+  virtual ~GrpcLibraryInterface() = default;
+  virtual void init() = 0;
+  virtual void shutdown() = 0;
+};
+
+/// Initialized by \a grpc::GrpcLibraryInitializer from
+/// <grpcpp/impl/grpc_library.h>
+extern GrpcLibraryInterface* g_glip;
+
+/// Classes that require gRPC to be initialized should inherit from this class.
+class GrpcLibraryCodegen {
+ public:
+  GrpcLibraryCodegen(bool call_grpc_init = true) : grpc_init_called_(false) {
+    if (call_grpc_init) {
+      GPR_CODEGEN_ASSERT(g_glip &&
+                         "gRPC library not initialized. See "
+                         "grpc::internal::GrpcLibraryInitializer.");
+      g_glip->init();
+      grpc_init_called_ = true;
+    }
+  }
+  virtual ~GrpcLibraryCodegen() {
+    if (grpc_init_called_) {
+      GPR_CODEGEN_ASSERT(g_glip &&
+                         "gRPC library not initialized. See "
+                         "grpc::internal::GrpcLibraryInitializer.");
+      g_glip->shutdown();
+    }
+  }
+
+ private:
+  bool grpc_init_called_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/intercepted_channel.h b/third_party/grpc/include/grpcpp/impl/codegen/intercepted_channel.h
new file mode 100644
index 0000000..5255a6d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/intercepted_channel.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H
+#define GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H
+
+#include <grpcpp/impl/codegen/channel_interface.h>
+
+namespace grpc {
+
+namespace internal {
+
+class InterceptorBatchMethodsImpl;
+
+/// An InterceptedChannel is available to client Interceptors. An
+/// InterceptedChannel is unique to an interceptor, and when an RPC is started
+/// on this channel, only those interceptors that come after this interceptor
+/// see the RPC.
+class InterceptedChannel : public ChannelInterface {
+ public:
+  virtual ~InterceptedChannel() { channel_ = nullptr; }
+
+  /// Get the current channel state. If the channel is in IDLE and
+  /// \a try_to_connect is set to true, try to connect.
+  grpc_connectivity_state GetState(bool try_to_connect) override {
+    return channel_->GetState(try_to_connect);
+  }
+
+ private:
+  InterceptedChannel(ChannelInterface* channel, size_t pos)
+      : channel_(channel), interceptor_pos_(pos) {}
+
+  Call CreateCall(const RpcMethod& method, ClientContext* context,
+                  CompletionQueue* cq) override {
+    return channel_->CreateCallInternal(method, context, cq, interceptor_pos_);
+  }
+
+  void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) override {
+    return channel_->PerformOpsOnCall(ops, call);
+  }
+  void* RegisterMethod(const char* method) override {
+    return channel_->RegisterMethod(method);
+  }
+
+  void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
+                               gpr_timespec deadline, CompletionQueue* cq,
+                               void* tag) override {
+    return channel_->NotifyOnStateChangeImpl(last_observed, deadline, cq, tag);
+  }
+  bool WaitForStateChangeImpl(grpc_connectivity_state last_observed,
+                              gpr_timespec deadline) override {
+    return channel_->WaitForStateChangeImpl(last_observed, deadline);
+  }
+
+  CompletionQueue* CallbackCQ() override { return channel_->CallbackCQ(); }
+
+  ChannelInterface* channel_;
+  size_t interceptor_pos_;
+
+  friend class InterceptorBatchMethodsImpl;
+};
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/interceptor.h b/third_party/grpc/include/grpcpp/impl/codegen/interceptor.h
new file mode 100644
index 0000000..46175cd
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/interceptor.h
@@ -0,0 +1,175 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTOR_H
+#define GRPCPP_IMPL_CODEGEN_INTERCEPTOR_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/metadata_map.h>
+
+namespace grpc {
+
+class ChannelInterface;
+class Status;
+
+namespace experimental {
+
+/// An enumeration of different possible points at which the \a Intercept
+/// method of the \a Interceptor interface may be called. Any given call
+/// to \a Intercept will include one or more of these hook points, and
+/// each hook point makes certain types of information available to the
+/// interceptor.
+/// In these enumeration names, PRE_SEND means that an interception has taken
+/// place between the time the application provided a certain type of data
+/// (e.g., initial metadata, status) and the time that that data goes to the
+/// other side. POST_SEND means that the data has been committed for going to
+/// the other side (even if it has not yet been received at the other side).
+/// PRE_RECV means an interception between the time that a certain
+/// operation has been requested and it is available. POST_RECV means that a
+/// result is available but has not yet been passed back to the application.
+enum class InterceptionHookPoints {
+  /// The first two in this list are for clients and servers
+  PRE_SEND_INITIAL_METADATA,
+  PRE_SEND_MESSAGE,
+  PRE_SEND_STATUS,  // server only
+  PRE_SEND_CLOSE,   // client only: WritesDone for stream; after write in unary
+  /// The following three are for hijacked clients only and can only be
+  /// registered by the global interceptor
+  PRE_RECV_INITIAL_METADATA,
+  PRE_RECV_MESSAGE,
+  PRE_RECV_STATUS,
+  /// The following two are for all clients and servers
+  POST_RECV_INITIAL_METADATA,
+  POST_RECV_MESSAGE,
+  POST_RECV_STATUS,  // client only
+  POST_RECV_CLOSE,   // server only
+  /// This is a special hook point available to both clients and servers when
+  /// TryCancel() is performed.
+  ///  - No other hook points will be present along with this.
+  ///  - It is illegal for an interceptor to block/delay this operation.
+  ///  - ALL interceptors see this hook point irrespective of whether the
+  ///    RPC was hijacked or not.
+  PRE_SEND_CANCEL,
+  NUM_INTERCEPTION_HOOKS
+};
+
+/// Class that is passed as an argument to the \a Intercept method
+/// of the application's \a Interceptor interface implementation. It has five
+/// purposes:
+///   1. Indicate which hook points are present at a specific interception
+///   2. Allow an interceptor to inform the library that an RPC should
+///      continue to the next stage of its processing (which may be another
+///      interceptor or the main path of the library)
+///   3. Allow an interceptor to hijack the processing of the RPC (only for
+///      client-side RPCs with PRE_SEND_INITIAL_METADATA) so that it does not
+///      proceed with normal processing beyond that stage
+///   4. Access the relevant fields of an RPC at each interception point
+///   5. Set some fields of an RPC at each interception point, when possible
+class InterceptorBatchMethods {
+ public:
+  virtual ~InterceptorBatchMethods(){};
+  /// Determine whether the current batch has an interception hook point
+  /// of type \a type
+  virtual bool QueryInterceptionHookPoint(InterceptionHookPoints type) = 0;
+  /// Signal that the interceptor is done intercepting the current batch of the
+  /// RPC. Every interceptor must either call Proceed or Hijack on each
+  /// interception. In most cases, only Proceed will be used. Explicit use of
+  /// Proceed is what enables interceptors to delay the processing of RPCs
+  /// while they perform other work.
+  /// Proceed is a no-op if the batch contains PRE_SEND_CANCEL. Simply returning
+  /// from the Intercept method does the job of continuing the RPC in this case.
+  /// This is because PRE_SEND_CANCEL is always in a separate batch and is not
+  /// allowed to be delayed.
+  virtual void Proceed() = 0;
+  /// Indicate that the interceptor has hijacked the RPC (only valid if the
+  /// batch contains send_initial_metadata on the client side). Later
+  /// interceptors in the interceptor list will not be called. Later batches
+  /// on the same RPC will go through interception, but only up to the point
+  /// of the hijacking interceptor.
+  virtual void Hijack() = 0;
+
+  /// Returns a modifable ByteBuffer holding the serialized form of the message
+  /// that is going to be sent. Valid for PRE_SEND_MESSAGE interceptions.
+  /// A return value of nullptr indicates that this ByteBuffer is not valid.
+  virtual ByteBuffer* GetSendMessage() = 0;
+
+  /// Returns a modifiable multimap of the initial metadata to be sent. Valid
+  /// for PRE_SEND_INITIAL_METADATA interceptions. A value of nullptr indicates
+  /// that this field is not valid.
+  virtual std::multimap<grpc::string, grpc::string>*
+  GetSendInitialMetadata() = 0;
+
+  /// Returns the status to be sent. Valid for PRE_SEND_STATUS interceptions.
+  virtual Status GetSendStatus() = 0;
+
+  /// Overwrites the status with \a status. Valid for PRE_SEND_STATUS
+  /// interceptions.
+  virtual void ModifySendStatus(const Status& status) = 0;
+
+  /// Returns a modifiable multimap of the trailing metadata to be sent. Valid
+  /// for PRE_SEND_STATUS interceptions. A value of nullptr indicates
+  /// that this field is not valid.
+  virtual std::multimap<grpc::string, grpc::string>*
+  GetSendTrailingMetadata() = 0;
+
+  /// Returns a pointer to the modifiable received message. Note that the
+  /// message is already deserialized but the type is not set; the interceptor
+  /// should static_cast to the appropriate type before using it. This is valid
+  /// for POST_RECV_MESSAGE interceptions; nullptr for not valid
+  virtual void* GetRecvMessage() = 0;
+
+  /// Returns a modifiable multimap of the received initial metadata.
+  /// Valid for POST_RECV_INITIAL_METADATA interceptions; nullptr if not valid
+  virtual std::multimap<grpc::string_ref, grpc::string_ref>*
+  GetRecvInitialMetadata() = 0;
+
+  /// Returns a modifiable view of the received status on POST_RECV_STATUS
+  /// interceptions; nullptr if not valid.
+  virtual Status* GetRecvStatus() = 0;
+
+  /// Returns a modifiable multimap of the received trailing metadata on
+  /// POST_RECV_STATUS interceptions; nullptr if not valid
+  virtual std::multimap<grpc::string_ref, grpc::string_ref>*
+  GetRecvTrailingMetadata() = 0;
+
+  /// Gets an intercepted channel. When a call is started on this interceptor,
+  /// only interceptors after the current interceptor are created from the
+  /// factory objects registered with the channel. This allows calls to be
+  /// started from interceptors without infinite regress through the interceptor
+  /// list.
+  virtual std::unique_ptr<ChannelInterface> GetInterceptedChannel() = 0;
+};
+
+/// Interface for an interceptor. Interceptor authors must create a class
+/// that derives from this parent class.
+class Interceptor {
+ public:
+  virtual ~Interceptor() {}
+
+  /// The one public method of an Interceptor interface. Override this to
+  /// trigger the desired actions at the hook points described above.
+  virtual void Intercept(InterceptorBatchMethods* methods) = 0;
+};
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_INTERCEPTOR_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/interceptor_common.h b/third_party/grpc/include/grpcpp/impl/codegen/interceptor_common.h
new file mode 100644
index 0000000..d0aa23c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/interceptor_common.h
@@ -0,0 +1,458 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H
+#define GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H
+
+#include <array>
+#include <functional>
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_op_set_interface.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/intercepted_channel.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+namespace grpc {
+namespace internal {
+
+class InterceptorBatchMethodsImpl
+    : public experimental::InterceptorBatchMethods {
+ public:
+  InterceptorBatchMethodsImpl() {
+    for (auto i = static_cast<experimental::InterceptionHookPoints>(0);
+         i < experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS;
+         i = static_cast<experimental::InterceptionHookPoints>(
+             static_cast<size_t>(i) + 1)) {
+      hooks_[static_cast<size_t>(i)] = false;
+    }
+  }
+
+  ~InterceptorBatchMethodsImpl() {}
+
+  bool QueryInterceptionHookPoint(
+      experimental::InterceptionHookPoints type) override {
+    return hooks_[static_cast<size_t>(type)];
+  }
+
+  void Proceed() override {
+    if (call_->client_rpc_info() != nullptr) {
+      return ProceedClient();
+    }
+    GPR_CODEGEN_ASSERT(call_->server_rpc_info() != nullptr);
+    ProceedServer();
+  }
+
+  void Hijack() override {
+    // Only the client can hijack when sending down initial metadata
+    GPR_CODEGEN_ASSERT(!reverse_ && ops_ != nullptr &&
+                       call_->client_rpc_info() != nullptr);
+    // It is illegal to call Hijack twice
+    GPR_CODEGEN_ASSERT(!ran_hijacking_interceptor_);
+    auto* rpc_info = call_->client_rpc_info();
+    rpc_info->hijacked_ = true;
+    rpc_info->hijacked_interceptor_ = current_interceptor_index_;
+    ClearHookPoints();
+    ops_->SetHijackingState();
+    ran_hijacking_interceptor_ = true;
+    rpc_info->RunInterceptor(this, current_interceptor_index_);
+  }
+
+  void AddInterceptionHookPoint(experimental::InterceptionHookPoints type) {
+    hooks_[static_cast<size_t>(type)] = true;
+  }
+
+  ByteBuffer* GetSendMessage() override { return send_message_; }
+
+  std::multimap<grpc::string, grpc::string>* GetSendInitialMetadata() override {
+    return send_initial_metadata_;
+  }
+
+  Status GetSendStatus() override {
+    return Status(static_cast<StatusCode>(*code_), *error_message_,
+                  *error_details_);
+  }
+
+  void ModifySendStatus(const Status& status) override {
+    *code_ = static_cast<grpc_status_code>(status.error_code());
+    *error_details_ = status.error_details();
+    *error_message_ = status.error_message();
+  }
+
+  std::multimap<grpc::string, grpc::string>* GetSendTrailingMetadata()
+      override {
+    return send_trailing_metadata_;
+  }
+
+  void* GetRecvMessage() override { return recv_message_; }
+
+  std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvInitialMetadata()
+      override {
+    return recv_initial_metadata_->map();
+  }
+
+  Status* GetRecvStatus() override { return recv_status_; }
+
+  std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvTrailingMetadata()
+      override {
+    return recv_trailing_metadata_->map();
+  }
+
+  void SetSendMessage(ByteBuffer* buf) { send_message_ = buf; }
+
+  void SetSendInitialMetadata(
+      std::multimap<grpc::string, grpc::string>* metadata) {
+    send_initial_metadata_ = metadata;
+  }
+
+  void SetSendStatus(grpc_status_code* code, grpc::string* error_details,
+                     grpc::string* error_message) {
+    code_ = code;
+    error_details_ = error_details;
+    error_message_ = error_message;
+  }
+
+  void SetSendTrailingMetadata(
+      std::multimap<grpc::string, grpc::string>* metadata) {
+    send_trailing_metadata_ = metadata;
+  }
+
+  void SetRecvMessage(void* message) { recv_message_ = message; }
+
+  void SetRecvInitialMetadata(MetadataMap* map) {
+    recv_initial_metadata_ = map;
+  }
+
+  void SetRecvStatus(Status* status) { recv_status_ = status; }
+
+  void SetRecvTrailingMetadata(MetadataMap* map) {
+    recv_trailing_metadata_ = map;
+  }
+
+  std::unique_ptr<ChannelInterface> GetInterceptedChannel() override {
+    auto* info = call_->client_rpc_info();
+    if (info == nullptr) {
+      return std::unique_ptr<ChannelInterface>(nullptr);
+    }
+    // The intercepted channel starts from the interceptor just after the
+    // current interceptor
+    return std::unique_ptr<ChannelInterface>(new InterceptedChannel(
+        info->channel(), current_interceptor_index_ + 1));
+  }
+
+  // Clears all state
+  void ClearState() {
+    reverse_ = false;
+    ran_hijacking_interceptor_ = false;
+    ClearHookPoints();
+  }
+
+  // Prepares for Post_recv operations
+  void SetReverse() {
+    reverse_ = true;
+    ran_hijacking_interceptor_ = false;
+    ClearHookPoints();
+  }
+
+  // This needs to be set before interceptors are run
+  void SetCall(Call* call) { call_ = call; }
+
+  // This needs to be set before interceptors are run using RunInterceptors().
+  // Alternatively, RunInterceptors(std::function<void(void)> f) can be used.
+  void SetCallOpSetInterface(CallOpSetInterface* ops) { ops_ = ops; }
+
+  // Returns true if no interceptors are run. This should be used only by
+  // subclasses of CallOpSetInterface. SetCall and SetCallOpSetInterface should
+  // have been called before this. After all the interceptors are done running,
+  // either ContinueFillOpsAfterInterception or
+  // ContinueFinalizeOpsAfterInterception will be called. Note that neither of
+  // them is invoked if there were no interceptors registered.
+  bool RunInterceptors() {
+    GPR_CODEGEN_ASSERT(ops_);
+    auto* client_rpc_info = call_->client_rpc_info();
+    if (client_rpc_info != nullptr) {
+      if (client_rpc_info->interceptors_.size() == 0) {
+        return true;
+      } else {
+        RunClientInterceptors();
+        return false;
+      }
+    }
+
+    auto* server_rpc_info = call_->server_rpc_info();
+    if (server_rpc_info == nullptr ||
+        server_rpc_info->interceptors_.size() == 0) {
+      return true;
+    }
+    RunServerInterceptors();
+    return false;
+  }
+
+  // Returns true if no interceptors are run. Returns false otherwise if there
+  // are interceptors registered. After the interceptors are done running \a f
+  // will be invoked. This is to be used only by BaseAsyncRequest and
+  // SyncRequest.
+  bool RunInterceptors(std::function<void(void)> f) {
+    // This is used only by the server for initial call request
+    GPR_CODEGEN_ASSERT(reverse_ == true);
+    GPR_CODEGEN_ASSERT(call_->client_rpc_info() == nullptr);
+    auto* server_rpc_info = call_->server_rpc_info();
+    if (server_rpc_info == nullptr ||
+        server_rpc_info->interceptors_.size() == 0) {
+      return true;
+    }
+    callback_ = std::move(f);
+    RunServerInterceptors();
+    return false;
+  }
+
+ private:
+  void RunClientInterceptors() {
+    auto* rpc_info = call_->client_rpc_info();
+    if (!reverse_) {
+      current_interceptor_index_ = 0;
+    } else {
+      if (rpc_info->hijacked_) {
+        current_interceptor_index_ = rpc_info->hijacked_interceptor_;
+      } else {
+        current_interceptor_index_ = rpc_info->interceptors_.size() - 1;
+      }
+    }
+    rpc_info->RunInterceptor(this, current_interceptor_index_);
+  }
+
+  void RunServerInterceptors() {
+    auto* rpc_info = call_->server_rpc_info();
+    if (!reverse_) {
+      current_interceptor_index_ = 0;
+    } else {
+      current_interceptor_index_ = rpc_info->interceptors_.size() - 1;
+    }
+    rpc_info->RunInterceptor(this, current_interceptor_index_);
+  }
+
+  void ProceedClient() {
+    auto* rpc_info = call_->client_rpc_info();
+    if (rpc_info->hijacked_ && !reverse_ &&
+        current_interceptor_index_ == rpc_info->hijacked_interceptor_ &&
+        !ran_hijacking_interceptor_) {
+      // We now need to provide hijacked recv ops to this interceptor
+      ClearHookPoints();
+      ops_->SetHijackingState();
+      ran_hijacking_interceptor_ = true;
+      rpc_info->RunInterceptor(this, current_interceptor_index_);
+      return;
+    }
+    if (!reverse_) {
+      current_interceptor_index_++;
+      // We are going down the stack of interceptors
+      if (current_interceptor_index_ < rpc_info->interceptors_.size()) {
+        if (rpc_info->hijacked_ &&
+            current_interceptor_index_ > rpc_info->hijacked_interceptor_) {
+          // This is a hijacked RPC and we are done with hijacking
+          ops_->ContinueFillOpsAfterInterception();
+        } else {
+          rpc_info->RunInterceptor(this, current_interceptor_index_);
+        }
+      } else {
+        // we are done running all the interceptors without any hijacking
+        ops_->ContinueFillOpsAfterInterception();
+      }
+    } else {
+      // We are going up the stack of interceptors
+      if (current_interceptor_index_ > 0) {
+        // Continue running interceptors
+        current_interceptor_index_--;
+        rpc_info->RunInterceptor(this, current_interceptor_index_);
+      } else {
+        // we are done running all the interceptors without any hijacking
+        ops_->ContinueFinalizeResultAfterInterception();
+      }
+    }
+  }
+
+  void ProceedServer() {
+    auto* rpc_info = call_->server_rpc_info();
+    if (!reverse_) {
+      current_interceptor_index_++;
+      if (current_interceptor_index_ < rpc_info->interceptors_.size()) {
+        return rpc_info->RunInterceptor(this, current_interceptor_index_);
+      } else if (ops_) {
+        return ops_->ContinueFillOpsAfterInterception();
+      }
+    } else {
+      // We are going up the stack of interceptors
+      if (current_interceptor_index_ > 0) {
+        // Continue running interceptors
+        current_interceptor_index_--;
+        return rpc_info->RunInterceptor(this, current_interceptor_index_);
+      } else if (ops_) {
+        return ops_->ContinueFinalizeResultAfterInterception();
+      }
+    }
+    GPR_CODEGEN_ASSERT(callback_);
+    callback_();
+  }
+
+  void ClearHookPoints() {
+    for (auto i = static_cast<experimental::InterceptionHookPoints>(0);
+         i < experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS;
+         i = static_cast<experimental::InterceptionHookPoints>(
+             static_cast<size_t>(i) + 1)) {
+      hooks_[static_cast<size_t>(i)] = false;
+    }
+  }
+
+  std::array<bool,
+             static_cast<size_t>(
+                 experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS)>
+      hooks_;
+
+  size_t current_interceptor_index_ = 0;  // Current iterator
+  bool reverse_ = false;
+  bool ran_hijacking_interceptor_ = false;
+  Call* call_ = nullptr;  // The Call object is present along with CallOpSet
+                          // object/callback
+  CallOpSetInterface* ops_ = nullptr;
+  std::function<void(void)> callback_;
+
+  ByteBuffer* send_message_ = nullptr;
+
+  std::multimap<grpc::string, grpc::string>* send_initial_metadata_;
+
+  grpc_status_code* code_ = nullptr;
+  grpc::string* error_details_ = nullptr;
+  grpc::string* error_message_ = nullptr;
+  Status send_status_;
+
+  std::multimap<grpc::string, grpc::string>* send_trailing_metadata_ = nullptr;
+
+  void* recv_message_ = nullptr;
+
+  MetadataMap* recv_initial_metadata_ = nullptr;
+
+  Status* recv_status_ = nullptr;
+
+  MetadataMap* recv_trailing_metadata_ = nullptr;
+};
+
+// A special implementation of InterceptorBatchMethods to send a Cancel
+// notification down the interceptor stack
+class CancelInterceptorBatchMethods
+    : public experimental::InterceptorBatchMethods {
+ public:
+  bool QueryInterceptionHookPoint(
+      experimental::InterceptionHookPoints type) override {
+    if (type == experimental::InterceptionHookPoints::PRE_SEND_CANCEL) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  void Proceed() override {
+    // This is a no-op. For actual continuation of the RPC simply needs to
+    // return from the Intercept method
+  }
+
+  void Hijack() override {
+    // Only the client can hijack when sending down initial metadata
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call Hijack on a method which has a "
+                       "Cancel notification");
+  }
+
+  ByteBuffer* GetSendMessage() override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetSendMessage on a method which "
+                       "has a Cancel notification");
+    return nullptr;
+  }
+
+  std::multimap<grpc::string, grpc::string>* GetSendInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetSendInitialMetadata on a "
+                       "method which has a Cancel notification");
+    return nullptr;
+  }
+
+  Status GetSendStatus() override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetSendStatus on a method which "
+                       "has a Cancel notification");
+    return Status();
+  }
+
+  void ModifySendStatus(const Status& status) override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call ModifySendStatus on a method "
+                       "which has a Cancel notification");
+    return;
+  }
+
+  std::multimap<grpc::string, grpc::string>* GetSendTrailingMetadata()
+      override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetSendTrailingMetadata on a "
+                       "method which has a Cancel notification");
+    return nullptr;
+  }
+
+  void* GetRecvMessage() override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetRecvMessage on a method which "
+                       "has a Cancel notification");
+    return nullptr;
+  }
+
+  std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvInitialMetadata()
+      override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetRecvInitialMetadata on a "
+                       "method which has a Cancel notification");
+    return nullptr;
+  }
+
+  Status* GetRecvStatus() override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetRecvStatus on a method which "
+                       "has a Cancel notification");
+    return nullptr;
+  }
+
+  std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvTrailingMetadata()
+      override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetRecvTrailingMetadata on a "
+                       "method which has a Cancel notification");
+    return nullptr;
+  }
+
+  std::unique_ptr<ChannelInterface> GetInterceptedChannel() override {
+    GPR_CODEGEN_ASSERT(false &&
+                       "It is illegal to call GetInterceptedChannel on a "
+                       "method which has a Cancel notification");
+    return std::unique_ptr<ChannelInterface>(nullptr);
+  }
+};
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/metadata_map.h b/third_party/grpc/include/grpcpp/impl/codegen/metadata_map.h
new file mode 100644
index 0000000..9cec54d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/metadata_map.h
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_METADATA_MAP_H
+#define GRPCPP_IMPL_CODEGEN_METADATA_MAP_H
+
+#include <map>
+
+#include <grpc/impl/codegen/log.h>
+#include <grpcpp/impl/codegen/slice.h>
+
+namespace grpc {
+
+namespace internal {
+
+const char kBinaryErrorDetailsKey[] = "grpc-status-details-bin";
+
+class MetadataMap {
+ public:
+  MetadataMap() { Setup(); }
+
+  ~MetadataMap() { Destroy(); }
+
+  grpc::string GetBinaryErrorDetails() {
+    // if filled_, extract from the multimap for O(log(n))
+    if (filled_) {
+      auto iter = map_.find(kBinaryErrorDetailsKey);
+      if (iter != map_.end()) {
+        return grpc::string(iter->second.begin(), iter->second.length());
+      }
+    }
+    // if not yet filled, take the O(n) lookup to avoid allocating the
+    // multimap until it is requested.
+    // TODO(ncteisen): plumb this through core as a first class object, just
+    // like code and message.
+    else {
+      for (size_t i = 0; i < arr_.count; i++) {
+        if (strncmp(reinterpret_cast<const char*>(
+                        GRPC_SLICE_START_PTR(arr_.metadata[i].key)),
+                    kBinaryErrorDetailsKey,
+                    GRPC_SLICE_LENGTH(arr_.metadata[i].key)) == 0) {
+          return grpc::string(reinterpret_cast<const char*>(
+                                  GRPC_SLICE_START_PTR(arr_.metadata[i].value)),
+                              GRPC_SLICE_LENGTH(arr_.metadata[i].value));
+        }
+      }
+    }
+    return grpc::string();
+  }
+
+  std::multimap<grpc::string_ref, grpc::string_ref>* map() {
+    FillMap();
+    return &map_;
+  }
+  grpc_metadata_array* arr() { return &arr_; }
+
+  void Reset() {
+    filled_ = false;
+    map_.clear();
+    Destroy();
+    Setup();
+  }
+
+ private:
+  bool filled_ = false;
+  grpc_metadata_array arr_;
+  std::multimap<grpc::string_ref, grpc::string_ref> map_;
+
+  void Destroy() {
+    g_core_codegen_interface->grpc_metadata_array_destroy(&arr_);
+  }
+
+  void Setup() { memset(&arr_, 0, sizeof(arr_)); }
+
+  void FillMap() {
+    if (filled_) return;
+    filled_ = true;
+    for (size_t i = 0; i < arr_.count; i++) {
+      // TODO(yangg) handle duplicates?
+      map_.insert(std::pair<grpc::string_ref, grpc::string_ref>(
+          StringRefFromSlice(&arr_.metadata[i].key),
+          StringRefFromSlice(&arr_.metadata[i].value)));
+    }
+  }
+};
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_METADATA_MAP_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/method_handler_impl.h b/third_party/grpc/include/grpcpp/impl/codegen/method_handler_impl.h
new file mode 100644
index 0000000..dd53f97
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/method_handler_impl.h
@@ -0,0 +1,347 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
+#define GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
+
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+#include <grpcpp/impl/codegen/sync_stream.h>
+
+namespace grpc {
+
+namespace internal {
+
+// Invoke the method handler, fill in the status, and
+// return whether or not we finished safely (without an exception).
+// Note that exception handling is 0-cost in most compiler/library
+// implementations (except when an exception is actually thrown),
+// so this process doesn't require additional overhead in the common case.
+// Additionally, we don't need to return if we caught an exception or not;
+// the handling is the same in either case.
+template <class Callable>
+Status CatchingFunctionHandler(Callable&& handler) {
+#if GRPC_ALLOW_EXCEPTIONS
+  try {
+    return handler();
+  } catch (...) {
+    return Status(StatusCode::UNKNOWN, "Unexpected error in RPC handling");
+  }
+#else   // GRPC_ALLOW_EXCEPTIONS
+  return handler();
+#endif  // GRPC_ALLOW_EXCEPTIONS
+}
+
+/// A wrapper class of an application provided rpc method handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler : public MethodHandler {
+ public:
+  RpcMethodHandler(std::function<Status(ServiceType*, ServerContext*,
+                                        const RequestType*, ResponseType*)>
+                       func,
+                   ServiceType* service)
+      : func_(func), service_(service) {}
+
+  void RunHandler(const HandlerParameter& param) final {
+    ResponseType rsp;
+    Status status = param.status;
+    if (status.ok()) {
+      status = CatchingFunctionHandler([this, &param, &rsp] {
+        return func_(service_, param.server_context,
+                     static_cast<RequestType*>(param.request), &rsp);
+      });
+      static_cast<RequestType*>(param.request)->~RequestType();
+    }
+
+    GPR_CODEGEN_ASSERT(!param.server_context->sent_initial_metadata_);
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpServerSendStatus>
+        ops;
+    ops.SendInitialMetadata(&param.server_context->initial_metadata_,
+                            param.server_context->initial_metadata_flags());
+    if (param.server_context->compression_level_set()) {
+      ops.set_compression_level(param.server_context->compression_level());
+    }
+    if (status.ok()) {
+      status = ops.SendMessage(rsp);
+    }
+    ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
+    param.call->PerformOps(&ops);
+    param.call->cq()->Pluck(&ops);
+  }
+
+  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+                    Status* status) final {
+    ByteBuffer buf;
+    buf.set_buffer(req);
+    auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call, sizeof(RequestType))) RequestType();
+    *status = SerializationTraits<RequestType>::Deserialize(&buf, request);
+    buf.Release();
+    if (status->ok()) {
+      return request;
+    }
+    request->~RequestType();
+    return nullptr;
+  }
+
+ private:
+  /// Application provided rpc handler function.
+  std::function<Status(ServiceType*, ServerContext*, const RequestType*,
+                       ResponseType*)>
+      func_;
+  // The class the above handler function lives in.
+  ServiceType* service_;
+};
+
+/// A wrapper class of an application provided client streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler : public MethodHandler {
+ public:
+  ClientStreamingHandler(
+      std::function<Status(ServiceType*, ServerContext*,
+                           ServerReader<RequestType>*, ResponseType*)>
+          func,
+      ServiceType* service)
+      : func_(func), service_(service) {}
+
+  void RunHandler(const HandlerParameter& param) final {
+    ServerReader<RequestType> reader(param.call, param.server_context);
+    ResponseType rsp;
+    Status status = CatchingFunctionHandler([this, &param, &reader, &rsp] {
+      return func_(service_, param.server_context, &reader, &rsp);
+    });
+
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpServerSendStatus>
+        ops;
+    if (!param.server_context->sent_initial_metadata_) {
+      ops.SendInitialMetadata(&param.server_context->initial_metadata_,
+                              param.server_context->initial_metadata_flags());
+      if (param.server_context->compression_level_set()) {
+        ops.set_compression_level(param.server_context->compression_level());
+      }
+    }
+    if (status.ok()) {
+      status = ops.SendMessage(rsp);
+    }
+    ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
+    param.call->PerformOps(&ops);
+    param.call->cq()->Pluck(&ops);
+  }
+
+ private:
+  std::function<Status(ServiceType*, ServerContext*, ServerReader<RequestType>*,
+                       ResponseType*)>
+      func_;
+  ServiceType* service_;
+};
+
+/// A wrapper class of an application provided server streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler : public MethodHandler {
+ public:
+  ServerStreamingHandler(
+      std::function<Status(ServiceType*, ServerContext*, const RequestType*,
+                           ServerWriter<ResponseType>*)>
+          func,
+      ServiceType* service)
+      : func_(func), service_(service) {}
+
+  void RunHandler(const HandlerParameter& param) final {
+    Status status = param.status;
+    if (status.ok()) {
+      ServerWriter<ResponseType> writer(param.call, param.server_context);
+      status = CatchingFunctionHandler([this, &param, &writer] {
+        return func_(service_, param.server_context,
+                     static_cast<RequestType*>(param.request), &writer);
+      });
+      static_cast<RequestType*>(param.request)->~RequestType();
+    }
+
+    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
+    if (!param.server_context->sent_initial_metadata_) {
+      ops.SendInitialMetadata(&param.server_context->initial_metadata_,
+                              param.server_context->initial_metadata_flags());
+      if (param.server_context->compression_level_set()) {
+        ops.set_compression_level(param.server_context->compression_level());
+      }
+    }
+    ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
+    param.call->PerformOps(&ops);
+    if (param.server_context->has_pending_ops_) {
+      param.call->cq()->Pluck(&param.server_context->pending_ops_);
+    }
+    param.call->cq()->Pluck(&ops);
+  }
+
+  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+                    Status* status) final {
+    ByteBuffer buf;
+    buf.set_buffer(req);
+    auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call, sizeof(RequestType))) RequestType();
+    *status = SerializationTraits<RequestType>::Deserialize(&buf, request);
+    buf.Release();
+    if (status->ok()) {
+      return request;
+    }
+    request->~RequestType();
+    return nullptr;
+  }
+
+ private:
+  std::function<Status(ServiceType*, ServerContext*, const RequestType*,
+                       ServerWriter<ResponseType>*)>
+      func_;
+  ServiceType* service_;
+};
+
+/// A wrapper class of an application provided bidi-streaming handler.
+/// 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:
+  TemplatedBidiStreamingHandler(
+      std::function<Status(ServerContext*, Streamer*)> func)
+      : func_(func), write_needed_(WriteNeeded) {}
+
+  void RunHandler(const HandlerParameter& param) final {
+    Streamer stream(param.call, param.server_context);
+    Status status = CatchingFunctionHandler([this, &param, &stream] {
+      return func_(param.server_context, &stream);
+    });
+
+    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
+    if (!param.server_context->sent_initial_metadata_) {
+      ops.SendInitialMetadata(&param.server_context->initial_metadata_,
+                              param.server_context->initial_metadata_flags());
+      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);
+    if (param.server_context->has_pending_ops_) {
+      param.call->cq()->Pluck(&param.server_context->pending_ops_);
+    }
+    param.call->cq()->Pluck(&ops);
+  }
+
+ private:
+  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) {}
+};
+
+template <class RequestType, class ResponseType>
+class SplitServerStreamingHandler
+    : public TemplatedBidiStreamingHandler<
+          ServerSplitStreamer<RequestType, ResponseType>, false> {
+ public:
+  explicit SplitServerStreamingHandler(
+      std::function<Status(ServerContext*,
+                           ServerSplitStreamer<RequestType, ResponseType>*)>
+          func)
+      : TemplatedBidiStreamingHandler<
+            ServerSplitStreamer<RequestType, ResponseType>, false>(func) {}
+};
+
+/// General method handler class for errors that prevent real method use
+/// e.g., handle unknown method by returning UNIMPLEMENTED error.
+template <StatusCode code>
+class ErrorMethodHandler : public MethodHandler {
+ public:
+  template <class T>
+  static void FillOps(ServerContext* context, T* ops) {
+    Status status(code, "");
+    if (!context->sent_initial_metadata_) {
+      ops->SendInitialMetadata(&context->initial_metadata_,
+                               context->initial_metadata_flags());
+      if (context->compression_level_set()) {
+        ops->set_compression_level(context->compression_level());
+      }
+      context->sent_initial_metadata_ = true;
+    }
+    ops->ServerSendStatus(&context->trailing_metadata_, status);
+  }
+
+  void RunHandler(const HandlerParameter& param) final {
+    CallOpSet<CallOpSendInitialMetadata, CallOpServerSendStatus> ops;
+    FillOps(param.server_context, &ops);
+    param.call->PerformOps(&ops);
+    param.call->cq()->Pluck(&ops);
+  }
+
+  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+                    Status* status) final {
+    // We have to destroy any request payload
+    if (req != nullptr) {
+      g_core_codegen_interface->grpc_byte_buffer_destroy(req);
+    }
+    return nullptr;
+  }
+};
+
+typedef ErrorMethodHandler<StatusCode::UNIMPLEMENTED> UnknownMethodHandler;
+typedef ErrorMethodHandler<StatusCode::RESOURCE_EXHAUSTED>
+    ResourceExhaustedHandler;
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_reader.h b/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_reader.h
new file mode 100644
index 0000000..9acae47
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_reader.h
@@ -0,0 +1,151 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_READER_H
+#define GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_READER_H
+
+#include <type_traits>
+
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/config_protobuf.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/status.h>
+
+/// This header provides an object that reads bytes directly from a
+/// grpc::ByteBuffer, via the ZeroCopyInputStream interface
+
+namespace grpc {
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+/// This is a specialization of the protobuf class ZeroCopyInputStream
+/// The principle is to get one chunk of data at a time from the proto layer,
+/// with options to backup (re-see some bytes) or skip (forward past some bytes)
+///
+/// Read more about ZeroCopyInputStream interface here:
+/// https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyInputStream
+class ProtoBufferReader : public ::grpc::protobuf::io::ZeroCopyInputStream {
+ public:
+  /// Constructs buffer reader from \a buffer. Will set \a status() to non ok
+  /// if \a buffer is invalid (the internal buffer has not been initialized).
+  explicit ProtoBufferReader(ByteBuffer* buffer)
+      : byte_count_(0), backup_count_(0), status_() {
+    /// Implemented through a grpc_byte_buffer_reader which iterates
+    /// over the slices that make up a byte buffer
+    if (!buffer->Valid() ||
+        !g_core_codegen_interface->grpc_byte_buffer_reader_init(
+            &reader_, buffer->c_buffer())) {
+      status_ = Status(StatusCode::INTERNAL,
+                       "Couldn't initialize byte buffer reader");
+    }
+  }
+
+  ~ProtoBufferReader() {
+    if (status_.ok()) {
+      g_core_codegen_interface->grpc_byte_buffer_reader_destroy(&reader_);
+    }
+  }
+
+  /// Give the proto library a chunk of data from the stream. The caller
+  /// may safely read from data[0, size - 1].
+  bool Next(const void** data, int* size) override {
+    if (!status_.ok()) {
+      return false;
+    }
+    /// If we have backed up previously, we need to return the backed-up slice
+    if (backup_count_ > 0) {
+      *data = GRPC_SLICE_START_PTR(slice_) + GRPC_SLICE_LENGTH(slice_) -
+              backup_count_;
+      GPR_CODEGEN_ASSERT(backup_count_ <= INT_MAX);
+      *size = (int)backup_count_;
+      backup_count_ = 0;
+      return true;
+    }
+    /// Otherwise get the next slice from the byte buffer reader
+    if (!g_core_codegen_interface->grpc_byte_buffer_reader_next(&reader_,
+                                                                &slice_)) {
+      return false;
+    }
+    g_core_codegen_interface->grpc_slice_unref(slice_);
+    *data = GRPC_SLICE_START_PTR(slice_);
+    // On win x64, int is only 32bit
+    GPR_CODEGEN_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX);
+    byte_count_ += * size = (int)GRPC_SLICE_LENGTH(slice_);
+    return true;
+  }
+
+  /// Returns the status of the buffer reader.
+  Status status() const { return status_; }
+
+  /// The proto library calls this to indicate that we should back up \a count
+  /// bytes that have already been returned by the last call of Next.
+  /// So do the backup and have that ready for a later Next.
+  void BackUp(int count) override {
+    GPR_CODEGEN_ASSERT(count <= static_cast<int>(GRPC_SLICE_LENGTH(slice_)));
+    backup_count_ = count;
+  }
+
+  /// The proto library calls this to skip over \a count bytes. Implement this
+  /// using Next and BackUp combined.
+  bool Skip(int count) override {
+    const void* data;
+    int size;
+    while (Next(&data, &size)) {
+      if (size >= count) {
+        BackUp(size - count);
+        return true;
+      }
+      // size < count;
+      count -= size;
+    }
+    // error or we have too large count;
+    return false;
+  }
+
+  /// Returns the total number of bytes read since this object was created.
+  grpc::protobuf::int64 ByteCount() const override {
+    return byte_count_ - backup_count_;
+  }
+
+  // These protected members are needed to support internal optimizations.
+  // they expose internal bits of grpc core that are NOT stable. If you have
+  // a use case needs to use one of these functions, please send an email to
+  // https://groups.google.com/forum/#!forum/grpc-io.
+ protected:
+  void set_byte_count(int64_t byte_count) { byte_count_ = byte_count; }
+  int64_t backup_count() { return backup_count_; }
+  void set_backup_count(int64_t backup_count) { backup_count_ = backup_count; }
+  grpc_byte_buffer_reader* reader() { return &reader_; }
+  grpc_slice* slice() { return &slice_; }
+
+ private:
+  int64_t byte_count_;              ///< total bytes read since object creation
+  int64_t backup_count_;            ///< how far backed up in the stream we are
+  grpc_byte_buffer_reader reader_;  ///< internal object to read \a grpc_slice
+                                    ///< from the \a grpc_byte_buffer
+  grpc_slice slice_;                ///< current slice passed back to the caller
+  Status status_;                   ///< status of the entire object
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_READER_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_writer.h b/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_writer.h
new file mode 100644
index 0000000..fdff467
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_writer.h
@@ -0,0 +1,167 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_WRITER_H
+#define GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_WRITER_H
+
+#include <type_traits>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/config_protobuf.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/status.h>
+
+/// This header provides an object that writes bytes directly into a
+/// grpc::ByteBuffer, via the ZeroCopyOutputStream interface
+
+namespace grpc {
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+// Forward declaration for testing use only
+namespace internal {
+class ProtoBufferWriterPeer;
+}  // namespace internal
+
+const int kProtoBufferWriterMaxBufferLength = 1024 * 1024;
+
+/// This is a specialization of the protobuf class ZeroCopyOutputStream.
+/// The principle is to give the proto layer one buffer of bytes at a time
+/// that it can use to serialize the next portion of the message, with the
+/// option to "backup" if more buffer is given than required at the last buffer.
+///
+/// Read more about ZeroCopyOutputStream interface here:
+/// https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyOutputStream
+class ProtoBufferWriter : public ::grpc::protobuf::io::ZeroCopyOutputStream {
+ public:
+  /// Constructor for this derived class
+  ///
+  /// \param[out] byte_buffer A pointer to the grpc::ByteBuffer created
+  /// \param block_size How big are the chunks to allocate at a time
+  /// \param total_size How many total bytes are required for this proto
+  ProtoBufferWriter(ByteBuffer* byte_buffer, int block_size, int total_size)
+      : block_size_(block_size),
+        total_size_(total_size),
+        byte_count_(0),
+        have_backup_(false) {
+    GPR_CODEGEN_ASSERT(!byte_buffer->Valid());
+    /// Create an empty raw byte buffer and look at its underlying slice buffer
+    grpc_byte_buffer* bp =
+        g_core_codegen_interface->grpc_raw_byte_buffer_create(NULL, 0);
+    byte_buffer->set_buffer(bp);
+    slice_buffer_ = &bp->data.raw.slice_buffer;
+  }
+
+  ~ProtoBufferWriter() {
+    if (have_backup_) {
+      g_core_codegen_interface->grpc_slice_unref(backup_slice_);
+    }
+  }
+
+  /// Give the proto library the next buffer of bytes and its size. It is
+  /// safe for the caller to write from data[0, size - 1].
+  bool Next(void** data, int* size) override {
+    // Protobuf should not ask for more memory than total_size_.
+    GPR_CODEGEN_ASSERT(byte_count_ < total_size_);
+    // 1. Use the remaining backup slice if we have one
+    // 2. Otherwise allocate a slice, up to the remaining length needed
+    //    or our maximum allocation size
+    // 3. Provide the slice start and size available
+    // 4. Add the slice being returned to the slice buffer
+    size_t remain = total_size_ - byte_count_;
+    if (have_backup_) {
+      /// If we have a backup slice, we should use it first
+      slice_ = backup_slice_;
+      have_backup_ = false;
+      if (GRPC_SLICE_LENGTH(slice_) > remain) {
+        GRPC_SLICE_SET_LENGTH(slice_, remain);
+      }
+    } else {
+      // When less than a whole block is needed, only allocate that much.
+      // But make sure the allocated slice is not inlined.
+      size_t allocate_length =
+          remain > static_cast<size_t>(block_size_) ? block_size_ : remain;
+      slice_ = g_core_codegen_interface->grpc_slice_malloc(
+          allocate_length > GRPC_SLICE_INLINED_SIZE
+              ? allocate_length
+              : GRPC_SLICE_INLINED_SIZE + 1);
+    }
+    *data = GRPC_SLICE_START_PTR(slice_);
+    // On win x64, int is only 32bit
+    GPR_CODEGEN_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX);
+    byte_count_ += * size = (int)GRPC_SLICE_LENGTH(slice_);
+    g_core_codegen_interface->grpc_slice_buffer_add(slice_buffer_, slice_);
+    return true;
+  }
+
+  /// Backup by \a count bytes because Next returned more bytes than needed
+  /// (only used in the last buffer). \a count must be less than or equal too
+  /// the last buffer returned from next.
+  void BackUp(int count) override {
+    /// 1. Remove the partially-used last slice from the slice buffer
+    /// 2. Split it into the needed (if any) and unneeded part
+    /// 3. Add the needed part back to the slice buffer
+    /// 4. Mark that we still have the remaining part (for later use/unref)
+    GPR_CODEGEN_ASSERT(count <= static_cast<int>(GRPC_SLICE_LENGTH(slice_)));
+    g_core_codegen_interface->grpc_slice_buffer_pop(slice_buffer_);
+    if ((size_t)count == GRPC_SLICE_LENGTH(slice_)) {
+      backup_slice_ = slice_;
+    } else {
+      backup_slice_ = g_core_codegen_interface->grpc_slice_split_tail(
+          &slice_, GRPC_SLICE_LENGTH(slice_) - count);
+      g_core_codegen_interface->grpc_slice_buffer_add(slice_buffer_, slice_);
+    }
+    // It's dangerous to keep an inlined grpc_slice as the backup slice, since
+    // on a following Next() call, a reference will be returned to this slice
+    // via GRPC_SLICE_START_PTR, which will not be an address held by
+    // slice_buffer_.
+    have_backup_ = backup_slice_.refcount != NULL;
+    byte_count_ -= count;
+  }
+
+  /// Returns the total number of bytes written since this object was created.
+  grpc::protobuf::int64 ByteCount() const override { return byte_count_; }
+
+  // These protected members are needed to support internal optimizations.
+  // they expose internal bits of grpc core that are NOT stable. If you have
+  // a use case needs to use one of these functions, please send an email to
+  // https://groups.google.com/forum/#!forum/grpc-io.
+ protected:
+  grpc_slice_buffer* slice_buffer() { return slice_buffer_; }
+  void set_byte_count(int64_t byte_count) { byte_count_ = byte_count; }
+
+ private:
+  // friend for testing purposes only
+  friend class internal::ProtoBufferWriterPeer;
+  const int block_size_;  ///< size to alloc for each new \a grpc_slice needed
+  const int total_size_;  ///< byte size of proto being serialized
+  int64_t byte_count_;    ///< bytes written since this object was created
+  grpc_slice_buffer*
+      slice_buffer_;  ///< internal buffer of slices holding the serialized data
+  bool have_backup_;  ///< if we are holding a backup slice or not
+  grpc_slice backup_slice_;  ///< holds space we can still write to, if the
+                             ///< caller has called BackUp
+  grpc_slice slice_;         ///< current slice passed back to the caller
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_WRITER_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/proto_utils.h b/third_party/grpc/include/grpcpp/impl/codegen/proto_utils.h
new file mode 100644
index 0000000..d9db6de
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/proto_utils.h
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H
+#define GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H
+
+#include <type_traits>
+
+#include <grpc/impl/codegen/byte_buffer_reader.h>
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/config_protobuf.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/proto_buffer_reader.h>
+#include <grpcpp/impl/codegen/proto_buffer_writer.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/impl/codegen/status.h>
+
+/// This header provides serialization and deserialization between gRPC
+/// messages serialized using protobuf and the C++ objects they represent.
+
+namespace grpc {
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+// ProtoBufferWriter must be a subclass of ::protobuf::io::ZeroCopyOutputStream.
+template <class ProtoBufferWriter, class T>
+Status GenericSerialize(const grpc::protobuf::Message& msg, ByteBuffer* bb,
+                        bool* own_buffer) {
+  static_assert(std::is_base_of<protobuf::io::ZeroCopyOutputStream,
+                                ProtoBufferWriter>::value,
+                "ProtoBufferWriter must be a subclass of "
+                "::protobuf::io::ZeroCopyOutputStream");
+  *own_buffer = true;
+  int byte_size = msg.ByteSize();
+  if ((size_t)byte_size <= GRPC_SLICE_INLINED_SIZE) {
+    Slice slice(byte_size);
+    // We serialize directly into the allocated slices memory
+    GPR_CODEGEN_ASSERT(slice.end() == msg.SerializeWithCachedSizesToArray(
+                                          const_cast<uint8_t*>(slice.begin())));
+    ByteBuffer tmp(&slice, 1);
+    bb->Swap(&tmp);
+
+    return g_core_codegen_interface->ok();
+  }
+  ProtoBufferWriter writer(bb, kProtoBufferWriterMaxBufferLength, byte_size);
+  return msg.SerializeToZeroCopyStream(&writer)
+             ? g_core_codegen_interface->ok()
+             : Status(StatusCode::INTERNAL, "Failed to serialize message");
+}
+
+// BufferReader must be a subclass of ::protobuf::io::ZeroCopyInputStream.
+template <class ProtoBufferReader, class T>
+Status GenericDeserialize(ByteBuffer* buffer, grpc::protobuf::Message* msg) {
+  static_assert(std::is_base_of<protobuf::io::ZeroCopyInputStream,
+                                ProtoBufferReader>::value,
+                "ProtoBufferReader must be a subclass of "
+                "::protobuf::io::ZeroCopyInputStream");
+  if (buffer == nullptr) {
+    return Status(StatusCode::INTERNAL, "No payload");
+  }
+  Status result = g_core_codegen_interface->ok();
+  {
+    ProtoBufferReader reader(buffer);
+    if (!reader.status().ok()) {
+      return reader.status();
+    }
+    ::grpc::protobuf::io::CodedInputStream decoder(&reader);
+    decoder.SetTotalBytesLimit(INT_MAX, INT_MAX);
+    if (!msg->ParseFromCodedStream(&decoder)) {
+      result = Status(StatusCode::INTERNAL, msg->InitializationErrorString());
+    }
+    if (!decoder.ConsumedEntireMessage()) {
+      result = Status(StatusCode::INTERNAL, "Did not read entire message");
+    }
+  }
+  buffer->Clear();
+  return result;
+}
+
+// this is needed so the following class does not conflict with protobuf
+// serializers that utilize internal-only tools.
+#ifdef GRPC_OPEN_SOURCE_PROTO
+// This class provides a protobuf serializer. It translates between protobuf
+// objects and grpc_byte_buffers. More information about SerializationTraits can
+// be found in include/grpcpp/impl/codegen/serialization_traits.h.
+template <class T>
+class SerializationTraits<T, typename std::enable_if<std::is_base_of<
+                                 grpc::protobuf::Message, T>::value>::type> {
+ public:
+  static Status Serialize(const grpc::protobuf::Message& msg, ByteBuffer* bb,
+                          bool* own_buffer) {
+    return GenericSerialize<ProtoBufferWriter, T>(msg, bb, own_buffer);
+  }
+
+  static Status Deserialize(ByteBuffer* buffer, grpc::protobuf::Message* msg) {
+    return GenericDeserialize<ProtoBufferReader, T>(buffer, msg);
+  }
+};
+#endif
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/rpc_method.h b/third_party/grpc/include/grpcpp/impl/codegen/rpc_method.h
new file mode 100644
index 0000000..9dcde95
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/rpc_method.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_RPC_METHOD_H
+#define GRPCPP_IMPL_CODEGEN_RPC_METHOD_H
+
+#include <memory>
+
+#include <grpcpp/impl/codegen/channel_interface.h>
+
+namespace grpc {
+namespace internal {
+/// Descriptor of an RPC method
+class RpcMethod {
+ public:
+  enum RpcType {
+    NORMAL_RPC = 0,
+    CLIENT_STREAMING,  // request streaming
+    SERVER_STREAMING,  // response streaming
+    BIDI_STREAMING
+  };
+
+  RpcMethod(const char* name, RpcType type)
+      : name_(name), method_type_(type), channel_tag_(NULL) {}
+
+  RpcMethod(const char* name, RpcType type,
+            const std::shared_ptr<ChannelInterface>& channel)
+      : name_(name),
+        method_type_(type),
+        channel_tag_(channel->RegisterMethod(name)) {}
+
+  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_;
+  RpcType method_type_;
+  void* const channel_tag_;
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_RPC_METHOD_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/rpc_service_method.h b/third_party/grpc/include/grpcpp/impl/codegen/rpc_service_method.h
new file mode 100644
index 0000000..f465c5f
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/rpc_service_method.h
@@ -0,0 +1,150 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
+#define GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
+
+#include <climits>
+#include <functional>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpc/impl/codegen/log.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+class ServerContext;
+
+namespace internal {
+/// Base class for running an RPC handler.
+class MethodHandler {
+ public:
+  virtual ~MethodHandler() {}
+  struct HandlerParameter {
+    /// Constructor for HandlerParameter
+    ///
+    /// \param c : the gRPC Call structure for this server call
+    /// \param context : the ServerContext structure for this server call
+    /// \param req : the request payload, if appropriate for this RPC
+    /// \param req_status : the request status after any interceptors have run
+    /// \param rpc_requester : used only by the callback API. It is a function
+    ///        called by the RPC Controller to request another RPC (and also
+    ///        to set up the state required to make that request possible)
+    HandlerParameter(Call* c, ServerContext* context, void* req,
+                     Status req_status, std::function<void()> requester)
+        : call(c),
+          server_context(context),
+          request(req),
+          status(req_status),
+          call_requester(std::move(requester)) {}
+    ~HandlerParameter() {}
+    Call* call;
+    ServerContext* server_context;
+    void* request;
+    Status status;
+    std::function<void()> call_requester;
+  };
+  virtual void RunHandler(const HandlerParameter& param) = 0;
+
+  /* Returns a pointer to the deserialized request. \a status reflects the
+     result of deserialization. This pointer and the status should be filled in
+     a HandlerParameter and passed to RunHandler. It is illegal to access the
+     pointer after calling RunHandler. Ownership of the deserialized request is
+     retained by the handler. Returns nullptr if deserialization failed. */
+  virtual void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+                            Status* status) {
+    GPR_CODEGEN_ASSERT(req == nullptr);
+    return nullptr;
+  }
+};
+
+/// Server side rpc method class
+class RpcServiceMethod : public RpcMethod {
+ public:
+  /// Takes ownership of the handler
+  RpcServiceMethod(const char* name, RpcMethod::RpcType type,
+                   MethodHandler* handler)
+      : RpcMethod(name, type),
+        server_tag_(nullptr),
+        api_type_(ApiType::SYNC),
+        handler_(handler) {}
+
+  enum class ApiType {
+    SYNC,
+    ASYNC,
+    RAW,
+    CALL_BACK,  // not CALLBACK because that is reserved in Windows
+    RAW_CALL_BACK,
+  };
+
+  void set_server_tag(void* tag) { server_tag_ = tag; }
+  void* server_tag() const { return server_tag_; }
+  /// if MethodHandler is nullptr, then this is an async method
+  MethodHandler* handler() const { return handler_.get(); }
+  ApiType api_type() const { return api_type_; }
+  void SetHandler(MethodHandler* handler) { handler_.reset(handler); }
+  void SetServerApiType(RpcServiceMethod::ApiType type) {
+    if ((api_type_ == ApiType::SYNC) &&
+        (type == ApiType::ASYNC || type == ApiType::RAW)) {
+      // this marks this method as async
+      handler_.reset();
+    } else if (api_type_ != ApiType::SYNC) {
+      // this is not an error condition, as it allows users to declare a server
+      // like WithRawMethod_foo<AsyncService>. However since it
+      // overwrites behavior, it should be logged.
+      gpr_log(
+          GPR_INFO,
+          "You are marking method %s as '%s', even though it was "
+          "previously marked '%s'. This behavior will overwrite the original "
+          "behavior. If you expected this then ignore this message.",
+          name(), TypeToString(api_type_), TypeToString(type));
+    }
+    api_type_ = type;
+  }
+
+ private:
+  void* server_tag_;
+  ApiType api_type_;
+  std::unique_ptr<MethodHandler> handler_;
+
+  const char* TypeToString(RpcServiceMethod::ApiType type) {
+    switch (type) {
+      case ApiType::SYNC:
+        return "sync";
+      case ApiType::ASYNC:
+        return "async";
+      case ApiType::RAW:
+        return "raw";
+      case ApiType::CALL_BACK:
+        return "callback";
+      case ApiType::RAW_CALL_BACK:
+        return "raw_callback";
+      default:
+        GPR_UNREACHABLE_CODE(return "unknown");
+    }
+  }
+};
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/security/auth_context.h b/third_party/grpc/include/grpcpp/impl/codegen/security/auth_context.h
new file mode 100644
index 0000000..0e30f7c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/security/auth_context.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
+#define GRPCPP_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
+
+#include <iterator>
+#include <vector>
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+struct grpc_auth_context;
+struct grpc_auth_property;
+struct grpc_auth_property_iterator;
+
+namespace grpc {
+class SecureAuthContext;
+
+typedef std::pair<grpc::string_ref, grpc::string_ref> AuthProperty;
+
+class AuthPropertyIterator
+    : public std::iterator<std::input_iterator_tag, const AuthProperty> {
+ public:
+  ~AuthPropertyIterator();
+  AuthPropertyIterator& operator++();
+  AuthPropertyIterator operator++(int);
+  bool operator==(const AuthPropertyIterator& rhs) const;
+  bool operator!=(const AuthPropertyIterator& rhs) const;
+  const AuthProperty operator*();
+
+ protected:
+  AuthPropertyIterator();
+  AuthPropertyIterator(const grpc_auth_property* property,
+                       const grpc_auth_property_iterator* iter);
+
+ private:
+  friend class SecureAuthContext;
+  const grpc_auth_property* property_;
+  // The following items form a grpc_auth_property_iterator.
+  const grpc_auth_context* ctx_;
+  size_t index_;
+  const char* name_;
+};
+
+/// Class encapsulating the Authentication Information.
+///
+/// It includes the secure identity of the peer, the type of secure transport
+/// used as well as any other properties required by the authorization layer.
+class AuthContext {
+ public:
+  virtual ~AuthContext() {}
+
+  /// Returns true if the peer is authenticated.
+  virtual bool IsPeerAuthenticated() const = 0;
+
+  /// A peer identity.
+  ///
+  /// It is, in general, comprised of one or more properties (in which case they
+  /// have the same name).
+  virtual std::vector<grpc::string_ref> GetPeerIdentity() const = 0;
+  virtual grpc::string GetPeerIdentityPropertyName() const = 0;
+
+  /// Returns all the property values with the given name.
+  virtual std::vector<grpc::string_ref> FindPropertyValues(
+      const grpc::string& name) const = 0;
+
+  /// Iteration over all the properties.
+  virtual AuthPropertyIterator begin() const = 0;
+  virtual AuthPropertyIterator end() const = 0;
+
+  /// Mutation functions: should only be used by an AuthMetadataProcessor.
+  virtual void AddProperty(const grpc::string& key,
+                           const grpc::string_ref& value) = 0;
+  virtual bool SetPeerIdentityPropertyName(const grpc::string& name) = 0;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/serialization_traits.h b/third_party/grpc/include/grpcpp/impl/codegen/serialization_traits.h
new file mode 100644
index 0000000..8f792232
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/serialization_traits.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
+#define GRPCPP_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
+
+namespace grpc {
+
+/// Defines how to serialize and deserialize some type.
+///
+/// Used for hooking different message serialization API's into GRPC.
+/// Each SerializationTraits<Message> implementation must provide the
+/// following functions:
+/// 1.  static Status Serialize(const Message& msg,
+///                             ByteBuffer* buffer,
+///                             bool* own_buffer);
+///     OR
+///     static Status Serialize(const Message& msg,
+///                             grpc_byte_buffer** buffer,
+///                             bool* own_buffer);
+///     The former is preferred; the latter is deprecated
+///
+/// 2.  static Status Deserialize(ByteBuffer* buffer,
+///                               Message* msg);
+///     OR
+///     static Status Deserialize(grpc_byte_buffer* buffer,
+///                               Message* msg);
+///     The former is preferred; the latter is deprecated
+///
+/// Serialize is required to convert message to a ByteBuffer, and
+/// return that byte buffer through *buffer. *own_buffer should
+/// be set to true if the caller owns said byte buffer, or false if
+/// ownership is retained elsewhere.
+///
+/// Deserialize is required to convert buffer into the message stored at
+/// 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.
+template <class Message,
+          class UnusedButHereForPartialTemplateSpecialization = void>
+class SerializationTraits;
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERIALIZATION_TRAITS_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/server_callback.h b/third_party/grpc/include/grpcpp/impl/codegen/server_callback.h
new file mode 100644
index 0000000..1854f6e
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/server_callback.h
@@ -0,0 +1,890 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H
+#define GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H
+
+#include <atomic>
+#include <functional>
+#include <type_traits>
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_op_set.h>
+#include <grpcpp/impl/codegen/callback_common.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/codegen/server_interface.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+// Declare base class of all reactors as internal
+namespace internal {
+
+class ServerReactor {
+ public:
+  virtual ~ServerReactor() = default;
+  virtual void OnDone() {}
+  virtual void OnCancel() {}
+};
+
+}  // namespace internal
+
+namespace experimental {
+
+// Forward declarations
+template <class Request, class Response>
+class ServerReadReactor;
+template <class Request, class Response>
+class ServerWriteReactor;
+template <class Request, class Response>
+class ServerBidiReactor;
+
+// For unary RPCs, the exposed controller class is only an interface
+// and the actual implementation is an internal class.
+class ServerCallbackRpcController {
+ public:
+  virtual ~ServerCallbackRpcController() = default;
+
+  // The method handler must call this function when it is done so that
+  // the library knows to free its resources
+  virtual void Finish(Status s) = 0;
+
+  // Allow the method handler to push out the initial metadata before
+  // the response and status are ready
+  virtual void SendInitialMetadata(std::function<void(bool)>) = 0;
+};
+
+// NOTE: The actual streaming object classes are provided
+// as API only to support mocking. There are no implementations of
+// these class interfaces in the API.
+template <class Request>
+class ServerCallbackReader {
+ public:
+  virtual ~ServerCallbackReader() {}
+  virtual void Finish(Status s) = 0;
+  virtual void SendInitialMetadata() = 0;
+  virtual void Read(Request* msg) = 0;
+
+ protected:
+  template <class Response>
+  void BindReactor(ServerReadReactor<Request, Response>* reactor) {
+    reactor->BindReader(this);
+  }
+};
+
+template <class Response>
+class ServerCallbackWriter {
+ public:
+  virtual ~ServerCallbackWriter() {}
+
+  virtual void Finish(Status s) = 0;
+  virtual void SendInitialMetadata() = 0;
+  virtual void Write(const Response* msg, WriteOptions options) = 0;
+  virtual void WriteAndFinish(const Response* msg, WriteOptions options,
+                              Status s) {
+    // Default implementation that can/should be overridden
+    Write(msg, std::move(options));
+    Finish(std::move(s));
+  };
+
+ protected:
+  template <class Request>
+  void BindReactor(ServerWriteReactor<Request, Response>* reactor) {
+    reactor->BindWriter(this);
+  }
+};
+
+template <class Request, class Response>
+class ServerCallbackReaderWriter {
+ public:
+  virtual ~ServerCallbackReaderWriter() {}
+
+  virtual void Finish(Status s) = 0;
+  virtual void SendInitialMetadata() = 0;
+  virtual void Read(Request* msg) = 0;
+  virtual void Write(const Response* msg, WriteOptions options) = 0;
+  virtual void WriteAndFinish(const Response* msg, WriteOptions options,
+                              Status s) {
+    // Default implementation that can/should be overridden
+    Write(msg, std::move(options));
+    Finish(std::move(s));
+  };
+
+ protected:
+  void BindReactor(ServerBidiReactor<Request, Response>* reactor) {
+    reactor->BindStream(this);
+  }
+};
+
+// The following classes are reactors that are to be implemented
+// by the user, returned as the result of the method handler for
+// a callback method, and activated by the call to OnStarted
+template <class Request, class Response>
+class ServerBidiReactor : public internal::ServerReactor {
+ public:
+  ~ServerBidiReactor() = default;
+  virtual void OnStarted(ServerContext*) {}
+  virtual void OnSendInitialMetadataDone(bool ok) {}
+  virtual void OnReadDone(bool ok) {}
+  virtual void OnWriteDone(bool ok) {}
+
+  void StartSendInitialMetadata() { stream_->SendInitialMetadata(); }
+  void StartRead(Request* msg) { stream_->Read(msg); }
+  void StartWrite(const Response* msg) { StartWrite(msg, WriteOptions()); }
+  void StartWrite(const Response* msg, WriteOptions options) {
+    stream_->Write(msg, std::move(options));
+  }
+  void StartWriteAndFinish(const Response* msg, WriteOptions options,
+                           Status s) {
+    stream_->WriteAndFinish(msg, std::move(options), std::move(s));
+  }
+  void StartWriteLast(const Response* msg, WriteOptions options) {
+    StartWrite(msg, std::move(options.set_last_message()));
+  }
+  void Finish(Status s) { stream_->Finish(std::move(s)); }
+
+ private:
+  friend class ServerCallbackReaderWriter<Request, Response>;
+  void BindStream(ServerCallbackReaderWriter<Request, Response>* stream) {
+    stream_ = stream;
+  }
+
+  ServerCallbackReaderWriter<Request, Response>* stream_;
+};
+
+template <class Request, class Response>
+class ServerReadReactor : public internal::ServerReactor {
+ public:
+  ~ServerReadReactor() = default;
+  virtual void OnStarted(ServerContext*, Response* resp) {}
+  virtual void OnSendInitialMetadataDone(bool ok) {}
+  virtual void OnReadDone(bool ok) {}
+
+  void StartSendInitialMetadata() { reader_->SendInitialMetadata(); }
+  void StartRead(Request* msg) { reader_->Read(msg); }
+  void Finish(Status s) { reader_->Finish(std::move(s)); }
+
+ private:
+  friend class ServerCallbackReader<Request>;
+  void BindReader(ServerCallbackReader<Request>* reader) { reader_ = reader; }
+
+  ServerCallbackReader<Request>* reader_;
+};
+
+template <class Request, class Response>
+class ServerWriteReactor : public internal::ServerReactor {
+ public:
+  ~ServerWriteReactor() = default;
+  virtual void OnStarted(ServerContext*, const Request* req) {}
+  virtual void OnSendInitialMetadataDone(bool ok) {}
+  virtual void OnWriteDone(bool ok) {}
+
+  void StartSendInitialMetadata() { writer_->SendInitialMetadata(); }
+  void StartWrite(const Response* msg) { StartWrite(msg, WriteOptions()); }
+  void StartWrite(const Response* msg, WriteOptions options) {
+    writer_->Write(msg, std::move(options));
+  }
+  void StartWriteAndFinish(const Response* msg, WriteOptions options,
+                           Status s) {
+    writer_->WriteAndFinish(msg, std::move(options), std::move(s));
+  }
+  void StartWriteLast(const Response* msg, WriteOptions options) {
+    StartWrite(msg, std::move(options.set_last_message()));
+  }
+  void Finish(Status s) { writer_->Finish(std::move(s)); }
+
+ private:
+  friend class ServerCallbackWriter<Response>;
+  void BindWriter(ServerCallbackWriter<Response>* writer) { writer_ = writer; }
+
+  ServerCallbackWriter<Response>* writer_;
+};
+
+}  // namespace experimental
+
+namespace internal {
+
+template <class Request, class Response>
+class UnimplementedReadReactor
+    : public experimental::ServerReadReactor<Request, Response> {
+ public:
+  void OnDone() override { delete this; }
+  void OnStarted(ServerContext*, Response*) override {
+    this->Finish(Status(StatusCode::UNIMPLEMENTED, ""));
+  }
+};
+
+template <class Request, class Response>
+class UnimplementedWriteReactor
+    : public experimental::ServerWriteReactor<Request, Response> {
+ public:
+  void OnDone() override { delete this; }
+  void OnStarted(ServerContext*, const Request*) override {
+    this->Finish(Status(StatusCode::UNIMPLEMENTED, ""));
+  }
+};
+
+template <class Request, class Response>
+class UnimplementedBidiReactor
+    : public experimental::ServerBidiReactor<Request, Response> {
+ public:
+  void OnDone() override { delete this; }
+  void OnStarted(ServerContext*) override {
+    this->Finish(Status(StatusCode::UNIMPLEMENTED, ""));
+  }
+};
+
+template <class RequestType, class ResponseType>
+class CallbackUnaryHandler : public MethodHandler {
+ public:
+  CallbackUnaryHandler(
+      std::function<void(ServerContext*, const RequestType*, ResponseType*,
+                         experimental::ServerCallbackRpcController*)>
+          func)
+      : func_(func) {}
+  void RunHandler(const HandlerParameter& param) final {
+    // Arena allocate a controller structure (that includes request/response)
+    g_core_codegen_interface->grpc_call_ref(param.call->call());
+    auto* controller = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        param.call->call(), sizeof(ServerCallbackRpcControllerImpl)))
+        ServerCallbackRpcControllerImpl(
+            param.server_context, param.call,
+            static_cast<RequestType*>(param.request),
+            std::move(param.call_requester));
+    Status status = param.status;
+
+    if (status.ok()) {
+      // Call the actual function handler and expect the user to call finish
+      CatchingCallback(func_, param.server_context, controller->request(),
+                       controller->response(), controller);
+    } else {
+      // if deserialization failed, we need to fail the call
+      controller->Finish(status);
+    }
+  }
+
+  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+                    Status* status) final {
+    ByteBuffer buf;
+    buf.set_buffer(req);
+    auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call, sizeof(RequestType))) RequestType();
+    *status = SerializationTraits<RequestType>::Deserialize(&buf, request);
+    buf.Release();
+    if (status->ok()) {
+      return request;
+    }
+    request->~RequestType();
+    return nullptr;
+  }
+
+ private:
+  std::function<void(ServerContext*, const RequestType*, ResponseType*,
+                     experimental::ServerCallbackRpcController*)>
+      func_;
+
+  // The implementation class of ServerCallbackRpcController is a private member
+  // of CallbackUnaryHandler since it is never exposed anywhere, and this allows
+  // it to take advantage of CallbackUnaryHandler's friendships.
+  class ServerCallbackRpcControllerImpl
+      : public experimental::ServerCallbackRpcController {
+   public:
+    void Finish(Status s) override {
+      finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); },
+                      &finish_ops_);
+      if (!ctx_->sent_initial_metadata_) {
+        finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                        ctx_->initial_metadata_flags());
+        if (ctx_->compression_level_set()) {
+          finish_ops_.set_compression_level(ctx_->compression_level());
+        }
+        ctx_->sent_initial_metadata_ = true;
+      }
+      // The response is dropped if the status is not OK.
+      if (s.ok()) {
+        finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_,
+                                     finish_ops_.SendMessage(resp_));
+      } else {
+        finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
+      }
+      finish_ops_.set_core_cq_tag(&finish_tag_);
+      call_.PerformOps(&finish_ops_);
+    }
+
+    void SendInitialMetadata(std::function<void(bool)> f) override {
+      GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+      callbacks_outstanding_++;
+      // TODO(vjpai): Consider taking f as a move-capture if we adopt C++14
+      //              and if performance of this operation matters
+      meta_tag_.Set(call_.call(),
+                    [this, f](bool ok) {
+                      f(ok);
+                      MaybeDone();
+                    },
+                    &meta_ops_);
+      meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                    ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        meta_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+      meta_ops_.set_core_cq_tag(&meta_tag_);
+      call_.PerformOps(&meta_ops_);
+    }
+
+   private:
+    friend class CallbackUnaryHandler<RequestType, ResponseType>;
+
+    ServerCallbackRpcControllerImpl(ServerContext* ctx, Call* call,
+                                    const RequestType* req,
+                                    std::function<void()> call_requester)
+        : ctx_(ctx),
+          call_(*call),
+          req_(req),
+          call_requester_(std::move(call_requester)) {
+      ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, nullptr);
+    }
+
+    ~ServerCallbackRpcControllerImpl() { req_->~RequestType(); }
+
+    const RequestType* request() { return req_; }
+    ResponseType* response() { return &resp_; }
+
+    void MaybeDone() {
+      if (--callbacks_outstanding_ == 0) {
+        grpc_call* call = call_.call();
+        auto call_requester = std::move(call_requester_);
+        this->~ServerCallbackRpcControllerImpl();  // explicitly call destructor
+        g_core_codegen_interface->grpc_call_unref(call);
+        call_requester();
+      }
+    }
+
+    CallOpSet<CallOpSendInitialMetadata> meta_ops_;
+    CallbackWithSuccessTag meta_tag_;
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpServerSendStatus>
+        finish_ops_;
+    CallbackWithSuccessTag finish_tag_;
+
+    ServerContext* ctx_;
+    Call call_;
+    const RequestType* req_;
+    ResponseType resp_;
+    std::function<void()> call_requester_;
+    std::atomic_int callbacks_outstanding_{
+        2};  // reserve for Finish and CompletionOp
+  };
+};
+
+template <class RequestType, class ResponseType>
+class CallbackClientStreamingHandler : public MethodHandler {
+ public:
+  CallbackClientStreamingHandler(
+      std::function<
+          experimental::ServerReadReactor<RequestType, ResponseType>*()>
+          func)
+      : func_(std::move(func)) {}
+  void RunHandler(const HandlerParameter& param) final {
+    // Arena allocate a reader structure (that includes response)
+    g_core_codegen_interface->grpc_call_ref(param.call->call());
+
+    experimental::ServerReadReactor<RequestType, ResponseType>* reactor =
+        param.status.ok()
+            ? CatchingReactorCreator<
+                  experimental::ServerReadReactor<RequestType, ResponseType>>(
+                  func_)
+            : nullptr;
+
+    if (reactor == nullptr) {
+      // if deserialization or reactor creator failed, we need to fail the call
+      reactor = new UnimplementedReadReactor<RequestType, ResponseType>;
+    }
+
+    auto* reader = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        param.call->call(), sizeof(ServerCallbackReaderImpl)))
+        ServerCallbackReaderImpl(param.server_context, param.call,
+                                 std::move(param.call_requester), reactor);
+
+    reader->BindReactor(reactor);
+    reactor->OnStarted(param.server_context, reader->response());
+    reader->MaybeDone();
+  }
+
+ private:
+  std::function<experimental::ServerReadReactor<RequestType, ResponseType>*()>
+      func_;
+
+  class ServerCallbackReaderImpl
+      : public experimental::ServerCallbackReader<RequestType> {
+   public:
+    void Finish(Status s) override {
+      finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); },
+                      &finish_ops_);
+      if (!ctx_->sent_initial_metadata_) {
+        finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                        ctx_->initial_metadata_flags());
+        if (ctx_->compression_level_set()) {
+          finish_ops_.set_compression_level(ctx_->compression_level());
+        }
+        ctx_->sent_initial_metadata_ = true;
+      }
+      // The response is dropped if the status is not OK.
+      if (s.ok()) {
+        finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_,
+                                     finish_ops_.SendMessage(resp_));
+      } else {
+        finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
+      }
+      finish_ops_.set_core_cq_tag(&finish_tag_);
+      call_.PerformOps(&finish_ops_);
+    }
+
+    void SendInitialMetadata() override {
+      GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+      callbacks_outstanding_++;
+      meta_tag_.Set(call_.call(),
+                    [this](bool ok) {
+                      reactor_->OnSendInitialMetadataDone(ok);
+                      MaybeDone();
+                    },
+                    &meta_ops_);
+      meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                    ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        meta_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+      meta_ops_.set_core_cq_tag(&meta_tag_);
+      call_.PerformOps(&meta_ops_);
+    }
+
+    void Read(RequestType* req) override {
+      callbacks_outstanding_++;
+      read_ops_.RecvMessage(req);
+      call_.PerformOps(&read_ops_);
+    }
+
+   private:
+    friend class CallbackClientStreamingHandler<RequestType, ResponseType>;
+
+    ServerCallbackReaderImpl(
+        ServerContext* ctx, Call* call, std::function<void()> call_requester,
+        experimental::ServerReadReactor<RequestType, ResponseType>* reactor)
+        : ctx_(ctx),
+          call_(*call),
+          call_requester_(std::move(call_requester)),
+          reactor_(reactor) {
+      ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor);
+      read_tag_.Set(call_.call(),
+                    [this](bool ok) {
+                      reactor_->OnReadDone(ok);
+                      MaybeDone();
+                    },
+                    &read_ops_);
+      read_ops_.set_core_cq_tag(&read_tag_);
+    }
+
+    ~ServerCallbackReaderImpl() {}
+
+    ResponseType* response() { return &resp_; }
+
+    void MaybeDone() {
+      if (--callbacks_outstanding_ == 0) {
+        reactor_->OnDone();
+        grpc_call* call = call_.call();
+        auto call_requester = std::move(call_requester_);
+        this->~ServerCallbackReaderImpl();  // explicitly call destructor
+        g_core_codegen_interface->grpc_call_unref(call);
+        call_requester();
+      }
+    }
+
+    CallOpSet<CallOpSendInitialMetadata> meta_ops_;
+    CallbackWithSuccessTag meta_tag_;
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpServerSendStatus>
+        finish_ops_;
+    CallbackWithSuccessTag finish_tag_;
+    CallOpSet<CallOpRecvMessage<RequestType>> read_ops_;
+    CallbackWithSuccessTag read_tag_;
+
+    ServerContext* ctx_;
+    Call call_;
+    ResponseType resp_;
+    std::function<void()> call_requester_;
+    experimental::ServerReadReactor<RequestType, ResponseType>* reactor_;
+    std::atomic_int callbacks_outstanding_{
+        3};  // reserve for OnStarted, Finish, and CompletionOp
+  };
+};
+
+template <class RequestType, class ResponseType>
+class CallbackServerStreamingHandler : public MethodHandler {
+ public:
+  CallbackServerStreamingHandler(
+      std::function<
+          experimental::ServerWriteReactor<RequestType, ResponseType>*()>
+          func)
+      : func_(std::move(func)) {}
+  void RunHandler(const HandlerParameter& param) final {
+    // Arena allocate a writer structure
+    g_core_codegen_interface->grpc_call_ref(param.call->call());
+
+    experimental::ServerWriteReactor<RequestType, ResponseType>* reactor =
+        param.status.ok()
+            ? CatchingReactorCreator<
+                  experimental::ServerWriteReactor<RequestType, ResponseType>>(
+                  func_)
+            : nullptr;
+
+    if (reactor == nullptr) {
+      // if deserialization or reactor creator failed, we need to fail the call
+      reactor = new UnimplementedWriteReactor<RequestType, ResponseType>;
+    }
+
+    auto* writer = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        param.call->call(), sizeof(ServerCallbackWriterImpl)))
+        ServerCallbackWriterImpl(param.server_context, param.call,
+                                 static_cast<RequestType*>(param.request),
+                                 std::move(param.call_requester), reactor);
+    writer->BindReactor(reactor);
+    reactor->OnStarted(param.server_context, writer->request());
+    writer->MaybeDone();
+  }
+
+  void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
+                    Status* status) final {
+    ByteBuffer buf;
+    buf.set_buffer(req);
+    auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        call, sizeof(RequestType))) RequestType();
+    *status = SerializationTraits<RequestType>::Deserialize(&buf, request);
+    buf.Release();
+    if (status->ok()) {
+      return request;
+    }
+    request->~RequestType();
+    return nullptr;
+  }
+
+ private:
+  std::function<experimental::ServerWriteReactor<RequestType, ResponseType>*()>
+      func_;
+
+  class ServerCallbackWriterImpl
+      : public experimental::ServerCallbackWriter<ResponseType> {
+   public:
+    void Finish(Status s) override {
+      finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); },
+                      &finish_ops_);
+      finish_ops_.set_core_cq_tag(&finish_tag_);
+
+      if (!ctx_->sent_initial_metadata_) {
+        finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                        ctx_->initial_metadata_flags());
+        if (ctx_->compression_level_set()) {
+          finish_ops_.set_compression_level(ctx_->compression_level());
+        }
+        ctx_->sent_initial_metadata_ = true;
+      }
+      finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
+      call_.PerformOps(&finish_ops_);
+    }
+
+    void SendInitialMetadata() override {
+      GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+      callbacks_outstanding_++;
+      meta_tag_.Set(call_.call(),
+                    [this](bool ok) {
+                      reactor_->OnSendInitialMetadataDone(ok);
+                      MaybeDone();
+                    },
+                    &meta_ops_);
+      meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                    ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        meta_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+      meta_ops_.set_core_cq_tag(&meta_tag_);
+      call_.PerformOps(&meta_ops_);
+    }
+
+    void Write(const ResponseType* resp, WriteOptions options) override {
+      callbacks_outstanding_++;
+      if (options.is_last_message()) {
+        options.set_buffer_hint();
+      }
+      if (!ctx_->sent_initial_metadata_) {
+        write_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                       ctx_->initial_metadata_flags());
+        if (ctx_->compression_level_set()) {
+          write_ops_.set_compression_level(ctx_->compression_level());
+        }
+        ctx_->sent_initial_metadata_ = true;
+      }
+      // TODO(vjpai): don't assert
+      GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*resp, options).ok());
+      call_.PerformOps(&write_ops_);
+    }
+
+    void WriteAndFinish(const ResponseType* resp, WriteOptions options,
+                        Status s) override {
+      // This combines the write into the finish callback
+      // Don't send any message if the status is bad
+      if (s.ok()) {
+        // TODO(vjpai): don't assert
+        GPR_CODEGEN_ASSERT(finish_ops_.SendMessage(*resp, options).ok());
+      }
+      Finish(std::move(s));
+    }
+
+   private:
+    friend class CallbackServerStreamingHandler<RequestType, ResponseType>;
+
+    ServerCallbackWriterImpl(
+        ServerContext* ctx, Call* call, const RequestType* req,
+        std::function<void()> call_requester,
+        experimental::ServerWriteReactor<RequestType, ResponseType>* reactor)
+        : ctx_(ctx),
+          call_(*call),
+          req_(req),
+          call_requester_(std::move(call_requester)),
+          reactor_(reactor) {
+      ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor);
+      write_tag_.Set(call_.call(),
+                     [this](bool ok) {
+                       reactor_->OnWriteDone(ok);
+                       MaybeDone();
+                     },
+                     &write_ops_);
+      write_ops_.set_core_cq_tag(&write_tag_);
+    }
+    ~ServerCallbackWriterImpl() { req_->~RequestType(); }
+
+    const RequestType* request() { return req_; }
+
+    void MaybeDone() {
+      if (--callbacks_outstanding_ == 0) {
+        reactor_->OnDone();
+        grpc_call* call = call_.call();
+        auto call_requester = std::move(call_requester_);
+        this->~ServerCallbackWriterImpl();  // explicitly call destructor
+        g_core_codegen_interface->grpc_call_unref(call);
+        call_requester();
+      }
+    }
+
+    CallOpSet<CallOpSendInitialMetadata> meta_ops_;
+    CallbackWithSuccessTag meta_tag_;
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpServerSendStatus>
+        finish_ops_;
+    CallbackWithSuccessTag finish_tag_;
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> write_ops_;
+    CallbackWithSuccessTag write_tag_;
+
+    ServerContext* ctx_;
+    Call call_;
+    const RequestType* req_;
+    std::function<void()> call_requester_;
+    experimental::ServerWriteReactor<RequestType, ResponseType>* reactor_;
+    std::atomic_int callbacks_outstanding_{
+        3};  // reserve for OnStarted, Finish, and CompletionOp
+  };
+};
+
+template <class RequestType, class ResponseType>
+class CallbackBidiHandler : public MethodHandler {
+ public:
+  CallbackBidiHandler(
+      std::function<
+          experimental::ServerBidiReactor<RequestType, ResponseType>*()>
+          func)
+      : func_(std::move(func)) {}
+  void RunHandler(const HandlerParameter& param) final {
+    g_core_codegen_interface->grpc_call_ref(param.call->call());
+
+    experimental::ServerBidiReactor<RequestType, ResponseType>* reactor =
+        param.status.ok()
+            ? CatchingReactorCreator<
+                  experimental::ServerBidiReactor<RequestType, ResponseType>>(
+                  func_)
+            : nullptr;
+
+    if (reactor == nullptr) {
+      // if deserialization or reactor creator failed, we need to fail the call
+      reactor = new UnimplementedBidiReactor<RequestType, ResponseType>;
+    }
+
+    auto* stream = new (g_core_codegen_interface->grpc_call_arena_alloc(
+        param.call->call(), sizeof(ServerCallbackReaderWriterImpl)))
+        ServerCallbackReaderWriterImpl(param.server_context, param.call,
+                                       std::move(param.call_requester),
+                                       reactor);
+
+    stream->BindReactor(reactor);
+    reactor->OnStarted(param.server_context);
+    stream->MaybeDone();
+  }
+
+ private:
+  std::function<experimental::ServerBidiReactor<RequestType, ResponseType>*()>
+      func_;
+
+  class ServerCallbackReaderWriterImpl
+      : public experimental::ServerCallbackReaderWriter<RequestType,
+                                                        ResponseType> {
+   public:
+    void Finish(Status s) override {
+      finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); },
+                      &finish_ops_);
+      finish_ops_.set_core_cq_tag(&finish_tag_);
+
+      if (!ctx_->sent_initial_metadata_) {
+        finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                        ctx_->initial_metadata_flags());
+        if (ctx_->compression_level_set()) {
+          finish_ops_.set_compression_level(ctx_->compression_level());
+        }
+        ctx_->sent_initial_metadata_ = true;
+      }
+      finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s);
+      call_.PerformOps(&finish_ops_);
+    }
+
+    void SendInitialMetadata() override {
+      GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+      callbacks_outstanding_++;
+      meta_tag_.Set(call_.call(),
+                    [this](bool ok) {
+                      reactor_->OnSendInitialMetadataDone(ok);
+                      MaybeDone();
+                    },
+                    &meta_ops_);
+      meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                    ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        meta_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+      meta_ops_.set_core_cq_tag(&meta_tag_);
+      call_.PerformOps(&meta_ops_);
+    }
+
+    void Write(const ResponseType* resp, WriteOptions options) override {
+      callbacks_outstanding_++;
+      if (options.is_last_message()) {
+        options.set_buffer_hint();
+      }
+      if (!ctx_->sent_initial_metadata_) {
+        write_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                       ctx_->initial_metadata_flags());
+        if (ctx_->compression_level_set()) {
+          write_ops_.set_compression_level(ctx_->compression_level());
+        }
+        ctx_->sent_initial_metadata_ = true;
+      }
+      // TODO(vjpai): don't assert
+      GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*resp, options).ok());
+      call_.PerformOps(&write_ops_);
+    }
+
+    void WriteAndFinish(const ResponseType* resp, WriteOptions options,
+                        Status s) override {
+      // Don't send any message if the status is bad
+      if (s.ok()) {
+        // TODO(vjpai): don't assert
+        GPR_CODEGEN_ASSERT(finish_ops_.SendMessage(*resp, options).ok());
+      }
+      Finish(std::move(s));
+    }
+
+    void Read(RequestType* req) override {
+      callbacks_outstanding_++;
+      read_ops_.RecvMessage(req);
+      call_.PerformOps(&read_ops_);
+    }
+
+   private:
+    friend class CallbackBidiHandler<RequestType, ResponseType>;
+
+    ServerCallbackReaderWriterImpl(
+        ServerContext* ctx, Call* call, std::function<void()> call_requester,
+        experimental::ServerBidiReactor<RequestType, ResponseType>* reactor)
+        : ctx_(ctx),
+          call_(*call),
+          call_requester_(std::move(call_requester)),
+          reactor_(reactor) {
+      ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor);
+      write_tag_.Set(call_.call(),
+                     [this](bool ok) {
+                       reactor_->OnWriteDone(ok);
+                       MaybeDone();
+                     },
+                     &write_ops_);
+      write_ops_.set_core_cq_tag(&write_tag_);
+      read_tag_.Set(call_.call(),
+                    [this](bool ok) {
+                      reactor_->OnReadDone(ok);
+                      MaybeDone();
+                    },
+                    &read_ops_);
+      read_ops_.set_core_cq_tag(&read_tag_);
+    }
+    ~ServerCallbackReaderWriterImpl() {}
+
+    void MaybeDone() {
+      if (--callbacks_outstanding_ == 0) {
+        reactor_->OnDone();
+        grpc_call* call = call_.call();
+        auto call_requester = std::move(call_requester_);
+        this->~ServerCallbackReaderWriterImpl();  // explicitly call destructor
+        g_core_codegen_interface->grpc_call_unref(call);
+        call_requester();
+      }
+    }
+
+    CallOpSet<CallOpSendInitialMetadata> meta_ops_;
+    CallbackWithSuccessTag meta_tag_;
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
+              CallOpServerSendStatus>
+        finish_ops_;
+    CallbackWithSuccessTag finish_tag_;
+    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> write_ops_;
+    CallbackWithSuccessTag write_tag_;
+    CallOpSet<CallOpRecvMessage<RequestType>> read_ops_;
+    CallbackWithSuccessTag read_tag_;
+
+    ServerContext* ctx_;
+    Call call_;
+    std::function<void()> call_requester_;
+    experimental::ServerBidiReactor<RequestType, ResponseType>* reactor_;
+    std::atomic_int callbacks_outstanding_{
+        3};  // reserve for OnStarted, Finish, and CompletionOp
+  };
+};
+
+}  // namespace internal
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/server_context.h b/third_party/grpc/include/grpcpp/impl/codegen/server_context.h
new file mode 100644
index 0000000..affe61b
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/server_context.h
@@ -0,0 +1,370 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H
+#define GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpc/impl/codegen/compression_types.h>
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_op_set.h>
+#include <grpcpp/impl/codegen/callback_common.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/create_auth_context.h>
+#include <grpcpp/impl/codegen/metadata_map.h>
+#include <grpcpp/impl/codegen/security/auth_context.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+#include <grpcpp/impl/codegen/time.h>
+
+struct grpc_metadata;
+struct grpc_call;
+struct census_context;
+
+namespace grpc {
+class ClientContext;
+template <class W, class R>
+class ServerAsyncReader;
+template <class W>
+class ServerAsyncWriter;
+template <class W>
+class ServerAsyncResponseWriter;
+template <class W, class R>
+class ServerAsyncReaderWriter;
+template <class R>
+class ServerReader;
+template <class W>
+class ServerWriter;
+namespace internal {
+template <class W, class R>
+class ServerReaderWriterBody;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler;
+template <class RequestType, class ResponseType>
+class CallbackUnaryHandler;
+template <class RequestType, class ResponseType>
+class CallbackClientStreamingHandler;
+template <class RequestType, class ResponseType>
+class CallbackServerStreamingHandler;
+template <class RequestType, class ResponseType>
+class CallbackBidiHandler;
+template <class Streamer, bool WriteNeeded>
+class TemplatedBidiStreamingHandler;
+template <StatusCode code>
+class ErrorMethodHandler;
+class Call;
+class ServerReactor;
+}  // namespace internal
+
+class CompletionQueue;
+class Server;
+class ServerInterface;
+
+namespace testing {
+class InteropServerContextInspector;
+class ServerContextTestSpouse;
+}  // namespace testing
+
+/// A ServerContext allows the person implementing a service handler to:
+///
+/// - Add custom initial and trailing metadata key-value pairs that will
+///   propagated to the client side.
+/// - Control call settings such as compression and authentication.
+/// - Access metadata coming from the client.
+/// - Get performance metrics (ie, census).
+///
+/// Context settings are only relevant to the call handler they are supplied to,
+/// that is to say, they aren't sticky across multiple calls. Some of these
+/// settings, such as the compression options, can be made persistent at server
+/// construction time by specifying the appropriate \a ChannelArguments
+/// to a \a grpc::ServerBuilder, via \a ServerBuilder::AddChannelArgument.
+///
+/// \warning ServerContext instances should \em not be reused across rpcs.
+class ServerContext {
+ public:
+  ServerContext();  // for async calls
+  ~ServerContext();
+
+  /// Return the deadline for the server call.
+  std::chrono::system_clock::time_point deadline() const {
+    return Timespec2Timepoint(deadline_);
+  }
+
+  /// Return a \a gpr_timespec representation of the server call's deadline.
+  gpr_timespec raw_deadline() const { return deadline_; }
+
+  /// Add the (\a key, \a value) pair to the initial metadata
+  /// associated with a server call. These are made available at the client side
+  /// by the \a grpc::ClientContext::GetServerInitialMetadata() method.
+  ///
+  /// \warning This method should only be called before sending initial metadata
+  /// to the client (which can happen explicitly, or implicitly when sending a
+  /// a response message or status to the client).
+  ///
+  /// \param key The metadata key. If \a value is binary data, it must
+  /// end in "-bin".
+  /// \param value The metadata value. If its value is binary, the key name
+  /// must end in "-bin".
+  ///
+  /// Metadata must conform to the following format:
+  /// Custom-Metadata -> Binary-Header / ASCII-Header
+  /// Binary-Header -> {Header-Name "-bin" } {binary value}
+  /// ASCII-Header -> Header-Name ASCII-Value
+  /// Header-Name -> 1*( %x30-39 / %x61-7A / "_" / "-" / ".") ; 0-9 a-z _ - .
+  /// ASCII-Value -> 1*( %x20-%x7E ) ; space and printable ASCII
+  void AddInitialMetadata(const grpc::string& key, const grpc::string& value);
+
+  /// Add the (\a key, \a value) pair to the initial metadata
+  /// associated with a server call. These are made available at the client
+  /// side by the \a grpc::ClientContext::GetServerTrailingMetadata() method.
+  ///
+  /// \warning This method should only be called before sending trailing
+  /// metadata to the client (which happens when the call is finished and a
+  /// status is sent to the client).
+  ///
+  /// \param key The metadata key. If \a value is binary data,
+  /// it must end in "-bin".
+  /// \param value The metadata value. If its value is binary, the key name
+  /// must end in "-bin".
+  ///
+  /// Metadata must conform to the following format:
+  /// Custom-Metadata -> Binary-Header / ASCII-Header
+  /// Binary-Header -> {Header-Name "-bin" } {binary value}
+  /// ASCII-Header -> Header-Name ASCII-Value
+  /// Header-Name -> 1*( %x30-39 / %x61-7A / "_" / "-" / ".") ; 0-9 a-z _ - .
+  /// ASCII-Value -> 1*( %x20-%x7E ) ; space and printable ASCII
+  void AddTrailingMetadata(const grpc::string& key, const grpc::string& value);
+
+  /// IsCancelled is always safe to call when using sync or callback API.
+  /// When using async API, it is only safe to call IsCancelled after
+  /// the AsyncNotifyWhenDone tag has been delivered.
+  bool IsCancelled() const;
+
+  /// Cancel the Call from the server. This is a best-effort API and
+  /// depending on when it is called, the RPC may still appear successful to
+  /// the client.
+  /// For example, if TryCancel() is called on a separate thread, it might race
+  /// with the server handler which might return success to the client before
+  /// TryCancel() was even started by the thread.
+  ///
+  /// It is the caller's responsibility to prevent such races and ensure that if
+  /// TryCancel() is called, the serverhandler must return Status::CANCELLED.
+  /// The only exception is that if the serverhandler is already returning an
+  /// error status code, it is ok to not return Status::CANCELLED even if
+  /// TryCancel() was called.
+  ///
+  /// Note that TryCancel() does not change any of the tags that are pending
+  /// on the completion queue. All pending tags will still be delivered
+  /// (though their ok result may reflect the effect of cancellation).
+  void TryCancel() const;
+
+  /// Return a collection of initial metadata key-value pairs sent from the
+  /// client. Note that keys may happen more than
+  /// once (ie, a \a std::multimap is returned).
+  ///
+  /// It is safe to use this method after initial metadata has been received,
+  /// Calls always begin with the client sending initial metadata, so this is
+  /// safe to access as soon as the call has begun on the server side.
+  ///
+  /// \return A multimap of initial metadata key-value pairs from the server.
+  const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata()
+      const {
+    return *client_metadata_.map();
+  }
+
+  /// Return the compression algorithm to be used by the server call.
+  grpc_compression_level compression_level() const {
+    return compression_level_;
+  }
+
+  /// Set \a level to be the compression level used for the server call.
+  ///
+  /// \param level The compression level used for the server call.
+  void set_compression_level(grpc_compression_level level) {
+    compression_level_set_ = true;
+    compression_level_ = level;
+  }
+
+  /// Return a bool indicating whether the compression level for this call
+  /// has been set (either implicitly or through a previous call to
+  /// \a set_compression_level.
+  bool compression_level_set() const { return compression_level_set_; }
+
+  /// Return the compression algorithm the server call will request be used.
+  /// Note that the gRPC runtime may decide to ignore this request, for example,
+  /// due to resource constraints, or if the server is aware the client doesn't
+  /// support the requested algorithm.
+  grpc_compression_algorithm compression_algorithm() const {
+    return compression_algorithm_;
+  }
+  /// Set \a algorithm to be the compression algorithm used for the server call.
+  ///
+  /// \param algorithm The compression algorithm used for the server call.
+  void set_compression_algorithm(grpc_compression_algorithm algorithm);
+
+  /// Set the serialized load reporting costs in \a cost_data for the call.
+  void SetLoadReportingCosts(const std::vector<grpc::string>& cost_data);
+
+  /// Return the authentication context for this server call.
+  ///
+  /// \see grpc::AuthContext.
+  std::shared_ptr<const AuthContext> auth_context() const {
+    if (auth_context_.get() == nullptr) {
+      auth_context_ = CreateAuthContext(call_);
+    }
+    return auth_context_;
+  }
+
+  /// Return the peer uri in a string.
+  /// WARNING: this value is never authenticated or subject to any security
+  /// related code. It must not be used for any authentication related
+  /// functionality. Instead, use auth_context.
+  grpc::string peer() const;
+
+  /// Get the census context associated with this server call.
+  const struct census_context* census_context() const;
+
+  /// Async only. Has to be called before the rpc starts.
+  /// Returns the tag in completion queue when the rpc finishes.
+  /// IsCancelled() can then be called to check whether the rpc was cancelled.
+  /// TODO(vjpai): Fix this so that the tag is returned even if the call never
+  /// starts (https://github.com/grpc/grpc/issues/10136).
+  void AsyncNotifyWhenDone(void* tag) {
+    has_notify_when_done_tag_ = true;
+    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::testing::ServerContextTestSpouse;
+  friend class ::grpc::ServerInterface;
+  friend class ::grpc::Server;
+  template <class W, class R>
+  friend class ::grpc::ServerAsyncReader;
+  template <class W>
+  friend class ::grpc::ServerAsyncWriter;
+  template <class W>
+  friend class ::grpc::ServerAsyncResponseWriter;
+  template <class W, class R>
+  friend class ::grpc::ServerAsyncReaderWriter;
+  template <class R>
+  friend class ::grpc::ServerReader;
+  template <class W>
+  friend class ::grpc::ServerWriter;
+  template <class W, class R>
+  friend class ::grpc::internal::ServerReaderWriterBody;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ClientStreamingHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ::grpc::internal::ServerStreamingHandler;
+  template <class Streamer, bool WriteNeeded>
+  friend class ::grpc::internal::TemplatedBidiStreamingHandler;
+  template <class RequestType, class ResponseType>
+  friend class ::grpc::internal::CallbackUnaryHandler;
+  template <class RequestType, class ResponseType>
+  friend class ::grpc::internal::CallbackClientStreamingHandler;
+  template <class RequestType, class ResponseType>
+  friend class ::grpc::internal::CallbackServerStreamingHandler;
+  template <class RequestType, class ResponseType>
+  friend class ::grpc::internal::CallbackBidiHandler;
+  template <StatusCode code>
+  friend class internal::ErrorMethodHandler;
+  friend class ::grpc::ClientContext;
+
+  /// Prevent copying.
+  ServerContext(const ServerContext&);
+  ServerContext& operator=(const ServerContext&);
+
+  class CompletionOp;
+
+  void BeginCompletionOp(internal::Call* call,
+                         std::function<void(bool)> callback,
+                         internal::ServerReactor* reactor);
+  /// Return the tag queued by BeginCompletionOp()
+  internal::CompletionQueueTag* GetCompletionOpTag();
+
+  ServerContext(gpr_timespec deadline, grpc_metadata_array* arr);
+
+  void set_call(grpc_call* call) { call_ = call; }
+
+  void BindDeadlineAndMetadata(gpr_timespec deadline, grpc_metadata_array* arr);
+
+  void Clear();
+
+  void Setup(gpr_timespec deadline);
+
+  uint32_t initial_metadata_flags() const { return 0; }
+
+  experimental::ServerRpcInfo* set_server_rpc_info(
+      const char* method, internal::RpcMethod::RpcType type,
+      const std::vector<
+          std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>&
+          creators) {
+    if (creators.size() != 0) {
+      rpc_info_ = new experimental::ServerRpcInfo(this, method, type);
+      rpc_info_->RegisterInterceptors(creators);
+    }
+    return rpc_info_;
+  }
+
+  CompletionOp* completion_op_;
+  bool has_notify_when_done_tag_;
+  void* async_notify_when_done_tag_;
+  internal::CallbackWithSuccessTag completion_tag_;
+
+  gpr_timespec deadline_;
+  grpc_call* call_;
+  CompletionQueue* cq_;
+  bool sent_initial_metadata_;
+  mutable std::shared_ptr<const AuthContext> auth_context_;
+  mutable internal::MetadataMap client_metadata_;
+  std::multimap<grpc::string, grpc::string> initial_metadata_;
+  std::multimap<grpc::string, grpc::string> trailing_metadata_;
+
+  bool compression_level_set_;
+  grpc_compression_level compression_level_;
+  grpc_compression_algorithm compression_algorithm_;
+
+  internal::CallOpSet<internal::CallOpSendInitialMetadata,
+                      internal::CallOpSendMessage>
+      pending_ops_;
+  bool has_pending_ops_;
+
+  experimental::ServerRpcInfo* rpc_info_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/server_interceptor.h b/third_party/grpc/include/grpcpp/impl/codegen/server_interceptor.h
new file mode 100644
index 0000000..3e71b3f
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/server_interceptor.h
@@ -0,0 +1,143 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H
+#define GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H
+
+#include <atomic>
+#include <vector>
+
+#include <grpcpp/impl/codegen/interceptor.h>
+#include <grpcpp/impl/codegen/rpc_method.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+namespace grpc {
+
+class ServerContext;
+
+namespace internal {
+class InterceptorBatchMethodsImpl;
+}
+
+namespace experimental {
+class ServerRpcInfo;
+
+// A factory interface for creation of server interceptors. A vector of
+// factories can be provided to ServerBuilder which will be used to create a new
+// vector of server interceptors per RPC. Server interceptor authors should
+// create a subclass of ServerInterceptorFactorInterface which creates objects
+// of their interceptors.
+class ServerInterceptorFactoryInterface {
+ public:
+  virtual ~ServerInterceptorFactoryInterface() {}
+  // Returns a pointer to an Interceptor object on successful creation, nullptr
+  // otherwise. If nullptr is returned, this server interceptor factory is
+  // ignored for the purposes of that RPC.
+  virtual Interceptor* CreateServerInterceptor(ServerRpcInfo* info) = 0;
+};
+
+/// ServerRpcInfo represents the state of a particular RPC as it
+/// appears to an interceptor. It is created and owned by the library and
+/// passed to the CreateServerInterceptor method of the application's
+/// ServerInterceptorFactoryInterface implementation
+class ServerRpcInfo {
+ public:
+  /// Type categorizes RPCs by unary or streaming type
+  enum class Type { UNARY, CLIENT_STREAMING, SERVER_STREAMING, BIDI_STREAMING };
+
+  ~ServerRpcInfo(){};
+
+  // Delete all copy and move constructors and assignments
+  ServerRpcInfo(const ServerRpcInfo&) = delete;
+  ServerRpcInfo& operator=(const ServerRpcInfo&) = delete;
+  ServerRpcInfo(ServerRpcInfo&&) = delete;
+  ServerRpcInfo& operator=(ServerRpcInfo&&) = delete;
+
+  // Getter methods
+
+  /// Return the fully-specified method name
+  const char* method() const { return method_; }
+
+  /// Return the type of the RPC (unary or a streaming flavor)
+  Type type() const { return type_; }
+
+  /// Return a pointer to the underlying ServerContext structure associated
+  /// with the RPC to support features that apply to it
+  grpc::ServerContext* server_context() { return ctx_; }
+
+ private:
+  static_assert(Type::UNARY ==
+                    static_cast<Type>(internal::RpcMethod::NORMAL_RPC),
+                "violated expectation about Type enum");
+  static_assert(Type::CLIENT_STREAMING ==
+                    static_cast<Type>(internal::RpcMethod::CLIENT_STREAMING),
+                "violated expectation about Type enum");
+  static_assert(Type::SERVER_STREAMING ==
+                    static_cast<Type>(internal::RpcMethod::SERVER_STREAMING),
+                "violated expectation about Type enum");
+  static_assert(Type::BIDI_STREAMING ==
+                    static_cast<Type>(internal::RpcMethod::BIDI_STREAMING),
+                "violated expectation about Type enum");
+
+  ServerRpcInfo(grpc::ServerContext* ctx, const char* method,
+                internal::RpcMethod::RpcType type)
+      : ctx_(ctx), method_(method), type_(static_cast<Type>(type)) {
+    ref_.store(1);
+  }
+
+  // Runs interceptor at pos \a pos.
+  void RunInterceptor(
+      experimental::InterceptorBatchMethods* interceptor_methods, size_t pos) {
+    GPR_CODEGEN_ASSERT(pos < interceptors_.size());
+    interceptors_[pos]->Intercept(interceptor_methods);
+  }
+
+  void RegisterInterceptors(
+      const std::vector<
+          std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>&
+          creators) {
+    for (const auto& creator : creators) {
+      auto* interceptor = creator->CreateServerInterceptor(this);
+      if (interceptor != nullptr) {
+        interceptors_.push_back(
+            std::unique_ptr<experimental::Interceptor>(interceptor));
+      }
+    }
+  }
+
+  void Ref() { ref_++; }
+  void Unref() {
+    if (--ref_ == 0) {
+      delete this;
+    }
+  }
+
+  grpc::ServerContext* ctx_ = nullptr;
+  const char* method_ = nullptr;
+  const Type type_;
+  std::atomic_int ref_;
+  std::vector<std::unique_ptr<experimental::Interceptor>> interceptors_;
+
+  friend class internal::InterceptorBatchMethodsImpl;
+  friend class grpc::ServerContext;
+};
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/server_interface.h b/third_party/grpc/include/grpcpp/impl/codegen/server_interface.h
new file mode 100644
index 0000000..e0e2629
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/server_interface.h
@@ -0,0 +1,362 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H
+#define GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/call_hook.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+#include <grpcpp/impl/codegen/server_context.h>
+
+namespace grpc {
+
+class AsyncGenericService;
+class Channel;
+class GenericServerContext;
+class ServerCompletionQueue;
+class ServerContext;
+class ServerCredentials;
+class Service;
+
+extern CoreCodegenInterface* g_core_codegen_interface;
+
+/// Models a gRPC server.
+///
+/// Servers are configured and started via \a grpc::ServerBuilder.
+namespace internal {
+class ServerAsyncStreamingInterface;
+}  // namespace internal
+
+class ServerInterface : public internal::CallHook {
+ public:
+  virtual ~ServerInterface() {}
+
+  /// \a Shutdown does the following things:
+  ///
+  /// 1. Shutdown the server: deactivate all listening ports, mark it in
+  ///    "shutdown mode" so that further call Request's or incoming RPC matches
+  ///    are no longer allowed. Also return all Request'ed-but-not-yet-active
+  ///    calls as failed (!ok). This refers to calls that have been requested
+  ///    at the server by the server-side library or application code but that
+  ///    have not yet been matched to incoming RPCs from the client. Note that
+  ///    this would even include default calls added automatically by the gRPC
+  ///    C++ API without the user's input (e.g., "Unimplemented RPC method")
+  ///
+  /// 2. Block until all rpc method handlers invoked automatically by the sync
+  ///    API finish.
+  ///
+  /// 3. If all pending calls complete (and all their operations are
+  ///    retrieved by Next) before \a deadline expires, this finishes
+  ///    gracefully. Otherwise, forcefully cancel all pending calls associated
+  ///    with the server after \a deadline expires. In the case of the sync API,
+  ///    if the RPC function for a streaming call has already been started and
+  ///    takes a week to complete, the RPC function won't be forcefully
+  ///    terminated (since that would leave state corrupt and incomplete) and
+  ///    the method handler will just keep running (which will prevent the
+  ///    server from completing the "join" operation that it needs to do at
+  ///    shutdown time).
+  ///
+  /// 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.
+  /// They must also be drained (by repeated Next) after being shutdown.
+  ///
+  /// \param deadline How long to wait until pending rpcs are forcefully
+  /// terminated.
+  template <class T>
+  void Shutdown(const T& deadline) {
+    ShutdownInternal(TimePoint<T>(deadline).raw_time());
+  }
+
+  /// Shutdown the server without a deadline and forced cancellation.
+  ///
+  /// 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(
+        g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_MONOTONIC));
+  }
+
+  /// Block waiting for all work to complete.
+  ///
+  /// \warning The server must be either shutting down or some other thread must
+  /// call \a Shutdown for this function to ever return.
+  virtual void Wait() = 0;
+
+ protected:
+  friend class ::grpc::Service;
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the Server instance.
+  virtual bool RegisterService(const grpc::string* host, Service* service) = 0;
+
+  /// Register a generic service. This call does not take ownership of the
+  /// service. The service must exist for the lifetime of the Server instance.
+  virtual void RegisterAsyncGenericService(AsyncGenericService* service) = 0;
+
+  /// Tries to bind \a server to the given \a addr.
+  ///
+  /// It can be invoked multiple times.
+  ///
+  /// \param addr The address to try to bind to the server (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.).
+  /// \params creds The credentials associated with the server.
+  ///
+  /// \return bound port number on sucess, 0 on failure.
+  ///
+  /// \warning It's an error to call this method on an already started server.
+  virtual int AddListeningPort(const grpc::string& addr,
+                               ServerCredentials* creds) = 0;
+
+  /// Start the server.
+  ///
+  /// \param cqs Completion queues for handling asynchronous services. The
+  /// caller is required to keep all completion queues live until the server is
+  /// destroyed.
+  /// \param num_cqs How many completion queues does \a cqs hold.
+  virtual void Start(ServerCompletionQueue** cqs, size_t num_cqs) = 0;
+
+  virtual void ShutdownInternal(gpr_timespec deadline) = 0;
+
+  virtual int max_receive_message_size() const = 0;
+
+  virtual grpc_server* server() = 0;
+
+  virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                                internal::Call* call) = 0;
+
+  class BaseAsyncRequest : public internal::CompletionQueueTag {
+   public:
+    BaseAsyncRequest(ServerInterface* server, ServerContext* context,
+                     internal::ServerAsyncStreamingInterface* stream,
+                     CompletionQueue* call_cq,
+                     ServerCompletionQueue* notification_cq, void* tag,
+                     bool delete_on_finalize);
+    virtual ~BaseAsyncRequest();
+
+    bool FinalizeResult(void** tag, bool* status) override;
+
+   private:
+    void ContinueFinalizeResultAfterInterception();
+
+   protected:
+    ServerInterface* const server_;
+    ServerContext* const context_;
+    internal::ServerAsyncStreamingInterface* const stream_;
+    CompletionQueue* const call_cq_;
+    ServerCompletionQueue* const notification_cq_;
+    void* const tag_;
+    const bool delete_on_finalize_;
+    grpc_call* call_;
+    internal::Call call_wrapper_;
+    internal::InterceptorBatchMethodsImpl interceptor_methods_;
+    bool done_intercepting_;
+  };
+
+  /// RegisteredAsyncRequest is not part of the C++ API
+  class RegisteredAsyncRequest : public BaseAsyncRequest {
+   public:
+    RegisteredAsyncRequest(ServerInterface* server, ServerContext* context,
+                           internal::ServerAsyncStreamingInterface* stream,
+                           CompletionQueue* call_cq,
+                           ServerCompletionQueue* notification_cq, void* tag,
+                           const char* name, internal::RpcMethod::RpcType type);
+
+    virtual bool FinalizeResult(void** tag, bool* status) override {
+      /* If we are done intercepting, then there is nothing more for us to do */
+      if (done_intercepting_) {
+        return BaseAsyncRequest::FinalizeResult(tag, status);
+      }
+      call_wrapper_ = internal::Call(
+          call_, server_, call_cq_, server_->max_receive_message_size(),
+          context_->set_server_rpc_info(name_, type_,
+                                        *server_->interceptor_creators()));
+      return BaseAsyncRequest::FinalizeResult(tag, status);
+    }
+
+   protected:
+    void IssueRequest(void* registered_method, grpc_byte_buffer** payload,
+                      ServerCompletionQueue* notification_cq);
+    const char* name_;
+    const internal::RpcMethod::RpcType type_;
+  };
+
+  class NoPayloadAsyncRequest final : public RegisteredAsyncRequest {
+   public:
+    NoPayloadAsyncRequest(internal::RpcServiceMethod* registered_method,
+                          ServerInterface* server, ServerContext* context,
+                          internal::ServerAsyncStreamingInterface* stream,
+                          CompletionQueue* call_cq,
+                          ServerCompletionQueue* notification_cq, void* tag)
+        : RegisteredAsyncRequest(
+              server, context, stream, call_cq, notification_cq, tag,
+              registered_method->name(), registered_method->method_type()) {
+      IssueRequest(registered_method->server_tag(), nullptr, notification_cq);
+    }
+
+    // uses RegisteredAsyncRequest::FinalizeResult
+  };
+
+  template <class Message>
+  class PayloadAsyncRequest final : public RegisteredAsyncRequest {
+   public:
+    PayloadAsyncRequest(internal::RpcServiceMethod* registered_method,
+                        ServerInterface* server, ServerContext* context,
+                        internal::ServerAsyncStreamingInterface* stream,
+                        CompletionQueue* call_cq,
+                        ServerCompletionQueue* notification_cq, void* tag,
+                        Message* request)
+        : RegisteredAsyncRequest(
+              server, context, stream, call_cq, notification_cq, tag,
+              registered_method->name(), registered_method->method_type()),
+          registered_method_(registered_method),
+          server_(server),
+          context_(context),
+          stream_(stream),
+          call_cq_(call_cq),
+          notification_cq_(notification_cq),
+          tag_(tag),
+          request_(request) {
+      IssueRequest(registered_method->server_tag(), payload_.bbuf_ptr(),
+                   notification_cq);
+    }
+
+    ~PayloadAsyncRequest() {
+      payload_.Release();  // We do not own the payload_
+    }
+
+    bool FinalizeResult(void** tag, bool* status) override {
+      /* If we are done intercepting, then there is nothing more for us to do */
+      if (done_intercepting_) {
+        return RegisteredAsyncRequest::FinalizeResult(tag, status);
+      }
+      if (*status) {
+        if (!payload_.Valid() || !SerializationTraits<Message>::Deserialize(
+                                      payload_.bbuf_ptr(), request_)
+                                      .ok()) {
+          // If deserialization fails, we cancel the call and instantiate
+          // a new instance of ourselves to request another call.  We then
+          // return false, which prevents the call from being returned to
+          // the application.
+          g_core_codegen_interface->grpc_call_cancel_with_status(
+              call_, GRPC_STATUS_INTERNAL, "Unable to parse request", nullptr);
+          g_core_codegen_interface->grpc_call_unref(call_);
+          new PayloadAsyncRequest(registered_method_, server_, context_,
+                                  stream_, call_cq_, notification_cq_, tag_,
+                                  request_);
+          delete this;
+          return false;
+        }
+      }
+      /* Set interception point for recv message */
+      interceptor_methods_.AddInterceptionHookPoint(
+          experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+      interceptor_methods_.SetRecvMessage(request_);
+      return RegisteredAsyncRequest::FinalizeResult(tag, status);
+    }
+
+   private:
+    internal::RpcServiceMethod* const registered_method_;
+    ServerInterface* const server_;
+    ServerContext* const context_;
+    internal::ServerAsyncStreamingInterface* const stream_;
+    CompletionQueue* const call_cq_;
+
+    ServerCompletionQueue* const notification_cq_;
+    void* const tag_;
+    Message* const request_;
+    ByteBuffer payload_;
+  };
+
+  class GenericAsyncRequest : public BaseAsyncRequest {
+   public:
+    GenericAsyncRequest(ServerInterface* server, GenericServerContext* context,
+                        internal::ServerAsyncStreamingInterface* stream,
+                        CompletionQueue* call_cq,
+                        ServerCompletionQueue* notification_cq, void* tag,
+                        bool delete_on_finalize);
+
+    bool FinalizeResult(void** tag, bool* status) override;
+
+   private:
+    grpc_call_details call_details_;
+  };
+
+  template <class Message>
+  void RequestAsyncCall(internal::RpcServiceMethod* method,
+                        ServerContext* context,
+                        internal::ServerAsyncStreamingInterface* stream,
+                        CompletionQueue* call_cq,
+                        ServerCompletionQueue* notification_cq, void* tag,
+                        Message* message) {
+    GPR_CODEGEN_ASSERT(method);
+    new PayloadAsyncRequest<Message>(method, this, context, stream, call_cq,
+                                     notification_cq, tag, message);
+  }
+
+  void RequestAsyncCall(internal::RpcServiceMethod* method,
+                        ServerContext* context,
+                        internal::ServerAsyncStreamingInterface* stream,
+                        CompletionQueue* call_cq,
+                        ServerCompletionQueue* notification_cq, void* tag) {
+    GPR_CODEGEN_ASSERT(method);
+    new NoPayloadAsyncRequest(method, this, context, stream, call_cq,
+                              notification_cq, tag);
+  }
+
+  void RequestAsyncGenericCall(GenericServerContext* context,
+                               internal::ServerAsyncStreamingInterface* stream,
+                               CompletionQueue* call_cq,
+                               ServerCompletionQueue* notification_cq,
+                               void* tag) {
+    new GenericAsyncRequest(this, context, stream, call_cq, notification_cq,
+                            tag, true);
+  }
+
+ private:
+  // EXPERIMENTAL
+  // Getter method for the vector of interceptor factory objects.
+  // Returns a nullptr (rather than being pure) since this is a post-1.0 method
+  // and adding a new pure method to an interface would be a breaking change
+  // (even though this is private and non-API)
+  virtual std::vector<
+      std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>*
+  interceptor_creators() {
+    return nullptr;
+  }
+
+  // EXPERIMENTAL
+  // A method to get the callbackable completion queue associated with this
+  // server. If the return value is nullptr, this server doesn't support
+  // callback operations.
+  // TODO(vjpai): Consider a better default like using a global CQ
+  // Returns nullptr (rather than being pure) since this is a post-1.0 method
+  // and adding a new pure method to an interface would be a breaking change
+  // (even though this is private and non-API)
+  virtual CompletionQueue* CallbackCQ() { return nullptr; }
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/service_type.h b/third_party/grpc/include/grpcpp/impl/codegen/service_type.h
new file mode 100644
index 0000000..332a04c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/service_type.h
@@ -0,0 +1,239 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H
+#define GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+#include <grpcpp/impl/codegen/serialization_traits.h>
+#include <grpcpp/impl/codegen/server_interface.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+class CompletionQueue;
+class Server;
+class ServerInterface;
+class ServerCompletionQueue;
+class ServerContext;
+
+namespace internal {
+class Call;
+class ServerAsyncStreamingInterface {
+ public:
+  virtual ~ServerAsyncStreamingInterface() {}
+
+  /// Request notification of the sending of initial metadata to the client.
+  /// Completion will be notified by \a tag on the associated completion
+  /// queue. This call is optional, but if it is used, it cannot be used
+  /// concurrently with or after the \a Finish method.
+  ///
+  /// \param[in] tag Tag identifying this request.
+  virtual void SendInitialMetadata(void* tag) = 0;
+
+ private:
+  friend class ::grpc::ServerInterface;
+  virtual void BindCall(Call* call) = 0;
+};
+}  // namespace internal
+
+/// Desriptor of an RPC service and its various RPC methods
+class Service {
+ public:
+  Service() : server_(nullptr) {}
+  virtual ~Service() {}
+
+  bool has_async_methods() const {
+    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
+      if (*it && (*it)->handler() == nullptr) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool has_synchronous_methods() const {
+    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
+      if (*it &&
+          (*it)->api_type() == internal::RpcServiceMethod::ApiType::SYNC) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool has_callback_methods() const {
+    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
+      if (*it && ((*it)->api_type() ==
+                      internal::RpcServiceMethod::ApiType::CALL_BACK ||
+                  (*it)->api_type() ==
+                      internal::RpcServiceMethod::ApiType::RAW_CALL_BACK)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool has_generic_methods() const {
+    for (auto it = methods_.begin(); it != methods_.end(); ++it) {
+      if (it->get() == nullptr) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+ protected:
+  // TODO(vjpai): Promote experimental contents once callback API is accepted
+  class experimental_type {
+   public:
+    explicit experimental_type(Service* service) : service_(service) {}
+
+    void MarkMethodCallback(int index, internal::MethodHandler* handler) {
+      // This does not have to be a hard error, however no one has approached us
+      // with a use case yet. Please file an issue if you believe you have one.
+      size_t idx = static_cast<size_t>(index);
+      GPR_CODEGEN_ASSERT(
+          service_->methods_[idx].get() != nullptr &&
+          "Cannot mark the method as 'callback' because it has already been "
+          "marked as 'generic'.");
+      service_->methods_[idx]->SetHandler(handler);
+      service_->methods_[idx]->SetServerApiType(
+          internal::RpcServiceMethod::ApiType::CALL_BACK);
+    }
+
+    void MarkMethodRawCallback(int index, internal::MethodHandler* handler) {
+      // This does not have to be a hard error, however no one has approached us
+      // with a use case yet. Please file an issue if you believe you have one.
+      size_t idx = static_cast<size_t>(index);
+      GPR_CODEGEN_ASSERT(
+          service_->methods_[idx].get() != nullptr &&
+          "Cannot mark the method as 'raw callback' because it has already "
+          "been marked as 'generic'.");
+      service_->methods_[idx]->SetHandler(handler);
+      service_->methods_[idx]->SetServerApiType(
+          internal::RpcServiceMethod::ApiType::RAW_CALL_BACK);
+    }
+
+   private:
+    Service* service_;
+  };
+
+  experimental_type experimental() { return experimental_type(this); }
+
+  template <class Message>
+  void RequestAsyncUnary(int index, ServerContext* context, Message* request,
+                         internal::ServerAsyncStreamingInterface* stream,
+                         CompletionQueue* call_cq,
+                         ServerCompletionQueue* notification_cq, void* tag) {
+    // Typecast the index to size_t for indexing into a vector
+    // while preserving the API that existed before a compiler
+    // warning was first seen (grpc/grpc#11664)
+    size_t idx = static_cast<size_t>(index);
+    server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq,
+                              notification_cq, tag, request);
+  }
+  void RequestAsyncClientStreaming(
+      int index, ServerContext* context,
+      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+      ServerCompletionQueue* notification_cq, void* tag) {
+    size_t idx = static_cast<size_t>(index);
+    server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq,
+                              notification_cq, tag);
+  }
+  template <class Message>
+  void RequestAsyncServerStreaming(
+      int index, ServerContext* context, Message* request,
+      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+      ServerCompletionQueue* notification_cq, void* tag) {
+    size_t idx = static_cast<size_t>(index);
+    server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq,
+                              notification_cq, tag, request);
+  }
+  void RequestAsyncBidiStreaming(
+      int index, ServerContext* context,
+      internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+      ServerCompletionQueue* notification_cq, void* tag) {
+    size_t idx = static_cast<size_t>(index);
+    server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq,
+                              notification_cq, tag);
+  }
+
+  void AddMethod(internal::RpcServiceMethod* method) {
+    methods_.emplace_back(method);
+  }
+
+  void MarkMethodAsync(int index) {
+    // This does not have to be a hard error, however no one has approached us
+    // with a use case yet. Please file an issue if you believe you have one.
+    size_t idx = static_cast<size_t>(index);
+    GPR_CODEGEN_ASSERT(
+        methods_[idx].get() != nullptr &&
+        "Cannot mark the method as 'async' because it has already been "
+        "marked as 'generic'.");
+    methods_[idx]->SetServerApiType(internal::RpcServiceMethod::ApiType::ASYNC);
+  }
+
+  void MarkMethodRaw(int index) {
+    // This does not have to be a hard error, however no one has approached us
+    // with a use case yet. Please file an issue if you believe you have one.
+    size_t idx = static_cast<size_t>(index);
+    GPR_CODEGEN_ASSERT(methods_[idx].get() != nullptr &&
+                       "Cannot mark the method as 'raw' because it has already "
+                       "been marked as 'generic'.");
+    methods_[idx]->SetServerApiType(internal::RpcServiceMethod::ApiType::RAW);
+  }
+
+  void MarkMethodGeneric(int index) {
+    // This does not have to be a hard error, however no one has approached us
+    // with a use case yet. Please file an issue if you believe you have one.
+    size_t idx = static_cast<size_t>(index);
+    GPR_CODEGEN_ASSERT(
+        methods_[idx]->handler() != nullptr &&
+        "Cannot mark the method as 'generic' because it has already been "
+        "marked as 'async' or 'raw'.");
+    methods_[idx].reset();
+  }
+
+  void MarkMethodStreamed(int index, internal::MethodHandler* streamed_method) {
+    // This does not have to be a hard error, however no one has approached us
+    // with a use case yet. Please file an issue if you believe you have one.
+    size_t idx = static_cast<size_t>(index);
+    GPR_CODEGEN_ASSERT(methods_[idx] && methods_[idx]->handler() &&
+                       "Cannot mark an async or generic method Streamed");
+    methods_[idx]->SetHandler(streamed_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,
+    // and split server-side streaming is BIDI_STREAMING with 1 read and
+    // any number of writes, in that order.
+    methods_[idx]->SetMethodType(internal::RpcMethod::BIDI_STREAMING);
+  }
+
+ private:
+  friend class Server;
+  friend class ServerInterface;
+  ServerInterface* server_;
+  std::vector<std::unique_ptr<internal::RpcServiceMethod>> methods_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/slice.h b/third_party/grpc/include/grpcpp/impl/codegen/slice.h
new file mode 100644
index 0000000..8966559
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/slice.h
@@ -0,0 +1,143 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SLICE_H
+#define GRPCPP_IMPL_CODEGEN_SLICE_H
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/string_ref.h>
+
+#include <grpc/impl/codegen/slice.h>
+
+namespace grpc {
+
+/// A wrapper around \a grpc_slice.
+///
+/// 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.
+class Slice final {
+ public:
+  /// Construct an empty slice.
+  Slice() : slice_(g_core_codegen_interface->grpc_empty_slice()) {}
+  /// Destructor - drops one reference.
+  ~Slice() { g_core_codegen_interface->grpc_slice_unref(slice_); }
+
+  enum AddRef { ADD_REF };
+  /// Construct a slice from \a slice, adding a reference.
+  Slice(grpc_slice slice, AddRef)
+      : slice_(g_core_codegen_interface->grpc_slice_ref(slice)) {}
+
+  enum StealRef { STEAL_REF };
+  /// Construct a slice from \a slice, stealing a reference.
+  Slice(grpc_slice slice, StealRef) : slice_(slice) {}
+
+  /// Allocate a slice of specified size
+  Slice(size_t len)
+      : slice_(g_core_codegen_interface->grpc_slice_malloc(len)) {}
+
+  /// Construct a slice from a copied buffer
+  Slice(const void* buf, size_t len)
+      : slice_(g_core_codegen_interface->grpc_slice_from_copied_buffer(
+            reinterpret_cast<const char*>(buf), len)) {}
+
+  /// Construct a slice from a copied string
+  Slice(const grpc::string& str)
+      : slice_(g_core_codegen_interface->grpc_slice_from_copied_buffer(
+            str.c_str(), str.length())) {}
+
+  enum StaticSlice { STATIC_SLICE };
+
+  /// Construct a slice from a static buffer
+  Slice(const void* buf, size_t len, StaticSlice)
+      : slice_(g_core_codegen_interface->grpc_slice_from_static_buffer(
+            reinterpret_cast<const char*>(buf), len)) {}
+
+  /// Copy constructor, adds a reference.
+  Slice(const Slice& other)
+      : slice_(g_core_codegen_interface->grpc_slice_ref(other.slice_)) {}
+
+  /// Assignment, reference count is unchanged.
+  Slice& operator=(Slice other) {
+    std::swap(slice_, other.slice_);
+    return *this;
+  }
+
+  /// 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
+  /// user data pointer passed in at destruction. Can be the same as buf or
+  /// different (e.g., if data is part of a larger structure that must be
+  /// destroyed when the data is no longer needed)
+  Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data)
+      : slice_(g_core_codegen_interface->grpc_slice_new_with_user_data(
+            buf, len, destroy, user_data)) {}
+
+  /// Specialization of above for common case where buf == user_data
+  Slice(void* buf, size_t len, void (*destroy)(void*))
+      : Slice(buf, len, destroy, buf) {}
+
+  /// Similar to the above but has a destroy that also takes slice length
+  Slice(void* buf, size_t len, void (*destroy)(void*, size_t))
+      : slice_(g_core_codegen_interface->grpc_slice_new_with_len(buf, len,
+                                                                 destroy)) {}
+
+  /// Byte size.
+  size_t size() const { return GRPC_SLICE_LENGTH(slice_); }
+
+  /// Raw pointer to the beginning (first element) of the slice.
+  const uint8_t* begin() const { return GRPC_SLICE_START_PTR(slice_); }
+
+  /// Raw pointer to the end (one byte \em past the last element) of the slice.
+  const uint8_t* end() const { return GRPC_SLICE_END_PTR(slice_); }
+
+  /// Raw C slice. Caller needs to call grpc_slice_unref when done.
+  grpc_slice c_slice() const {
+    return g_core_codegen_interface->grpc_slice_ref(slice_);
+  }
+
+ private:
+  friend class ByteBuffer;
+
+  grpc_slice slice_;
+};
+
+inline grpc::string_ref StringRefFromSlice(const grpc_slice* slice) {
+  return grpc::string_ref(
+      reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(*slice)),
+      GRPC_SLICE_LENGTH(*slice));
+}
+
+inline grpc::string StringFromCopiedSlice(grpc_slice slice) {
+  return grpc::string(reinterpret_cast<char*>(GRPC_SLICE_START_PTR(slice)),
+                      GRPC_SLICE_LENGTH(slice));
+}
+
+inline grpc_slice SliceReferencingString(const grpc::string& str) {
+  return g_core_codegen_interface->grpc_slice_from_static_buffer(str.data(),
+                                                                 str.length());
+}
+
+inline grpc_slice SliceFromCopiedString(const grpc::string& str) {
+  return g_core_codegen_interface->grpc_slice_from_copied_buffer(str.data(),
+                                                                 str.length());
+}
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SLICE_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/status.h b/third_party/grpc/include/grpcpp/impl/codegen/status.h
new file mode 100644
index 0000000..e625a76
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/status.h
@@ -0,0 +1,133 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_STATUS_H
+#define GRPCPP_IMPL_CODEGEN_STATUS_H
+
+#include <grpc/impl/codegen/status.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/status_code_enum.h>
+
+namespace grpc {
+
+/// Did it work? If it didn't, why?
+///
+/// See \a grpc::StatusCode for details on the available code and their meaning.
+class Status {
+ public:
+  /// Construct an OK instance.
+  Status() : code_(StatusCode::OK) {
+    // Static assertions to make sure that the C++ API value correctly
+    // maps to the core surface API value
+    static_assert(StatusCode::OK == static_cast<StatusCode>(GRPC_STATUS_OK),
+                  "Mismatched status code");
+    static_assert(
+        StatusCode::CANCELLED == static_cast<StatusCode>(GRPC_STATUS_CANCELLED),
+        "Mismatched status code");
+    static_assert(
+        StatusCode::UNKNOWN == static_cast<StatusCode>(GRPC_STATUS_UNKNOWN),
+        "Mismatched status code");
+    static_assert(StatusCode::INVALID_ARGUMENT ==
+                      static_cast<StatusCode>(GRPC_STATUS_INVALID_ARGUMENT),
+                  "Mismatched status code");
+    static_assert(StatusCode::DEADLINE_EXCEEDED ==
+                      static_cast<StatusCode>(GRPC_STATUS_DEADLINE_EXCEEDED),
+                  "Mismatched status code");
+    static_assert(
+        StatusCode::NOT_FOUND == static_cast<StatusCode>(GRPC_STATUS_NOT_FOUND),
+        "Mismatched status code");
+    static_assert(StatusCode::ALREADY_EXISTS ==
+                      static_cast<StatusCode>(GRPC_STATUS_ALREADY_EXISTS),
+                  "Mismatched status code");
+    static_assert(StatusCode::PERMISSION_DENIED ==
+                      static_cast<StatusCode>(GRPC_STATUS_PERMISSION_DENIED),
+                  "Mismatched status code");
+    static_assert(StatusCode::UNAUTHENTICATED ==
+                      static_cast<StatusCode>(GRPC_STATUS_UNAUTHENTICATED),
+                  "Mismatched status code");
+    static_assert(StatusCode::RESOURCE_EXHAUSTED ==
+                      static_cast<StatusCode>(GRPC_STATUS_RESOURCE_EXHAUSTED),
+                  "Mismatched status code");
+    static_assert(StatusCode::FAILED_PRECONDITION ==
+                      static_cast<StatusCode>(GRPC_STATUS_FAILED_PRECONDITION),
+                  "Mismatched status code");
+    static_assert(
+        StatusCode::ABORTED == static_cast<StatusCode>(GRPC_STATUS_ABORTED),
+        "Mismatched status code");
+    static_assert(StatusCode::OUT_OF_RANGE ==
+                      static_cast<StatusCode>(GRPC_STATUS_OUT_OF_RANGE),
+                  "Mismatched status code");
+    static_assert(StatusCode::UNIMPLEMENTED ==
+                      static_cast<StatusCode>(GRPC_STATUS_UNIMPLEMENTED),
+                  "Mismatched status code");
+    static_assert(
+        StatusCode::INTERNAL == static_cast<StatusCode>(GRPC_STATUS_INTERNAL),
+        "Mismatched status code");
+    static_assert(StatusCode::UNAVAILABLE ==
+                      static_cast<StatusCode>(GRPC_STATUS_UNAVAILABLE),
+                  "Mismatched status code");
+    static_assert(
+        StatusCode::DATA_LOSS == static_cast<StatusCode>(GRPC_STATUS_DATA_LOSS),
+        "Mismatched status code");
+  }
+
+  /// Construct an instance with associated \a code and \a error_message.
+  /// It is an error to construct an OK status with non-empty \a error_message.
+  Status(StatusCode code, const grpc::string& error_message)
+      : code_(code), error_message_(error_message) {}
+
+  /// Construct an instance with \a code,  \a error_message and
+  /// \a error_details. It is an error to construct an OK status with non-empty
+  /// \a error_message and/or \a error_details.
+  Status(StatusCode code, const grpc::string& error_message,
+         const grpc::string& error_details)
+      : code_(code),
+        error_message_(error_message),
+        binary_error_details_(error_details) {}
+
+  // Pre-defined special status objects.
+  /// An OK pre-defined instance.
+  static const Status& OK;
+  /// A CANCELLED pre-defined instance.
+  static const Status& CANCELLED;
+
+  /// Return the instance's error code.
+  StatusCode error_code() const { return code_; }
+  /// Return the instance's error message.
+  grpc::string error_message() const { return error_message_; }
+  /// Return the (binary) error details.
+  // Usually it contains a serialized google.rpc.Status proto.
+  grpc::string error_details() const { return binary_error_details_; }
+
+  /// Is the status OK?
+  bool ok() const { return code_ == StatusCode::OK; }
+
+  // Ignores any errors. This method does nothing except potentially suppress
+  // complaints from any tools that are checking that errors are not dropped on
+  // the floor.
+  void IgnoreError() const {}
+
+ private:
+  StatusCode code_;
+  grpc::string error_message_;
+  grpc::string binary_error_details_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_STATUS_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/status_code_enum.h b/third_party/grpc/include/grpcpp/impl/codegen/status_code_enum.h
new file mode 100644
index 0000000..09943f1
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/status_code_enum.h
@@ -0,0 +1,142 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_STATUS_CODE_ENUM_H
+#define GRPCPP_IMPL_CODEGEN_STATUS_CODE_ENUM_H
+
+namespace grpc {
+
+enum StatusCode {
+  /// Not an error; returned on success.
+  OK = 0,
+
+  /// The operation was cancelled (typically by the caller).
+  CANCELLED = 1,
+
+  /// Unknown error. An example of where this error may be returned is if a
+  /// Status value received from another address space belongs to an error-space
+  /// that is not known in this address space. Also errors raised by APIs that
+  /// do not return enough error information may be converted to this error.
+  UNKNOWN = 2,
+
+  /// Client specified an invalid argument. Note that this differs from
+  /// FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are
+  /// problematic regardless of the state of the system (e.g., a malformed file
+  /// name).
+  INVALID_ARGUMENT = 3,
+
+  /// Deadline expired before operation could complete. For operations that
+  /// change the state of the system, this error may be returned even if the
+  /// operation has completed successfully. For example, a successful response
+  /// from a server could have been delayed long enough for the deadline to
+  /// expire.
+  DEADLINE_EXCEEDED = 4,
+
+  /// Some requested entity (e.g., file or directory) was not found.
+  NOT_FOUND = 5,
+
+  /// Some entity that we attempted to create (e.g., file or directory) already
+  /// exists.
+  ALREADY_EXISTS = 6,
+
+  /// The caller does not have permission to execute the specified operation.
+  /// PERMISSION_DENIED must not be used for rejections caused by exhausting
+  /// some resource (use RESOURCE_EXHAUSTED instead for those errors).
+  /// PERMISSION_DENIED must not be used if the caller can not be identified
+  /// (use UNAUTHENTICATED instead for those errors).
+  PERMISSION_DENIED = 7,
+
+  /// The request does not have valid authentication credentials for the
+  /// operation.
+  UNAUTHENTICATED = 16,
+
+  /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
+  /// entire file system is out of space.
+  RESOURCE_EXHAUSTED = 8,
+
+  /// Operation was rejected because the system is not in a state required for
+  /// the operation's execution. For example, directory to be deleted may be
+  /// non-empty, an rmdir operation is applied to a non-directory, etc.
+  ///
+  /// A litmus test that may help a service implementor in deciding
+  /// between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
+  ///  (a) Use UNAVAILABLE if the client can retry just the failing call.
+  ///  (b) Use ABORTED if the client should retry at a higher-level
+  ///      (e.g., restarting a read-modify-write sequence).
+  ///  (c) Use FAILED_PRECONDITION if the client should not retry until
+  ///      the system state has been explicitly fixed. E.g., if an "rmdir"
+  ///      fails because the directory is non-empty, FAILED_PRECONDITION
+  ///      should be returned since the client should not retry unless
+  ///      they have first fixed up the directory by deleting files from it.
+  ///  (d) Use FAILED_PRECONDITION if the client performs conditional
+  ///      REST Get/Update/Delete on a resource and the resource on the
+  ///      server does not match the condition. E.g., conflicting
+  ///      read-modify-write on the same resource.
+  FAILED_PRECONDITION = 9,
+
+  /// The operation was aborted, typically due to a concurrency issue like
+  /// sequencer check failures, transaction aborts, etc.
+  ///
+  /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
+  /// and UNAVAILABLE.
+  ABORTED = 10,
+
+  /// Operation was attempted past the valid range. E.g., seeking or reading
+  /// past end of file.
+  ///
+  /// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed
+  /// if the system state changes. For example, a 32-bit file system will
+  /// generate INVALID_ARGUMENT if asked to read at an offset that is not in the
+  /// range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from
+  /// an offset past the current file size.
+  ///
+  /// There is a fair bit of overlap between FAILED_PRECONDITION and
+  /// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error)
+  /// when it applies so that callers who are iterating through a space can
+  /// easily look for an OUT_OF_RANGE error to detect when they are done.
+  OUT_OF_RANGE = 11,
+
+  /// Operation is not implemented or not supported/enabled in this service.
+  UNIMPLEMENTED = 12,
+
+  /// Internal errors. Means some invariants expected by underlying System has
+  /// been broken. If you see one of these errors, Something is very broken.
+  INTERNAL = 13,
+
+  /// The service is currently unavailable. This is a most likely a transient
+  /// condition and may be corrected by retrying with a backoff.
+  ///
+  /// \warning Although data MIGHT not have been transmitted when this
+  /// status occurs, there is NOT A GUARANTEE that the server has not seen
+  /// anything. So in general it is unsafe to retry on this status code
+  /// if the call is non-idempotent.
+  ///
+  /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
+  /// and UNAVAILABLE.
+  UNAVAILABLE = 14,
+
+  /// Unrecoverable data loss or corruption.
+  DATA_LOSS = 15,
+
+  /// Force users to include a default branch:
+  DO_NOT_USE = -1
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_STATUS_CODE_ENUM_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/string_ref.h b/third_party/grpc/include/grpcpp/impl/codegen/string_ref.h
new file mode 100644
index 0000000..5d55fc4
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/string_ref.h
@@ -0,0 +1,146 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_STRING_REF_H
+#define GRPCPP_IMPL_CODEGEN_STRING_REF_H
+
+#include <string.h>
+
+#include <algorithm>
+#include <iosfwd>
+#include <iostream>
+#include <iterator>
+
+#include <grpcpp/impl/codegen/config.h>
+
+namespace grpc {
+
+/// This class is a non owning reference to a string.
+///
+/// It should be a strict subset of the upcoming std::string_ref.
+///
+/// \see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html
+///
+/// The constexpr is dropped or replaced with const for legacy compiler
+/// compatibility.
+class string_ref {
+ public:
+  /// types
+  typedef const char* const_iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+  /// constants
+  const static size_t npos;
+
+  /// construct/copy.
+  string_ref() : data_(nullptr), length_(0) {}
+  string_ref(const string_ref& other)
+      : data_(other.data_), length_(other.length_) {}
+  string_ref& operator=(const string_ref& rhs) {
+    data_ = rhs.data_;
+    length_ = rhs.length_;
+    return *this;
+  }
+
+  string_ref(const char* s) : data_(s), length_(strlen(s)) {}
+  string_ref(const char* s, size_t l) : data_(s), length_(l) {}
+  string_ref(const grpc::string& s) : data_(s.data()), length_(s.length()) {}
+
+  /// iterators
+  const_iterator begin() const { return data_; }
+  const_iterator end() const { return data_ + length_; }
+  const_iterator cbegin() const { return data_; }
+  const_iterator cend() const { return data_ + length_; }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(begin());
+  }
+  const_reverse_iterator crbegin() const {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator crend() const {
+    return const_reverse_iterator(begin());
+  }
+
+  /// capacity
+  size_t size() const { return length_; }
+  size_t length() const { return length_; }
+  size_t max_size() const { return length_; }
+  bool empty() const { return length_ == 0; }
+
+  /// element access
+  const char* data() const { return data_; }
+
+  /// string operations
+  int compare(string_ref x) const {
+    size_t min_size = length_ < x.length_ ? length_ : x.length_;
+    int r = memcmp(data_, x.data_, min_size);
+    if (r < 0) return -1;
+    if (r > 0) return 1;
+    if (length_ < x.length_) return -1;
+    if (length_ > x.length_) return 1;
+    return 0;
+  }
+
+  bool starts_with(string_ref x) const {
+    return length_ >= x.length_ && (memcmp(data_, x.data_, x.length_) == 0);
+  }
+
+  bool ends_with(string_ref x) const {
+    return length_ >= x.length_ &&
+           (memcmp(data_ + (length_ - x.length_), x.data_, x.length_) == 0);
+  }
+
+  size_t find(string_ref s) const {
+    auto it = std::search(cbegin(), cend(), s.cbegin(), s.cend());
+    return it == cend() ? npos : std::distance(cbegin(), it);
+  }
+
+  size_t find(char c) const {
+    auto it = std::find(cbegin(), cend(), c);
+    return it == cend() ? npos : std::distance(cbegin(), it);
+  }
+
+  string_ref substr(size_t pos, size_t n = npos) const {
+    if (pos > length_) pos = length_;
+    if (n > (length_ - pos)) n = length_ - pos;
+    return string_ref(data_ + pos, n);
+  }
+
+ private:
+  const char* data_;
+  size_t length_;
+};
+
+/// Comparison operators
+inline bool operator==(string_ref x, string_ref y) { return x.compare(y) == 0; }
+inline bool operator!=(string_ref x, string_ref y) { return x.compare(y) != 0; }
+inline bool operator<(string_ref x, string_ref y) { return x.compare(y) < 0; }
+inline bool operator<=(string_ref x, string_ref y) { return x.compare(y) <= 0; }
+inline bool operator>(string_ref x, string_ref y) { return x.compare(y) > 0; }
+inline bool operator>=(string_ref x, string_ref y) { return x.compare(y) >= 0; }
+
+inline std::ostream& operator<<(std::ostream& out, const string_ref& string) {
+  return out << grpc::string(string.begin(), string.end());
+}
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_STRING_REF_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/stub_options.h b/third_party/grpc/include/grpcpp/impl/codegen/stub_options.h
new file mode 100644
index 0000000..a56695a
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/stub_options.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H
+#define GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H
+
+namespace grpc {
+
+/// Useful interface for generated stubs
+class StubOptions {};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/sync_stream.h b/third_party/grpc/include/grpcpp/impl/codegen/sync_stream.h
new file mode 100644
index 0000000..6981076
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/sync_stream.h
@@ -0,0 +1,934 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H
+#define GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/channel_interface.h>
+#include <grpcpp/impl/codegen/client_context.h>
+#include <grpcpp/impl/codegen/completion_queue.h>
+#include <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/server_context.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/impl/codegen/status.h>
+
+namespace grpc {
+
+namespace internal {
+/// Common interface for all synchronous client side streaming.
+class ClientStreamingInterface {
+ public:
+  virtual ~ClientStreamingInterface() {}
+
+  /// Block waiting until the stream finishes and a final status of the call is
+  /// available.
+  ///
+  /// It is appropriate to call this method when both:
+  ///   * the calling code (client-side) has no more message to send
+  ///     (this can be declared implicitly by calling this method, or
+  ///     explicitly through an earlier call to <i>WritesDone</i> method of the
+  ///     class in use, e.g. \a ClientWriterInterface::WritesDone or
+  ///     \a ClientReaderWriterInterface::WritesDone).
+  ///   * there are no more messages to be received from the server (which can
+  ///     be known implicitly, or explicitly from an earlier call to \a
+  ///     ReaderInterface::Read that returned "false").
+  ///
+  /// This function will return either:
+  /// - when all incoming messages have been read and the server has
+  ///   returned status.
+  /// - when the server has returned a non-OK status.
+  /// - OR when the call failed for some reason and the library generated a
+  ///   status.
+  ///
+  /// Return values:
+  ///   - \a Status contains the status code, message and details for the call
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible trailing metadata sent from the server.
+  virtual Status Finish() = 0;
+};
+
+/// Common interface for all synchronous server side streaming.
+class ServerStreamingInterface {
+ public:
+  virtual ~ServerStreamingInterface() {}
+
+  /// Block to send initial metadata to client.
+  /// This call is optional, but if it is used, it cannot be used concurrently
+  /// with or after the \a Finish method.
+  ///
+  /// The initial metadata that will be sent to the client will be
+  /// taken from the \a ServerContext associated with the call.
+  virtual void SendInitialMetadata() = 0;
+};
+
+/// An interface that yields a sequence of messages of type \a R.
+template <class R>
+class ReaderInterface {
+ public:
+  virtual ~ReaderInterface() {}
+
+  /// Get an upper bound on the next message size available for reading on this
+  /// stream.
+  virtual bool NextMessageSize(uint32_t* sz) = 0;
+
+  /// Block to 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
+  /// Read on the same stream as the order of delivery will not be defined.
+  ///
+  /// \param[out] msg The read message.
+  ///
+  /// \return \a false when there will be no more incoming messages, either
+  /// because the other side has called \a WritesDone() or the stream has failed
+  /// (or been cancelled).
+  virtual bool Read(R* msg) = 0;
+};
+
+/// An interface that can be fed a sequence of messages of type \a W.
+template <class W>
+class WriterInterface {
+ public:
+  virtual ~WriterInterface() {}
+
+  /// Block to write \a msg to the stream with WriteOptions \a options.
+  /// This is thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \param msg The message to be written to the stream.
+  /// \param options The WriteOptions affecting the write operation.
+  ///
+  /// \return \a true on success, \a false when the stream has been closed.
+  virtual bool Write(const W& msg, WriteOptions options) = 0;
+
+  /// Block to write \a msg to the stream with default write options.
+  /// This is thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \param msg The message to be written to the stream.
+  ///
+  /// \return \a true on success, \a false when the stream has been closed.
+  inline bool Write(const W& msg) { return Write(msg, WriteOptions()); }
+
+  /// Write \a msg and coalesce it with the writing of trailing metadata, using
+  /// WriteOptions \a options.
+  ///
+  /// For client, WriteLast is equivalent of performing Write and WritesDone in
+  /// a single step. \a msg and trailing metadata are coalesced and sent on wire
+  /// by calling this function. For server, WriteLast buffers the \a msg.
+  /// The writing of \a msg is held until the service handler returns,
+  /// where \a msg and trailing metadata are coalesced and sent on wire.
+  /// Note that WriteLast can only buffer \a msg up to the flow control window
+  /// size. If \a msg size is larger than the window size, it will be sent on
+  /// wire without buffering.
+  ///
+  /// \param[in] msg The message to be written to the stream.
+  /// \param[in] options The WriteOptions to be used to write this message.
+  void WriteLast(const W& msg, WriteOptions options) {
+    Write(msg, options.set_last_message());
+  }
+};
+
+}  // namespace internal
+
+/// Client-side interface for streaming reads of message of type \a R.
+template <class R>
+class ClientReaderInterface : public internal::ClientStreamingInterface,
+                              public internal::ReaderInterface<R> {
+ public:
+  /// Block to wait for initial metadata from server. The received metadata
+  /// can only be accessed after this call returns. Should only be called before
+  /// the first read. Calling this method is optional, and if it is not called
+  /// the metadata will be available in ClientContext after the first read.
+  virtual void WaitForInitialMetadata() = 0;
+};
+
+namespace internal {
+template <class R>
+class ClientReaderFactory {
+ public:
+  template <class W>
+  static ClientReader<R>* Create(ChannelInterface* channel,
+                                 const ::grpc::internal::RpcMethod& method,
+                                 ClientContext* context, const W& request) {
+    return new ClientReader<R>(channel, method, context, request);
+  }
+};
+}  // namespace internal
+
+/// Synchronous (blocking) client-side API for doing server-streaming RPCs,
+/// where the stream of messages coming from the server has messages
+/// of type \a R.
+template <class R>
+class ClientReader final : public ClientReaderInterface<R> {
+ public:
+  /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for
+  /// semantics.
+  ///
+  //  Side effect:
+  ///   Once complete, the initial metadata read from
+  ///   the server will be accessable through the \a ClientContext used to
+  ///   construct this object.
+  void WaitForInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+        ops;
+    ops.RecvInitialMetadata(context_);
+    call_.PerformOps(&ops);
+    cq_.Pluck(&ops);  /// status ignored
+  }
+
+  bool NextMessageSize(uint32_t* sz) override {
+    *sz = call_.max_receive_message_size();
+    return true;
+  }
+
+  /// See the \a ReaderInterface.Read method for semantics.
+  /// Side effect:
+  ///   This also receives initial metadata from the server, if not
+  ///   already received (if initial metadata is received, it can be then
+  ///   accessed through the \a ClientContext associated with this call).
+  bool Read(R* msg) override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                                ::grpc::internal::CallOpRecvMessage<R>>
+        ops;
+    if (!context_->initial_metadata_received_) {
+      ops.RecvInitialMetadata(context_);
+    }
+    ops.RecvMessage(msg);
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops) && ops.got_message;
+  }
+
+  /// See the \a ClientStreamingInterface.Finish method for semantics.
+  ///
+  /// Side effect:
+  ///   The \a ClientContext associated with this call is updated with
+  ///   possible metadata received from the server.
+  Status Finish() override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientRecvStatus> ops;
+    Status status;
+    ops.ClientRecvStatus(context_, &status);
+    call_.PerformOps(&ops);
+    GPR_CODEGEN_ASSERT(cq_.Pluck(&ops));
+    return status;
+  }
+
+ private:
+  friend class internal::ClientReaderFactory<R>;
+  ClientContext* context_;
+  CompletionQueue cq_;
+  ::grpc::internal::Call call_;
+
+  /// Block to create a stream and write the initial metadata and \a request
+  /// out. Note that \a context will be used to fill in custom initial
+  /// metadata used to send to the server when starting the call.
+  template <class W>
+  ClientReader(::grpc::ChannelInterface* channel,
+               const ::grpc::internal::RpcMethod& method,
+               ClientContext* context, const W& request)
+      : context_(context),
+        cq_(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING,
+            nullptr}),  // Pluckable cq
+        call_(channel->CreateCall(method, context, &cq_)) {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                                ::grpc::internal::CallOpSendMessage,
+                                ::grpc::internal::CallOpClientSendClose>
+        ops;
+    ops.SendInitialMetadata(&context->send_initial_metadata_,
+                            context->initial_metadata_flags());
+    // TODO(ctiller): don't assert
+    GPR_CODEGEN_ASSERT(ops.SendMessage(request).ok());
+    ops.ClientSendClose();
+    call_.PerformOps(&ops);
+    cq_.Pluck(&ops);
+  }
+};
+
+/// Client-side interface for streaming writes of message type \a W.
+template <class W>
+class ClientWriterInterface : public internal::ClientStreamingInterface,
+                              public internal::WriterInterface<W> {
+ public:
+  /// Half close writing from the client. (signal that the stream of messages
+  /// coming from the client is complete).
+  /// Blocks until currently-pending writes are completed.
+  /// Thread safe with respect to \a ReaderInterface::Read operations only
+  ///
+  /// \return Whether the writes were successful.
+  virtual bool WritesDone() = 0;
+};
+
+namespace internal {
+template <class W>
+class ClientWriterFactory {
+ public:
+  template <class R>
+  static ClientWriter<W>* Create(::grpc::ChannelInterface* channel,
+                                 const ::grpc::internal::RpcMethod& method,
+                                 ClientContext* context, R* response) {
+    return new ClientWriter<W>(channel, method, context, response);
+  }
+};
+}  // namespace internal
+
+/// Synchronous (blocking) client-side API for doing client-streaming RPCs,
+/// where the outgoing message stream coming from the client has messages of
+/// type \a W.
+template <class W>
+class ClientWriter : public ClientWriterInterface<W> {
+ public:
+  /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for
+  /// semantics.
+  ///
+  //  Side effect:
+  ///   Once complete, the initial metadata read from the server will be
+  ///   accessable through the \a ClientContext used to construct this object.
+  void WaitForInitialMetadata() {
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+        ops;
+    ops.RecvInitialMetadata(context_);
+    call_.PerformOps(&ops);
+    cq_.Pluck(&ops);  // status ignored
+  }
+
+  /// See the WriterInterface.Write(const W& msg, WriteOptions options) method
+  /// for semantics.
+  ///
+  /// Side effect:
+  ///   Also sends initial metadata if not already sent (using the
+  ///   \a ClientContext associated with this call).
+  using ::grpc::internal::WriterInterface<W>::Write;
+  bool Write(const W& msg, WriteOptions options) override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                                ::grpc::internal::CallOpSendMessage,
+                                ::grpc::internal::CallOpClientSendClose>
+        ops;
+
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      ops.ClientSendClose();
+    }
+    if (context_->initial_metadata_corked_) {
+      ops.SendInitialMetadata(&context_->send_initial_metadata_,
+                              context_->initial_metadata_flags());
+      context_->set_initial_metadata_corked(false);
+    }
+    if (!ops.SendMessage(msg, options).ok()) {
+      return false;
+    }
+
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops);
+  }
+
+  bool WritesDone() override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops;
+    ops.ClientSendClose();
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops);
+  }
+
+  /// See the ClientStreamingInterface.Finish method for semantics.
+  /// Side effects:
+  ///   - Also receives initial metadata if not already received.
+  ///   - Attempts to fill in the \a response parameter passed
+  ///     to the constructor of this instance with the response
+  ///     message from the server.
+  Status Finish() override {
+    Status status;
+    if (!context_->initial_metadata_received_) {
+      finish_ops_.RecvInitialMetadata(context_);
+    }
+    finish_ops_.ClientRecvStatus(context_, &status);
+    call_.PerformOps(&finish_ops_);
+    GPR_CODEGEN_ASSERT(cq_.Pluck(&finish_ops_));
+    return status;
+  }
+
+ private:
+  friend class internal::ClientWriterFactory<W>;
+
+  /// Block to create a stream (i.e. send request headers and other initial
+  /// metadata to the server). Note that \a context will be used to fill
+  /// in custom initial metadata. \a response will be filled in with the
+  /// single expected response message from the server upon a successful
+  /// call to the \a Finish method of this instance.
+  template <class R>
+  ClientWriter(ChannelInterface* channel,
+               const ::grpc::internal::RpcMethod& method,
+               ClientContext* context, R* response)
+      : context_(context),
+        cq_(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING,
+            nullptr}),  // Pluckable cq
+        call_(channel->CreateCall(method, context, &cq_)) {
+    finish_ops_.RecvMessage(response);
+    finish_ops_.AllowNoMessage();
+
+    if (!context_->initial_metadata_corked_) {
+      ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+          ops;
+      ops.SendInitialMetadata(&context->send_initial_metadata_,
+                              context->initial_metadata_flags());
+      call_.PerformOps(&ops);
+      cq_.Pluck(&ops);
+    }
+  }
+
+  ClientContext* context_;
+  ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                              ::grpc::internal::CallOpGenericRecvMessage,
+                              ::grpc::internal::CallOpClientRecvStatus>
+      finish_ops_;
+  CompletionQueue cq_;
+  ::grpc::internal::Call call_;
+};
+
+/// Client-side interface for bi-directional streaming with
+/// client-to-server stream messages of type \a W and
+/// server-to-client stream messages of type \a R.
+template <class W, class R>
+class ClientReaderWriterInterface : public internal::ClientStreamingInterface,
+                                    public internal::WriterInterface<W>,
+                                    public internal::ReaderInterface<R> {
+ public:
+  /// Block to wait for initial metadata from server. The received metadata
+  /// can only be accessed after this call returns. Should only be called before
+  /// the first read. Calling this method is optional, and if it is not called
+  /// the metadata will be available in ClientContext after the first read.
+  virtual void WaitForInitialMetadata() = 0;
+
+  /// Half close writing from the client. (signal that the stream of messages
+  /// coming from the clinet is complete).
+  /// Blocks until currently-pending writes are completed.
+  /// Thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \return Whether the writes were successful.
+  virtual bool WritesDone() = 0;
+};
+
+namespace internal {
+template <class W, class R>
+class ClientReaderWriterFactory {
+ public:
+  static ClientReaderWriter<W, R>* Create(
+      ::grpc::ChannelInterface* channel,
+      const ::grpc::internal::RpcMethod& method, ClientContext* context) {
+    return new ClientReaderWriter<W, R>(channel, method, context);
+  }
+};
+}  // namespace internal
+
+/// Synchronous (blocking) client-side API for bi-directional streaming RPCs,
+/// where the outgoing message stream coming from the client has messages of
+/// type \a W, and the incoming messages stream coming from the server has
+/// messages of type \a R.
+template <class W, class R>
+class ClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
+ public:
+  /// Block waiting to read initial metadata from the server.
+  /// This call is optional, but if it is used, it cannot be used concurrently
+  /// with or after the \a Finish method.
+  ///
+  /// Once complete, the initial metadata read from the server will be
+  /// accessable through the \a ClientContext used to construct this object.
+  void WaitForInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_);
+
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata>
+        ops;
+    ops.RecvInitialMetadata(context_);
+    call_.PerformOps(&ops);
+    cq_.Pluck(&ops);  // status ignored
+  }
+
+  bool NextMessageSize(uint32_t* sz) override {
+    *sz = call_.max_receive_message_size();
+    return true;
+  }
+
+  /// See the \a ReaderInterface.Read method for semantics.
+  /// Side effect:
+  ///   Also receives initial metadata if not already received (updates the \a
+  ///   ClientContext associated with this call in that case).
+  bool Read(R* msg) override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                                ::grpc::internal::CallOpRecvMessage<R>>
+        ops;
+    if (!context_->initial_metadata_received_) {
+      ops.RecvInitialMetadata(context_);
+    }
+    ops.RecvMessage(msg);
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops) && ops.got_message;
+  }
+
+  /// See the \a WriterInterface.Write method for semantics.
+  ///
+  /// Side effect:
+  ///   Also sends initial metadata if not already sent (using the
+  ///   \a ClientContext associated with this call to fill in values).
+  using ::grpc::internal::WriterInterface<W>::Write;
+  bool Write(const W& msg, WriteOptions options) override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
+                                ::grpc::internal::CallOpSendMessage,
+                                ::grpc::internal::CallOpClientSendClose>
+        ops;
+
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+      ops.ClientSendClose();
+    }
+    if (context_->initial_metadata_corked_) {
+      ops.SendInitialMetadata(&context_->send_initial_metadata_,
+                              context_->initial_metadata_flags());
+      context_->set_initial_metadata_corked(false);
+    }
+    if (!ops.SendMessage(msg, options).ok()) {
+      return false;
+    }
+
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops);
+  }
+
+  bool WritesDone() override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops;
+    ops.ClientSendClose();
+    call_.PerformOps(&ops);
+    return cq_.Pluck(&ops);
+  }
+
+  /// See the ClientStreamingInterface.Finish method for semantics.
+  ///
+  /// Side effect:
+  ///   - the \a ClientContext associated with this call is updated with
+  ///     possible trailing metadata sent from the server.
+  Status Finish() override {
+    ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata,
+                                ::grpc::internal::CallOpClientRecvStatus>
+        ops;
+    if (!context_->initial_metadata_received_) {
+      ops.RecvInitialMetadata(context_);
+    }
+    Status status;
+    ops.ClientRecvStatus(context_, &status);
+    call_.PerformOps(&ops);
+    GPR_CODEGEN_ASSERT(cq_.Pluck(&ops));
+    return status;
+  }
+
+ private:
+  friend class internal::ClientReaderWriterFactory<W, R>;
+
+  ClientContext* context_;
+  CompletionQueue cq_;
+  ::grpc::internal::Call call_;
+
+  /// Block to create a stream and write the initial metadata and \a request
+  /// out. Note that \a context will be used to fill in custom initial metadata
+  /// used to send to the server when starting the call.
+  ClientReaderWriter(::grpc::ChannelInterface* channel,
+                     const ::grpc::internal::RpcMethod& method,
+                     ClientContext* context)
+      : context_(context),
+        cq_(grpc_completion_queue_attributes{
+            GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING,
+            nullptr}),  // Pluckable cq
+        call_(channel->CreateCall(method, context, &cq_)) {
+    if (!context_->initial_metadata_corked_) {
+      ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata>
+          ops;
+      ops.SendInitialMetadata(&context->send_initial_metadata_,
+                              context->initial_metadata_flags());
+      call_.PerformOps(&ops);
+      cq_.Pluck(&ops);
+    }
+  }
+};
+
+/// Server-side interface for streaming reads of message of type \a R.
+template <class R>
+class ServerReaderInterface : public internal::ServerStreamingInterface,
+                              public internal::ReaderInterface<R> {};
+
+/// Synchronous (blocking) server-side API for doing client-streaming RPCs,
+/// where the incoming message stream coming from the client has messages of
+/// type \a R.
+template <class R>
+class ServerReader final : public ServerReaderInterface<R> {
+ public:
+  /// See the \a ServerStreamingInterface.SendInitialMetadata method
+  /// for semantics. Note that initial metadata will be affected by the
+  /// \a ServerContext associated with this call.
+  void SendInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    internal::CallOpSet<internal::CallOpSendInitialMetadata> ops;
+    ops.SendInitialMetadata(&ctx_->initial_metadata_,
+                            ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_->PerformOps(&ops);
+    call_->cq()->Pluck(&ops);
+  }
+
+  bool NextMessageSize(uint32_t* sz) override {
+    *sz = call_->max_receive_message_size();
+    return true;
+  }
+
+  bool Read(R* msg) override {
+    internal::CallOpSet<internal::CallOpRecvMessage<R>> ops;
+    ops.RecvMessage(msg);
+    call_->PerformOps(&ops);
+    return call_->cq()->Pluck(&ops) && ops.got_message;
+  }
+
+ private:
+  internal::Call* const call_;
+  ServerContext* const ctx_;
+
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class internal::ClientStreamingHandler;
+
+  ServerReader(internal::Call* call, ServerContext* ctx)
+      : call_(call), ctx_(ctx) {}
+};
+
+/// Server-side interface for streaming writes of message of type \a W.
+template <class W>
+class ServerWriterInterface : public internal::ServerStreamingInterface,
+                              public internal::WriterInterface<W> {};
+
+/// Synchronous (blocking) server-side API for doing for doing a
+/// server-streaming RPCs, where the outgoing message stream coming from the
+/// server has messages of type \a W.
+template <class W>
+class ServerWriter final : public ServerWriterInterface<W> {
+ public:
+  /// See the \a ServerStreamingInterface.SendInitialMetadata method
+  /// for semantics.
+  /// Note that initial metadata will be affected by the
+  /// \a ServerContext associated with this call.
+  void SendInitialMetadata() override {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    internal::CallOpSet<internal::CallOpSendInitialMetadata> ops;
+    ops.SendInitialMetadata(&ctx_->initial_metadata_,
+                            ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_->PerformOps(&ops);
+    call_->cq()->Pluck(&ops);
+  }
+
+  /// See the \a WriterInterface.Write method for semantics.
+  ///
+  /// Side effect:
+  ///   Also sends initial metadata if not already sent (using the
+  ///   \a ClientContext associated with this call to fill in values).
+  using internal::WriterInterface<W>::Write;
+  bool Write(const W& msg, WriteOptions options) override {
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+    }
+
+    if (!ctx_->pending_ops_.SendMessage(msg, options).ok()) {
+      return false;
+    }
+    if (!ctx_->sent_initial_metadata_) {
+      ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                             ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    call_->PerformOps(&ctx_->pending_ops_);
+    // if this is the last message we defer the pluck until AFTER we start
+    // the trailing md op. This prevents hangs. See
+    // https://github.com/grpc/grpc/issues/11546
+    if (options.is_last_message()) {
+      ctx_->has_pending_ops_ = true;
+      return true;
+    }
+    ctx_->has_pending_ops_ = false;
+    return call_->cq()->Pluck(&ctx_->pending_ops_);
+  }
+
+ private:
+  internal::Call* const call_;
+  ServerContext* const ctx_;
+
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class internal::ServerStreamingHandler;
+
+  ServerWriter(internal::Call* call, ServerContext* ctx)
+      : call_(call), ctx_(ctx) {}
+};
+
+/// Server-side interface for bi-directional streaming.
+template <class W, class R>
+class ServerReaderWriterInterface : public internal::ServerStreamingInterface,
+                                    public internal::WriterInterface<W>,
+                                    public internal::ReaderInterface<R> {};
+
+/// Actual implementation of bi-directional streaming
+namespace internal {
+template <class W, class R>
+class ServerReaderWriterBody final {
+ public:
+  ServerReaderWriterBody(Call* call, ServerContext* ctx)
+      : call_(call), ctx_(ctx) {}
+
+  void SendInitialMetadata() {
+    GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_);
+
+    CallOpSet<CallOpSendInitialMetadata> ops;
+    ops.SendInitialMetadata(&ctx_->initial_metadata_,
+                            ctx_->initial_metadata_flags());
+    if (ctx_->compression_level_set()) {
+      ops.set_compression_level(ctx_->compression_level());
+    }
+    ctx_->sent_initial_metadata_ = true;
+    call_->PerformOps(&ops);
+    call_->cq()->Pluck(&ops);
+  }
+
+  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;
+  }
+
+  bool Write(const W& msg, WriteOptions options) {
+    if (options.is_last_message()) {
+      options.set_buffer_hint();
+    }
+    if (!ctx_->pending_ops_.SendMessage(msg, options).ok()) {
+      return false;
+    }
+    if (!ctx_->sent_initial_metadata_) {
+      ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
+                                             ctx_->initial_metadata_flags());
+      if (ctx_->compression_level_set()) {
+        ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
+      }
+      ctx_->sent_initial_metadata_ = true;
+    }
+    call_->PerformOps(&ctx_->pending_ops_);
+    // if this is the last message we defer the pluck until AFTER we start
+    // the trailing md op. This prevents hangs. See
+    // https://github.com/grpc/grpc/issues/11546
+    if (options.is_last_message()) {
+      ctx_->has_pending_ops_ = true;
+      return true;
+    }
+    ctx_->has_pending_ops_ = false;
+    return call_->cq()->Pluck(&ctx_->pending_ops_);
+  }
+
+ private:
+  Call* const call_;
+  ServerContext* const ctx_;
+};
+
+}  // namespace internal
+
+/// Synchronous (blocking) server-side API for a bidirectional
+/// streaming call, where the incoming message stream coming from the client has
+/// messages of type \a R, and the outgoing message streaming coming from
+/// the server has messages of type \a W.
+template <class W, class R>
+class ServerReaderWriter final : public ServerReaderWriterInterface<W, R> {
+ public:
+  /// See the \a ServerStreamingInterface.SendInitialMetadata method
+  /// for semantics. Note that initial metadata will be affected by the
+  /// \a ServerContext associated with this call.
+  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
+
+  bool NextMessageSize(uint32_t* sz) override {
+    return body_.NextMessageSize(sz);
+  }
+
+  bool Read(R* msg) override { return body_.Read(msg); }
+
+  /// See the \a WriterInterface.Write(const W& msg, WriteOptions options)
+  /// method for semantics.
+  /// Side effect:
+  ///   Also sends initial metadata if not already sent (using the \a
+  ///   ServerContext associated with this call).
+  using internal::WriterInterface<W>::Write;
+  bool Write(const W& msg, WriteOptions options) override {
+    return body_.Write(msg, options);
+  }
+
+ private:
+  internal::ServerReaderWriterBody<W, R> body_;
+
+  friend class internal::TemplatedBidiStreamingHandler<ServerReaderWriter<W, R>,
+                                                       false>;
+  ServerReaderWriter(internal::Call* call, ServerContext* ctx)
+      : body_(call, ctx) {}
+};
+
+/// 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 final
+    : public ServerReaderWriterInterface<ResponseType, RequestType> {
+ public:
+  /// Block to send initial metadata to client.
+  /// Implicit input parameter:
+  ///    - the \a ServerContext associated with this call will be used for
+  ///      sending initial metadata.
+  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
+
+  /// Get an upper bound on the request message size from the client.
+  bool NextMessageSize(uint32_t* sz) override {
+    return body_.NextMessageSize(sz);
+  }
+
+  /// Read a message of type \a R into \a msg. Completion will be notified by \a
+  /// tag on the associated completion queue.
+  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
+  /// should not be called concurrently with other streaming APIs
+  /// on the same stream. It is not meaningful to call it concurrently
+  /// with another \a ReaderInterface::Read on the same stream since reads on
+  /// the same stream are delivered in order.
+  ///
+  /// \param[out] msg Where to eventually store the read message.
+  /// \param[in] tag The tag identifying the operation.
+  bool Read(RequestType* request) override {
+    if (read_done_) {
+      return false;
+    }
+    read_done_ = true;
+    return body_.Read(request);
+  }
+
+  /// Block to write \a msg to the stream with WriteOptions \a options.
+  /// This is thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \param msg The message to be written to the stream.
+  /// \param options The WriteOptions affecting the write operation.
+  ///
+  /// \return \a true on success, \a false when the stream has been closed.
+  using internal::WriterInterface<ResponseType>::Write;
+  bool Write(const ResponseType& response, WriteOptions options) 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_;
+
+  friend class internal::TemplatedBidiStreamingHandler<
+      ServerUnaryStreamer<RequestType, ResponseType>, true>;
+  ServerUnaryStreamer(internal::Call* call, ServerContext* ctx)
+      : body_(call, ctx), read_done_(false), write_done_(false) {}
+};
+
+/// A class to represent a flow-controlled server-side streaming call.
+/// This is something of a hybrid between server-side and bidi streaming.
+/// This is invoked through a server-side streaming call on the client side,
+/// but the server responds to it as though it were a bidi streaming call that
+/// must first have exactly 1 Read and then any number of Writes.
+template <class RequestType, class ResponseType>
+class ServerSplitStreamer final
+    : public ServerReaderWriterInterface<ResponseType, RequestType> {
+ public:
+  /// Block to send initial metadata to client.
+  /// Implicit input parameter:
+  ///    - the \a ServerContext associated with this call will be used for
+  ///      sending initial metadata.
+  void SendInitialMetadata() override { body_.SendInitialMetadata(); }
+
+  /// Get an upper bound on the request message size from the client.
+  bool NextMessageSize(uint32_t* sz) override {
+    return body_.NextMessageSize(sz);
+  }
+
+  /// Read a message of type \a R into \a msg. Completion will be notified by \a
+  /// tag on the associated completion queue.
+  /// This is thread-safe with respect to \a Write or \a WritesDone methods. It
+  /// should not be called concurrently with other streaming APIs
+  /// on the same stream. It is not meaningful to call it concurrently
+  /// with another \a ReaderInterface::Read on the same stream since reads on
+  /// the same stream are delivered in order.
+  ///
+  /// \param[out] msg Where to eventually store the read message.
+  /// \param[in] tag The tag identifying the operation.
+  bool Read(RequestType* request) override {
+    if (read_done_) {
+      return false;
+    }
+    read_done_ = true;
+    return body_.Read(request);
+  }
+
+  /// Block to write \a msg to the stream with WriteOptions \a options.
+  /// This is thread-safe with respect to \a ReaderInterface::Read
+  ///
+  /// \param msg The message to be written to the stream.
+  /// \param options The WriteOptions affecting the write operation.
+  ///
+  /// \return \a true on success, \a false when the stream has been closed.
+  using internal::WriterInterface<ResponseType>::Write;
+  bool Write(const ResponseType& response, WriteOptions options) override {
+    return read_done_ && body_.Write(response, options);
+  }
+
+ private:
+  internal::ServerReaderWriterBody<ResponseType, RequestType> body_;
+  bool read_done_;
+
+  friend class internal::TemplatedBidiStreamingHandler<
+      ServerSplitStreamer<RequestType, ResponseType>, false>;
+  ServerSplitStreamer(internal::Call* call, ServerContext* ctx)
+      : body_(call, ctx), read_done_(false) {}
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H
diff --git a/third_party/grpc/include/grpcpp/impl/codegen/time.h b/third_party/grpc/include/grpcpp/impl/codegen/time.h
new file mode 100644
index 0000000..c32f254
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/codegen/time.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_CODEGEN_TIME_H
+#define GRPCPP_IMPL_CODEGEN_TIME_H
+
+#include <chrono>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpcpp/impl/codegen/config.h>
+
+namespace grpc {
+
+/** If you are trying to use CompletionQueue::AsyncNext with a time class that
+    isn't either gpr_timespec or std::chrono::system_clock::time_point, you
+    will most likely be looking at this comment as your compiler will have
+    fired an error below. In order to fix this issue, you have two potential
+    solutions:
+
+      1. Use gpr_timespec or std::chrono::system_clock::time_point instead
+      2. Specialize the TimePoint class with whichever time class that you
+         want to use here. See below for two examples of how to do this.
+ */
+template <typename T>
+class TimePoint {
+ public:
+  TimePoint(const T& time) { you_need_a_specialization_of_TimePoint(); }
+  gpr_timespec raw_time() {
+    gpr_timespec t;
+    return t;
+  }
+
+ private:
+  void you_need_a_specialization_of_TimePoint();
+};
+
+template <>
+class TimePoint<gpr_timespec> {
+ public:
+  TimePoint(const gpr_timespec& time) : time_(time) {}
+  gpr_timespec raw_time() { return time_; }
+
+ private:
+  gpr_timespec time_;
+};
+
+}  // namespace grpc
+
+namespace grpc {
+
+// from and to should be absolute time.
+void Timepoint2Timespec(const std::chrono::system_clock::time_point& from,
+                        gpr_timespec* to);
+void TimepointHR2Timespec(
+    const std::chrono::high_resolution_clock::time_point& from,
+    gpr_timespec* to);
+
+std::chrono::system_clock::time_point Timespec2Timepoint(gpr_timespec t);
+
+template <>
+class TimePoint<std::chrono::system_clock::time_point> {
+ public:
+  TimePoint(const std::chrono::system_clock::time_point& time) {
+    Timepoint2Timespec(time, &time_);
+  }
+  gpr_timespec raw_time() const { return time_; }
+
+ private:
+  gpr_timespec time_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_CODEGEN_TIME_H
diff --git a/third_party/grpc/include/grpcpp/impl/grpc_library.h b/third_party/grpc/include/grpcpp/impl/grpc_library.h
new file mode 100644
index 0000000..d1f3ff1
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/grpc_library.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_GRPC_LIBRARY_H
+#define GRPCPP_IMPL_GRPC_LIBRARY_H
+
+#include <iostream>
+
+#include <grpc/grpc.h>
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/core_codegen.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+namespace grpc {
+
+namespace internal {
+class GrpcLibrary final : public GrpcLibraryInterface {
+ public:
+  void init() override { grpc_init(); }
+  void shutdown() override { grpc_shutdown(); }
+};
+
+static GrpcLibrary g_gli;
+static CoreCodegen g_core_codegen;
+
+/// Instantiating this class ensures the proper initialization of gRPC.
+class GrpcLibraryInitializer final {
+ public:
+  GrpcLibraryInitializer() {
+    if (grpc::g_glip == nullptr) {
+      grpc::g_glip = &g_gli;
+    }
+    if (grpc::g_core_codegen_interface == nullptr) {
+      grpc::g_core_codegen_interface = &g_core_codegen;
+    }
+  }
+
+  /// A no-op method to force the linker to reference this class, which will
+  /// take care of initializing and shutting down the gRPC runtime.
+  int summon() { return 0; }
+};
+
+}  // namespace internal
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_GRPC_LIBRARY_H
diff --git a/third_party/grpc/include/grpcpp/impl/method_handler_impl.h b/third_party/grpc/include/grpcpp/impl/method_handler_impl.h
new file mode 100644
index 0000000..7f3be64
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/method_handler_impl.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_METHOD_HANDLER_IMPL_H
+#define GRPCPP_IMPL_METHOD_HANDLER_IMPL_H
+
+#include <grpcpp/impl/codegen/method_handler_impl.h>
+
+#endif  // GRPCPP_IMPL_METHOD_HANDLER_IMPL_H
diff --git a/third_party/grpc/include/grpcpp/impl/rpc_method.h b/third_party/grpc/include/grpcpp/impl/rpc_method.h
new file mode 100644
index 0000000..5da7041
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/rpc_method.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_RPC_METHOD_H
+#define GRPCPP_IMPL_RPC_METHOD_H
+
+#include <grpcpp/impl/codegen/rpc_method.h>
+
+#endif  // GRPCPP_IMPL_RPC_METHOD_H
diff --git a/third_party/grpc/include/grpcpp/impl/rpc_service_method.h b/third_party/grpc/include/grpcpp/impl/rpc_service_method.h
new file mode 100644
index 0000000..ef70a3a
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/rpc_service_method.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_RPC_SERVICE_METHOD_H
+#define GRPCPP_IMPL_RPC_SERVICE_METHOD_H
+
+#include <grpcpp/impl/codegen/rpc_service_method.h>
+
+#endif  // GRPCPP_IMPL_RPC_SERVICE_METHOD_H
diff --git a/third_party/grpc/include/grpcpp/impl/serialization_traits.h b/third_party/grpc/include/grpcpp/impl/serialization_traits.h
new file mode 100644
index 0000000..95194fb
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/serialization_traits.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_SERIALIZATION_TRAITS_H
+#define GRPCPP_IMPL_SERIALIZATION_TRAITS_H
+
+#include <grpcpp/impl/codegen/serialization_traits.h>
+
+#endif  // GRPCPP_IMPL_SERIALIZATION_TRAITS_H
diff --git a/third_party/grpc/include/grpcpp/impl/server_builder_option.h b/third_party/grpc/include/grpcpp/impl/server_builder_option.h
new file mode 100644
index 0000000..c7b7801
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/server_builder_option.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_SERVER_BUILDER_OPTION_H
+#define GRPCPP_IMPL_SERVER_BUILDER_OPTION_H
+
+#include <map>
+#include <memory>
+
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/support/channel_arguments.h>
+
+namespace grpc {
+
+/// Interface to pass an option to a \a ServerBuilder.
+class ServerBuilderOption {
+ public:
+  virtual ~ServerBuilderOption() {}
+  /// Alter the \a ChannelArguments used to create the gRPC server.
+  virtual void UpdateArguments(ChannelArguments* args) = 0;
+  /// Alter the ServerBuilderPlugin map that will be added into ServerBuilder.
+  virtual void UpdatePlugins(
+      std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) = 0;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_SERVER_BUILDER_OPTION_H
diff --git a/third_party/grpc/include/grpcpp/impl/server_builder_plugin.h b/third_party/grpc/include/grpcpp/impl/server_builder_plugin.h
new file mode 100644
index 0000000..39450b4
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/server_builder_plugin.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H
+#define GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H
+
+#include <memory>
+
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+
+class ServerBuilder;
+class ServerInitializer;
+class ChannelArguments;
+
+/// This interface is meant for internal usage only. Implementations of this
+/// interface should add themselves to a \a ServerBuilder instance through the
+/// \a InternalAddPluginFactory method.
+class ServerBuilderPlugin {
+ public:
+  virtual ~ServerBuilderPlugin() {}
+  virtual grpc::string name() = 0;
+
+  /// UpdateServerBuilder will be called at an early stage in
+  /// ServerBuilder::BuildAndStart(), right after the ServerBuilderOptions have
+  /// done their updates.
+  virtual void UpdateServerBuilder(ServerBuilder* builder) {}
+
+  /// InitServer will be called in ServerBuilder::BuildAndStart(), after the
+  /// Server instance is created.
+  virtual void InitServer(ServerInitializer* si) = 0;
+
+  /// Finish will be called at the end of ServerBuilder::BuildAndStart().
+  virtual void Finish(ServerInitializer* si) = 0;
+
+  /// ChangeArguments is an interface that can be used in
+  /// 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; }
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H
diff --git a/third_party/grpc/include/grpcpp/impl/server_initializer.h b/third_party/grpc/include/grpcpp/impl/server_initializer.h
new file mode 100644
index 0000000..f949fab
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/server_initializer.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_SERVER_INITIALIZER_H
+#define GRPCPP_IMPL_SERVER_INITIALIZER_H
+
+#include <memory>
+#include <vector>
+
+#include <grpcpp/server.h>
+
+namespace grpc {
+
+class Server;
+class Service;
+
+class ServerInitializer {
+ public:
+  ServerInitializer(Server* server) : server_(server) {}
+
+  bool RegisterService(std::shared_ptr<Service> service) {
+    if (!server_->RegisterService(nullptr, service.get())) {
+      return false;
+    }
+    default_services_.push_back(service);
+    return true;
+  }
+
+  const std::vector<grpc::string>* GetServiceList() {
+    return &server_->services_;
+  }
+
+ private:
+  Server* server_;
+  std::vector<std::shared_ptr<Service> > default_services_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_IMPL_SERVER_INITIALIZER_H
diff --git a/third_party/grpc/include/grpcpp/impl/service_type.h b/third_party/grpc/include/grpcpp/impl/service_type.h
new file mode 100644
index 0000000..250bc8c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/service_type.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_SERVICE_TYPE_H
+#define GRPCPP_IMPL_SERVICE_TYPE_H
+
+#include <grpcpp/impl/codegen/service_type.h>
+
+#endif  // GRPCPP_IMPL_SERVICE_TYPE_H
diff --git a/third_party/grpc/include/grpcpp/impl/sync_cxx11.h b/third_party/grpc/include/grpcpp/impl/sync_cxx11.h
new file mode 100644
index 0000000..76dcfe3
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/sync_cxx11.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_SYNC_CXX11_H
+#define GRPCPP_IMPL_SYNC_CXX11_H
+
+#include <grpcpp/impl/codegen/sync_cxx11.h>
+
+#endif  // GRPCPP_IMPL_SYNC_CXX11_H
diff --git a/third_party/grpc/include/grpcpp/impl/sync_no_cxx11.h b/third_party/grpc/include/grpcpp/impl/sync_no_cxx11.h
new file mode 100644
index 0000000..cc2d4f1
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/impl/sync_no_cxx11.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_IMPL_SYNC_NO_CXX11_H
+#define GRPCPP_IMPL_SYNC_NO_CXX11_H
+
+#include <grpcpp/impl/codegen/sync_no_cxx11.h>
+
+#endif  // GRPCPP_IMPL_SYNC_NO_CXX11_H
diff --git a/third_party/grpc/include/grpcpp/opencensus.h b/third_party/grpc/include/grpcpp/opencensus.h
new file mode 100644
index 0000000..29b221f
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/opencensus.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_OPENCENSUS_H
+#define GRPCPP_OPENCENSUS_H
+
+#include "opencensus/trace/span.h"
+
+namespace grpc {
+// These symbols in this file will not be included in the binary unless
+// grpc_opencensus_plugin build target was added as a dependency. At the moment
+// it is only setup to be built with Bazel.
+
+// Registers the OpenCensus plugin with gRPC, so that it will be used for future
+// RPCs. This must be called before any views are created.
+void RegisterOpenCensusPlugin();
+
+// RPC stats definitions, defined by
+// https://github.com/census-instrumentation/opencensus-specs/blob/master/stats/gRPC.md
+
+// Registers the cumulative gRPC views so that they will be exported by any
+// registered stats exporter. For on-task stats, construct a View using the
+// ViewDescriptors below.
+void RegisterOpenCensusViewsForExport();
+
+class ServerContext;
+
+// Returns the tracing Span for the current RPC.
+::opencensus::trace::Span GetSpanFromServerContext(ServerContext* context);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_OPENCENSUS_H
diff --git a/third_party/grpc/include/grpcpp/resource_quota.h b/third_party/grpc/include/grpcpp/resource_quota.h
new file mode 100644
index 0000000..50bd1cb
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/resource_quota.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_RESOURCE_QUOTA_H
+#define GRPCPP_RESOURCE_QUOTA_H
+
+struct grpc_resource_quota;
+
+#include <grpcpp/impl/codegen/config.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+namespace grpc {
+
+/// ResourceQuota represents a bound on memory and thread usage by the gRPC
+/// library. A ResourceQuota can be attached to a server (via \a ServerBuilder),
+/// or a client channel (via \a ChannelArguments).
+/// gRPC will attempt to keep memory and threads used by all attached entities
+/// below the ResourceQuota bound.
+class ResourceQuota final : private GrpcLibraryCodegen {
+ public:
+  /// \param name - a unique name for this ResourceQuota.
+  explicit ResourceQuota(const grpc::string& name);
+  ResourceQuota();
+  ~ResourceQuota();
+
+  /// Resize this \a ResourceQuota to a new size. If \a new_size is smaller
+  /// than the current size of the pool, memory usage will be monotonically
+  /// decreased until it falls under \a new_size.
+  /// No time bound is given for this to occur however.
+  ResourceQuota& Resize(size_t new_size);
+
+  /// Set the max number of threads that can be allocated from this
+  /// ResourceQuota object.
+  ///
+  /// If the new_max_threads value is smaller than the current value, no new
+  /// threads are allocated until the number of active threads fall below
+  /// new_max_threads. There is no time bound on when this may happen i.e none
+  /// of the current threads are forcefully destroyed and all threads run their
+  /// normal course.
+  ResourceQuota& SetMaxThreads(int new_max_threads);
+
+  grpc_resource_quota* c_resource_quota() const { return impl_; }
+
+ private:
+  ResourceQuota(const ResourceQuota& rhs);
+  ResourceQuota& operator=(const ResourceQuota& rhs);
+
+  grpc_resource_quota* const impl_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_RESOURCE_QUOTA_H
diff --git a/third_party/grpc/include/grpcpp/security/auth_context.h b/third_party/grpc/include/grpcpp/security/auth_context.h
new file mode 100644
index 0000000..7a6f2cb
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/security/auth_context.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SECURITY_AUTH_CONTEXT_H
+#define GRPCPP_SECURITY_AUTH_CONTEXT_H
+
+#include <grpcpp/impl/codegen/security/auth_context.h>
+
+#endif  // GRPCPP_SECURITY_AUTH_CONTEXT_H
diff --git a/third_party/grpc/include/grpcpp/security/auth_metadata_processor.h b/third_party/grpc/include/grpcpp/security/auth_metadata_processor.h
new file mode 100644
index 0000000..30e24c9
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/security/auth_metadata_processor.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H
+#define GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H
+
+#include <map>
+
+#include <grpcpp/security/auth_context.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/string_ref.h>
+
+namespace grpc {
+
+/// Interface allowing custom server-side authorization based on credentials
+/// encoded in metadata.  Objects of this type can be passed to
+/// \a ServerCredentials::SetAuthMetadataProcessor().
+class AuthMetadataProcessor {
+ public:
+  typedef std::multimap<grpc::string_ref, grpc::string_ref> InputMetadata;
+  typedef std::multimap<grpc::string, grpc::string> OutputMetadata;
+
+  virtual ~AuthMetadataProcessor() {}
+
+  /// If this method returns true, the \a Process function will be scheduled in
+  /// a different thread from the one processing the call.
+  virtual bool IsBlocking() const { return true; }
+
+  /// context is read/write: it contains the properties of the channel peer and
+  /// it is the job of the Process method to augment it with properties derived
+  /// from the passed-in auth_metadata.
+  /// consumed_auth_metadata needs to be filled with metadata that has been
+  /// consumed by the processor and will be removed from the call.
+  /// response_metadata is the metadata that will be sent as part of the
+  /// response.
+  /// If the return value is not Status::OK, the rpc call will be aborted with
+  /// the error code and error message sent back to the client.
+  virtual Status Process(const InputMetadata& auth_metadata,
+                         AuthContext* context,
+                         OutputMetadata* consumed_auth_metadata,
+                         OutputMetadata* response_metadata) = 0;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H
diff --git a/third_party/grpc/include/grpcpp/security/credentials.h b/third_party/grpc/include/grpcpp/security/credentials.h
new file mode 100644
index 0000000..d8c9e04
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/security/credentials.h
@@ -0,0 +1,277 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SECURITY_CREDENTIALS_H
+#define GRPCPP_SECURITY_CREDENTIALS_H
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpc/grpc_security_constants.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/security/auth_context.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/string_ref.h>
+
+struct grpc_call;
+
+namespace grpc {
+class ChannelArguments;
+class Channel;
+class SecureChannelCredentials;
+class CallCredentials;
+class SecureCallCredentials;
+
+class ChannelCredentials;
+
+namespace experimental {
+std::shared_ptr<Channel> CreateCustomChannelWithInterceptors(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds,
+    const ChannelArguments& args,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators);
+}  // namespace experimental
+
+/// A channel credentials object encapsulates all the state needed by a client
+/// to authenticate with a server for a given channel.
+/// It can make various assertions, e.g., about the client’s identity, role
+/// for all the calls on that channel.
+///
+/// \see https://grpc.io/docs/guides/auth.html
+class ChannelCredentials : private GrpcLibraryCodegen {
+ public:
+  ChannelCredentials();
+  ~ChannelCredentials();
+
+ protected:
+  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+      const std::shared_ptr<ChannelCredentials>& channel_creds,
+      const std::shared_ptr<CallCredentials>& call_creds);
+
+  virtual SecureChannelCredentials* AsSecureCredentials() = 0;
+
+ private:
+  friend std::shared_ptr<Channel> CreateCustomChannel(
+      const grpc::string& target,
+      const std::shared_ptr<ChannelCredentials>& creds,
+      const ChannelArguments& args);
+
+  friend std::shared_ptr<Channel>
+  experimental::CreateCustomChannelWithInterceptors(
+      const grpc::string& target,
+      const std::shared_ptr<ChannelCredentials>& creds,
+      const ChannelArguments& args,
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators);
+
+  virtual std::shared_ptr<Channel> CreateChannel(
+      const grpc::string& target, const ChannelArguments& args) = 0;
+
+  // This function should have been a pure virtual function, but it is
+  // implemented as a virtual function so that it does not break API.
+  virtual std::shared_ptr<Channel> CreateChannelWithInterceptors(
+      const grpc::string& target, const ChannelArguments& args,
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators) {
+    return nullptr;
+  };
+};
+
+/// A call credentials object encapsulates the state needed by a client to
+/// authenticate with a server for a given call on a channel.
+///
+/// \see https://grpc.io/docs/guides/auth.html
+class CallCredentials : private GrpcLibraryCodegen {
+ public:
+  CallCredentials();
+  ~CallCredentials();
+
+  /// Apply this instance's credentials to \a call.
+  virtual bool ApplyToCall(grpc_call* call) = 0;
+
+ protected:
+  friend std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+      const std::shared_ptr<ChannelCredentials>& channel_creds,
+      const std::shared_ptr<CallCredentials>& call_creds);
+
+  friend std::shared_ptr<CallCredentials> CompositeCallCredentials(
+      const std::shared_ptr<CallCredentials>& creds1,
+      const std::shared_ptr<CallCredentials>& creds2);
+
+  virtual SecureCallCredentials* AsSecureCredentials() = 0;
+};
+
+/// Options used to build SslCredentials.
+struct SslCredentialsOptions {
+  /// The buffer containing the PEM encoding of the server root certificates. If
+  /// this parameter is empty, the default roots will be used.  The default
+  /// roots can be overridden using the \a GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
+  /// environment variable pointing to a file on the file system containing the
+  /// roots.
+  grpc::string pem_root_certs;
+
+  /// The buffer containing the PEM encoding of the client's private key. This
+  /// parameter can be empty if the client does not have a private key.
+  grpc::string pem_private_key;
+
+  /// The buffer containing the PEM encoding of the client's certificate chain.
+  /// This parameter can be empty if the client does not have a certificate
+  /// chain.
+  grpc::string pem_cert_chain;
+};
+
+// Factories for building different types of Credentials The functions may
+// return empty shared_ptr when credentials cannot be created. If a
+// Credentials pointer is returned, it can still be invalid when used to create
+// a channel. A lame channel will be created then and all rpcs will fail on it.
+
+/// Builds credentials with reasonable defaults.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials();
+
+/// Builds SSL Credentials given SSL specific options
+std::shared_ptr<ChannelCredentials> SslCredentials(
+    const SslCredentialsOptions& options);
+
+/// Builds credentials for use when running in GCE
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials();
+
+/// Constant for maximum auth token lifetime.
+constexpr long kMaxAuthTokenLifetimeSecs = 3600;
+
+/// Builds Service Account JWT Access credentials.
+/// json_key is the JSON key string containing the client's private key.
+/// token_lifetime_seconds is the lifetime in seconds of each Json Web Token
+/// (JWT) created with this credentials. It should not exceed
+/// \a kMaxAuthTokenLifetimeSecs or will be cropped to this value.
+std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
+    const grpc::string& json_key,
+    long token_lifetime_seconds = kMaxAuthTokenLifetimeSecs);
+
+/// Builds refresh token credentials.
+/// json_refresh_token is the JSON string containing the refresh token along
+/// with a client_id and client_secret.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
+    const grpc::string& json_refresh_token);
+
+/// Builds access token credentials.
+/// access_token is an oauth2 access token that was fetched using an out of band
+/// mechanism.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> AccessTokenCredentials(
+    const grpc::string& access_token);
+
+/// Builds IAM credentials.
+///
+/// \warning Only use these credentials when connecting to a Google endpoint.
+/// Using these credentials to connect to any other service may result in this
+/// service being able to impersonate your client for requests to Google
+/// services.
+std::shared_ptr<CallCredentials> GoogleIAMCredentials(
+    const grpc::string& authorization_token,
+    const grpc::string& authority_selector);
+
+/// Combines a channel credentials and a call credentials into a composite
+/// channel credentials.
+std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+    const std::shared_ptr<ChannelCredentials>& channel_creds,
+    const std::shared_ptr<CallCredentials>& call_creds);
+
+/// Combines two call credentials objects into a composite call credentials.
+std::shared_ptr<CallCredentials> CompositeCallCredentials(
+    const std::shared_ptr<CallCredentials>& creds1,
+    const std::shared_ptr<CallCredentials>& creds2);
+
+/// Credentials for an unencrypted, unauthenticated channel
+std::shared_ptr<ChannelCredentials> InsecureChannelCredentials();
+
+/// Credentials for a channel using Cronet.
+std::shared_ptr<ChannelCredentials> CronetChannelCredentials(void* engine);
+
+/// User defined metadata credentials.
+class MetadataCredentialsPlugin {
+ public:
+  virtual ~MetadataCredentialsPlugin() {}
+
+  /// If this method returns true, the Process function will be scheduled in
+  /// a different thread from the one processing the call.
+  virtual bool IsBlocking() const { return true; }
+
+  /// Type of credentials this plugin is implementing.
+  virtual const char* GetType() const { return ""; }
+
+  /// Gets the auth metatada produced by this plugin.
+  /// The fully qualified method name is:
+  /// service_url + "/" + method_name.
+  /// The channel_auth_context contains (among other things), the identity of
+  /// the server.
+  virtual Status GetMetadata(
+      grpc::string_ref service_url, grpc::string_ref method_name,
+      const AuthContext& channel_auth_context,
+      std::multimap<grpc::string, grpc::string>* metadata) = 0;
+};
+
+std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
+    std::unique_ptr<MetadataCredentialsPlugin> plugin);
+
+namespace experimental {
+
+/// Options used to build AltsCredentials.
+struct AltsCredentialsOptions {
+  /// service accounts of target endpoint that will be acceptable
+  /// by the client. If service accounts are provided and none of them matches
+  /// that of the server, authentication will fail.
+  std::vector<grpc::string> target_service_accounts;
+};
+
+/// Builds ALTS Credentials given ALTS specific options
+std::shared_ptr<ChannelCredentials> AltsCredentials(
+    const AltsCredentialsOptions& options);
+
+/// Builds Local Credentials.
+std::shared_ptr<ChannelCredentials> LocalCredentials(
+    grpc_local_connect_type type);
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif  // GRPCPP_SECURITY_CREDENTIALS_H
diff --git a/third_party/grpc/include/grpcpp/security/server_credentials.h b/third_party/grpc/include/grpcpp/security/server_credentials.h
new file mode 100644
index 0000000..bd00a0a
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/security/server_credentials.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SECURITY_SERVER_CREDENTIALS_H
+#define GRPCPP_SECURITY_SERVER_CREDENTIALS_H
+
+#include <memory>
+#include <vector>
+
+#include <grpc/grpc_security_constants.h>
+#include <grpcpp/security/auth_metadata_processor.h>
+#include <grpcpp/support/config.h>
+
+struct grpc_server;
+
+namespace grpc {
+class Server;
+
+/// Wrapper around \a grpc_server_credentials, a way to authenticate a server.
+class ServerCredentials {
+ public:
+  virtual ~ServerCredentials();
+
+  /// This method is not thread-safe and has to be called before the server is
+  /// started. The last call to this function wins.
+  virtual void SetAuthMetadataProcessor(
+      const std::shared_ptr<AuthMetadataProcessor>& processor) = 0;
+
+ private:
+  friend class ::grpc::Server;
+
+  /// Tries to bind \a server to the given \a addr (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.)
+  ///
+  /// \return bound port number on sucess, 0 on failure.
+  // TODO(dgq): the "port" part seems to be a misnomer.
+  virtual int AddPortToServer(const grpc::string& addr,
+                              grpc_server* server) = 0;
+};
+
+/// Options to create ServerCredentials with SSL
+struct SslServerCredentialsOptions {
+  /// \warning Deprecated
+  SslServerCredentialsOptions()
+      : force_client_auth(false),
+        client_certificate_request(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE) {}
+  SslServerCredentialsOptions(
+      grpc_ssl_client_certificate_request_type request_type)
+      : force_client_auth(false), client_certificate_request(request_type) {}
+
+  struct PemKeyCertPair {
+    grpc::string private_key;
+    grpc::string cert_chain;
+  };
+  grpc::string pem_root_certs;
+  std::vector<PemKeyCertPair> pem_key_cert_pairs;
+  /// \warning Deprecated
+  bool force_client_auth;
+
+  /// If both \a force_client_auth and \a client_certificate_request
+  /// fields are set, \a force_client_auth takes effect, i.e.
+  /// \a REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+  /// will be enforced.
+  grpc_ssl_client_certificate_request_type client_certificate_request;
+};
+
+/// Builds SSL ServerCredentials given SSL specific options
+std::shared_ptr<ServerCredentials> SslServerCredentials(
+    const SslServerCredentialsOptions& options);
+
+/// Builds insecure server credentials.
+std::shared_ptr<ServerCredentials> InsecureServerCredentials();
+
+namespace experimental {
+
+/// Options to create ServerCredentials with ALTS
+struct AltsServerCredentialsOptions {
+  /// Add fields if needed.
+};
+
+/// Builds ALTS ServerCredentials given ALTS specific options
+std::shared_ptr<ServerCredentials> AltsServerCredentials(
+    const AltsServerCredentialsOptions& options);
+
+/// Builds Local ServerCredentials.
+std::shared_ptr<ServerCredentials> LocalServerCredentials(
+    grpc_local_connect_type type);
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif  // GRPCPP_SECURITY_SERVER_CREDENTIALS_H
diff --git a/third_party/grpc/include/grpcpp/server.h b/third_party/grpc/include/grpcpp/server.h
new file mode 100644
index 0000000..cdcac18
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/server.h
@@ -0,0 +1,288 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SERVER_H
+#define GRPCPP_SERVER_H
+
+#include <condition_variable>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+#include <grpcpp/impl/codegen/server_interface.h>
+#include <grpcpp/impl/rpc_service_method.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/status.h>
+
+struct grpc_server;
+
+namespace grpc {
+
+class AsyncGenericService;
+class HealthCheckServiceInterface;
+class ServerContext;
+class ServerInitializer;
+
+/// Represents a gRPC server.
+///
+/// Use a \a grpc::ServerBuilder to create, configure, and start
+/// \a Server instances.
+class Server : public ServerInterface, private GrpcLibraryCodegen {
+ public:
+  ~Server();
+
+  /// Block until the server shuts down.
+  ///
+  /// \warning The server must be either shutting down or some other thread must
+  /// call \a Shutdown for this function to ever return.
+  void Wait() override;
+
+  /// Global callbacks are a set of hooks that are called when server
+  /// events occur.  \a SetGlobalCallbacks method is used to register
+  /// the hooks with gRPC.  Note that
+  /// the \a GlobalCallbacks instance will be shared among all
+  /// \a Server instances in an application and can be set exactly
+  /// once per application.
+  class GlobalCallbacks {
+   public:
+    virtual ~GlobalCallbacks() {}
+    /// Called before server is created.
+    virtual void UpdateArguments(ChannelArguments* args) {}
+    /// Called before application callback for each synchronous server request
+    virtual void PreSynchronousRequest(ServerContext* context) = 0;
+    /// Called after application callback for each synchronous server request
+    virtual void PostSynchronousRequest(ServerContext* context) = 0;
+    /// Called before server is started.
+    virtual void PreServerStart(Server* server) {}
+    /// Called after a server port is added.
+    virtual void AddPort(Server* server, const grpc::string& addr,
+                         ServerCredentials* creds, int port) {}
+  };
+  /// Set the global callback object. Can only be called once per application.
+  /// Does not take ownership of callbacks, and expects the pointed to object
+  /// to be alive until all server objects in the process have been destroyed.
+  /// The same \a GlobalCallbacks object will be used throughout the
+  /// application and is shared among all \a Server objects.
+  static void SetGlobalCallbacks(GlobalCallbacks* callbacks);
+
+  /// Returns a \em raw pointer to the underlying \a grpc_server instance.
+  /// EXPERIMENTAL:  for internal/test use only
+  grpc_server* c_server();
+
+  /// Returns the health check service.
+  HealthCheckServiceInterface* GetHealthCheckService() const {
+    return health_check_service_.get();
+  }
+
+  /// Establish a channel for in-process communication
+  std::shared_ptr<Channel> InProcessChannel(const ChannelArguments& args);
+
+  /// NOTE: class experimental_type is not part of the public API of this class.
+  /// TODO(yashykt): Integrate into public API when this is no longer
+  /// experimental.
+  class experimental_type {
+   public:
+    explicit experimental_type(Server* server) : server_(server) {}
+
+    /// Establish a channel for in-process communication with client
+    /// interceptors
+    std::shared_ptr<Channel> InProcessChannelWithInterceptors(
+        const ChannelArguments& args,
+        std::vector<
+            std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+            interceptor_creators);
+
+   private:
+    Server* server_;
+  };
+
+  /// NOTE: The function experimental() is not stable public API. It is a view
+  /// to the experimental components of this class. It may be changed or removed
+  /// at any time.
+  experimental_type experimental() { return experimental_type(this); }
+
+ protected:
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the Server instance.
+  bool RegisterService(const grpc::string* host, Service* service) override;
+
+  /// Try binding the server to the given \a addr endpoint
+  /// (port, and optionally including IP address to bind to).
+  ///
+  /// It can be invoked multiple times. Should be used before
+  /// starting the server.
+  ///
+  /// \param addr The address to try to bind to the server (eg, localhost:1234,
+  /// 192.168.1.1:31416, [::1]:27182, etc.).
+  /// \param creds The credentials associated with the server.
+  ///
+  /// \return bound port number on success, 0 on failure.
+  ///
+  /// \warning It is an error to call this method on an already started server.
+  int AddListeningPort(const grpc::string& addr,
+                       ServerCredentials* creds) override;
+
+  /// NOTE: This is *NOT* a public API. The server constructors are supposed to
+  /// be used by \a ServerBuilder class only. The constructor will be made
+  /// 'private' very soon.
+  ///
+  /// Server constructors. To be used by \a ServerBuilder only.
+  ///
+  /// \param max_message_size Maximum message length that the channel can
+  /// receive.
+  ///
+  /// \param args The channel args
+  ///
+  /// \param sync_server_cqs The completion queues to use if the server is a
+  /// synchronous server (or a hybrid server). The server polls for new RPCs on
+  /// these queues
+  ///
+  /// \param min_pollers The minimum number of polling threads per server
+  /// completion queue (in param sync_server_cqs) to use for listening to
+  /// incoming requests (used only in case of sync server)
+  ///
+  /// \param max_pollers The maximum number of polling threads per server
+  /// completion queue (in param sync_server_cqs) to use for listening to
+  /// incoming requests (used only in case of sync server)
+  ///
+  /// \param sync_cq_timeout_msec The timeout to use when calling AsyncNext() on
+  /// server completion queues passed via sync_server_cqs param.
+  Server(int max_message_size, ChannelArguments* args,
+         std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
+             sync_server_cqs,
+         int min_pollers, int max_pollers, int sync_cq_timeout_msec,
+         grpc_resource_quota* server_rq = nullptr,
+         std::vector<
+             std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+             interceptor_creators = std::vector<std::unique_ptr<
+                 experimental::ServerInterceptorFactoryInterface>>());
+
+  /// Start the server.
+  ///
+  /// \param cqs Completion queues for handling asynchronous services. The
+  /// caller is required to keep all completion queues live until the server is
+  /// destroyed.
+  /// \param num_cqs How many completion queues does \a cqs hold.
+  void Start(ServerCompletionQueue** cqs, size_t num_cqs) override;
+
+  grpc_server* server() override { return server_; };
+
+ private:
+  std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>*
+  interceptor_creators() override {
+    return &interceptor_creators_;
+  }
+
+  friend class AsyncGenericService;
+  friend class ServerBuilder;
+  friend class ServerInitializer;
+
+  class SyncRequest;
+  class CallbackRequest;
+  class UnimplementedAsyncRequest;
+  class UnimplementedAsyncResponse;
+
+  /// SyncRequestThreadManager is an implementation of ThreadManager. This class
+  /// is responsible for polling for incoming RPCs and calling the RPC handlers.
+  /// This is only used in case of a Sync server (i.e a server exposing a sync
+  /// interface)
+  class SyncRequestThreadManager;
+
+  /// Register a generic service. This call does not take ownership of the
+  /// service. The service must exist for the lifetime of the Server instance.
+  void RegisterAsyncGenericService(AsyncGenericService* service) override;
+
+  void PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                        internal::Call* call) override;
+
+  void ShutdownInternal(gpr_timespec deadline) override;
+
+  int max_receive_message_size() const override {
+    return max_receive_message_size_;
+  };
+
+  CompletionQueue* CallbackCQ() override;
+
+  ServerInitializer* initializer();
+
+  // A vector of interceptor factory objects.
+  // This should be destroyed after health_check_service_ and this requirement
+  // is satisfied by declaring interceptor_creators_ before
+  // health_check_service_. (C++ mandates that member objects be destroyed in
+  // the reverse order of initialization.)
+  std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+      interceptor_creators_;
+
+  const int max_receive_message_size_;
+
+  /// The following completion queues are ONLY used in case of Sync API
+  /// i.e. if the server has any services with sync methods. The server uses
+  /// these completion queues to poll for new RPCs
+  std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
+      sync_server_cqs_;
+
+  /// List of \a ThreadManager instances (one for each cq in
+  /// the \a sync_server_cqs)
+  std::vector<std::unique_ptr<SyncRequestThreadManager>> sync_req_mgrs_;
+
+  /// Outstanding callback requests
+  std::vector<std::unique_ptr<CallbackRequest>> callback_reqs_;
+
+  // Server status
+  std::mutex mu_;
+  bool started_;
+  bool shutdown_;
+  bool shutdown_notified_;  // Was notify called on the shutdown_cv_
+
+  std::condition_variable shutdown_cv_;
+
+  std::shared_ptr<GlobalCallbacks> global_callbacks_;
+
+  std::vector<grpc::string> services_;
+  bool has_generic_service_;
+
+  // Pointer to the wrapped grpc_server.
+  grpc_server* server_;
+
+  std::unique_ptr<ServerInitializer> server_initializer_;
+
+  std::unique_ptr<HealthCheckServiceInterface> health_check_service_;
+  bool health_check_service_disabled_;
+
+  // A special handler for resource exhausted in sync case
+  std::unique_ptr<internal::MethodHandler> resource_exhausted_handler_;
+
+  // callback_cq_ references the callbackable completion queue associated
+  // with this server (if any). It is set on the first call to CallbackCQ().
+  // It is _not owned_ by the server; ownership belongs with its internal
+  // shutdown callback tag (invoked when the CQ is fully shutdown).
+  // It is protected by mu_
+  CompletionQueue* callback_cq_ = nullptr;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SERVER_H
diff --git a/third_party/grpc/include/grpcpp/server_builder.h b/third_party/grpc/include/grpcpp/server_builder.h
new file mode 100644
index 0000000..028b8cf
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/server_builder.h
@@ -0,0 +1,330 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SERVER_BUILDER_H
+#define GRPCPP_SERVER_BUILDER_H
+
+#include <climits>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/workaround_list.h>
+#include <grpcpp/impl/channel_argument_option.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
+#include <grpcpp/impl/server_builder_option.h>
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/support/config.h>
+
+struct grpc_resource_quota;
+
+namespace grpc {
+
+class AsyncGenericService;
+class ResourceQuota;
+class CompletionQueue;
+class Server;
+class ServerCompletionQueue;
+class ServerCredentials;
+class Service;
+
+namespace testing {
+class ServerBuilderPluginTest;
+}  // namespace testing
+
+/// A builder class for the creation and startup of \a grpc::Server instances.
+class ServerBuilder {
+ public:
+  ServerBuilder();
+  virtual ~ServerBuilder();
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Primary API's
+
+  /// Return a running server which is ready for processing calls.
+  /// Before calling, one typically needs to ensure that:
+  ///  1. a service is registered - so that the server knows what to serve
+  ///     (via RegisterService, or RegisterAsyncGenericService)
+  ///  2. a listening port has been added - so the server knows where to receive
+  ///     traffic (via AddListeningPort)
+  ///  3. [for async api only] completion queues have been added via
+  ///     AddCompletionQueue
+  virtual std::unique_ptr<Server> BuildAndStart();
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the \a Server instance returned
+  /// by \a BuildAndStart().
+  /// Matches requests with any :authority
+  ServerBuilder& RegisterService(Service* service);
+
+  /// Enlists an endpoint \a addr (port with an optional IP address) to
+  /// bind the \a grpc::Server object to be created to.
+  ///
+  /// It can be invoked multiple times.
+  ///
+  /// \param addr_uri The address to try to bind to the server in URI form. If
+  /// the scheme name is omitted, "dns:///" is assumed. To bind to any address,
+  /// please use IPv6 any, i.e., [::]:<port>, which also accepts IPv4
+  /// connections.  Valid values include dns:///localhost:1234, /
+  /// 192.168.1.1:31416, dns:///[::1]:27182, etc.).
+  /// \param creds The credentials associated with the server.
+  /// \param selected_port[out] If not `nullptr`, gets populated with the port
+  /// number bound to the \a grpc::Server for the corresponding endpoint after
+  /// it is successfully bound by BuildAndStart(), 0 otherwise. AddListeningPort
+  /// does not modify this pointer.
+  ServerBuilder& AddListeningPort(const grpc::string& addr_uri,
+                                  std::shared_ptr<ServerCredentials> creds,
+                                  int* selected_port = nullptr);
+
+  /// Add a completion queue for handling asynchronous services.
+  ///
+  /// Best performance is typically obtained by using one thread per polling
+  /// completion queue.
+  ///
+  /// Caller is required to shutdown the server prior to shutting down the
+  /// returned completion queue. Caller is also required to drain the
+  /// completion queue after shutting it down. A typical usage scenario:
+  ///
+  /// // While building the server:
+  /// ServerBuilder builder;
+  /// ...
+  /// cq_ = builder.AddCompletionQueue();
+  /// server_ = builder.BuildAndStart();
+  ///
+  /// // While shutting down the server;
+  /// server_->Shutdown();
+  /// cq_->Shutdown();  // Always *after* the associated server's Shutdown()!
+  /// // Drain the cq_ that was created
+  /// void* ignored_tag;
+  /// bool ignored_ok;
+  /// while (cq_->Next(&ignored_tag, &ignored_ok)) { }
+  ///
+  /// \param is_frequently_polled This is an optional parameter to inform gRPC
+  /// library about whether this completion queue would be frequently polled
+  /// (i.e. by calling \a Next() or \a AsyncNext()). The default value is
+  /// 'true' and is the recommended setting. Setting this to 'false' (i.e.
+  /// not polling the completion queue frequently) will have a significantly
+  /// negative performance impact and hence should not be used in production
+  /// use cases.
+  std::unique_ptr<ServerCompletionQueue> AddCompletionQueue(
+      bool is_frequently_polled = true);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Less commonly used RegisterService variants
+
+  /// Register a service. This call does not take ownership of the service.
+  /// The service must exist for the lifetime of the \a Server instance returned
+  /// by \a BuildAndStart().
+  /// Only matches requests with :authority \a host
+  ServerBuilder& RegisterService(const grpc::string& host, Service* service);
+
+  /// Register a generic service.
+  /// Matches requests with any :authority
+  /// This is mostly useful for writing generic gRPC Proxies where the exact
+  /// serialization format is unknown
+  ServerBuilder& RegisterAsyncGenericService(AsyncGenericService* service);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Fine control knobs
+
+  /// Set max receive message size in bytes.
+  /// The default is GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH.
+  ServerBuilder& SetMaxReceiveMessageSize(int max_receive_message_size) {
+    max_receive_message_size_ = max_receive_message_size;
+    return *this;
+  }
+
+  /// Set max send message size in bytes.
+  /// The default is GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH.
+  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.
+  ///
+  /// Incoming calls compressed with an unsupported algorithm will fail with
+  /// \a GRPC_STATUS_UNIMPLEMENTED.
+  ServerBuilder& SetCompressionAlgorithmSupportStatus(
+      grpc_compression_algorithm algorithm, bool enabled);
+
+  /// The default compression level to use for all channel calls in the
+  /// absence of a call-specific level.
+  ServerBuilder& SetDefaultCompressionLevel(grpc_compression_level level);
+
+  /// The default compression algorithm to use for all channel calls in the
+  /// absence of a call-specific level. Note that it overrides any compression
+  /// level set by \a SetDefaultCompressionLevel.
+  ServerBuilder& SetDefaultCompressionAlgorithm(
+      grpc_compression_algorithm algorithm);
+
+  /// Set the attached buffer pool for this server
+  ServerBuilder& SetResourceQuota(const ResourceQuota& resource_quota);
+
+  ServerBuilder& SetOption(std::unique_ptr<ServerBuilderOption> option);
+
+  /// Options for synchronous servers.
+  enum SyncServerOption {
+    NUM_CQS,         ///< Number of completion queues.
+    MIN_POLLERS,     ///< Minimum number of polling threads.
+    MAX_POLLERS,     ///< Maximum number of polling threads.
+    CQ_TIMEOUT_MSEC  ///< Completion queue timeout in milliseconds.
+  };
+
+  /// Only useful if this is a Synchronous server.
+  ServerBuilder& SetSyncServerOption(SyncServerOption option, int value);
+
+  /// Add a channel argument (an escape hatch to tuning core library parameters
+  /// directly)
+  template <class T>
+  ServerBuilder& AddChannelArgument(const grpc::string& arg, const T& value) {
+    return SetOption(MakeChannelArgumentOption(arg, value));
+  }
+
+  /// For internal use only: Register a ServerBuilderPlugin factory function.
+  static void InternalAddPluginFactory(
+      std::unique_ptr<ServerBuilderPlugin> (*CreatePlugin)());
+
+  /// Enable a server workaround. Do not use unless you know what the workaround
+  /// does. For explanation and detailed descriptions of workarounds, see
+  /// doc/workarounds.md.
+  ServerBuilder& EnableWorkaround(grpc_workaround_list id);
+
+  /// NOTE: class experimental_type is not part of the public API of this class.
+  /// TODO(yashykt): Integrate into public API when this is no longer
+  /// experimental.
+  class experimental_type {
+   public:
+    explicit experimental_type(ServerBuilder* builder) : builder_(builder) {}
+
+    void SetInterceptorCreators(
+        std::vector<
+            std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+            interceptor_creators) {
+      builder_->interceptor_creators_ = std::move(interceptor_creators);
+    }
+
+   private:
+    ServerBuilder* builder_;
+  };
+
+  /// NOTE: The function experimental() is not stable public API. It is a view
+  /// to the experimental components of this class. It may be changed or removed
+  /// at any time.
+  experimental_type experimental() { return experimental_type(this); }
+
+ protected:
+  /// Experimental, to be deprecated
+  struct Port {
+    grpc::string addr;
+    std::shared_ptr<ServerCredentials> creds;
+    int* selected_port;
+  };
+
+  /// Experimental, to be deprecated
+  typedef std::unique_ptr<grpc::string> HostString;
+  struct NamedService {
+    explicit NamedService(Service* s) : service(s) {}
+    NamedService(const grpc::string& h, Service* s)
+        : host(new grpc::string(h)), service(s) {}
+    HostString host;
+    Service* service;
+  };
+
+  /// Experimental, to be deprecated
+  std::vector<Port> ports() { return ports_; }
+
+  /// Experimental, to be deprecated
+  std::vector<NamedService*> services() {
+    std::vector<NamedService*> service_refs;
+    for (auto& ptr : services_) {
+      service_refs.push_back(ptr.get());
+    }
+    return service_refs;
+  }
+
+  /// Experimental, to be deprecated
+  std::vector<ServerBuilderOption*> options() {
+    std::vector<ServerBuilderOption*> option_refs;
+    for (auto& ptr : options_) {
+      option_refs.push_back(ptr.get());
+    }
+    return option_refs;
+  }
+
+ private:
+  friend class ::grpc::testing::ServerBuilderPluginTest;
+
+  struct SyncServerSettings {
+    SyncServerSettings()
+        : num_cqs(1), min_pollers(1), max_pollers(2), cq_timeout_msec(10000) {}
+
+    /// Number of server completion queues to create to listen to incoming RPCs.
+    int num_cqs;
+
+    /// Minimum number of threads per completion queue that should be listening
+    /// to incoming RPCs.
+    int min_pollers;
+
+    /// Maximum number of threads per completion queue that can be listening to
+    /// incoming RPCs.
+    int max_pollers;
+
+    /// The timeout for server completion queue's AsyncNext call.
+    int cq_timeout_msec;
+  };
+
+  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_;
+
+  SyncServerSettings sync_server_settings_;
+
+  /// List of completion queues added via \a AddCompletionQueue method.
+  std::vector<ServerCompletionQueue*> cqs_;
+
+  std::shared_ptr<ServerCredentials> creds_;
+  std::vector<std::unique_ptr<ServerBuilderPlugin>> plugins_;
+  grpc_resource_quota* resource_quota_;
+  AsyncGenericService* generic_service_;
+  struct {
+    bool is_set;
+    grpc_compression_level level;
+  } maybe_default_compression_level_;
+  struct {
+    bool is_set;
+    grpc_compression_algorithm algorithm;
+  } maybe_default_compression_algorithm_;
+  uint32_t enabled_compression_algorithms_bitset_;
+  std::vector<std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+      interceptor_creators_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SERVER_BUILDER_H
diff --git a/third_party/grpc/include/grpcpp/server_context.h b/third_party/grpc/include/grpcpp/server_context.h
new file mode 100644
index 0000000..45f2614
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/server_context.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SERVER_CONTEXT_H
+#define GRPCPP_SERVER_CONTEXT_H
+
+#include <grpcpp/impl/codegen/server_context.h>
+
+#endif  // GRPCPP_SERVER_CONTEXT_H
diff --git a/third_party/grpc/include/grpcpp/server_posix.h b/third_party/grpc/include/grpcpp/server_posix.h
new file mode 100644
index 0000000..ef3ee01
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/server_posix.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SERVER_POSIX_H
+#define GRPCPP_SERVER_POSIX_H
+
+#include <memory>
+
+#include <grpc/support/port_platform.h>
+#include <grpcpp/server.h>
+
+namespace grpc {
+
+#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
+
+/// Add a new client to a \a Server communicating over the given
+/// file descriptor.
+///
+/// \param server The server to add the client to.
+/// \param fd The file descriptor representing a socket.
+void AddInsecureChannelFromFd(Server* server, int fd);
+
+#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SERVER_POSIX_H
diff --git a/third_party/grpc/include/grpcpp/support/async_stream.h b/third_party/grpc/include/grpcpp/support/async_stream.h
new file mode 100644
index 0000000..ff9e455
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/async_stream.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_ASYNC_STREAM_H
+#define GRPCPP_SUPPORT_ASYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/async_stream.h>
+
+#endif  // GRPCPP_SUPPORT_ASYNC_STREAM_H
diff --git a/third_party/grpc/include/grpcpp/support/async_unary_call.h b/third_party/grpc/include/grpcpp/support/async_unary_call.h
new file mode 100644
index 0000000..2e5181c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/async_unary_call.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H
+#define GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H
+
+#include <grpcpp/impl/codegen/async_unary_call.h>
+
+#endif  // GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H
diff --git a/third_party/grpc/include/grpcpp/support/byte_buffer.h b/third_party/grpc/include/grpcpp/support/byte_buffer.h
new file mode 100644
index 0000000..53aeff1
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/byte_buffer.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_BYTE_BUFFER_H
+#define GRPCPP_SUPPORT_BYTE_BUFFER_H
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpcpp/impl/codegen/byte_buffer.h>
+#include <grpcpp/impl/serialization_traits.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/slice.h>
+#include <grpcpp/support/status.h>
+
+#endif  // GRPCPP_SUPPORT_BYTE_BUFFER_H
diff --git a/third_party/grpc/include/grpcpp/support/channel_arguments.h b/third_party/grpc/include/grpcpp/support/channel_arguments.h
new file mode 100644
index 0000000..217929d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/channel_arguments.h
@@ -0,0 +1,148 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
+#define GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
+
+#include <list>
+#include <vector>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpcpp/support/config.h>
+
+namespace grpc {
+namespace testing {
+class ChannelArgumentsTest;
+}  // namespace testing
+
+class ResourceQuota;
+
+/// Options for channel creation. The user can use generic setters to pass
+/// key value pairs down to C channel creation code. For gRPC related options,
+/// concrete setters are provided.
+class ChannelArguments {
+ public:
+  ChannelArguments();
+  ~ChannelArguments();
+
+  ChannelArguments(const ChannelArguments& other);
+  ChannelArguments& operator=(ChannelArguments other) {
+    Swap(other);
+    return *this;
+  }
+
+  void Swap(ChannelArguments& other);
+
+  /// Dump arguments in this instance to \a channel_args. Does not take
+  /// ownership of \a channel_args.
+  ///
+  /// Note that the underlying arguments are shared. Changes made to either \a
+  /// channel_args or this instance would be reflected on both.
+  void SetChannelArgs(grpc_channel_args* channel_args) const;
+
+  // gRPC specific channel argument setters
+  /// Set target name override for SSL host name checking. This option is for
+  /// testing only and should never be used in production.
+  void SetSslTargetNameOverride(const grpc::string& name);
+  // TODO(yangg) add flow control options
+  /// Set the compression algorithm for the channel.
+  void SetCompressionAlgorithm(grpc_compression_algorithm algorithm);
+
+  /// Set the grpclb fallback timeout (in ms) for the channel. If this amount
+  /// of time has passed but we have not gotten any non-empty \a serverlist from
+  /// the balancer, we will fall back to use the backend address(es) returned by
+  /// the resolver.
+  void SetGrpclbFallbackTimeout(int fallback_timeout);
+
+  /// For client channel's, the socket mutator operates on
+  /// "channel" sockets. For server's, the socket mutator operates
+  /// only on "listen" sockets.
+  /// TODO(apolcyn): allow socket mutators to also operate
+  /// on server "channel" sockets, and adjust the socket mutator
+  /// object to be more speficic about which type of socket
+  /// it should operate on.
+  void SetSocketMutator(grpc_socket_mutator* mutator);
+
+  /// Set the string to prepend to the user agent.
+  void SetUserAgentPrefix(const grpc::string& user_agent_prefix);
+
+  /// Set the buffer pool to be attached to the constructed channel.
+  void SetResourceQuota(const ResourceQuota& resource_quota);
+
+  /// Set the max receive and send message sizes.
+  void SetMaxReceiveMessageSize(int size);
+  void SetMaxSendMessageSize(int size);
+
+  /// Set LB policy name.
+  /// Note that if the name resolver returns only balancer addresses, the
+  /// grpclb LB policy will be used, regardless of what is specified here.
+  void SetLoadBalancingPolicyName(const grpc::string& lb_policy_name);
+
+  /// Set service config in JSON form.
+  /// Primarily meant for use in unit tests.
+  void SetServiceConfigJSON(const grpc::string& service_config_json);
+
+  // Generic channel argument setters. Only for advanced use cases.
+  /// Set an integer argument \a value under \a key.
+  void SetInt(const grpc::string& key, int value);
+
+  // Generic channel argument setter. Only for advanced use cases.
+  /// Set a pointer argument \a value under \a key. Owership is not transferred.
+  void SetPointer(const grpc::string& key, void* value);
+
+  void SetPointerWithVtable(const grpc::string& key, void* value,
+                            const grpc_arg_pointer_vtable* vtable);
+
+  /// Set a textual argument \a value under \a key.
+  void SetString(const grpc::string& key, const grpc::string& value);
+
+  /// Return (by value) a C \a grpc_channel_args structure which points to
+  /// arguments owned by this \a ChannelArguments instance
+  grpc_channel_args c_channel_args() const {
+    grpc_channel_args out;
+    out.num_args = args_.size();
+    out.args = args_.empty() ? NULL : const_cast<grpc_arg*>(&args_[0]);
+    return out;
+  }
+
+ private:
+  friend class SecureChannelCredentials;
+  friend class testing::ChannelArgumentsTest;
+
+  /// Default pointer argument operations.
+  struct PointerVtableMembers {
+    static void* Copy(void* in) { return in; }
+    static void Destroy(void* in) {}
+    static int Compare(void* a, void* b) {
+      if (a < b) return -1;
+      if (a > b) return 1;
+      return 0;
+    }
+  };
+
+  // Returns empty string when it is not set.
+  grpc::string GetSslTargetNameOverride() const;
+
+  std::vector<grpc_arg> args_;
+  std::list<grpc::string> strings_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
diff --git a/third_party/grpc/include/grpcpp/support/client_callback.h b/third_party/grpc/include/grpcpp/support/client_callback.h
new file mode 100644
index 0000000..063fdc4
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/client_callback.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_CLIENT_CALLBACK_H
+#define GRPCPP_SUPPORT_CLIENT_CALLBACK_H
+
+#include <grpcpp/impl/codegen/client_callback.h>
+
+#endif  // GRPCPP_SUPPORT_CLIENT_CALLBACK_H
diff --git a/third_party/grpc/include/grpcpp/support/client_interceptor.h b/third_party/grpc/include/grpcpp/support/client_interceptor.h
new file mode 100644
index 0000000..50810e3
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/client_interceptor.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_CLIENT_INTERCEPTOR_H
+#define GRPCPP_SUPPORT_CLIENT_INTERCEPTOR_H
+
+#include <grpcpp/impl/codegen/client_interceptor.h>
+
+#endif  // GRPCPP_SUPPORT_CLIENT_INTERCEPTOR_H
diff --git a/third_party/grpc/include/grpcpp/support/config.h b/third_party/grpc/include/grpcpp/support/config.h
new file mode 100644
index 0000000..16bdab642
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/config.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_CONFIG_H
+#define GRPCPP_SUPPORT_CONFIG_H
+
+#include <grpcpp/impl/codegen/config.h>
+
+#endif  // GRPCPP_SUPPORT_CONFIG_H
diff --git a/third_party/grpc/include/grpcpp/support/error_details.h b/third_party/grpc/include/grpcpp/support/error_details.h
new file mode 100644
index 0000000..84931d9
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/error_details.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_ERROR_DETAILS_H
+#define GRPCPP_SUPPORT_ERROR_DETAILS_H
+
+#include <grpcpp/support/status.h>
+
+namespace google {
+namespace rpc {
+class Status;
+}  // namespace rpc
+}  // namespace google
+
+namespace grpc {
+
+/// Map a \a grpc::Status to a \a google::rpc::Status.
+/// The given \a to object will be cleared.
+/// On success, returns status with OK.
+/// Returns status with \a INVALID_ARGUMENT, if failed to deserialize.
+/// Returns status with \a FAILED_PRECONDITION, if \a to is nullptr.
+Status ExtractErrorDetails(const Status& from, ::google::rpc::Status* to);
+
+/// Map \a google::rpc::Status to a \a grpc::Status.
+/// Returns OK on success.
+/// Returns status with \a FAILED_PRECONDITION if \a to is nullptr.
+Status SetErrorDetails(const ::google::rpc::Status& from, Status* to);
+
+}  // namespace grpc
+
+#endif  // GRPCPP_SUPPORT_ERROR_DETAILS_H
diff --git a/third_party/grpc/include/grpcpp/support/interceptor.h b/third_party/grpc/include/grpcpp/support/interceptor.h
new file mode 100644
index 0000000..7ff7951
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/interceptor.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_INTERCEPTOR_H
+#define GRPCPP_SUPPORT_INTERCEPTOR_H
+
+#include <grpcpp/impl/codegen/interceptor.h>
+
+#endif  // GRPCPP_SUPPORT_INTERCEPTOR_H
diff --git a/third_party/grpc/include/grpcpp/support/proto_buffer_reader.h b/third_party/grpc/include/grpcpp/support/proto_buffer_reader.h
new file mode 100644
index 0000000..4cdb65d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/proto_buffer_reader.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_PROTO_BUFFER_READER_H
+#define GRPCPP_SUPPORT_PROTO_BUFFER_READER_H
+
+#include <grpcpp/impl/codegen/proto_buffer_reader.h>
+
+#endif  // GRPCPP_SUPPORT_PROTO_BUFFER_READER_H
diff --git a/third_party/grpc/include/grpcpp/support/proto_buffer_writer.h b/third_party/grpc/include/grpcpp/support/proto_buffer_writer.h
new file mode 100644
index 0000000..01cf29c
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/proto_buffer_writer.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_PROTO_BUFFER_WRITER_H
+#define GRPCPP_SUPPORT_PROTO_BUFFER_WRITER_H
+
+#include <grpcpp/impl/codegen/proto_buffer_writer.h>
+
+#endif  // GRPCPP_SUPPORT_PROTO_BUFFER_WRITER_H
diff --git a/third_party/grpc/include/grpcpp/support/server_callback.h b/third_party/grpc/include/grpcpp/support/server_callback.h
new file mode 100644
index 0000000..b0aeeb5
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/server_callback.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_SERVER_CALLBACK_H
+#define GRPCPP_SUPPORT_SERVER_CALLBACK_H
+
+#include <grpcpp/impl/codegen/server_callback.h>
+
+#endif  // GRPCPP_SUPPORT_SERVER_CALLBACK_H
diff --git a/third_party/grpc/include/grpcpp/support/server_interceptor.h b/third_party/grpc/include/grpcpp/support/server_interceptor.h
new file mode 100644
index 0000000..b0a6229
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/server_interceptor.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_SERVER_INTERCEPTOR_H
+#define GRPCPP_SUPPORT_SERVER_INTERCEPTOR_H
+
+#include <grpcpp/impl/codegen/server_interceptor.h>
+
+#endif  // GRPCPP_SUPPORT_SERVER_INTERCEPTOR_H
diff --git a/third_party/grpc/include/grpcpp/support/slice.h b/third_party/grpc/include/grpcpp/support/slice.h
new file mode 100644
index 0000000..eaeb29a
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/slice.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_SLICE_H
+#define GRPCPP_SUPPORT_SLICE_H
+
+#include <grpc/slice.h>
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/support/config.h>
+
+#endif  // GRPCPP_SUPPORT_SLICE_H
diff --git a/third_party/grpc/include/grpcpp/support/status.h b/third_party/grpc/include/grpcpp/support/status.h
new file mode 100644
index 0000000..91b629f
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/status.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_STATUS_H
+#define GRPCPP_SUPPORT_STATUS_H
+
+#include <grpcpp/impl/codegen/status.h>
+
+#endif  // GRPCPP_SUPPORT_STATUS_H
diff --git a/third_party/grpc/include/grpcpp/support/status_code_enum.h b/third_party/grpc/include/grpcpp/support/status_code_enum.h
new file mode 100644
index 0000000..bfb47f3
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/status_code_enum.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_STATUS_CODE_ENUM_H
+#define GRPCPP_SUPPORT_STATUS_CODE_ENUM_H
+
+#include <grpcpp/impl/codegen/status_code_enum.h>
+
+#endif  // GRPCPP_SUPPORT_STATUS_CODE_ENUM_H
diff --git a/third_party/grpc/include/grpcpp/support/string_ref.h b/third_party/grpc/include/grpcpp/support/string_ref.h
new file mode 100644
index 0000000..0e0d3d4
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/string_ref.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_STRING_REF_H
+#define GRPCPP_SUPPORT_STRING_REF_H
+
+#include <grpcpp/impl/codegen/string_ref.h>
+
+#endif  // GRPCPP_SUPPORT_STRING_REF_H
diff --git a/third_party/grpc/include/grpcpp/support/stub_options.h b/third_party/grpc/include/grpcpp/support/stub_options.h
new file mode 100644
index 0000000..e9700ea
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/stub_options.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_STUB_OPTIONS_H
+#define GRPCPP_SUPPORT_STUB_OPTIONS_H
+
+#include <grpcpp/impl/codegen/stub_options.h>
+
+#endif  // GRPCPP_SUPPORT_STUB_OPTIONS_H
diff --git a/third_party/grpc/include/grpcpp/support/sync_stream.h b/third_party/grpc/include/grpcpp/support/sync_stream.h
new file mode 100644
index 0000000..ea60b6d
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/sync_stream.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_SYNC_STREAM_H
+#define GRPCPP_SUPPORT_SYNC_STREAM_H
+
+#include <grpcpp/impl/codegen/sync_stream.h>
+
+#endif  // GRPCPP_SUPPORT_SYNC_STREAM_H
diff --git a/third_party/grpc/include/grpcpp/support/time.h b/third_party/grpc/include/grpcpp/support/time.h
new file mode 100644
index 0000000..c7408ff
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/support/time.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_SUPPORT_TIME_H
+#define GRPCPP_SUPPORT_TIME_H
+
+#include <grpcpp/impl/codegen/time.h>
+
+#endif  // GRPCPP_SUPPORT_TIME_H
diff --git a/third_party/grpc/include/grpcpp/test/mock_stream.h b/third_party/grpc/include/grpcpp/test/mock_stream.h
new file mode 100644
index 0000000..93963f7
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/test/mock_stream.h
@@ -0,0 +1,148 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_TEST_MOCK_STREAM_H
+#define GRPCPP_TEST_MOCK_STREAM_H
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/support/async_stream.h>
+#include <grpcpp/support/async_unary_call.h>
+#include <grpcpp/support/sync_stream.h>
+
+namespace grpc {
+namespace testing {
+
+template <class R>
+class MockClientReader : public ClientReaderInterface<R> {
+ public:
+  MockClientReader() = default;
+
+  /// ClientStreamingInterface
+  MOCK_METHOD0_T(Finish, Status());
+
+  /// ReaderInterface
+  MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
+  MOCK_METHOD1_T(Read, bool(R*));
+
+  /// ClientReaderInterface
+  MOCK_METHOD0_T(WaitForInitialMetadata, void());
+};
+
+template <class W>
+class MockClientWriter : public ClientWriterInterface<W> {
+ public:
+  MockClientWriter() = default;
+
+  /// ClientStreamingInterface
+  MOCK_METHOD0_T(Finish, Status());
+
+  /// WriterInterface
+  MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
+
+  /// ClientWriterInterface
+  MOCK_METHOD0_T(WritesDone, bool());
+};
+
+template <class W, class R>
+class MockClientReaderWriter : public ClientReaderWriterInterface<W, R> {
+ public:
+  MockClientReaderWriter() = default;
+
+  /// ClientStreamingInterface
+  MOCK_METHOD0_T(Finish, Status());
+
+  /// ReaderInterface
+  MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
+  MOCK_METHOD1_T(Read, bool(R*));
+
+  /// WriterInterface
+  MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
+
+  /// ClientReaderWriterInterface
+  MOCK_METHOD0_T(WaitForInitialMetadata, void());
+  MOCK_METHOD0_T(WritesDone, bool());
+};
+
+/// TODO: We do not support mocking an async RPC for now.
+
+template <class R>
+class MockClientAsyncResponseReader
+    : public ClientAsyncResponseReaderInterface<R> {
+ public:
+  MockClientAsyncResponseReader() = default;
+
+  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
+  MOCK_METHOD3_T(Finish, void(R*, Status*, void*));
+};
+
+template <class R>
+class MockClientAsyncReader : public ClientAsyncReaderInterface<R> {
+ public:
+  MockClientAsyncReader() = default;
+
+  /// ClientAsyncStreamingInterface
+  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
+  MOCK_METHOD2_T(Finish, void(Status*, void*));
+
+  /// AsyncReaderInterface
+  MOCK_METHOD2_T(Read, void(R*, void*));
+};
+
+template <class W>
+class MockClientAsyncWriter : public ClientAsyncWriterInterface<W> {
+ public:
+  MockClientAsyncWriter() = default;
+
+  /// ClientAsyncStreamingInterface
+  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
+  MOCK_METHOD2_T(Finish, void(Status*, void*));
+
+  /// AsyncWriterInterface
+  MOCK_METHOD2_T(Write, void(const W&, void*));
+
+  /// ClientAsyncWriterInterface
+  MOCK_METHOD1_T(WritesDone, void(void*));
+};
+
+template <class W, class R>
+class MockClientAsyncReaderWriter
+    : public ClientAsyncReaderWriterInterface<W, R> {
+ public:
+  MockClientAsyncReaderWriter() = default;
+
+  /// ClientAsyncStreamingInterface
+  MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
+  MOCK_METHOD2_T(Finish, void(Status*, void*));
+
+  /// AsyncWriterInterface
+  MOCK_METHOD2_T(Write, void(const W&, void*));
+
+  /// AsyncReaderInterface
+  MOCK_METHOD2_T(Read, void(R*, void*));
+
+  /// ClientAsyncReaderWriterInterface
+  MOCK_METHOD1_T(WritesDone, void(void*));
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPCPP_TEST_MOCK_STREAM_H
diff --git a/third_party/grpc/include/grpcpp/test/server_context_test_spouse.h b/third_party/grpc/include/grpcpp/test/server_context_test_spouse.h
new file mode 100644
index 0000000..f5ca552
--- /dev/null
+++ b/third_party/grpc/include/grpcpp/test/server_context_test_spouse.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
+#define GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
+
+#include <map>
+
+#include <grpcpp/server_context.h>
+
+namespace grpc {
+namespace testing {
+
+/// A test-only class to access private members and methods of ServerContext.
+class ServerContextTestSpouse {
+ public:
+  explicit ServerContextTestSpouse(ServerContext* ctx) : ctx_(ctx) {}
+
+  /// Inject client metadata to the ServerContext for the test. The test spouse
+  /// must be alive when \a ServerContext::client_metadata is called.
+  void AddClientMetadata(const grpc::string& key, const grpc::string& value) {
+    client_metadata_storage_.insert(
+        std::pair<grpc::string, grpc::string>(key, value));
+    ctx_->client_metadata_.map()->clear();
+    for (auto iter = client_metadata_storage_.begin();
+         iter != client_metadata_storage_.end(); ++iter) {
+      ctx_->client_metadata_.map()->insert(
+          std::pair<grpc::string_ref, grpc::string_ref>(
+              iter->first.c_str(),
+              grpc::string_ref(iter->second.data(), iter->second.size())));
+    }
+  }
+
+  std::multimap<grpc::string, grpc::string> GetInitialMetadata() const {
+    return ctx_->initial_metadata_;
+  }
+
+  std::multimap<grpc::string, grpc::string> GetTrailingMetadata() const {
+    return ctx_->trailing_metadata_;
+  }
+
+ private:
+  ServerContext* ctx_;  // not owned
+  std::multimap<grpc::string, grpc::string> client_metadata_storage_;
+};
+
+}  // namespace testing
+}  // namespace grpc
+
+#endif  // GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H
diff --git a/third_party/grpc/netinet_tcp_h.patch b/third_party/grpc/netinet_tcp_h.patch
new file mode 100644
index 0000000..965a29c
--- /dev/null
+++ b/third_party/grpc/netinet_tcp_h.patch
@@ -0,0 +1,60 @@
+From c6d4eb057648e53040a5ec6178bbe73e212d19d4 Mon Sep 17 00:00:00 2001
+From: Ryan Beasley <beasleyr@vmware.com>
+Date: Fri, 29 Mar 2019 05:06:33 -0700
+Subject: [PATCH] bazel/grpc: include linux/tcp.h on Linux pre-glibc 2.17
+
+Bazel 0.24.0 upgraded grpc from 1.13.0 to 1.18.0, and the latter makes use of
+the TCP_USER_TIMEOUT socket option.  Problem:  grpc conditions the option on
+the Linux kernel version but sources the option from glibc's `netinet/tcp.h`.
+glibc != Linux kernel, and that option wasn't imported to glibc until 2.17.
+
+We can't just build Bazel with glibc 2.17 because we still have to support
+CentOS 6 dev nodes which ship with glibc 2.12.  So instead this change
+tweaks the grpc headers to conditionally include <linux/tcp.h> instead of
+<netinet/tcp.h>.
+
+Resolves https://github.com/bazelbuild/bazel/issues/7890.
+---
+ third_party/grpc/src/core/lib/iomgr/port.h                       | 9 +++++++++
+ third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc | 4 ++++
+ 2 files changed, 13 insertions(+)
+
+diff --git a/third_party/grpc/src/core/lib/iomgr/port.h b/third_party/grpc/src/core/lib/iomgr/port.h
+index c8046b21dc..1aea6c1b3e 100644
+--- a/third_party/grpc/src/core/lib/iomgr/port.h
++++ b/third_party/grpc/src/core/lib/iomgr/port.h
+@@ -85,6 +85,15 @@
+ #ifdef LINUX_VERSION_CODE
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+ #define GRPC_HAVE_TCP_USER_TIMEOUT
++#ifdef __GLIBC_PREREQ
++#if !(__GLIBC_PREREQ(2, 17))
++/*
++ * TCP_USER_TIMEOUT wasn't imported to glibc until 2.17. Use Linux system
++ * header instead.
++ */
++#define GRPC_LINUX_TCP_H 1
++#endif /* __GLIBC_PREREQ(2, 17) */
++#endif /* ifdef __GLIBC_PREREQ */
+ #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) */
+ #endif /* LINUX_VERSION_CODE */
+ #ifndef __GLIBC__
+diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc
+index 4c337a0521..ea0adb1f6a 100644
+--- a/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc
++++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc
+@@ -30,7 +30,11 @@
+ #include <fcntl.h>
+ #include <limits.h>
+ #include <netinet/in.h>
++#ifdef GRPC_LINUX_TCP_H
++#include <linux/tcp.h>
++#else
+ #include <netinet/tcp.h>
++#endif
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/socket.h>
+-- 
+2.14.1
+
diff --git a/third_party/grpc/src/compiler/README.md b/third_party/grpc/src/compiler/README.md
new file mode 100644
index 0000000..d5684af
--- /dev/null
+++ b/third_party/grpc/src/compiler/README.md
@@ -0,0 +1,4 @@
+# Overview
+
+This directory contains source code for gRPC protocol buffer compiler (*protoc*) plugins. Along with `protoc`,
+these plugins are used to generate gRPC client and server stubs from `.proto` files.
diff --git a/third_party/grpc/src/compiler/config.h b/third_party/grpc/src/compiler/config.h
new file mode 100644
index 0000000..cfdc303
--- /dev/null
+++ b/third_party/grpc/src/compiler/config.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef SRC_COMPILER_CONFIG_H
+#define SRC_COMPILER_CONFIG_H
+
+#include <grpcpp/impl/codegen/config_protobuf.h>
+
+#ifndef GRPC_CUSTOM_CODEGENERATOR
+#include <google/protobuf/compiler/code_generator.h>
+#define GRPC_CUSTOM_CODEGENERATOR ::google::protobuf::compiler::CodeGenerator
+#define GRPC_CUSTOM_GENERATORCONTEXT \
+  ::google::protobuf::compiler::GeneratorContext
+#endif
+
+#ifndef GRPC_CUSTOM_PRINTER
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#define GRPC_CUSTOM_PRINTER ::google::protobuf::io::Printer
+#define GRPC_CUSTOM_CODEDOUTPUTSTREAM ::google::protobuf::io::CodedOutputStream
+#define GRPC_CUSTOM_STRINGOUTPUTSTREAM \
+  ::google::protobuf::io::StringOutputStream
+#endif
+
+#ifndef GRPC_CUSTOM_PLUGINMAIN
+#include <google/protobuf/compiler/plugin.h>
+#define GRPC_CUSTOM_PLUGINMAIN ::google::protobuf::compiler::PluginMain
+#endif
+
+#ifndef GRPC_CUSTOM_PARSEGENERATORPARAMETER
+#include <google/protobuf/compiler/code_generator.h>
+#define GRPC_CUSTOM_PARSEGENERATORPARAMETER \
+  ::google::protobuf::compiler::ParseGeneratorParameter
+#endif
+
+#ifndef GRPC_CUSTOM_STRING
+#include <string>
+#define GRPC_CUSTOM_STRING std::string
+#endif
+
+namespace grpc {
+
+typedef GRPC_CUSTOM_STRING string;
+
+namespace protobuf {
+
+namespace compiler {
+typedef GRPC_CUSTOM_CODEGENERATOR CodeGenerator;
+typedef GRPC_CUSTOM_GENERATORCONTEXT GeneratorContext;
+static inline int PluginMain(int argc, char* argv[],
+                             const CodeGenerator* generator) {
+  return GRPC_CUSTOM_PLUGINMAIN(argc, argv, generator);
+}
+static inline void ParseGeneratorParameter(
+    const string& parameter, std::vector<std::pair<string, string> >* options) {
+  GRPC_CUSTOM_PARSEGENERATORPARAMETER(parameter, options);
+}
+
+}  // namespace compiler
+namespace io {
+typedef GRPC_CUSTOM_PRINTER Printer;
+typedef GRPC_CUSTOM_CODEDOUTPUTSTREAM CodedOutputStream;
+typedef GRPC_CUSTOM_STRINGOUTPUTSTREAM StringOutputStream;
+}  // namespace io
+}  // namespace protobuf
+}  // namespace grpc
+
+namespace grpc_cpp_generator {
+
+static const char* const kCppGeneratorMessageHeaderExt = ".pb.h";
+static const char* const kCppGeneratorServiceHeaderExt = ".grpc.pb.h";
+
+}  // namespace grpc_cpp_generator
+
+#endif  // SRC_COMPILER_CONFIG_H
diff --git a/third_party/grpc/src/compiler/cpp_generator.cc b/third_party/grpc/src/compiler/cpp_generator.cc
new file mode 100644
index 0000000..b004687
--- /dev/null
+++ b/third_party/grpc/src/compiler/cpp_generator.cc
@@ -0,0 +1,2247 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <map>
+
+#include "src/compiler/cpp_generator.h"
+
+#include <sstream>
+
+namespace grpc_cpp_generator {
+namespace {
+
+template <class T>
+grpc::string as_string(T x) {
+  std::ostringstream out;
+  out << x;
+  return out.str();
+}
+
+inline bool ClientOnlyStreaming(const grpc_generator::Method* method) {
+  return method->ClientStreaming() && !method->ServerStreaming();
+}
+
+inline bool ServerOnlyStreaming(const grpc_generator::Method* method) {
+  return !method->ClientStreaming() && method->ServerStreaming();
+}
+
+grpc::string FilenameIdentifier(const grpc::string& filename) {
+  grpc::string result;
+  for (unsigned i = 0; i < filename.size(); i++) {
+    char c = filename[i];
+    if (isalnum(c)) {
+      result.push_back(c);
+    } else {
+      static char hex[] = "0123456789abcdef";
+      result.push_back('_');
+      result.push_back(hex[(c >> 4) & 0xf]);
+      result.push_back(hex[c & 0xf]);
+    }
+  }
+  return result;
+}
+}  // namespace
+
+template <class T, size_t N>
+T* array_end(T (&array)[N]) {
+  return array + N;
+}
+
+void PrintIncludes(grpc_generator::Printer* printer,
+                   const std::vector<grpc::string>& headers,
+                   bool use_system_headers, const grpc::string& search_path) {
+  std::map<grpc::string, grpc::string> vars;
+
+  vars["l"] = use_system_headers ? '<' : '"';
+  vars["r"] = use_system_headers ? '>' : '"';
+
+  if (!search_path.empty()) {
+    vars["l"] += search_path;
+    if (search_path[search_path.size() - 1] != '/') {
+      vars["l"] += '/';
+    }
+  }
+
+  for (auto i = headers.begin(); i != headers.end(); i++) {
+    vars["h"] = *i;
+    printer->Print(vars, "#include $l$$h$$r$\n");
+  }
+}
+
+grpc::string GetHeaderPrologue(grpc_generator::File* file,
+                               const Parameters& /*params*/) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+
+    vars["filename"] = file->filename();
+    vars["filename_identifier"] = FilenameIdentifier(file->filename());
+    vars["filename_base"] = file->filename_without_ext();
+    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
+
+    printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
+    printer->Print(vars,
+                   "// If you make any local change, they will be lost.\n");
+    printer->Print(vars, "// source: $filename$\n");
+    grpc::string leading_comments = file->GetLeadingComments("//");
+    if (!leading_comments.empty()) {
+      printer->Print(vars, "// Original file comments:\n");
+      printer->PrintRaw(leading_comments.c_str());
+    }
+    printer->Print(vars, "#ifndef GRPC_$filename_identifier$__INCLUDED\n");
+    printer->Print(vars, "#define GRPC_$filename_identifier$__INCLUDED\n");
+    printer->Print(vars, "\n");
+    printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
+    printer->Print(vars, file->additional_headers().c_str());
+    printer->Print(vars, "\n");
+  }
+  return output;
+}
+
+grpc::string GetHeaderIncludes(grpc_generator::File* file,
+                               const Parameters& params) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+
+    if (!params.additional_header_includes.empty()) {
+      PrintIncludes(printer.get(), params.additional_header_includes, false,
+                    "");
+    }
+    static const char* headers_strs[] = {
+        "functional",
+        "grpcpp/impl/codegen/async_generic_service.h",
+        "grpcpp/impl/codegen/async_stream.h",
+        "grpcpp/impl/codegen/async_unary_call.h",
+        "grpcpp/impl/codegen/client_callback.h",
+        "grpcpp/impl/codegen/method_handler_impl.h",
+        "grpcpp/impl/codegen/proto_utils.h",
+        "grpcpp/impl/codegen/rpc_method.h",
+        "grpcpp/impl/codegen/server_callback.h",
+        "grpcpp/impl/codegen/service_type.h",
+        "grpcpp/impl/codegen/status.h",
+        "grpcpp/impl/codegen/stub_options.h",
+        "grpcpp/impl/codegen/sync_stream.h"};
+    std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
+    PrintIncludes(printer.get(), headers, params.use_system_headers,
+                  params.grpc_search_path);
+    printer->Print(vars, "\n");
+    printer->Print(vars, "namespace grpc {\n");
+    printer->Print(vars, "class CompletionQueue;\n");
+    printer->Print(vars, "class Channel;\n");
+    printer->Print(vars, "class ServerCompletionQueue;\n");
+    printer->Print(vars, "class ServerContext;\n");
+    printer->Print(vars, "}  // namespace grpc\n\n");
+
+    if (!file->package().empty()) {
+      std::vector<grpc::string> parts = file->package_parts();
+
+      for (auto part = parts.begin(); part != parts.end(); part++) {
+        vars["part"] = *part;
+        printer->Print(vars, "namespace $part$ {\n");
+      }
+      printer->Print(vars, "\n");
+    }
+  }
+  return output;
+}
+
+void PrintHeaderClientMethodInterfaces(
+    grpc_generator::Printer* printer, const grpc_generator::Method* method,
+    std::map<grpc::string, grpc::string>* vars, bool is_public) {
+  (*vars)["Method"] = method->name();
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+
+  struct {
+    grpc::string prefix;
+    grpc::string method_params;  // extra arguments to method
+    grpc::string raw_args;       // extra arguments to raw version of method
+  } async_prefixes[] = {{"Async", ", void* tag", ", tag"},
+                        {"PrepareAsync", "", ""}};
+
+  if (is_public) {
+    if (method->NoStreaming()) {
+      printer->Print(
+          *vars,
+          "virtual ::grpc::Status $Method$(::grpc::ClientContext* context, "
+          "const $Request$& request, $Response$* response) = 0;\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        printer->Print(
+            *vars,
+            "std::unique_ptr< "
+            "::grpc::ClientAsyncResponseReaderInterface< $Response$>> "
+            "$AsyncPrefix$$Method$(::grpc::ClientContext* context, "
+            "const $Request$& request, "
+            "::grpc::CompletionQueue* cq) {\n");
+        printer->Indent();
+        printer->Print(
+            *vars,
+            "return std::unique_ptr< "
+            "::grpc::ClientAsyncResponseReaderInterface< $Response$>>("
+            "$AsyncPrefix$$Method$Raw(context, request, cq));\n");
+        printer->Outdent();
+        printer->Print("}\n");
+      }
+    } else if (ClientOnlyStreaming(method)) {
+      printer->Print(
+          *vars,
+          "std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>"
+          " $Method$("
+          "::grpc::ClientContext* context, $Response$* response) {\n");
+      printer->Indent();
+      printer->Print(
+          *vars,
+          "return std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>"
+          "($Method$Raw(context, response));\n");
+      printer->Outdent();
+      printer->Print("}\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(
+            *vars,
+            "std::unique_ptr< ::grpc::ClientAsyncWriterInterface< $Request$>>"
+            " $AsyncPrefix$$Method$(::grpc::ClientContext* context, "
+            "$Response$* "
+            "response, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+        printer->Indent();
+        printer->Print(*vars,
+                       "return std::unique_ptr< "
+                       "::grpc::ClientAsyncWriterInterface< $Request$>>("
+                       "$AsyncPrefix$$Method$Raw(context, response, "
+                       "cq$AsyncRawArgs$));\n");
+        printer->Outdent();
+        printer->Print("}\n");
+      }
+    } else if (ServerOnlyStreaming(method)) {
+      printer->Print(
+          *vars,
+          "std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>"
+          " $Method$(::grpc::ClientContext* context, const $Request$& request)"
+          " {\n");
+      printer->Indent();
+      printer->Print(
+          *vars,
+          "return std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>"
+          "($Method$Raw(context, request));\n");
+      printer->Outdent();
+      printer->Print("}\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(
+            *vars,
+            "std::unique_ptr< ::grpc::ClientAsyncReaderInterface< $Response$>> "
+            "$AsyncPrefix$$Method$("
+            "::grpc::ClientContext* context, const $Request$& request, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+        printer->Indent();
+        printer->Print(
+            *vars,
+            "return std::unique_ptr< "
+            "::grpc::ClientAsyncReaderInterface< $Response$>>("
+            "$AsyncPrefix$$Method$Raw(context, request, cq$AsyncRawArgs$));\n");
+        printer->Outdent();
+        printer->Print("}\n");
+      }
+    } else if (method->BidiStreaming()) {
+      printer->Print(*vars,
+                     "std::unique_ptr< ::grpc::ClientReaderWriterInterface< "
+                     "$Request$, $Response$>> "
+                     "$Method$(::grpc::ClientContext* context) {\n");
+      printer->Indent();
+      printer->Print(
+          *vars,
+          "return std::unique_ptr< "
+          "::grpc::ClientReaderWriterInterface< $Request$, $Response$>>("
+          "$Method$Raw(context));\n");
+      printer->Outdent();
+      printer->Print("}\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(
+            *vars,
+            "std::unique_ptr< "
+            "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>> "
+            "$AsyncPrefix$$Method$(::grpc::ClientContext* context, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+        printer->Indent();
+        printer->Print(
+            *vars,
+            "return std::unique_ptr< "
+            "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>>("
+            "$AsyncPrefix$$Method$Raw(context, cq$AsyncRawArgs$));\n");
+        printer->Outdent();
+        printer->Print("}\n");
+      }
+    }
+  } else {
+    if (method->NoStreaming()) {
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        printer->Print(
+            *vars,
+            "virtual ::grpc::ClientAsyncResponseReaderInterface< $Response$>* "
+            "$AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, "
+            "const $Request$& request, "
+            "::grpc::CompletionQueue* cq) = 0;\n");
+      }
+    } else if (ClientOnlyStreaming(method)) {
+      printer->Print(
+          *vars,
+          "virtual ::grpc::ClientWriterInterface< $Request$>*"
+          " $Method$Raw("
+          "::grpc::ClientContext* context, $Response$* response) = 0;\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        printer->Print(
+            *vars,
+            "virtual ::grpc::ClientAsyncWriterInterface< $Request$>*"
+            " $AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, "
+            "$Response$* response, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) = 0;\n");
+      }
+    } else if (ServerOnlyStreaming(method)) {
+      printer->Print(
+          *vars,
+          "virtual ::grpc::ClientReaderInterface< $Response$>* "
+          "$Method$Raw("
+          "::grpc::ClientContext* context, const $Request$& request) = 0;\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        printer->Print(
+            *vars,
+            "virtual ::grpc::ClientAsyncReaderInterface< $Response$>* "
+            "$AsyncPrefix$$Method$Raw("
+            "::grpc::ClientContext* context, const $Request$& request, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) = 0;\n");
+      }
+    } else if (method->BidiStreaming()) {
+      printer->Print(*vars,
+                     "virtual ::grpc::ClientReaderWriterInterface< $Request$, "
+                     "$Response$>* "
+                     "$Method$Raw(::grpc::ClientContext* context) = 0;\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        printer->Print(
+            *vars,
+            "virtual ::grpc::ClientAsyncReaderWriterInterface< "
+            "$Request$, $Response$>* "
+            "$AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) = 0;\n");
+      }
+    }
+  }
+}
+
+void PrintHeaderClientMethod(grpc_generator::Printer* printer,
+                             const grpc_generator::Method* method,
+                             std::map<grpc::string, grpc::string>* vars,
+                             bool is_public) {
+  (*vars)["Method"] = method->name();
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+  struct {
+    grpc::string prefix;
+    grpc::string method_params;  // extra arguments to method
+    grpc::string raw_args;       // extra arguments to raw version of method
+  } async_prefixes[] = {{"Async", ", void* tag", ", tag"},
+                        {"PrepareAsync", "", ""}};
+
+  if (is_public) {
+    if (method->NoStreaming()) {
+      printer->Print(
+          *vars,
+          "::grpc::Status $Method$(::grpc::ClientContext* context, "
+          "const $Request$& request, $Response$* response) override;\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        printer->Print(
+            *vars,
+            "std::unique_ptr< ::grpc::ClientAsyncResponseReader< $Response$>> "
+            "$AsyncPrefix$$Method$(::grpc::ClientContext* context, "
+            "const $Request$& request, "
+            "::grpc::CompletionQueue* cq) {\n");
+        printer->Indent();
+        printer->Print(*vars,
+                       "return std::unique_ptr< "
+                       "::grpc::ClientAsyncResponseReader< $Response$>>("
+                       "$AsyncPrefix$$Method$Raw(context, request, cq));\n");
+        printer->Outdent();
+        printer->Print("}\n");
+      }
+    } else if (ClientOnlyStreaming(method)) {
+      printer->Print(
+          *vars,
+          "std::unique_ptr< ::grpc::ClientWriter< $Request$>>"
+          " $Method$("
+          "::grpc::ClientContext* context, $Response$* response) {\n");
+      printer->Indent();
+      printer->Print(*vars,
+                     "return std::unique_ptr< ::grpc::ClientWriter< $Request$>>"
+                     "($Method$Raw(context, response));\n");
+      printer->Outdent();
+      printer->Print("}\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(*vars,
+                       "std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>"
+                       " $AsyncPrefix$$Method$(::grpc::ClientContext* context, "
+                       "$Response$* response, "
+                       "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+        printer->Indent();
+        printer->Print(
+            *vars,
+            "return std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>("
+            "$AsyncPrefix$$Method$Raw(context, response, "
+            "cq$AsyncRawArgs$));\n");
+        printer->Outdent();
+        printer->Print("}\n");
+      }
+    } else if (ServerOnlyStreaming(method)) {
+      printer->Print(
+          *vars,
+          "std::unique_ptr< ::grpc::ClientReader< $Response$>>"
+          " $Method$(::grpc::ClientContext* context, const $Request$& request)"
+          " {\n");
+      printer->Indent();
+      printer->Print(
+          *vars,
+          "return std::unique_ptr< ::grpc::ClientReader< $Response$>>"
+          "($Method$Raw(context, request));\n");
+      printer->Outdent();
+      printer->Print("}\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(
+            *vars,
+            "std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>> "
+            "$AsyncPrefix$$Method$("
+            "::grpc::ClientContext* context, const $Request$& request, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+        printer->Indent();
+        printer->Print(
+            *vars,
+            "return std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>>("
+            "$AsyncPrefix$$Method$Raw(context, request, cq$AsyncRawArgs$));\n");
+        printer->Outdent();
+        printer->Print("}\n");
+      }
+    } else if (method->BidiStreaming()) {
+      printer->Print(
+          *vars,
+          "std::unique_ptr< ::grpc::ClientReaderWriter< $Request$, $Response$>>"
+          " $Method$(::grpc::ClientContext* context) {\n");
+      printer->Indent();
+      printer->Print(*vars,
+                     "return std::unique_ptr< "
+                     "::grpc::ClientReaderWriter< $Request$, $Response$>>("
+                     "$Method$Raw(context));\n");
+      printer->Outdent();
+      printer->Print("}\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(*vars,
+                       "std::unique_ptr<  ::grpc::ClientAsyncReaderWriter< "
+                       "$Request$, $Response$>> "
+                       "$AsyncPrefix$$Method$(::grpc::ClientContext* context, "
+                       "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+        printer->Indent();
+        printer->Print(
+            *vars,
+            "return std::unique_ptr< "
+            "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>>("
+            "$AsyncPrefix$$Method$Raw(context, cq$AsyncRawArgs$));\n");
+        printer->Outdent();
+        printer->Print("}\n");
+      }
+    }
+  } else {
+    if (method->NoStreaming()) {
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        printer->Print(
+            *vars,
+            "::grpc::ClientAsyncResponseReader< $Response$>* "
+            "$AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, "
+            "const $Request$& request, "
+            "::grpc::CompletionQueue* cq) override;\n");
+      }
+    } else if (ClientOnlyStreaming(method)) {
+      printer->Print(*vars,
+                     "::grpc::ClientWriter< $Request$>* $Method$Raw("
+                     "::grpc::ClientContext* context, $Response$* response) "
+                     "override;\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(
+            *vars,
+            "::grpc::ClientAsyncWriter< $Request$>* $AsyncPrefix$$Method$Raw("
+            "::grpc::ClientContext* context, $Response$* response, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) override;\n");
+      }
+    } else if (ServerOnlyStreaming(method)) {
+      printer->Print(*vars,
+                     "::grpc::ClientReader< $Response$>* $Method$Raw("
+                     "::grpc::ClientContext* context, const $Request$& request)"
+                     " override;\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(
+            *vars,
+            "::grpc::ClientAsyncReader< $Response$>* $AsyncPrefix$$Method$Raw("
+            "::grpc::ClientContext* context, const $Request$& request, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) override;\n");
+      }
+    } else if (method->BidiStreaming()) {
+      printer->Print(*vars,
+                     "::grpc::ClientReaderWriter< $Request$, $Response$>* "
+                     "$Method$Raw(::grpc::ClientContext* context) override;\n");
+      for (auto async_prefix : async_prefixes) {
+        (*vars)["AsyncPrefix"] = async_prefix.prefix;
+        (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+        (*vars)["AsyncRawArgs"] = async_prefix.raw_args;
+        printer->Print(
+            *vars,
+            "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* "
+            "$AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, "
+            "::grpc::CompletionQueue* cq$AsyncMethodParams$) override;\n");
+      }
+    }
+  }
+}
+
+void PrintHeaderClientMethodCallbackInterfacesStart(
+    grpc_generator::Printer* printer,
+    std::map<grpc::string, grpc::string>* vars) {
+  // This declares the interface for the callback-based API. The components
+  // are pure; even though this is new (post-1.0) API, it can be pure because
+  // it is an entirely new interface that happens to be scoped within
+  // StubInterface, not new additions to StubInterface itself
+  printer->Print("class experimental_async_interface {\n");
+  // All methods in this new interface are public. There is no need for private
+  // "Raw" methods since the callback-based API returns unowned raw pointers
+  printer->Print(" public:\n");
+  printer->Indent();
+  printer->Print("virtual ~experimental_async_interface() {}\n");
+}
+
+void PrintHeaderClientMethodCallbackInterfaces(
+    grpc_generator::Printer* printer, const grpc_generator::Method* method,
+    std::map<grpc::string, grpc::string>* vars, bool is_public) {
+  // Reserve is_public for future expansion
+  assert(is_public);
+
+  (*vars)["Method"] = method->name();
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+
+  if (method->NoStreaming()) {
+    printer->Print(*vars,
+                   "virtual void $Method$(::grpc::ClientContext* context, "
+                   "const $Request$* request, $Response$* response, "
+                   "std::function<void(::grpc::Status)>) = 0;\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "virtual void $Method$(::grpc::ClientContext* context, "
+                   "$Response$* response, "
+                   "::grpc::experimental::ClientWriteReactor< $Request$>* "
+                   "reactor) = 0;\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "virtual void $Method$(::grpc::ClientContext* context, "
+                   "$Request$* request, "
+                   "::grpc::experimental::ClientReadReactor< $Response$>* "
+                   "reactor) = 0;\n");
+  } else if (method->BidiStreaming()) {
+    printer->Print(*vars,
+                   "virtual void $Method$(::grpc::ClientContext* context, "
+                   "::grpc::experimental::ClientBidiReactor< "
+                   "$Request$,$Response$>* reactor) = 0;\n");
+  }
+}
+
+void PrintHeaderClientMethodCallbackInterfacesEnd(
+    grpc_generator::Printer* printer,
+    std::map<grpc::string, grpc::string>* vars) {
+  printer->Outdent();
+  printer->Print("};\n");
+
+  // Declare a function to give the async stub contents. It can't be pure
+  // since this is a new API in StubInterface, but it is meaningless by default
+  // (since any stub that wants to use it must have its own implementation of
+  // the callback functions therein), so make the default return value nullptr.
+  // Intentionally include the word "class" to avoid possible shadowing.
+  printer->Print(
+      "virtual class experimental_async_interface* experimental_async() { "
+      "return nullptr; }\n");
+}
+
+void PrintHeaderClientMethodCallbackStart(
+    grpc_generator::Printer* printer,
+    std::map<grpc::string, grpc::string>* vars) {
+  // This declares the stub entry for the callback-based API.
+  printer->Print("class experimental_async final :\n");
+  printer->Print("  public StubInterface::experimental_async_interface {\n");
+  printer->Print(" public:\n");
+  printer->Indent();
+}
+
+void PrintHeaderClientMethodCallback(grpc_generator::Printer* printer,
+                                     const grpc_generator::Method* method,
+                                     std::map<grpc::string, grpc::string>* vars,
+                                     bool is_public) {
+  // Reserve is_public for future expansion
+  assert(is_public);
+
+  (*vars)["Method"] = method->name();
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+
+  if (method->NoStreaming()) {
+    printer->Print(*vars,
+                   "void $Method$(::grpc::ClientContext* context, "
+                   "const $Request$* request, $Response$* response, "
+                   "std::function<void(::grpc::Status)>) override;\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "void $Method$(::grpc::ClientContext* context, "
+                   "$Response$* response, "
+                   "::grpc::experimental::ClientWriteReactor< $Request$>* "
+                   "reactor) override;\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "void $Method$(::grpc::ClientContext* context, "
+                   "$Request$* request, "
+                   "::grpc::experimental::ClientReadReactor< $Response$>* "
+                   "reactor) override;\n");
+
+  } else if (method->BidiStreaming()) {
+    printer->Print(*vars,
+                   "void $Method$(::grpc::ClientContext* context, "
+                   "::grpc::experimental::ClientBidiReactor< "
+                   "$Request$,$Response$>* reactor) override;\n");
+  }
+}
+
+void PrintHeaderClientMethodCallbackEnd(
+    grpc_generator::Printer* printer,
+    std::map<grpc::string, grpc::string>* vars) {
+  printer->Outdent();
+  printer->Print(" private:\n");
+  printer->Indent();
+  printer->Print("friend class Stub;\n");
+  printer->Print("explicit experimental_async(Stub* stub): stub_(stub) { }\n");
+  // include a function with a dummy use of stub_ to avoid an unused
+  // private member warning for service with no methods
+  printer->Print("Stub* stub() { return stub_; }\n");
+  printer->Print("Stub* stub_;\n");
+  printer->Outdent();
+  printer->Print("};\n");
+
+  printer->Print(
+      "class experimental_async_interface* experimental_async() override { "
+      "return &async_stub_; }\n");
+}
+
+void PrintHeaderClientMethodData(grpc_generator::Printer* printer,
+                                 const grpc_generator::Method* method,
+                                 std::map<grpc::string, grpc::string>* vars) {
+  (*vars)["Method"] = method->name();
+  printer->Print(*vars,
+                 "const ::grpc::internal::RpcMethod rpcmethod_$Method$_;\n");
+}
+
+void PrintHeaderServerMethodSync(grpc_generator::Printer* printer,
+                                 const grpc_generator::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();
+  printer->Print(method->GetLeadingComments("//").c_str());
+  if (method->NoStreaming()) {
+    printer->Print(*vars,
+                   "virtual ::grpc::Status $Method$("
+                   "::grpc::ServerContext* context, const $Request$* request, "
+                   "$Response$* response);\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "virtual ::grpc::Status $Method$("
+                   "::grpc::ServerContext* context, "
+                   "::grpc::ServerReader< $Request$>* reader, "
+                   "$Response$* response);\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "virtual ::grpc::Status $Method$("
+                   "::grpc::ServerContext* context, const $Request$* request, "
+                   "::grpc::ServerWriter< $Response$>* writer);\n");
+  } else if (method->BidiStreaming()) {
+    printer->Print(
+        *vars,
+        "virtual ::grpc::Status $Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerReaderWriter< $Response$, $Request$>* stream);"
+        "\n");
+  }
+  printer->Print(method->GetTrailingComments("//").c_str());
+}
+
+// Helper generator. Disables the sync API for Request and Response, then adds
+// in an async API for RealRequest and RealResponse types. This is to be used
+// to generate async and raw async APIs.
+void PrintHeaderServerAsyncMethodsHelper(
+    grpc_generator::Printer* printer, const grpc_generator::Method* method,
+    std::map<grpc::string, grpc::string>* vars) {
+  if (method->NoStreaming()) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, const $Request$* request, "
+        "$Response$* response) override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(
+        *vars,
+        "void Request$Method$("
+        "::grpc::ServerContext* context, $RealRequest$* request, "
+        "::grpc::ServerAsyncResponseWriter< $RealResponse$>* response, "
+        "::grpc::CompletionQueue* new_call_cq, "
+        "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
+    printer->Print(*vars,
+                   "  ::grpc::Service::RequestAsyncUnary($Idx$, context, "
+                   "request, response, new_call_cq, notification_cq, tag);\n");
+    printer->Print("}\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerReader< $Request$>* reader, "
+        "$Response$* response) override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(
+        *vars,
+        "void Request$Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerAsyncReader< $RealResponse$, $RealRequest$>* reader, "
+        "::grpc::CompletionQueue* new_call_cq, "
+        "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
+    printer->Print(*vars,
+                   "  ::grpc::Service::RequestAsyncClientStreaming($Idx$, "
+                   "context, reader, new_call_cq, notification_cq, tag);\n");
+    printer->Print("}\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, const $Request$* request, "
+        "::grpc::ServerWriter< $Response$>* writer) override "
+        "{\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(
+        *vars,
+        "void Request$Method$("
+        "::grpc::ServerContext* context, $RealRequest$* request, "
+        "::grpc::ServerAsyncWriter< $RealResponse$>* writer, "
+        "::grpc::CompletionQueue* new_call_cq, "
+        "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::RequestAsyncServerStreaming($Idx$, "
+        "context, request, writer, new_call_cq, notification_cq, tag);\n");
+    printer->Print("}\n");
+  } else if (method->BidiStreaming()) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) "
+        " override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(
+        *vars,
+        "void Request$Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerAsyncReaderWriter< $RealResponse$, $RealRequest$>* "
+        "stream, "
+        "::grpc::CompletionQueue* new_call_cq, "
+        "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
+    printer->Print(*vars,
+                   "  ::grpc::Service::RequestAsyncBidiStreaming($Idx$, "
+                   "context, stream, new_call_cq, notification_cq, tag);\n");
+    printer->Print("}\n");
+  }
+}
+
+void PrintHeaderServerMethodAsync(grpc_generator::Printer* printer,
+                                  const grpc_generator::Method* method,
+                                  std::map<grpc::string, grpc::string>* vars) {
+  (*vars)["Method"] = method->name();
+  // These will be disabled
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+  // These will be used for the async API
+  (*vars)["RealRequest"] = method->input_type_name();
+  (*vars)["RealResponse"] = method->output_type_name();
+  printer->Print(*vars, "template <class BaseClass>\n");
+  printer->Print(*vars,
+                 "class WithAsyncMethod_$Method$ : public BaseClass {\n");
+  printer->Print(
+      " private:\n"
+      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
+  printer->Print(" public:\n");
+  printer->Indent();
+  printer->Print(*vars,
+                 "WithAsyncMethod_$Method$() {\n"
+                 "  ::grpc::Service::MarkMethodAsync($Idx$);\n"
+                 "}\n");
+  printer->Print(*vars,
+                 "~WithAsyncMethod_$Method$() override {\n"
+                 "  BaseClassMustBeDerivedFromService(this);\n"
+                 "}\n");
+  PrintHeaderServerAsyncMethodsHelper(printer, method, vars);
+  printer->Outdent();
+  printer->Print(*vars, "};\n");
+}
+
+// Helper generator. Disables the sync API for Request and Response, then adds
+// in a callback API for RealRequest and RealResponse types. This is to be used
+// to generate callback and raw callback APIs.
+void PrintHeaderServerCallbackMethodsHelper(
+    grpc_generator::Printer* printer, const grpc_generator::Method* method,
+    std::map<grpc::string, grpc::string>* vars) {
+  if (method->NoStreaming()) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, const $Request$* request, "
+        "$Response$* response) override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(
+        *vars,
+        "virtual void $Method$("
+        "::grpc::ServerContext* context, const $RealRequest$* request, "
+        "$RealResponse$* response, "
+        "::grpc::experimental::ServerCallbackRpcController* "
+        "controller) { controller->Finish(::grpc::Status("
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\")); }\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerReader< $Request$>* reader, "
+        "$Response$* response) override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(*vars,
+                   "virtual ::grpc::experimental::ServerReadReactor< "
+                   "$RealRequest$, $RealResponse$>* $Method$() {\n"
+                   "  return new ::grpc::internal::UnimplementedReadReactor<\n"
+                   "    $RealRequest$, $RealResponse$>;}\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, const $Request$* request, "
+        "::grpc::ServerWriter< $Response$>* writer) override "
+        "{\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(*vars,
+                   "virtual ::grpc::experimental::ServerWriteReactor< "
+                   "$RealRequest$, $RealResponse$>* $Method$() {\n"
+                   "  return new ::grpc::internal::UnimplementedWriteReactor<\n"
+                   "    $RealRequest$, $RealResponse$>;}\n");
+  } else if (method->BidiStreaming()) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) "
+        " override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(*vars,
+                   "virtual ::grpc::experimental::ServerBidiReactor< "
+                   "$RealRequest$, $RealResponse$>* $Method$() {\n"
+                   "  return new ::grpc::internal::UnimplementedBidiReactor<\n"
+                   "    $RealRequest$, $RealResponse$>;}\n");
+  }
+}
+
+void PrintHeaderServerMethodCallback(
+    grpc_generator::Printer* printer, const grpc_generator::Method* method,
+    std::map<grpc::string, grpc::string>* vars) {
+  (*vars)["Method"] = method->name();
+  // These will be disabled
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+  // These will be used for the callback API
+  (*vars)["RealRequest"] = method->input_type_name();
+  (*vars)["RealResponse"] = method->output_type_name();
+  printer->Print(*vars, "template <class BaseClass>\n");
+  printer->Print(
+      *vars,
+      "class ExperimentalWithCallbackMethod_$Method$ : public BaseClass {\n");
+  printer->Print(
+      " private:\n"
+      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
+  printer->Print(" public:\n");
+  printer->Indent();
+  printer->Print(*vars, "ExperimentalWithCallbackMethod_$Method$() {\n");
+  if (method->NoStreaming()) {
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
+        "    new ::grpc::internal::CallbackUnaryHandler< "
+        "$RealRequest$, $RealResponse$>(\n"
+        "      [this](::grpc::ServerContext* context,\n"
+        "             const $RealRequest$* request,\n"
+        "             $RealResponse$* response,\n"
+        "             ::grpc::experimental::ServerCallbackRpcController* "
+        "controller) {\n"
+        "               return this->$"
+        "Method$(context, request, response, controller);\n"
+        "             }));\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
+        "    new ::grpc::internal::CallbackClientStreamingHandler< "
+        "$RealRequest$, $RealResponse$>(\n"
+        "      [this] { return this->$Method$(); }));\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
+        "    new ::grpc::internal::CallbackServerStreamingHandler< "
+        "$RealRequest$, $RealResponse$>(\n"
+        "      [this] { return this->$Method$(); }));\n");
+  } else if (method->BidiStreaming()) {
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
+        "    new ::grpc::internal::CallbackBidiHandler< "
+        "$RealRequest$, $RealResponse$>(\n"
+        "      [this] { return this->$Method$(); }));\n");
+  }
+  printer->Print(*vars, "}\n");
+  printer->Print(*vars,
+                 "~ExperimentalWithCallbackMethod_$Method$() override {\n"
+                 "  BaseClassMustBeDerivedFromService(this);\n"
+                 "}\n");
+  PrintHeaderServerCallbackMethodsHelper(printer, method, vars);
+  printer->Outdent();
+  printer->Print(*vars, "};\n");
+}
+
+void PrintHeaderServerMethodRawCallback(
+    grpc_generator::Printer* printer, const grpc_generator::Method* method,
+    std::map<grpc::string, grpc::string>* vars) {
+  (*vars)["Method"] = method->name();
+  // These will be disabled
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+  // These will be used for raw API
+  (*vars)["RealRequest"] = "::grpc::ByteBuffer";
+  (*vars)["RealResponse"] = "::grpc::ByteBuffer";
+  printer->Print(*vars, "template <class BaseClass>\n");
+  printer->Print(*vars,
+                 "class ExperimentalWithRawCallbackMethod_$Method$ : public "
+                 "BaseClass {\n");
+  printer->Print(
+      " private:\n"
+      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
+  printer->Print(" public:\n");
+  printer->Indent();
+  printer->Print(*vars, "ExperimentalWithRawCallbackMethod_$Method$() {\n");
+  if (method->NoStreaming()) {
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
+        "    new ::grpc::internal::CallbackUnaryHandler< "
+        "$RealRequest$, $RealResponse$>(\n"
+        "      [this](::grpc::ServerContext* context,\n"
+        "             const $RealRequest$* request,\n"
+        "             $RealResponse$* response,\n"
+        "             ::grpc::experimental::ServerCallbackRpcController* "
+        "controller) {\n"
+        "               this->$"
+        "Method$(context, request, response, controller);\n"
+        "             }));\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
+        "    new ::grpc::internal::CallbackClientStreamingHandler< "
+        "$RealRequest$, $RealResponse$>(\n"
+        "      [this] { return this->$Method$(); }));\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
+        "    new ::grpc::internal::CallbackServerStreamingHandler< "
+        "$RealRequest$, $RealResponse$>(\n"
+        "      [this] { return this->$Method$(); }));\n");
+  } else if (method->BidiStreaming()) {
+    printer->Print(
+        *vars,
+        "  ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
+        "    new ::grpc::internal::CallbackBidiHandler< "
+        "$RealRequest$, $RealResponse$>(\n"
+        "      [this] { return this->$Method$(); }));\n");
+  }
+  printer->Print(*vars, "}\n");
+  printer->Print(*vars,
+                 "~ExperimentalWithRawCallbackMethod_$Method$() override {\n"
+                 "  BaseClassMustBeDerivedFromService(this);\n"
+                 "}\n");
+  PrintHeaderServerCallbackMethodsHelper(printer, method, vars);
+  printer->Outdent();
+  printer->Print(*vars, "};\n");
+}
+
+void PrintHeaderServerMethodStreamedUnary(
+    grpc_generator::Printer* printer, const grpc_generator::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::MarkMethodStreamed($Idx$,\n"
+                   "    new ::grpc::internal::StreamedUnaryHandler< $Request$, "
+                   "$Response$>(std::bind"
+                   "(&WithStreamedUnaryMethod_$Method$<BaseClass>::"
+                   "Streamed$Method$, this, std::placeholders::_1, "
+                   "std::placeholders::_2)));\n"
+                   "}\n");
+    printer->Print(*vars,
+                   "~WithStreamedUnaryMethod_$Method$() 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) 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 PrintHeaderServerMethodSplitStreaming(
+    grpc_generator::Printer* printer, const grpc_generator::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 (ServerOnlyStreaming(method)) {
+    printer->Print(*vars, "template <class BaseClass>\n");
+    printer->Print(*vars,
+                   "class WithSplitStreamingMethod_$Method$ : "
+                   "public BaseClass {\n");
+    printer->Print(
+        " private:\n"
+        "  void BaseClassMustBeDerivedFromService(const Service *service) "
+        "{}\n");
+    printer->Print(" public:\n");
+    printer->Indent();
+    printer->Print(
+        *vars,
+        "WithSplitStreamingMethod_$Method$() {\n"
+        "  ::grpc::Service::MarkMethodStreamed($Idx$,\n"
+        "    new ::grpc::internal::SplitServerStreamingHandler< $Request$, "
+        "$Response$>(std::bind"
+        "(&WithSplitStreamingMethod_$Method$<BaseClass>::"
+        "Streamed$Method$, this, std::placeholders::_1, "
+        "std::placeholders::_2)));\n"
+        "}\n");
+    printer->Print(*vars,
+                   "~WithSplitStreamingMethod_$Method$() 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, "
+        "::grpc::ServerWriter< $Response$>* writer) override "
+        "{\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+    printer->Print(*vars,
+                   "// replace default version of method with split streamed\n"
+                   "virtual ::grpc::Status Streamed$Method$("
+                   "::grpc::ServerContext* context, "
+                   "::grpc::ServerSplitStreamer< "
+                   "$Request$,$Response$>* server_split_streamer)"
+                   " = 0;\n");
+    printer->Outdent();
+    printer->Print(*vars, "};\n");
+  }
+}
+
+void PrintHeaderServerMethodGeneric(
+    grpc_generator::Printer* printer, const grpc_generator::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();
+  printer->Print(*vars, "template <class BaseClass>\n");
+  printer->Print(*vars,
+                 "class WithGenericMethod_$Method$ : public BaseClass {\n");
+  printer->Print(
+      " private:\n"
+      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
+  printer->Print(" public:\n");
+  printer->Indent();
+  printer->Print(*vars,
+                 "WithGenericMethod_$Method$() {\n"
+                 "  ::grpc::Service::MarkMethodGeneric($Idx$);\n"
+                 "}\n");
+  printer->Print(*vars,
+                 "~WithGenericMethod_$Method$() override {\n"
+                 "  BaseClassMustBeDerivedFromService(this);\n"
+                 "}\n");
+  if (method->NoStreaming()) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, const $Request$* request, "
+        "$Response$* response) override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerReader< $Request$>* reader, "
+        "$Response$* response) override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, const $Request$* request, "
+        "::grpc::ServerWriter< $Response$>* writer) override "
+        "{\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+  } else if (method->BidiStreaming()) {
+    printer->Print(
+        *vars,
+        "// disable synchronous version of this method\n"
+        "::grpc::Status $Method$("
+        "::grpc::ServerContext* context, "
+        "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) "
+        " override {\n"
+        "  abort();\n"
+        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
+        "}\n");
+  }
+  printer->Outdent();
+  printer->Print(*vars, "};\n");
+}
+
+void PrintHeaderServerMethodRaw(grpc_generator::Printer* printer,
+                                const grpc_generator::Method* method,
+                                std::map<grpc::string, grpc::string>* vars) {
+  (*vars)["Method"] = method->name();
+  // These will be disabled
+  (*vars)["Request"] = method->input_type_name();
+  (*vars)["Response"] = method->output_type_name();
+  // These will be used for raw API
+  (*vars)["RealRequest"] = "::grpc::ByteBuffer";
+  (*vars)["RealResponse"] = "::grpc::ByteBuffer";
+  printer->Print(*vars, "template <class BaseClass>\n");
+  printer->Print(*vars, "class WithRawMethod_$Method$ : public BaseClass {\n");
+  printer->Print(
+      " private:\n"
+      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
+  printer->Print(" public:\n");
+  printer->Indent();
+  printer->Print(*vars,
+                 "WithRawMethod_$Method$() {\n"
+                 "  ::grpc::Service::MarkMethodRaw($Idx$);\n"
+                 "}\n");
+  printer->Print(*vars,
+                 "~WithRawMethod_$Method$() override {\n"
+                 "  BaseClassMustBeDerivedFromService(this);\n"
+                 "}\n");
+  PrintHeaderServerAsyncMethodsHelper(printer, method, vars);
+  printer->Outdent();
+  printer->Print(*vars, "};\n");
+}
+
+void PrintHeaderService(grpc_generator::Printer* printer,
+                        const grpc_generator::Service* service,
+                        std::map<grpc::string, grpc::string>* vars) {
+  (*vars)["Service"] = service->name();
+
+  printer->Print(service->GetLeadingComments("//").c_str());
+  printer->Print(*vars,
+                 "class $Service$ final {\n"
+                 " public:\n");
+  printer->Indent();
+
+  // Service metadata
+  printer->Print(*vars,
+                 "static constexpr char const* service_full_name() {\n"
+                 "  return \"$Package$$Service$\";\n"
+                 "}\n");
+
+  // Client side
+  printer->Print(
+      "class StubInterface {\n"
+      " public:\n");
+  printer->Indent();
+  printer->Print("virtual ~StubInterface() {}\n");
+  for (int i = 0; i < service->method_count(); ++i) {
+    printer->Print(service->method(i)->GetLeadingComments("//").c_str());
+    PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars,
+                                      true);
+    printer->Print(service->method(i)->GetTrailingComments("//").c_str());
+  }
+  PrintHeaderClientMethodCallbackInterfacesStart(printer, vars);
+  for (int i = 0; i < service->method_count(); ++i) {
+    printer->Print(service->method(i)->GetLeadingComments("//").c_str());
+    PrintHeaderClientMethodCallbackInterfaces(printer, service->method(i).get(),
+                                              vars, true);
+    printer->Print(service->method(i)->GetTrailingComments("//").c_str());
+  }
+  PrintHeaderClientMethodCallbackInterfacesEnd(printer, vars);
+  printer->Outdent();
+  printer->Print("private:\n");
+  printer->Indent();
+  for (int i = 0; i < service->method_count(); ++i) {
+    PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars,
+                                      false);
+  }
+  printer->Outdent();
+  printer->Print("};\n");
+  printer->Print(
+      "class Stub final : public StubInterface"
+      " {\n public:\n");
+  printer->Indent();
+  printer->Print(
+      "Stub(const std::shared_ptr< ::grpc::ChannelInterface>& "
+      "channel);\n");
+  for (int i = 0; i < service->method_count(); ++i) {
+    PrintHeaderClientMethod(printer, service->method(i).get(), vars, true);
+  }
+  PrintHeaderClientMethodCallbackStart(printer, vars);
+  for (int i = 0; i < service->method_count(); ++i) {
+    PrintHeaderClientMethodCallback(printer, service->method(i).get(), vars,
+                                    true);
+  }
+  PrintHeaderClientMethodCallbackEnd(printer, vars);
+  printer->Outdent();
+  printer->Print("\n private:\n");
+  printer->Indent();
+  printer->Print("std::shared_ptr< ::grpc::ChannelInterface> channel_;\n");
+  printer->Print("class experimental_async async_stub_{this};\n");
+  for (int i = 0; i < service->method_count(); ++i) {
+    PrintHeaderClientMethod(printer, service->method(i).get(), vars, false);
+  }
+  for (int i = 0; i < service->method_count(); ++i) {
+    PrintHeaderClientMethodData(printer, service->method(i).get(), vars);
+  }
+  printer->Outdent();
+  printer->Print("};\n");
+  printer->Print(
+      "static std::unique_ptr<Stub> NewStub(const std::shared_ptr< "
+      "::grpc::ChannelInterface>& channel, "
+      "const ::grpc::StubOptions& options = ::grpc::StubOptions());\n");
+
+  printer->Print("\n");
+
+  // Server side - base
+  printer->Print(
+      "class Service : public ::grpc::Service {\n"
+      " public:\n");
+  printer->Indent();
+  printer->Print("Service();\n");
+  printer->Print("virtual ~Service();\n");
+  for (int i = 0; i < service->method_count(); ++i) {
+    PrintHeaderServerMethodSync(printer, service->method(i).get(), vars);
+  }
+  printer->Outdent();
+  printer->Print("};\n");
+
+  // Server side - Asynchronous
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintHeaderServerMethodAsync(printer, service->method(i).get(), vars);
+  }
+
+  printer->Print("typedef ");
+
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["method_name"] = service->method(i)->name();
+    printer->Print(*vars, "WithAsyncMethod_$method_name$<");
+  }
+  printer->Print("Service");
+  for (int i = 0; i < service->method_count(); ++i) {
+    printer->Print(" >");
+  }
+  printer->Print(" AsyncService;\n");
+
+  // Server side - Callback
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintHeaderServerMethodCallback(printer, service->method(i).get(), vars);
+  }
+
+  printer->Print("typedef ");
+
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["method_name"] = service->method(i)->name();
+    printer->Print(*vars, "ExperimentalWithCallbackMethod_$method_name$<");
+  }
+  printer->Print("Service");
+  for (int i = 0; i < service->method_count(); ++i) {
+    printer->Print(" >");
+  }
+  printer->Print(" ExperimentalCallbackService;\n");
+
+  // Server side - Generic
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintHeaderServerMethodGeneric(printer, service->method(i).get(), vars);
+  }
+
+  // Server side - Raw
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintHeaderServerMethodRaw(printer, service->method(i).get(), vars);
+  }
+
+  // Server side - Raw Callback
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintHeaderServerMethodRawCallback(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)->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");
+
+  // Server side - controlled server-side streaming
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintHeaderServerMethodSplitStreaming(printer, service->method(i).get(),
+                                          vars);
+  }
+
+  printer->Print("typedef ");
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["method_name"] = service->method(i)->name();
+    auto method = service->method(i);
+    if (ServerOnlyStreaming(method.get())) {
+      printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<");
+    }
+  }
+  printer->Print("Service");
+  for (int i = 0; i < service->method_count(); ++i) {
+    auto method = service->method(i);
+    if (ServerOnlyStreaming(method.get())) {
+      printer->Print(" >");
+    }
+  }
+  printer->Print(" SplitStreamedService;\n");
+
+  // Server side - typedef for controlled both unary and server-side streaming
+  printer->Print("typedef ");
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["method_name"] = service->method(i)->name();
+    auto method = service->method(i);
+    if (ServerOnlyStreaming(method.get())) {
+      printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<");
+    }
+    if (service->method(i)->NoStreaming()) {
+      printer->Print(*vars, "WithStreamedUnaryMethod_$method_name$<");
+    }
+  }
+  printer->Print("Service");
+  for (int i = 0; i < service->method_count(); ++i) {
+    auto method = service->method(i);
+    if (service->method(i)->NoStreaming() ||
+        ServerOnlyStreaming(method.get())) {
+      printer->Print(" >");
+    }
+  }
+  printer->Print(" StreamedService;\n");
+
+  printer->Outdent();
+  printer->Print("};\n");
+  printer->Print(service->GetTrailingComments("//").c_str());
+}
+
+grpc::string GetHeaderServices(grpc_generator::File* file,
+                               const Parameters& params) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+    // Package string is empty or ends with a dot. It is used to fully qualify
+    // method names.
+    vars["Package"] = file->package();
+    if (!file->package().empty()) {
+      vars["Package"].append(".");
+    }
+
+    if (!params.services_namespace.empty()) {
+      vars["services_namespace"] = params.services_namespace;
+      printer->Print(vars, "\nnamespace $services_namespace$ {\n\n");
+    }
+
+    for (int i = 0; i < file->service_count(); ++i) {
+      PrintHeaderService(printer.get(), file->service(i).get(), &vars);
+      printer->Print("\n");
+    }
+
+    if (!params.services_namespace.empty()) {
+      printer->Print(vars, "}  // namespace $services_namespace$\n\n");
+    }
+  }
+  return output;
+}
+
+grpc::string GetHeaderEpilogue(grpc_generator::File* file,
+                               const Parameters& /*params*/) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+
+    vars["filename"] = file->filename();
+    vars["filename_identifier"] = FilenameIdentifier(file->filename());
+
+    if (!file->package().empty()) {
+      std::vector<grpc::string> parts = file->package_parts();
+
+      for (auto part = parts.rbegin(); part != parts.rend(); part++) {
+        vars["part"] = *part;
+        printer->Print(vars, "}  // namespace $part$\n");
+      }
+      printer->Print(vars, "\n");
+    }
+
+    printer->Print(vars, "\n");
+    printer->Print(vars, "#endif  // GRPC_$filename_identifier$__INCLUDED\n");
+
+    printer->Print(file->GetTrailingComments("//").c_str());
+  }
+  return output;
+}
+
+grpc::string GetSourcePrologue(grpc_generator::File* file,
+                               const Parameters& /*params*/) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+
+    vars["filename"] = file->filename();
+    vars["filename_base"] = file->filename_without_ext();
+    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
+    vars["service_header_ext"] = kCppGeneratorServiceHeaderExt;
+
+    printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
+    printer->Print(vars,
+                   "// If you make any local change, they will be lost.\n");
+    printer->Print(vars, "// source: $filename$\n\n");
+
+    printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
+    printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n");
+    printer->Print(vars, "\n");
+  }
+  return output;
+}
+
+grpc::string GetSourceIncludes(grpc_generator::File* file,
+                               const Parameters& params) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+
+    static const char* headers_strs[] = {
+        "functional",
+        "grpcpp/impl/codegen/async_stream.h",
+        "grpcpp/impl/codegen/async_unary_call.h",
+        "grpcpp/impl/codegen/channel_interface.h",
+        "grpcpp/impl/codegen/client_unary_call.h",
+        "grpcpp/impl/codegen/client_callback.h",
+        "grpcpp/impl/codegen/method_handler_impl.h",
+        "grpcpp/impl/codegen/rpc_service_method.h",
+        "grpcpp/impl/codegen/server_callback.h",
+        "grpcpp/impl/codegen/service_type.h",
+        "grpcpp/impl/codegen/sync_stream.h"};
+    std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
+    PrintIncludes(printer.get(), headers, params.use_system_headers,
+                  params.grpc_search_path);
+
+    if (!file->package().empty()) {
+      std::vector<grpc::string> parts = file->package_parts();
+
+      for (auto part = parts.begin(); part != parts.end(); part++) {
+        vars["part"] = *part;
+        printer->Print(vars, "namespace $part$ {\n");
+      }
+    }
+
+    printer->Print(vars, "\n");
+  }
+  return output;
+}
+
+void PrintSourceClientMethod(grpc_generator::Printer* printer,
+                             const grpc_generator::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();
+  struct {
+    grpc::string prefix;
+    grpc::string start;          // bool literal expressed as string
+    grpc::string method_params;  // extra arguments to method
+    grpc::string create_args;    // extra arguments to creator
+  } async_prefixes[] = {{"Async", "true", ", void* tag", ", tag"},
+                        {"PrepareAsync", "false", "", ", nullptr"}};
+  if (method->NoStreaming()) {
+    printer->Print(*vars,
+                   "::grpc::Status $ns$$Service$::Stub::$Method$("
+                   "::grpc::ClientContext* context, "
+                   "const $Request$& request, $Response$* response) {\n");
+    printer->Print(*vars,
+                   "  return ::grpc::internal::BlockingUnaryCall"
+                   "(channel_.get(), rpcmethod_$Method$_, "
+                   "context, request, response);\n}\n\n");
+
+    printer->Print(*vars,
+                   "void $ns$$Service$::Stub::experimental_async::$Method$("
+                   "::grpc::ClientContext* context, "
+                   "const $Request$* request, $Response$* response, "
+                   "std::function<void(::grpc::Status)> f) {\n");
+    printer->Print(*vars,
+                   "  return ::grpc::internal::CallbackUnaryCall"
+                   "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, "
+                   "context, request, response, std::move(f));\n}\n\n");
+
+    for (auto async_prefix : async_prefixes) {
+      (*vars)["AsyncPrefix"] = async_prefix.prefix;
+      (*vars)["AsyncStart"] = async_prefix.start;
+      printer->Print(*vars,
+                     "::grpc::ClientAsyncResponseReader< $Response$>* "
+                     "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw(::grpc::"
+                     "ClientContext* context, "
+                     "const $Request$& request, "
+                     "::grpc::CompletionQueue* cq) {\n");
+      printer->Print(
+          *vars,
+          "  return "
+          "::grpc::internal::ClientAsyncResponseReaderFactory< $Response$>"
+          "::Create(channel_.get(), cq, "
+          "rpcmethod_$Method$_, "
+          "context, request, $AsyncStart$);\n"
+          "}\n\n");
+    }
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "::grpc::ClientWriter< $Request$>* "
+                   "$ns$$Service$::Stub::$Method$Raw("
+                   "::grpc::ClientContext* context, $Response$* response) {\n");
+    printer->Print(
+        *vars,
+        "  return ::grpc::internal::ClientWriterFactory< $Request$>::Create("
+        "channel_.get(), "
+        "rpcmethod_$Method$_, "
+        "context, response);\n"
+        "}\n\n");
+
+    printer->Print(
+        *vars,
+        "void $ns$$Service$::"
+        "Stub::experimental_async::$Method$(::grpc::ClientContext* context, "
+        "$Response$* response, "
+        "::grpc::experimental::ClientWriteReactor< $Request$>* reactor) {\n");
+    printer->Print(*vars,
+                   "  ::grpc::internal::ClientCallbackWriterFactory< "
+                   "$Request$>::Create("
+                   "stub_->channel_.get(), "
+                   "stub_->rpcmethod_$Method$_, "
+                   "context, response, reactor);\n"
+                   "}\n\n");
+
+    for (auto async_prefix : async_prefixes) {
+      (*vars)["AsyncPrefix"] = async_prefix.prefix;
+      (*vars)["AsyncStart"] = async_prefix.start;
+      (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+      (*vars)["AsyncCreateArgs"] = async_prefix.create_args;
+      printer->Print(*vars,
+                     "::grpc::ClientAsyncWriter< $Request$>* "
+                     "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw("
+                     "::grpc::ClientContext* context, $Response$* response, "
+                     "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+      printer->Print(
+          *vars,
+          "  return ::grpc::internal::ClientAsyncWriterFactory< $Request$>"
+          "::Create(channel_.get(), cq, "
+          "rpcmethod_$Method$_, "
+          "context, response, $AsyncStart$$AsyncCreateArgs$);\n"
+          "}\n\n");
+    }
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "::grpc::ClientReader< $Response$>* "
+        "$ns$$Service$::Stub::$Method$Raw("
+        "::grpc::ClientContext* context, const $Request$& request) {\n");
+    printer->Print(
+        *vars,
+        "  return ::grpc::internal::ClientReaderFactory< $Response$>::Create("
+        "channel_.get(), "
+        "rpcmethod_$Method$_, "
+        "context, request);\n"
+        "}\n\n");
+
+    printer->Print(
+        *vars,
+        "void $ns$$Service$::Stub::experimental_async::$Method$(::grpc::"
+        "ClientContext* context, "
+        "$Request$* request, "
+        "::grpc::experimental::ClientReadReactor< $Response$>* reactor) {\n");
+    printer->Print(*vars,
+                   "  ::grpc::internal::ClientCallbackReaderFactory< "
+                   "$Response$>::Create("
+                   "stub_->channel_.get(), "
+                   "stub_->rpcmethod_$Method$_, "
+                   "context, request, reactor);\n"
+                   "}\n\n");
+
+    for (auto async_prefix : async_prefixes) {
+      (*vars)["AsyncPrefix"] = async_prefix.prefix;
+      (*vars)["AsyncStart"] = async_prefix.start;
+      (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+      (*vars)["AsyncCreateArgs"] = async_prefix.create_args;
+      printer->Print(
+          *vars,
+          "::grpc::ClientAsyncReader< $Response$>* "
+          "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw("
+          "::grpc::ClientContext* context, const $Request$& request, "
+          "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+      printer->Print(
+          *vars,
+          "  return ::grpc::internal::ClientAsyncReaderFactory< $Response$>"
+          "::Create(channel_.get(), cq, "
+          "rpcmethod_$Method$_, "
+          "context, request, $AsyncStart$$AsyncCreateArgs$);\n"
+          "}\n\n");
+    }
+  } else if (method->BidiStreaming()) {
+    printer->Print(
+        *vars,
+        "::grpc::ClientReaderWriter< $Request$, $Response$>* "
+        "$ns$$Service$::Stub::$Method$Raw(::grpc::ClientContext* context) {\n");
+    printer->Print(*vars,
+                   "  return ::grpc::internal::ClientReaderWriterFactory< "
+                   "$Request$, $Response$>::Create("
+                   "channel_.get(), "
+                   "rpcmethod_$Method$_, "
+                   "context);\n"
+                   "}\n\n");
+
+    printer->Print(
+        *vars,
+        "void $ns$$Service$::Stub::experimental_async::$Method$(::grpc::"
+        "ClientContext* context, "
+        "::grpc::experimental::ClientBidiReactor< $Request$,$Response$>* "
+        "reactor) {\n");
+    printer->Print(*vars,
+                   "  ::grpc::internal::ClientCallbackReaderWriterFactory< "
+                   "$Request$,$Response$>::Create("
+                   "stub_->channel_.get(), "
+                   "stub_->rpcmethod_$Method$_, "
+                   "context, reactor);\n"
+                   "}\n\n");
+
+    for (auto async_prefix : async_prefixes) {
+      (*vars)["AsyncPrefix"] = async_prefix.prefix;
+      (*vars)["AsyncStart"] = async_prefix.start;
+      (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+      (*vars)["AsyncCreateArgs"] = async_prefix.create_args;
+      printer->Print(*vars,
+                     "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* "
+                     "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw(::grpc::"
+                     "ClientContext* context, "
+                     "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n");
+      printer->Print(*vars,
+                     "  return "
+                     "::grpc::internal::ClientAsyncReaderWriterFactory< "
+                     "$Request$, $Response$>::Create("
+                     "channel_.get(), cq, "
+                     "rpcmethod_$Method$_, "
+                     "context, $AsyncStart$$AsyncCreateArgs$);\n"
+                     "}\n\n");
+    }
+  }
+}
+
+void PrintSourceServerMethod(grpc_generator::Printer* printer,
+                             const grpc_generator::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,
+                   "::grpc::Status $ns$$Service$::Service::$Method$("
+                   "::grpc::ServerContext* context, "
+                   "const $Request$* request, $Response$* response) {\n");
+    printer->Print("  (void) context;\n");
+    printer->Print("  (void) request;\n");
+    printer->Print("  (void) response;\n");
+    printer->Print(
+        "  return ::grpc::Status("
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
+    printer->Print("}\n\n");
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "::grpc::Status $ns$$Service$::Service::$Method$("
+                   "::grpc::ServerContext* context, "
+                   "::grpc::ServerReader< $Request$>* reader, "
+                   "$Response$* response) {\n");
+    printer->Print("  (void) context;\n");
+    printer->Print("  (void) reader;\n");
+    printer->Print("  (void) response;\n");
+    printer->Print(
+        "  return ::grpc::Status("
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
+    printer->Print("}\n\n");
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(*vars,
+                   "::grpc::Status $ns$$Service$::Service::$Method$("
+                   "::grpc::ServerContext* context, "
+                   "const $Request$* request, "
+                   "::grpc::ServerWriter< $Response$>* writer) {\n");
+    printer->Print("  (void) context;\n");
+    printer->Print("  (void) request;\n");
+    printer->Print("  (void) writer;\n");
+    printer->Print(
+        "  return ::grpc::Status("
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
+    printer->Print("}\n\n");
+  } else if (method->BidiStreaming()) {
+    printer->Print(*vars,
+                   "::grpc::Status $ns$$Service$::Service::$Method$("
+                   "::grpc::ServerContext* context, "
+                   "::grpc::ServerReaderWriter< $Response$, $Request$>* "
+                   "stream) {\n");
+    printer->Print("  (void) context;\n");
+    printer->Print("  (void) stream;\n");
+    printer->Print(
+        "  return ::grpc::Status("
+        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
+    printer->Print("}\n\n");
+  }
+}
+
+void PrintSourceService(grpc_generator::Printer* printer,
+                        const grpc_generator::Service* service,
+                        std::map<grpc::string, grpc::string>* vars) {
+  (*vars)["Service"] = service->name();
+
+  if (service->method_count() > 0) {
+    printer->Print(*vars,
+                   "static const char* $prefix$$Service$_method_names[] = {\n");
+    for (int i = 0; i < service->method_count(); ++i) {
+      (*vars)["Method"] = service->method(i)->name();
+      printer->Print(*vars, "  \"/$Package$$Service$/$Method$\",\n");
+    }
+    printer->Print(*vars, "};\n\n");
+  }
+
+  printer->Print(*vars,
+                 "std::unique_ptr< $ns$$Service$::Stub> $ns$$Service$::NewStub("
+                 "const std::shared_ptr< ::grpc::ChannelInterface>& channel, "
+                 "const ::grpc::StubOptions& options) {\n"
+                 "  (void)options;\n"
+                 "  std::unique_ptr< $ns$$Service$::Stub> stub(new "
+                 "$ns$$Service$::Stub(channel));\n"
+                 "  return stub;\n"
+                 "}\n\n");
+  printer->Print(*vars,
+                 "$ns$$Service$::Stub::Stub(const std::shared_ptr< "
+                 "::grpc::ChannelInterface>& channel)\n");
+  printer->Indent();
+  printer->Print(": channel_(channel)");
+  for (int i = 0; i < service->method_count(); ++i) {
+    auto method = service->method(i);
+    (*vars)["Method"] = method->name();
+    (*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 (ClientOnlyStreaming(method.get())) {
+      (*vars)["StreamingType"] = "CLIENT_STREAMING";
+    } else if (ServerOnlyStreaming(method.get())) {
+      (*vars)["StreamingType"] = "SERVER_STREAMING";
+    } else {
+      (*vars)["StreamingType"] = "BIDI_STREAMING";
+    }
+    printer->Print(*vars,
+                   ", rpcmethod_$Method$_("
+                   "$prefix$$Service$_method_names[$Idx$], "
+                   "::grpc::internal::RpcMethod::$StreamingType$, "
+                   "channel"
+                   ")\n");
+  }
+  printer->Print("{}\n\n");
+  printer->Outdent();
+
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintSourceClientMethod(printer, service->method(i).get(), vars);
+  }
+
+  printer->Print(*vars, "$ns$$Service$::Service::Service() {\n");
+  printer->Indent();
+  for (int i = 0; i < service->method_count(); ++i) {
+    auto method = service->method(i);
+    (*vars)["Idx"] = as_string(i);
+    (*vars)["Method"] = method->name();
+    (*vars)["Request"] = method->input_type_name();
+    (*vars)["Response"] = method->output_type_name();
+    if (method->NoStreaming()) {
+      printer->Print(
+          *vars,
+          "AddMethod(new ::grpc::internal::RpcServiceMethod(\n"
+          "    $prefix$$Service$_method_names[$Idx$],\n"
+          "    ::grpc::internal::RpcMethod::NORMAL_RPC,\n"
+          "    new ::grpc::internal::RpcMethodHandler< $ns$$Service$::Service, "
+          "$Request$, "
+          "$Response$>(\n"
+          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
+    } else if (ClientOnlyStreaming(method.get())) {
+      printer->Print(
+          *vars,
+          "AddMethod(new ::grpc::internal::RpcServiceMethod(\n"
+          "    $prefix$$Service$_method_names[$Idx$],\n"
+          "    ::grpc::internal::RpcMethod::CLIENT_STREAMING,\n"
+          "    new ::grpc::internal::ClientStreamingHandler< "
+          "$ns$$Service$::Service, $Request$, $Response$>(\n"
+          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
+    } else if (ServerOnlyStreaming(method.get())) {
+      printer->Print(
+          *vars,
+          "AddMethod(new ::grpc::internal::RpcServiceMethod(\n"
+          "    $prefix$$Service$_method_names[$Idx$],\n"
+          "    ::grpc::internal::RpcMethod::SERVER_STREAMING,\n"
+          "    new ::grpc::internal::ServerStreamingHandler< "
+          "$ns$$Service$::Service, $Request$, $Response$>(\n"
+          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
+    } else if (method->BidiStreaming()) {
+      printer->Print(
+          *vars,
+          "AddMethod(new ::grpc::internal::RpcServiceMethod(\n"
+          "    $prefix$$Service$_method_names[$Idx$],\n"
+          "    ::grpc::internal::RpcMethod::BIDI_STREAMING,\n"
+          "    new ::grpc::internal::BidiStreamingHandler< "
+          "$ns$$Service$::Service, $Request$, $Response$>(\n"
+          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
+    }
+  }
+  printer->Outdent();
+  printer->Print(*vars, "}\n\n");
+  printer->Print(*vars,
+                 "$ns$$Service$::Service::~Service() {\n"
+                 "}\n\n");
+  for (int i = 0; i < service->method_count(); ++i) {
+    (*vars)["Idx"] = as_string(i);
+    PrintSourceServerMethod(printer, service->method(i).get(), vars);
+  }
+}
+
+grpc::string GetSourceServices(grpc_generator::File* file,
+                               const Parameters& params) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+    // Package string is empty or ends with a dot. It is used to fully qualify
+    // method names.
+    vars["Package"] = file->package();
+    if (!file->package().empty()) {
+      vars["Package"].append(".");
+    }
+    if (!params.services_namespace.empty()) {
+      vars["ns"] = params.services_namespace + "::";
+      vars["prefix"] = params.services_namespace;
+    } else {
+      vars["ns"] = "";
+      vars["prefix"] = "";
+    }
+
+    for (int i = 0; i < file->service_count(); ++i) {
+      PrintSourceService(printer.get(), file->service(i).get(), &vars);
+      printer->Print("\n");
+    }
+  }
+  return output;
+}
+
+grpc::string GetSourceEpilogue(grpc_generator::File* file,
+                               const Parameters& /*params*/) {
+  grpc::string temp;
+
+  if (!file->package().empty()) {
+    std::vector<grpc::string> parts = file->package_parts();
+
+    for (auto part = parts.begin(); part != parts.end(); part++) {
+      temp.append("}  // namespace ");
+      temp.append(*part);
+      temp.append("\n");
+    }
+    temp.append("\n");
+  }
+
+  return temp;
+}
+
+// TODO(mmukhi): Make sure we need parameters or not.
+grpc::string GetMockPrologue(grpc_generator::File* file,
+                             const Parameters& /*params*/) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+
+    vars["filename"] = file->filename();
+    vars["filename_base"] = file->filename_without_ext();
+    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
+    vars["service_header_ext"] = kCppGeneratorServiceHeaderExt;
+
+    printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
+    printer->Print(vars,
+                   "// If you make any local change, they will be lost.\n");
+    printer->Print(vars, "// source: $filename$\n\n");
+
+    printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
+    printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n");
+    printer->Print(vars, file->additional_headers().c_str());
+    printer->Print(vars, "\n");
+  }
+  return output;
+}
+
+// TODO(mmukhi): Add client-stream and completion-queue headers.
+grpc::string GetMockIncludes(grpc_generator::File* file,
+                             const Parameters& params) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+
+    static const char* headers_strs[] = {
+        "grpcpp/impl/codegen/async_stream.h",
+        "grpcpp/impl/codegen/sync_stream.h",
+    };
+    std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
+    PrintIncludes(printer.get(), headers, params.use_system_headers,
+                  params.grpc_search_path);
+
+    std::vector<grpc::string> gmock_header;
+    if (params.gmock_search_path.empty()) {
+      gmock_header.push_back("gmock/gmock.h");
+      PrintIncludes(printer.get(), gmock_header, params.use_system_headers,
+                    params.grpc_search_path);
+    } else {
+      gmock_header.push_back("gmock.h");
+      // We use local includes when a gmock_search_path is given
+      PrintIncludes(printer.get(), gmock_header, false,
+                    params.gmock_search_path);
+    }
+
+    if (!file->package().empty()) {
+      std::vector<grpc::string> parts = file->package_parts();
+
+      for (auto part = parts.begin(); part != parts.end(); part++) {
+        vars["part"] = *part;
+        printer->Print(vars, "namespace $part$ {\n");
+      }
+    }
+
+    printer->Print(vars, "\n");
+  }
+  return output;
+}
+
+void PrintMockClientMethods(grpc_generator::Printer* printer,
+                            const grpc_generator::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();
+
+  struct {
+    grpc::string prefix;
+    grpc::string method_params;  // extra arguments to method
+    int extra_method_param_count;
+  } async_prefixes[] = {{"Async", ", void* tag", 1}, {"PrepareAsync", "", 0}};
+
+  if (method->NoStreaming()) {
+    printer->Print(
+        *vars,
+        "MOCK_METHOD3($Method$, ::grpc::Status(::grpc::ClientContext* context, "
+        "const $Request$& request, $Response$* response));\n");
+    for (auto async_prefix : async_prefixes) {
+      (*vars)["AsyncPrefix"] = async_prefix.prefix;
+      printer->Print(
+          *vars,
+          "MOCK_METHOD3($AsyncPrefix$$Method$Raw, "
+          "::grpc::ClientAsyncResponseReaderInterface< $Response$>*"
+          "(::grpc::ClientContext* context, const $Request$& request, "
+          "::grpc::CompletionQueue* cq));\n");
+    }
+  } else if (ClientOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "MOCK_METHOD2($Method$Raw, "
+        "::grpc::ClientWriterInterface< $Request$>*"
+        "(::grpc::ClientContext* context, $Response$* response));\n");
+    for (auto async_prefix : async_prefixes) {
+      (*vars)["AsyncPrefix"] = async_prefix.prefix;
+      (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+      (*vars)["MockArgs"] =
+          std::to_string(3 + async_prefix.extra_method_param_count);
+      printer->Print(*vars,
+                     "MOCK_METHOD$MockArgs$($AsyncPrefix$$Method$Raw, "
+                     "::grpc::ClientAsyncWriterInterface< $Request$>*"
+                     "(::grpc::ClientContext* context, $Response$* response, "
+                     "::grpc::CompletionQueue* cq$AsyncMethodParams$));\n");
+    }
+  } else if (ServerOnlyStreaming(method)) {
+    printer->Print(
+        *vars,
+        "MOCK_METHOD2($Method$Raw, "
+        "::grpc::ClientReaderInterface< $Response$>*"
+        "(::grpc::ClientContext* context, const $Request$& request));\n");
+    for (auto async_prefix : async_prefixes) {
+      (*vars)["AsyncPrefix"] = async_prefix.prefix;
+      (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+      (*vars)["MockArgs"] =
+          std::to_string(3 + async_prefix.extra_method_param_count);
+      printer->Print(
+          *vars,
+          "MOCK_METHOD$MockArgs$($AsyncPrefix$$Method$Raw, "
+          "::grpc::ClientAsyncReaderInterface< $Response$>*"
+          "(::grpc::ClientContext* context, const $Request$& request, "
+          "::grpc::CompletionQueue* cq$AsyncMethodParams$));\n");
+    }
+  } else if (method->BidiStreaming()) {
+    printer->Print(
+        *vars,
+        "MOCK_METHOD1($Method$Raw, "
+        "::grpc::ClientReaderWriterInterface< $Request$, $Response$>*"
+        "(::grpc::ClientContext* context));\n");
+    for (auto async_prefix : async_prefixes) {
+      (*vars)["AsyncPrefix"] = async_prefix.prefix;
+      (*vars)["AsyncMethodParams"] = async_prefix.method_params;
+      (*vars)["MockArgs"] =
+          std::to_string(2 + async_prefix.extra_method_param_count);
+      printer->Print(
+          *vars,
+          "MOCK_METHOD$MockArgs$($AsyncPrefix$$Method$Raw, "
+          "::grpc::ClientAsyncReaderWriterInterface<$Request$, $Response$>*"
+          "(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq"
+          "$AsyncMethodParams$));\n");
+    }
+  }
+}
+
+void PrintMockService(grpc_generator::Printer* printer,
+                      const grpc_generator::Service* service,
+                      std::map<grpc::string, grpc::string>* vars) {
+  (*vars)["Service"] = service->name();
+
+  printer->Print(*vars,
+                 "class Mock$Service$Stub : public $Service$::StubInterface {\n"
+                 " public:\n");
+  printer->Indent();
+  for (int i = 0; i < service->method_count(); ++i) {
+    PrintMockClientMethods(printer, service->method(i).get(), vars);
+  }
+  printer->Outdent();
+  printer->Print("};\n");
+}
+
+grpc::string GetMockServices(grpc_generator::File* file,
+                             const Parameters& params) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto printer = file->CreatePrinter(&output);
+    std::map<grpc::string, grpc::string> vars;
+    // Package string is empty or ends with a dot. It is used to fully qualify
+    // method names.
+    vars["Package"] = file->package();
+    if (!file->package().empty()) {
+      vars["Package"].append(".");
+    }
+
+    if (!params.services_namespace.empty()) {
+      vars["services_namespace"] = params.services_namespace;
+      printer->Print(vars, "\nnamespace $services_namespace$ {\n\n");
+    }
+
+    for (int i = 0; i < file->service_count(); i++) {
+      PrintMockService(printer.get(), file->service(i).get(), &vars);
+      printer->Print("\n");
+    }
+
+    if (!params.services_namespace.empty()) {
+      printer->Print(vars, "} // namespace $services_namespace$\n\n");
+    }
+  }
+  return output;
+}
+
+grpc::string GetMockEpilogue(grpc_generator::File* file,
+                             const Parameters& /*params*/) {
+  grpc::string temp;
+
+  if (!file->package().empty()) {
+    std::vector<grpc::string> parts = file->package_parts();
+
+    for (auto part = parts.begin(); part != parts.end(); part++) {
+      temp.append("} // namespace ");
+      temp.append(*part);
+      temp.append("\n");
+    }
+    temp.append("\n");
+  }
+
+  return temp;
+}
+
+}  // namespace grpc_cpp_generator
diff --git a/third_party/grpc/src/compiler/cpp_generator.h b/third_party/grpc/src/compiler/cpp_generator.h
new file mode 100644
index 0000000..d88ef75
--- /dev/null
+++ b/third_party/grpc/src/compiler/cpp_generator.h
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
+#define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
+
+// cpp_generator.h/.cc do not directly depend on GRPC/ProtoBuf, such that they
+// can be used to generate code for other serialization systems, such as
+// FlatBuffers.
+
+#include <memory>
+#include <vector>
+
+#include "src/compiler/config.h"
+#include "src/compiler/schema_interface.h"
+
+#ifndef GRPC_CUSTOM_STRING
+#include <string>
+#define GRPC_CUSTOM_STRING std::string
+#endif
+
+namespace grpc {
+
+typedef GRPC_CUSTOM_STRING string;
+
+}  // namespace grpc
+
+namespace grpc_cpp_generator {
+
+// Contains all the parameters that are parsed from the command line.
+struct Parameters {
+  // Puts the service into a namespace
+  grpc::string services_namespace;
+  // Use system includes (<>) or local includes ("")
+  bool use_system_headers;
+  // Prefix to any grpc include
+  grpc::string grpc_search_path;
+  // Generate Google Mock code to facilitate unit testing.
+  bool generate_mock_code;
+  // Google Mock search path, when non-empty, local includes will be used.
+  grpc::string gmock_search_path;
+  // *EXPERIMENTAL* Additional include files in grpc.pb.h
+  std::vector<grpc::string> additional_header_includes;
+};
+
+// Return the prologue of the generated header file.
+grpc::string GetHeaderPrologue(grpc_generator::File* file,
+                               const Parameters& params);
+
+// Return the includes needed for generated header file.
+grpc::string GetHeaderIncludes(grpc_generator::File* file,
+                               const Parameters& params);
+
+// Return the includes needed for generated source file.
+grpc::string GetSourceIncludes(grpc_generator::File* file,
+                               const Parameters& params);
+
+// Return the epilogue of the generated header file.
+grpc::string GetHeaderEpilogue(grpc_generator::File* file,
+                               const Parameters& params);
+
+// Return the prologue of the generated source file.
+grpc::string GetSourcePrologue(grpc_generator::File* file,
+                               const Parameters& params);
+
+// Return the services for generated header file.
+grpc::string GetHeaderServices(grpc_generator::File* file,
+                               const Parameters& params);
+
+// Return the services for generated source file.
+grpc::string GetSourceServices(grpc_generator::File* file,
+                               const Parameters& params);
+
+// Return the epilogue of the generated source file.
+grpc::string GetSourceEpilogue(grpc_generator::File* file,
+                               const Parameters& params);
+
+// Return the prologue of the generated mock file.
+grpc::string GetMockPrologue(grpc_generator::File* file,
+                             const Parameters& params);
+
+// Return the includes needed for generated mock file.
+grpc::string GetMockIncludes(grpc_generator::File* file,
+                             const Parameters& params);
+
+// Return the services for generated mock file.
+grpc::string GetMockServices(grpc_generator::File* file,
+                             const Parameters& params);
+
+// Return the epilogue of generated mock file.
+grpc::string GetMockEpilogue(grpc_generator::File* file,
+                             const Parameters& params);
+
+// Return the prologue of the generated mock file.
+grpc::string GetMockPrologue(grpc_generator::File* file,
+                             const Parameters& params);
+
+// Return the includes needed for generated mock file.
+grpc::string GetMockIncludes(grpc_generator::File* file,
+                             const Parameters& params);
+
+// Return the services for generated mock file.
+grpc::string GetMockServices(grpc_generator::File* file,
+                             const Parameters& params);
+
+// Return the epilogue of generated mock file.
+grpc::string GetMockEpilogue(grpc_generator::File* file,
+                             const Parameters& params);
+
+}  // namespace grpc_cpp_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
diff --git a/third_party/grpc/src/compiler/cpp_generator_helpers.h b/third_party/grpc/src/compiler/cpp_generator_helpers.h
new file mode 100644
index 0000000..b8efcbd
--- /dev/null
+++ b/third_party/grpc/src/compiler/cpp_generator_helpers.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_CPP_GENERATOR_HELPERS_H
+#define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_HELPERS_H
+
+#include <map>
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+
+namespace grpc_cpp_generator {
+
+inline grpc::string DotsToColons(const grpc::string& name) {
+  return grpc_generator::StringReplace(name, ".", "::");
+}
+
+inline grpc::string DotsToUnderscores(const grpc::string& name) {
+  return grpc_generator::StringReplace(name, ".", "_");
+}
+
+inline grpc::string ClassName(const grpc::protobuf::Descriptor* descriptor,
+                              bool qualified) {
+  // Find "outer", the descriptor of the top-level message in which
+  // "descriptor" is embedded.
+  const grpc::protobuf::Descriptor* outer = descriptor;
+  while (outer->containing_type() != NULL) outer = outer->containing_type();
+
+  const grpc::string& outer_name = outer->full_name();
+  grpc::string inner_name = descriptor->full_name().substr(outer_name.size());
+
+  if (qualified) {
+    return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name);
+  } else {
+    return outer->name() + DotsToUnderscores(inner_name);
+  }
+}
+
+// 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 GetCppComments(const DescriptorType* desc, bool leading) {
+  return grpc_generator::GetPrefixedComments(desc, leading, "//");
+}
+
+}  // namespace grpc_cpp_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_HELPERS_H
diff --git a/third_party/grpc/src/compiler/cpp_plugin.cc b/third_party/grpc/src/compiler/cpp_plugin.cc
new file mode 100644
index 0000000..c8ab788
--- /dev/null
+++ b/third_party/grpc/src/compiler/cpp_plugin.cc
@@ -0,0 +1,146 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// Generates cpp gRPC service interface out of Protobuf IDL.
+//
+
+#include <memory>
+#include <sstream>
+
+#include "src/compiler/config.h"
+
+#include "src/compiler/cpp_generator.h"
+#include "src/compiler/generator_helpers.h"
+#include "src/compiler/protobuf_plugin.h"
+
+class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
+ public:
+  CppGrpcGenerator() {}
+  virtual ~CppGrpcGenerator() {}
+
+  virtual bool Generate(const grpc::protobuf::FileDescriptor* file,
+                        const grpc::string& parameter,
+                        grpc::protobuf::compiler::GeneratorContext* context,
+                        grpc::string* error) const {
+    if (file->options().cc_generic_services()) {
+      *error =
+          "cpp grpc proto compiler plugin does not work with generic "
+          "services. To generate cpp grpc APIs, please set \""
+          "cc_generic_service = false\".";
+      return false;
+    }
+
+    grpc_cpp_generator::Parameters generator_parameters;
+    generator_parameters.use_system_headers = true;
+    generator_parameters.generate_mock_code = false;
+
+    ProtoBufFile pbfile(file);
+
+    if (!parameter.empty()) {
+      std::vector<grpc::string> parameters_list =
+          grpc_generator::tokenize(parameter, ",");
+      for (auto parameter_string = parameters_list.begin();
+           parameter_string != parameters_list.end(); parameter_string++) {
+        std::vector<grpc::string> param =
+            grpc_generator::tokenize(*parameter_string, "=");
+        if (param[0] == "services_namespace") {
+          generator_parameters.services_namespace = param[1];
+        } else if (param[0] == "use_system_headers") {
+          if (param[1] == "true") {
+            generator_parameters.use_system_headers = true;
+          } else if (param[1] == "false") {
+            generator_parameters.use_system_headers = false;
+          } else {
+            *error = grpc::string("Invalid parameter: ") + *parameter_string;
+            return false;
+          }
+        } else if (param[0] == "grpc_search_path") {
+          generator_parameters.grpc_search_path = param[1];
+        } else if (param[0] == "generate_mock_code") {
+          if (param[1] == "true") {
+            generator_parameters.generate_mock_code = true;
+          } else if (param[1] != "false") {
+            *error = grpc::string("Invalid parameter: ") + *parameter_string;
+            return false;
+          }
+        } else if (param[0] == "gmock_search_path") {
+          generator_parameters.gmock_search_path = param[1];
+        } else if (param[0] == "additional_header_includes") {
+          generator_parameters.additional_header_includes =
+              grpc_generator::tokenize(param[1], ":");
+        } else {
+          *error = grpc::string("Unknown parameter: ") + *parameter_string;
+          return false;
+        }
+      }
+    }
+
+    grpc::string file_name = grpc_generator::StripProto(file->name());
+
+    grpc::string header_code =
+        grpc_cpp_generator::GetHeaderPrologue(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetHeaderIncludes(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetHeaderServices(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetHeaderEpilogue(&pbfile, generator_parameters);
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> header_output(
+        context->Open(file_name + ".grpc.pb.h"));
+    grpc::protobuf::io::CodedOutputStream header_coded_out(header_output.get());
+    header_coded_out.WriteRaw(header_code.data(), header_code.size());
+
+    grpc::string source_code =
+        grpc_cpp_generator::GetSourcePrologue(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetSourceIncludes(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetSourceServices(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetSourceEpilogue(&pbfile, generator_parameters);
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> source_output(
+        context->Open(file_name + ".grpc.pb.cc"));
+    grpc::protobuf::io::CodedOutputStream source_coded_out(source_output.get());
+    source_coded_out.WriteRaw(source_code.data(), source_code.size());
+
+    if (!generator_parameters.generate_mock_code) {
+      return true;
+    }
+    grpc::string mock_code =
+        grpc_cpp_generator::GetMockPrologue(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetMockIncludes(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetMockServices(&pbfile, generator_parameters) +
+        grpc_cpp_generator::GetMockEpilogue(&pbfile, generator_parameters);
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> mock_output(
+        context->Open(file_name + "_mock.grpc.pb.h"));
+    grpc::protobuf::io::CodedOutputStream mock_coded_out(mock_output.get());
+    mock_coded_out.WriteRaw(mock_code.data(), mock_code.size());
+
+    return true;
+  }
+
+ private:
+  // Insert the given code into the given file at the given insertion point.
+  void Insert(grpc::protobuf::compiler::GeneratorContext* context,
+              const grpc::string& filename, const grpc::string& insertion_point,
+              const grpc::string& code) const {
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
+        context->OpenForInsert(filename, insertion_point));
+    grpc::protobuf::io::CodedOutputStream coded_out(output.get());
+    coded_out.WriteRaw(code.data(), code.size());
+  }
+};
+
+int main(int argc, char* argv[]) {
+  CppGrpcGenerator generator;
+  return grpc::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/third_party/grpc/src/compiler/csharp_generator.cc b/third_party/grpc/src/compiler/csharp_generator.cc
new file mode 100644
index 0000000..59ddbd8
--- /dev/null
+++ b/third_party/grpc/src/compiler/csharp_generator.cc
@@ -0,0 +1,739 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <cctype>
+#include <map>
+#include <sstream>
+#include <vector>
+
+#include "src/compiler/config.h"
+#include "src/compiler/csharp_generator.h"
+#include "src/compiler/csharp_generator_helpers.h"
+
+using google::protobuf::compiler::csharp::GetClassName;
+using google::protobuf::compiler::csharp::GetFileNamespace;
+using google::protobuf::compiler::csharp::GetReflectionClassName;
+using grpc::protobuf::Descriptor;
+using grpc::protobuf::FileDescriptor;
+using grpc::protobuf::MethodDescriptor;
+using grpc::protobuf::ServiceDescriptor;
+using grpc::protobuf::io::Printer;
+using grpc::protobuf::io::StringOutputStream;
+using grpc_generator::GetMethodType;
+using grpc_generator::METHODTYPE_BIDI_STREAMING;
+using grpc_generator::METHODTYPE_CLIENT_STREAMING;
+using grpc_generator::METHODTYPE_NO_STREAMING;
+using grpc_generator::METHODTYPE_SERVER_STREAMING;
+using grpc_generator::MethodType;
+using grpc_generator::StringReplace;
+using std::map;
+using std::vector;
+
+namespace grpc_csharp_generator {
+namespace {
+
+// This function is a massaged version of
+// https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc
+// Currently, we cannot easily reuse the functionality as
+// google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header.
+// TODO(jtattermusch): reuse the functionality from google/protobuf.
+bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer,
+                                grpc::protobuf::SourceLocation location) {
+  grpc::string comments = location.leading_comments.empty()
+                              ? location.trailing_comments
+                              : location.leading_comments;
+  if (comments.empty()) {
+    return false;
+  }
+  // XML escaping... no need for apostrophes etc as the whole text is going to
+  // be a child
+  // node of a summary element, not part of an attribute.
+  comments = grpc_generator::StringReplace(comments, "&", "&amp;", true);
+  comments = grpc_generator::StringReplace(comments, "<", "&lt;", true);
+
+  std::vector<grpc::string> lines;
+  grpc_generator::Split(comments, '\n', &lines);
+  // TODO: We really should work out which part to put in the summary and which
+  // to put in the remarks...
+  // but that needs to be part of a bigger effort to understand the markdown
+  // better anyway.
+  printer->Print("/// <summary>\n");
+  bool last_was_empty = false;
+  // We squash multiple blank lines down to one, and remove any trailing blank
+  // lines. We need
+  // to preserve the blank lines themselves, as this is relevant in the
+  // markdown.
+  // Note that we can't remove leading or trailing whitespace as *that's*
+  // relevant in markdown too.
+  // (We don't skip "just whitespace" lines, either.)
+  for (std::vector<grpc::string>::iterator it = lines.begin();
+       it != lines.end(); ++it) {
+    grpc::string line = *it;
+    if (line.empty()) {
+      last_was_empty = true;
+    } else {
+      if (last_was_empty) {
+        printer->Print("///\n");
+      }
+      last_was_empty = false;
+      printer->Print("///$line$\n", "line", *it);
+    }
+  }
+  printer->Print("/// </summary>\n");
+  return true;
+}
+
+template <typename DescriptorType>
+bool GenerateDocCommentBody(grpc::protobuf::io::Printer* printer,
+                            const DescriptorType* descriptor) {
+  grpc::protobuf::SourceLocation location;
+  if (!descriptor->GetSourceLocation(&location)) {
+    return false;
+  }
+  return GenerateDocCommentBodyImpl(printer, location);
+}
+
+void GenerateDocCommentServerMethod(grpc::protobuf::io::Printer* printer,
+                                    const MethodDescriptor* method) {
+  if (GenerateDocCommentBody(printer, method)) {
+    if (method->client_streaming()) {
+      printer->Print(
+          "/// <param name=\"requestStream\">Used for reading requests from "
+          "the client.</param>\n");
+    } else {
+      printer->Print(
+          "/// <param name=\"request\">The request received from the "
+          "client.</param>\n");
+    }
+    if (method->server_streaming()) {
+      printer->Print(
+          "/// <param name=\"responseStream\">Used for sending responses back "
+          "to the client.</param>\n");
+    }
+    printer->Print(
+        "/// <param name=\"context\">The context of the server-side call "
+        "handler being invoked.</param>\n");
+    if (method->server_streaming()) {
+      printer->Print(
+          "/// <returns>A task indicating completion of the "
+          "handler.</returns>\n");
+    } else {
+      printer->Print(
+          "/// <returns>The response to send back to the client (wrapped by a "
+          "task).</returns>\n");
+    }
+  }
+}
+
+void GenerateDocCommentClientMethod(grpc::protobuf::io::Printer* printer,
+                                    const MethodDescriptor* method,
+                                    bool is_sync, bool use_call_options) {
+  if (GenerateDocCommentBody(printer, method)) {
+    if (!method->client_streaming()) {
+      printer->Print(
+          "/// <param name=\"request\">The request to send to the "
+          "server.</param>\n");
+    }
+    if (!use_call_options) {
+      printer->Print(
+          "/// <param name=\"headers\">The initial metadata to send with the "
+          "call. This parameter is optional.</param>\n");
+      printer->Print(
+          "/// <param name=\"deadline\">An optional deadline for the call. The "
+          "call will be cancelled if deadline is hit.</param>\n");
+      printer->Print(
+          "/// <param name=\"cancellationToken\">An optional token for "
+          "canceling the call.</param>\n");
+    } else {
+      printer->Print(
+          "/// <param name=\"options\">The options for the call.</param>\n");
+    }
+    if (is_sync) {
+      printer->Print(
+          "/// <returns>The response received from the server.</returns>\n");
+    } else {
+      printer->Print("/// <returns>The call object.</returns>\n");
+    }
+  }
+}
+
+std::string GetServiceClassName(const ServiceDescriptor* service) {
+  return service->name();
+}
+
+std::string GetClientClassName(const ServiceDescriptor* service) {
+  return service->name() + "Client";
+}
+
+std::string GetServerClassName(const ServiceDescriptor* service) {
+  return service->name() + "Base";
+}
+
+std::string GetCSharpMethodType(MethodType method_type) {
+  switch (method_type) {
+    case METHODTYPE_NO_STREAMING:
+      return "grpc::MethodType.Unary";
+    case METHODTYPE_CLIENT_STREAMING:
+      return "grpc::MethodType.ClientStreaming";
+    case METHODTYPE_SERVER_STREAMING:
+      return "grpc::MethodType.ServerStreaming";
+    case METHODTYPE_BIDI_STREAMING:
+      return "grpc::MethodType.DuplexStreaming";
+  }
+  GOOGLE_LOG(FATAL) << "Can't get here.";
+  return "";
+}
+
+std::string GetServiceNameFieldName() { return "__ServiceName"; }
+
+std::string GetMarshallerFieldName(const Descriptor* message) {
+  return "__Marshaller_" +
+         grpc_generator::StringReplace(message->full_name(), ".", "_", true);
+}
+
+std::string GetMethodFieldName(const MethodDescriptor* method) {
+  return "__Method_" + method->name();
+}
+
+std::string GetMethodRequestParamMaybe(const MethodDescriptor* method,
+                                       bool invocation_param = false) {
+  if (method->client_streaming()) {
+    return "";
+  }
+  if (invocation_param) {
+    return "request, ";
+  }
+  return GetClassName(method->input_type()) + " request, ";
+}
+
+std::string GetAccessLevel(bool internal_access) {
+  return internal_access ? "internal" : "public";
+}
+
+std::string GetMethodReturnTypeClient(const MethodDescriptor* method) {
+  switch (GetMethodType(method)) {
+    case METHODTYPE_NO_STREAMING:
+      return "grpc::AsyncUnaryCall<" + GetClassName(method->output_type()) +
+             ">";
+    case METHODTYPE_CLIENT_STREAMING:
+      return "grpc::AsyncClientStreamingCall<" +
+             GetClassName(method->input_type()) + ", " +
+             GetClassName(method->output_type()) + ">";
+    case METHODTYPE_SERVER_STREAMING:
+      return "grpc::AsyncServerStreamingCall<" +
+             GetClassName(method->output_type()) + ">";
+    case METHODTYPE_BIDI_STREAMING:
+      return "grpc::AsyncDuplexStreamingCall<" +
+             GetClassName(method->input_type()) + ", " +
+             GetClassName(method->output_type()) + ">";
+  }
+  GOOGLE_LOG(FATAL) << "Can't get here.";
+  return "";
+}
+
+std::string GetMethodRequestParamServer(const MethodDescriptor* method) {
+  switch (GetMethodType(method)) {
+    case METHODTYPE_NO_STREAMING:
+    case METHODTYPE_SERVER_STREAMING:
+      return GetClassName(method->input_type()) + " request";
+    case METHODTYPE_CLIENT_STREAMING:
+    case METHODTYPE_BIDI_STREAMING:
+      return "grpc::IAsyncStreamReader<" + GetClassName(method->input_type()) +
+             "> requestStream";
+  }
+  GOOGLE_LOG(FATAL) << "Can't get here.";
+  return "";
+}
+
+std::string GetMethodReturnTypeServer(const MethodDescriptor* method) {
+  switch (GetMethodType(method)) {
+    case METHODTYPE_NO_STREAMING:
+    case METHODTYPE_CLIENT_STREAMING:
+      return "global::System.Threading.Tasks.Task<" +
+             GetClassName(method->output_type()) + ">";
+    case METHODTYPE_SERVER_STREAMING:
+    case METHODTYPE_BIDI_STREAMING:
+      return "global::System.Threading.Tasks.Task";
+  }
+  GOOGLE_LOG(FATAL) << "Can't get here.";
+  return "";
+}
+
+std::string GetMethodResponseStreamMaybe(const MethodDescriptor* method) {
+  switch (GetMethodType(method)) {
+    case METHODTYPE_NO_STREAMING:
+    case METHODTYPE_CLIENT_STREAMING:
+      return "";
+    case METHODTYPE_SERVER_STREAMING:
+    case METHODTYPE_BIDI_STREAMING:
+      return ", grpc::IServerStreamWriter<" +
+             GetClassName(method->output_type()) + "> responseStream";
+  }
+  GOOGLE_LOG(FATAL) << "Can't get here.";
+  return "";
+}
+
+// Gets vector of all messages used as input or output types.
+std::vector<const Descriptor*> GetUsedMessages(
+    const ServiceDescriptor* service) {
+  std::set<const Descriptor*> descriptor_set;
+  std::vector<const Descriptor*>
+      result;  // vector is to maintain stable ordering
+  for (int i = 0; i < service->method_count(); i++) {
+    const MethodDescriptor* method = service->method(i);
+    if (descriptor_set.find(method->input_type()) == descriptor_set.end()) {
+      descriptor_set.insert(method->input_type());
+      result.push_back(method->input_type());
+    }
+    if (descriptor_set.find(method->output_type()) == descriptor_set.end()) {
+      descriptor_set.insert(method->output_type());
+      result.push_back(method->output_type());
+    }
+  }
+  return result;
+}
+
+void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
+  std::vector<const Descriptor*> used_messages = GetUsedMessages(service);
+  for (size_t i = 0; i < used_messages.size(); i++) {
+    const Descriptor* message = used_messages[i];
+    out->Print(
+        "static readonly grpc::Marshaller<$type$> $fieldname$ = "
+        "grpc::Marshallers.Create((arg) => "
+        "global::Google.Protobuf.MessageExtensions.ToByteArray(arg), "
+        "$type$.Parser.ParseFrom);\n",
+        "fieldname", GetMarshallerFieldName(message), "type",
+        GetClassName(message));
+  }
+  out->Print("\n");
+}
+
+void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) {
+  out->Print(
+      "static readonly grpc::Method<$request$, $response$> $fieldname$ = new "
+      "grpc::Method<$request$, $response$>(\n",
+      "fieldname", GetMethodFieldName(method), "request",
+      GetClassName(method->input_type()), "response",
+      GetClassName(method->output_type()));
+  out->Indent();
+  out->Indent();
+  out->Print("$methodtype$,\n", "methodtype",
+             GetCSharpMethodType(GetMethodType(method)));
+  out->Print("$servicenamefield$,\n", "servicenamefield",
+             GetServiceNameFieldName());
+  out->Print("\"$methodname$\",\n", "methodname", method->name());
+  out->Print("$requestmarshaller$,\n", "requestmarshaller",
+             GetMarshallerFieldName(method->input_type()));
+  out->Print("$responsemarshaller$);\n", "responsemarshaller",
+             GetMarshallerFieldName(method->output_type()));
+  out->Print("\n");
+  out->Outdent();
+  out->Outdent();
+}
+
+void GenerateServiceDescriptorProperty(Printer* out,
+                                       const ServiceDescriptor* service) {
+  std::ostringstream index;
+  index << service->index();
+  out->Print("/// <summary>Service descriptor</summary>\n");
+  out->Print(
+      "public static global::Google.Protobuf.Reflection.ServiceDescriptor "
+      "Descriptor\n");
+  out->Print("{\n");
+  out->Print("  get { return $umbrella$.Descriptor.Services[$index$]; }\n",
+             "umbrella", GetReflectionClassName(service->file()), "index",
+             index.str());
+  out->Print("}\n");
+  out->Print("\n");
+}
+
+void GenerateServerClass(Printer* out, const ServiceDescriptor* service) {
+  out->Print(
+      "/// <summary>Base class for server-side implementations of "
+      "$servicename$</summary>\n",
+      "servicename", GetServiceClassName(service));
+  out->Print("public abstract partial class $name$\n", "name",
+             GetServerClassName(service));
+  out->Print("{\n");
+  out->Indent();
+  for (int i = 0; i < service->method_count(); i++) {
+    const MethodDescriptor* method = service->method(i);
+    GenerateDocCommentServerMethod(out, method);
+    out->Print(
+        "public virtual $returntype$ "
+        "$methodname$($request$$response_stream_maybe$, "
+        "grpc::ServerCallContext context)\n",
+        "methodname", method->name(), "returntype",
+        GetMethodReturnTypeServer(method), "request",
+        GetMethodRequestParamServer(method), "response_stream_maybe",
+        GetMethodResponseStreamMaybe(method));
+    out->Print("{\n");
+    out->Indent();
+    out->Print(
+        "throw new grpc::RpcException("
+        "new grpc::Status(grpc::StatusCode.Unimplemented, \"\"));\n");
+    out->Outdent();
+    out->Print("}\n\n");
+  }
+  out->Outdent();
+  out->Print("}\n");
+  out->Print("\n");
+}
+
+void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
+  out->Print("/// <summary>Client for $servicename$</summary>\n", "servicename",
+             GetServiceClassName(service));
+  out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n", "name",
+             GetClientClassName(service));
+  out->Print("{\n");
+  out->Indent();
+
+  // constructors
+  out->Print(
+      "/// <summary>Creates a new client for $servicename$</summary>\n"
+      "/// <param name=\"channel\">The channel to use to make remote "
+      "calls.</param>\n",
+      "servicename", GetServiceClassName(service));
+  out->Print("public $name$(grpc::Channel channel) : base(channel)\n", "name",
+             GetClientClassName(service));
+  out->Print("{\n");
+  out->Print("}\n");
+  out->Print(
+      "/// <summary>Creates a new client for $servicename$ that uses a custom "
+      "<c>CallInvoker</c>.</summary>\n"
+      "/// <param name=\"callInvoker\">The callInvoker to use to make remote "
+      "calls.</param>\n",
+      "servicename", GetServiceClassName(service));
+  out->Print(
+      "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n",
+      "name", GetClientClassName(service));
+  out->Print("{\n");
+  out->Print("}\n");
+  out->Print(
+      "/// <summary>Protected parameterless constructor to allow creation"
+      " of test doubles.</summary>\n");
+  out->Print("protected $name$() : base()\n", "name",
+             GetClientClassName(service));
+  out->Print("{\n");
+  out->Print("}\n");
+  out->Print(
+      "/// <summary>Protected constructor to allow creation of configured "
+      "clients.</summary>\n"
+      "/// <param name=\"configuration\">The client configuration.</param>\n");
+  out->Print(
+      "protected $name$(ClientBaseConfiguration configuration)"
+      " : base(configuration)\n",
+      "name", GetClientClassName(service));
+  out->Print("{\n");
+  out->Print("}\n\n");
+
+  for (int i = 0; i < service->method_count(); i++) {
+    const MethodDescriptor* method = service->method(i);
+    MethodType method_type = GetMethodType(method);
+
+    if (method_type == METHODTYPE_NO_STREAMING) {
+      // unary calls have an extra synchronous stub method
+      GenerateDocCommentClientMethod(out, method, true, false);
+      out->Print(
+          "public virtual $response$ $methodname$($request$ request, "
+          "grpc::Metadata "
+          "headers = null, global::System.DateTime? deadline = null, "
+          "global::System.Threading.CancellationToken "
+          "cancellationToken = "
+          "default(global::System.Threading.CancellationToken))\n",
+          "methodname", method->name(), "request",
+          GetClassName(method->input_type()), "response",
+          GetClassName(method->output_type()));
+      out->Print("{\n");
+      out->Indent();
+      out->Print(
+          "return $methodname$(request, new grpc::CallOptions(headers, "
+          "deadline, "
+          "cancellationToken));\n",
+          "methodname", method->name());
+      out->Outdent();
+      out->Print("}\n");
+
+      // overload taking CallOptions as a param
+      GenerateDocCommentClientMethod(out, method, true, true);
+      out->Print(
+          "public virtual $response$ $methodname$($request$ request, "
+          "grpc::CallOptions options)\n",
+          "methodname", method->name(), "request",
+          GetClassName(method->input_type()), "response",
+          GetClassName(method->output_type()));
+      out->Print("{\n");
+      out->Indent();
+      out->Print(
+          "return CallInvoker.BlockingUnaryCall($methodfield$, null, options, "
+          "request);\n",
+          "methodfield", GetMethodFieldName(method));
+      out->Outdent();
+      out->Print("}\n");
+    }
+
+    std::string method_name = method->name();
+    if (method_type == METHODTYPE_NO_STREAMING) {
+      method_name += "Async";  // prevent name clash with synchronous method.
+    }
+    GenerateDocCommentClientMethod(out, method, false, false);
+    out->Print(
+        "public virtual $returntype$ "
+        "$methodname$($request_maybe$grpc::Metadata "
+        "headers = null, global::System.DateTime? deadline = null, "
+        "global::System.Threading.CancellationToken "
+        "cancellationToken = "
+        "default(global::System.Threading.CancellationToken))\n",
+        "methodname", method_name, "request_maybe",
+        GetMethodRequestParamMaybe(method), "returntype",
+        GetMethodReturnTypeClient(method));
+    out->Print("{\n");
+    out->Indent();
+
+    out->Print(
+        "return $methodname$($request_maybe$new grpc::CallOptions(headers, "
+        "deadline, "
+        "cancellationToken));\n",
+        "methodname", method_name, "request_maybe",
+        GetMethodRequestParamMaybe(method, true));
+    out->Outdent();
+    out->Print("}\n");
+
+    // overload taking CallOptions as a param
+    GenerateDocCommentClientMethod(out, method, false, true);
+    out->Print(
+        "public virtual $returntype$ "
+        "$methodname$($request_maybe$grpc::CallOptions "
+        "options)\n",
+        "methodname", method_name, "request_maybe",
+        GetMethodRequestParamMaybe(method), "returntype",
+        GetMethodReturnTypeClient(method));
+    out->Print("{\n");
+    out->Indent();
+    switch (GetMethodType(method)) {
+      case METHODTYPE_NO_STREAMING:
+        out->Print(
+            "return CallInvoker.AsyncUnaryCall($methodfield$, null, options, "
+            "request);\n",
+            "methodfield", GetMethodFieldName(method));
+        break;
+      case METHODTYPE_CLIENT_STREAMING:
+        out->Print(
+            "return CallInvoker.AsyncClientStreamingCall($methodfield$, null, "
+            "options);\n",
+            "methodfield", GetMethodFieldName(method));
+        break;
+      case METHODTYPE_SERVER_STREAMING:
+        out->Print(
+            "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, "
+            "options, request);\n",
+            "methodfield", GetMethodFieldName(method));
+        break;
+      case METHODTYPE_BIDI_STREAMING:
+        out->Print(
+            "return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, "
+            "options);\n",
+            "methodfield", GetMethodFieldName(method));
+        break;
+      default:
+        GOOGLE_LOG(FATAL) << "Can't get here.";
+    }
+    out->Outdent();
+    out->Print("}\n");
+  }
+
+  // override NewInstance method
+  out->Print(
+      "/// <summary>Creates a new instance of client from given "
+      "<c>ClientBaseConfiguration</c>.</summary>\n");
+  out->Print(
+      "protected override $name$ NewInstance(ClientBaseConfiguration "
+      "configuration)\n",
+      "name", GetClientClassName(service));
+  out->Print("{\n");
+  out->Indent();
+  out->Print("return new $name$(configuration);\n", "name",
+             GetClientClassName(service));
+  out->Outdent();
+  out->Print("}\n");
+
+  out->Outdent();
+  out->Print("}\n");
+  out->Print("\n");
+}
+
+void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) {
+  out->Print(
+      "/// <summary>Creates service definition that can be registered with a "
+      "server</summary>\n");
+  out->Print(
+      "/// <param name=\"serviceImpl\">An object implementing the server-side"
+      " handling logic.</param>\n");
+  out->Print(
+      "public static grpc::ServerServiceDefinition BindService($implclass$ "
+      "serviceImpl)\n",
+      "implclass", GetServerClassName(service));
+  out->Print("{\n");
+  out->Indent();
+
+  out->Print("return grpc::ServerServiceDefinition.CreateBuilder()");
+  out->Indent();
+  out->Indent();
+  for (int i = 0; i < service->method_count(); i++) {
+    const MethodDescriptor* method = service->method(i);
+    out->Print("\n.AddMethod($methodfield$, serviceImpl.$methodname$)",
+               "methodfield", GetMethodFieldName(method), "methodname",
+               method->name());
+  }
+  out->Print(".Build();\n");
+  out->Outdent();
+  out->Outdent();
+
+  out->Outdent();
+  out->Print("}\n");
+  out->Print("\n");
+}
+
+void GenerateBindServiceWithBinderMethod(Printer* out,
+                                         const ServiceDescriptor* service) {
+  out->Print(
+      "/// <summary>Register service method implementations with a service "
+      "binder. Useful when customizing the service binding logic.\n"
+      "/// Note: this method is part of an experimental API that can change or "
+      "be "
+      "removed without any prior notice.</summary>\n");
+  out->Print(
+      "/// <param name=\"serviceBinder\">Service methods will be bound by "
+      "calling <c>AddMethod</c> on this object."
+      "</param>\n");
+  out->Print(
+      "/// <param name=\"serviceImpl\">An object implementing the server-side"
+      " handling logic.</param>\n");
+  out->Print(
+      "public static void BindService(grpc::ServiceBinderBase serviceBinder, "
+      "$implclass$ "
+      "serviceImpl)\n",
+      "implclass", GetServerClassName(service));
+  out->Print("{\n");
+  out->Indent();
+
+  for (int i = 0; i < service->method_count(); i++) {
+    const MethodDescriptor* method = service->method(i);
+    out->Print(
+        "serviceBinder.AddMethod($methodfield$, serviceImpl.$methodname$);\n",
+        "methodfield", GetMethodFieldName(method), "methodname",
+        method->name());
+  }
+
+  out->Outdent();
+  out->Print("}\n");
+  out->Print("\n");
+}
+
+void GenerateService(Printer* out, const ServiceDescriptor* service,
+                     bool generate_client, bool generate_server,
+                     bool internal_access) {
+  GenerateDocCommentBody(out, service);
+  out->Print("$access_level$ static partial class $classname$\n",
+             "access_level", GetAccessLevel(internal_access), "classname",
+             GetServiceClassName(service));
+  out->Print("{\n");
+  out->Indent();
+  out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n",
+             "servicenamefield", GetServiceNameFieldName(), "servicename",
+             service->full_name());
+  out->Print("\n");
+
+  GenerateMarshallerFields(out, service);
+  for (int i = 0; i < service->method_count(); i++) {
+    GenerateStaticMethodField(out, service->method(i));
+  }
+  GenerateServiceDescriptorProperty(out, service);
+
+  if (generate_server) {
+    GenerateServerClass(out, service);
+  }
+  if (generate_client) {
+    GenerateClientStub(out, service);
+  }
+  if (generate_server) {
+    GenerateBindServiceMethod(out, service);
+    GenerateBindServiceWithBinderMethod(out, service);
+  }
+
+  out->Outdent();
+  out->Print("}\n");
+}
+
+}  // anonymous namespace
+
+grpc::string GetServices(const FileDescriptor* file, bool generate_client,
+                         bool generate_server, bool internal_access) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+
+    StringOutputStream output_stream(&output);
+    Printer out(&output_stream, '$');
+
+    // Don't write out any output if there no services, to avoid empty service
+    // files being generated for proto files that don't declare any.
+    if (file->service_count() == 0) {
+      return output;
+    }
+
+    // Write out a file header.
+    out.Print("// <auto-generated>\n");
+    out.Print(
+        "//     Generated by the protocol buffer compiler.  DO NOT EDIT!\n");
+    out.Print("//     source: $filename$\n", "filename", file->name());
+    out.Print("// </auto-generated>\n");
+
+    // use C++ style as there are no file-level XML comments in .NET
+    grpc::string leading_comments = GetCsharpComments(file, true);
+    if (!leading_comments.empty()) {
+      out.Print("// Original file comments:\n");
+      out.PrintRaw(leading_comments.c_str());
+    }
+
+    out.Print("#pragma warning disable 0414, 1591\n");
+
+    out.Print("#region Designer generated code\n");
+    out.Print("\n");
+    out.Print("using grpc = global::Grpc.Core;\n");
+    out.Print("\n");
+
+    grpc::string file_namespace = GetFileNamespace(file);
+    if (file_namespace != "") {
+      out.Print("namespace $namespace$ {\n", "namespace", file_namespace);
+      out.Indent();
+    }
+    for (int i = 0; i < file->service_count(); i++) {
+      GenerateService(&out, file->service(i), generate_client, generate_server,
+                      internal_access);
+    }
+    if (file_namespace != "") {
+      out.Outdent();
+      out.Print("}\n");
+    }
+    out.Print("#endregion\n");
+  }
+  return output;
+}
+
+}  // namespace grpc_csharp_generator
diff --git a/third_party/grpc/src/compiler/csharp_generator.h b/third_party/grpc/src/compiler/csharp_generator.h
new file mode 100644
index 0000000..fd36e11
--- /dev/null
+++ b/third_party/grpc/src/compiler/csharp_generator.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_H
+#define GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_H
+
+#include "src/compiler/config.h"
+
+#include <google/protobuf/compiler/csharp/csharp_names.h>
+
+namespace grpc_csharp_generator {
+
+grpc::string GetServices(const grpc::protobuf::FileDescriptor* file,
+                         bool generate_client, bool generate_server,
+                         bool internal_access);
+
+}  // namespace grpc_csharp_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_H
diff --git a/third_party/grpc/src/compiler/csharp_generator_helpers.h b/third_party/grpc/src/compiler/csharp_generator_helpers.h
new file mode 100644
index 0000000..8c89925
--- /dev/null
+++ b/third_party/grpc/src/compiler/csharp_generator_helpers.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_HELPERS_H
+#define GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_HELPERS_H
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+
+namespace grpc_csharp_generator {
+
+inline bool ServicesFilename(const grpc::protobuf::FileDescriptor* file,
+                             grpc::string* file_name_or_error) {
+  *file_name_or_error =
+      grpc_generator::FileNameInUpperCamel(file, false) + "Grpc.cs";
+  return true;
+}
+
+// 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 GetCsharpComments(const DescriptorType* desc,
+                                      bool leading) {
+  return grpc_generator::GetPrefixedComments(desc, leading, "//");
+}
+
+}  // namespace grpc_csharp_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_HELPERS_H
diff --git a/third_party/grpc/src/compiler/csharp_plugin.cc b/third_party/grpc/src/compiler/csharp_plugin.cc
new file mode 100644
index 0000000..5f13aa6
--- /dev/null
+++ b/third_party/grpc/src/compiler/csharp_plugin.cc
@@ -0,0 +1,77 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// Generates C# gRPC service interface out of Protobuf IDL.
+
+#include <memory>
+
+#include "src/compiler/config.h"
+#include "src/compiler/csharp_generator.h"
+#include "src/compiler/csharp_generator_helpers.h"
+
+class CSharpGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
+ public:
+  CSharpGrpcGenerator() {}
+  ~CSharpGrpcGenerator() {}
+
+  bool Generate(const grpc::protobuf::FileDescriptor* file,
+                const grpc::string& parameter,
+                grpc::protobuf::compiler::GeneratorContext* context,
+                grpc::string* error) const {
+    std::vector<std::pair<grpc::string, grpc::string> > options;
+    grpc::protobuf::compiler::ParseGeneratorParameter(parameter, &options);
+
+    bool generate_client = true;
+    bool generate_server = true;
+    bool internal_access = false;
+    for (size_t i = 0; i < options.size(); i++) {
+      if (options[i].first == "no_client") {
+        generate_client = false;
+      } else if (options[i].first == "no_server") {
+        generate_server = false;
+      } else if (options[i].first == "internal_access") {
+        internal_access = true;
+      } else {
+        *error = "Unknown generator option: " + options[i].first;
+        return false;
+      }
+    }
+
+    grpc::string code = grpc_csharp_generator::GetServices(
+        file, generate_client, generate_server, internal_access);
+    if (code.size() == 0) {
+      return true;  // don't generate a file if there are no services
+    }
+
+    // Get output file name.
+    grpc::string file_name;
+    if (!grpc_csharp_generator::ServicesFilename(file, &file_name)) {
+      return false;
+    }
+    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[]) {
+  CSharpGrpcGenerator generator;
+  return grpc::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/third_party/grpc/src/compiler/generator_helpers.h b/third_party/grpc/src/compiler/generator_helpers.h
new file mode 100644
index 0000000..747096f
--- /dev/null
+++ b/third_party/grpc/src/compiler/generator_helpers.h
@@ -0,0 +1,277 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H
+#define GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H
+
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "src/compiler/config.h"
+
+namespace grpc_generator {
+
+inline bool StripSuffix(grpc::string* filename, const grpc::string& suffix) {
+  if (filename->length() >= suffix.length()) {
+    size_t suffix_pos = filename->length() - suffix.length();
+    if (filename->compare(suffix_pos, grpc::string::npos, suffix) == 0) {
+      filename->resize(filename->size() - suffix.size());
+      return true;
+    }
+  }
+
+  return false;
+}
+
+inline bool StripPrefix(grpc::string* name, const grpc::string& prefix) {
+  if (name->length() >= prefix.length()) {
+    if (name->substr(0, prefix.size()) == prefix) {
+      *name = name->substr(prefix.size());
+      return true;
+    }
+  }
+  return false;
+}
+
+inline grpc::string StripProto(grpc::string filename) {
+  if (!StripSuffix(&filename, ".protodevel")) {
+    StripSuffix(&filename, ".proto");
+  }
+  return filename;
+}
+
+inline grpc::string StringReplace(grpc::string str, const grpc::string& from,
+                                  const grpc::string& to, bool replace_all) {
+  size_t pos = 0;
+
+  do {
+    pos = str.find(from, pos);
+    if (pos == grpc::string::npos) {
+      break;
+    }
+    str.replace(pos, from.length(), to);
+    pos += to.length();
+  } while (replace_all);
+
+  return str;
+}
+
+inline grpc::string StringReplace(grpc::string str, const grpc::string& from,
+                                  const grpc::string& to) {
+  return StringReplace(str, from, to, true);
+}
+
+inline std::vector<grpc::string> tokenize(const grpc::string& input,
+                                          const grpc::string& delimiters) {
+  std::vector<grpc::string> tokens;
+  size_t pos, last_pos = 0;
+
+  for (;;) {
+    bool done = false;
+    pos = input.find_first_of(delimiters, last_pos);
+    if (pos == grpc::string::npos) {
+      done = true;
+      pos = input.length();
+    }
+
+    tokens.push_back(input.substr(last_pos, pos - last_pos));
+    if (done) return tokens;
+
+    last_pos = pos + 1;
+  }
+}
+
+inline grpc::string CapitalizeFirstLetter(grpc::string s) {
+  if (s.empty()) {
+    return s;
+  }
+  s[0] = ::toupper(s[0]);
+  return s;
+}
+
+inline grpc::string LowercaseFirstLetter(grpc::string s) {
+  if (s.empty()) {
+    return s;
+  }
+  s[0] = ::tolower(s[0]);
+  return s;
+}
+
+inline grpc::string LowerUnderscoreToUpperCamel(grpc::string str) {
+  std::vector<grpc::string> tokens = tokenize(str, "_");
+  grpc::string result = "";
+  for (unsigned int i = 0; i < tokens.size(); i++) {
+    result += CapitalizeFirstLetter(tokens[i]);
+  }
+  return result;
+}
+
+inline grpc::string FileNameInUpperCamel(
+    const grpc::protobuf::FileDescriptor* file, bool include_package_path) {
+  std::vector<grpc::string> tokens = tokenize(StripProto(file->name()), "/");
+  grpc::string result = "";
+  if (include_package_path) {
+    for (unsigned int i = 0; i < tokens.size() - 1; i++) {
+      result += tokens[i] + "/";
+    }
+  }
+  result += LowerUnderscoreToUpperCamel(tokens.back());
+  return result;
+}
+
+inline grpc::string FileNameInUpperCamel(
+    const grpc::protobuf::FileDescriptor* file) {
+  return FileNameInUpperCamel(file, true);
+}
+
+enum MethodType {
+  METHODTYPE_NO_STREAMING,
+  METHODTYPE_CLIENT_STREAMING,
+  METHODTYPE_SERVER_STREAMING,
+  METHODTYPE_BIDI_STREAMING
+};
+
+inline MethodType GetMethodType(
+    const grpc::protobuf::MethodDescriptor* method) {
+  if (method->client_streaming()) {
+    if (method->server_streaming()) {
+      return METHODTYPE_BIDI_STREAMING;
+    } else {
+      return METHODTYPE_CLIENT_STREAMING;
+    }
+  } else {
+    if (method->server_streaming()) {
+      return METHODTYPE_SERVER_STREAMING;
+    } else {
+      return METHODTYPE_NO_STREAMING;
+    }
+  }
+}
+
+inline void Split(const grpc::string& s, char delim,
+                  std::vector<grpc::string>* append_to) {
+  std::istringstream iss(s);
+  grpc::string piece;
+  while (std::getline(iss, piece)) {
+    append_to->push_back(piece);
+  }
+}
+
+enum CommentType {
+  COMMENTTYPE_LEADING,
+  COMMENTTYPE_TRAILING,
+  COMMENTTYPE_LEADING_DETACHED
+};
+
+// Get all the raw comments and append each line without newline to out.
+template <typename DescriptorType>
+inline void GetComment(const DescriptorType* desc, CommentType type,
+                       std::vector<grpc::string>* out) {
+  grpc::protobuf::SourceLocation location;
+  if (!desc->GetSourceLocation(&location)) {
+    return;
+  }
+  if (type == COMMENTTYPE_LEADING || type == COMMENTTYPE_TRAILING) {
+    const grpc::string& comments = type == COMMENTTYPE_LEADING
+                                       ? location.leading_comments
+                                       : location.trailing_comments;
+    Split(comments, '\n', out);
+  } else if (type == COMMENTTYPE_LEADING_DETACHED) {
+    for (unsigned int i = 0; i < location.leading_detached_comments.size();
+         i++) {
+      Split(location.leading_detached_comments[i], '\n', out);
+      out->push_back("");
+    }
+  } else {
+    std::cerr << "Unknown comment type " << type << std::endl;
+    abort();
+  }
+}
+
+// Each raw comment line without newline is appended to out.
+// For file level leading and detached leading comments, we return comments
+// above syntax line. Return nothing for trailing comments.
+template <>
+inline void GetComment(const grpc::protobuf::FileDescriptor* desc,
+                       CommentType type, std::vector<grpc::string>* out) {
+  if (type == COMMENTTYPE_TRAILING) {
+    return;
+  }
+  grpc::protobuf::SourceLocation location;
+  std::vector<int> path;
+  path.push_back(grpc::protobuf::FileDescriptorProto::kSyntaxFieldNumber);
+  if (!desc->GetSourceLocation(path, &location)) {
+    return;
+  }
+  if (type == COMMENTTYPE_LEADING) {
+    Split(location.leading_comments, '\n', out);
+  } else if (type == COMMENTTYPE_LEADING_DETACHED) {
+    for (unsigned int i = 0; i < location.leading_detached_comments.size();
+         i++) {
+      Split(location.leading_detached_comments[i], '\n', out);
+      out->push_back("");
+    }
+  } else {
+    std::cerr << "Unknown comment type " << type << std::endl;
+    abort();
+  }
+}
+
+// Add prefix and newline to each comment line and concatenate them together.
+// Make sure there is a space after the prefix unless the line is empty.
+inline grpc::string GenerateCommentsWithPrefix(
+    const std::vector<grpc::string>& in, const grpc::string& prefix) {
+  std::ostringstream oss;
+  for (auto it = in.begin(); it != in.end(); it++) {
+    const grpc::string& elem = *it;
+    if (elem.empty()) {
+      oss << prefix << "\n";
+    } else if (elem[0] == ' ') {
+      oss << prefix << elem << "\n";
+    } else {
+      oss << prefix << " " << elem << "\n";
+    }
+  }
+  return oss.str();
+}
+
+template <typename DescriptorType>
+inline grpc::string GetPrefixedComments(const DescriptorType* desc,
+                                        bool leading,
+                                        const grpc::string& prefix) {
+  std::vector<grpc::string> out;
+  if (leading) {
+    grpc_generator::GetComment(
+        desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED, &out);
+    std::vector<grpc::string> leading;
+    grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
+                               &leading);
+    out.insert(out.end(), leading.begin(), leading.end());
+  } else {
+    grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
+                               &out);
+  }
+  return GenerateCommentsWithPrefix(out, prefix);
+}
+
+}  // namespace grpc_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H
diff --git a/third_party/grpc/src/compiler/node_generator.cc b/third_party/grpc/src/compiler/node_generator.cc
new file mode 100644
index 0000000..a430628
--- /dev/null
+++ b/third_party/grpc/src/compiler/node_generator.cc
@@ -0,0 +1,278 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <map>
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+#include "src/compiler/node_generator.h"
+#include "src/compiler/node_generator_helpers.h"
+
+using grpc::protobuf::Descriptor;
+using grpc::protobuf::FileDescriptor;
+using grpc::protobuf::MethodDescriptor;
+using grpc::protobuf::ServiceDescriptor;
+using grpc::protobuf::io::Printer;
+using grpc::protobuf::io::StringOutputStream;
+using std::map;
+
+namespace grpc_node_generator {
+namespace {
+
+// Returns the alias we assign to the module of the given .proto filename
+// when importing. Copied entirely from
+// github:google/protobuf/src/google/protobuf/compiler/js/js_generator.cc#L154
+grpc::string ModuleAlias(const grpc::string filename) {
+  // This scheme could technically cause problems if a file includes any 2 of:
+  //   foo/bar_baz.proto
+  //   foo_bar_baz.proto
+  //   foo_bar/baz.proto
+  //
+  // We'll worry about this problem if/when we actually see it.  This name isn't
+  // exposed to users so we can change it later if we need to.
+  grpc::string basename = grpc_generator::StripProto(filename);
+  basename = grpc_generator::StringReplace(basename, "-", "$");
+  basename = grpc_generator::StringReplace(basename, "/", "_");
+  basename = grpc_generator::StringReplace(basename, ".", "_");
+  return basename + "_pb";
+}
+
+// Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
+// message file foo/bar/baz.js
+grpc::string GetJSMessageFilename(const grpc::string& filename) {
+  grpc::string name = filename;
+  return grpc_generator::StripProto(name) + "_pb.js";
+}
+
+// Given a filename like foo/bar/baz.proto, returns the root directory
+// path ../../
+grpc::string GetRootPath(const grpc::string& from_filename,
+                         const grpc::string& to_filename) {
+  if (to_filename.find("google/protobuf") == 0) {
+    // Well-known types (.proto files in the google/protobuf directory) are
+    // assumed to come from the 'google-protobuf' npm package.  We may want to
+    // generalize this exception later by letting others put generated code in
+    // their own npm packages.
+    return "google-protobuf/";
+  }
+  size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
+  if (slashes == 0) {
+    return "./";
+  }
+  grpc::string result = "";
+  for (size_t i = 0; i < slashes; i++) {
+    result += "../";
+  }
+  return result;
+}
+
+// Return the relative path to load to_file from the directory containing
+// from_file, assuming that both paths are relative to the same directory
+grpc::string GetRelativePath(const grpc::string& from_file,
+                             const grpc::string& to_file) {
+  return GetRootPath(from_file, to_file) + to_file;
+}
+
+/* Finds all message types used in all services in the file, and returns them
+ * as a map of fully qualified message type name to message descriptor */
+map<grpc::string, const Descriptor*> GetAllMessages(
+    const FileDescriptor* file) {
+  map<grpc::string, const Descriptor*> message_types;
+  for (int service_num = 0; service_num < file->service_count();
+       service_num++) {
+    const ServiceDescriptor* service = file->service(service_num);
+    for (int method_num = 0; method_num < service->method_count();
+         method_num++) {
+      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->full_name()] = input_type;
+      message_types[output_type->full_name()] = output_type;
+    }
+  }
+  return message_types;
+}
+
+grpc::string MessageIdentifierName(const grpc::string& name) {
+  return grpc_generator::StringReplace(name, ".", "_");
+}
+
+grpc::string NodeObjectPath(const Descriptor* descriptor) {
+  grpc::string module_alias = ModuleAlias(descriptor->file()->name());
+  grpc::string name = descriptor->full_name();
+  grpc_generator::StripPrefix(&name, descriptor->file()->package() + ".");
+  return module_alias + "." + name;
+}
+
+// Prints out the message serializer and deserializer functions
+void PrintMessageTransformer(const Descriptor* descriptor, Printer* out,
+                             const Parameters& params) {
+  map<grpc::string, grpc::string> template_vars;
+  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");
+  out->Indent();
+  out->Print(template_vars, "if (!(arg instanceof $node_name$)) {\n");
+  out->Indent();
+  out->Print(template_vars,
+             "throw new Error('Expected argument of type $name$');\n");
+  out->Outdent();
+  out->Print("}\n");
+  if (params.minimum_node_version > 5) {
+    // Node version is > 5, we should use Buffer.from
+    out->Print("return Buffer.from(arg.serializeBinary());\n");
+  } else {
+    out->Print("return new Buffer(arg.serializeBinary());\n");
+  }
+  out->Outdent();
+  out->Print("}\n\n");
+
+  // Print the deserializer
+  out->Print(template_vars,
+             "function deserialize_$identifier_name$(buffer_arg) {\n");
+  out->Indent();
+  out->Print(
+      template_vars,
+      "return $node_name$.deserializeBinary(new Uint8Array(buffer_arg));\n");
+  out->Outdent();
+  out->Print("}\n\n");
+}
+
+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"] = NodeObjectPath(input_type);
+  vars["input_type_id"] = MessageIdentifierName(input_type->full_name());
+  vars["output_type"] = NodeObjectPath(output_type);
+  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");
+  out->Indent();
+  out->Print(vars, "path: '/$service_name$/$name$',\n");
+  out->Print(vars, "requestStream: $client_stream$,\n");
+  out->Print(vars, "responseStream: $server_stream$,\n");
+  out->Print(vars, "requestType: $input_type$,\n");
+  out->Print(vars, "responseType: $output_type$,\n");
+  out->Print(vars, "requestSerialize: serialize_$input_type_id$,\n");
+  out->Print(vars, "requestDeserialize: deserialize_$input_type_id$,\n");
+  out->Print(vars, "responseSerialize: serialize_$output_type_id$,\n");
+  out->Print(vars, "responseDeserialize: deserialize_$output_type_id$,\n");
+  out->Outdent();
+  out->Print("}");
+}
+
+// Prints out the service descriptor object
+void PrintService(const ServiceDescriptor* service, Printer* out) {
+  map<grpc::string, grpc::string> template_vars;
+  out->Print(GetNodeComments(service, true).c_str());
+  template_vars["name"] = service->name();
+  out->Print(template_vars, "var $name$Service = exports.$name$Service = {\n");
+  out->Indent();
+  for (int i = 0; i < service->method_count(); i++) {
+    grpc::string method_name =
+        grpc_generator::LowercaseFirstLetter(service->method(i)->name());
+    out->Print(GetNodeComments(service->method(i), true).c_str());
+    out->Print("$method_name$: ", "method_name", method_name);
+    PrintMethod(service->method(i), out);
+    out->Print(",\n");
+    out->Print(GetNodeComments(service->method(i), false).c_str());
+  }
+  out->Outdent();
+  out->Print("};\n\n");
+  out->Print(template_vars,
+             "exports.$name$Client = "
+             "grpc.makeGenericClientConstructor($name$Service);\n");
+  out->Print(GetNodeComments(service, false).c_str());
+}
+
+void PrintImports(const FileDescriptor* file, Printer* out) {
+  out->Print("var grpc = require('grpc');\n");
+  if (file->message_type_count() > 0) {
+    grpc::string file_path =
+        GetRelativePath(file->name(), GetJSMessageFilename(file->name()));
+    out->Print("var $module_alias$ = require('$file_path$');\n", "module_alias",
+               ModuleAlias(file->name()), "file_path", file_path);
+  }
+
+  for (int i = 0; i < file->dependency_count(); i++) {
+    grpc::string file_path = GetRelativePath(
+        file->name(), GetJSMessageFilename(file->dependency(i)->name()));
+    out->Print("var $module_alias$ = require('$file_path$');\n", "module_alias",
+               ModuleAlias(file->dependency(i)->name()), "file_path",
+               file_path);
+  }
+  out->Print("\n");
+}
+
+void PrintTransformers(const FileDescriptor* file, Printer* out,
+                       const Parameters& params) {
+  map<grpc::string, const Descriptor*> messages = GetAllMessages(file);
+  for (std::map<grpc::string, const Descriptor*>::iterator it =
+           messages.begin();
+       it != messages.end(); it++) {
+    PrintMessageTransformer(it->second, out, params);
+  }
+  out->Print("\n");
+}
+
+void PrintServices(const FileDescriptor* file, Printer* out) {
+  for (int i = 0; i < file->service_count(); i++) {
+    PrintService(file->service(i), out);
+  }
+}
+}  // namespace
+
+grpc::string GenerateFile(const FileDescriptor* file,
+                          const Parameters& params) {
+  grpc::string output;
+  {
+    StringOutputStream output_stream(&output);
+    Printer out(&output_stream, '$');
+
+    if (file->service_count() == 0) {
+      return output;
+    }
+    out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n");
+
+    grpc::string leading_comments = GetNodeComments(file, true);
+    if (!leading_comments.empty()) {
+      out.Print("// Original file comments:\n");
+      out.PrintRaw(leading_comments.c_str());
+    }
+
+    out.Print("'use strict';\n");
+
+    PrintImports(file, &out);
+
+    PrintTransformers(file, &out, params);
+
+    PrintServices(file, &out);
+
+    out.Print(GetNodeComments(file, false).c_str());
+  }
+  return output;
+}
+
+}  // namespace grpc_node_generator
diff --git a/third_party/grpc/src/compiler/node_generator.h b/third_party/grpc/src/compiler/node_generator.h
new file mode 100644
index 0000000..f3a5315
--- /dev/null
+++ b/third_party/grpc/src/compiler/node_generator.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_NODE_GENERATOR_H
+#define GRPC_INTERNAL_COMPILER_NODE_GENERATOR_H
+
+#include "src/compiler/config.h"
+
+namespace grpc_node_generator {
+
+// Contains all the parameters that are parsed from the command line.
+struct Parameters {
+  // Sets the earliest version of nodejs that needs to be supported.
+  int minimum_node_version;
+};
+
+grpc::string GenerateFile(const grpc::protobuf::FileDescriptor* file,
+                          const Parameters& params);
+
+}  // namespace grpc_node_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_NODE_GENERATOR_H
diff --git a/third_party/grpc/src/compiler/node_generator_helpers.h b/third_party/grpc/src/compiler/node_generator_helpers.h
new file mode 100644
index 0000000..82d2d84
--- /dev/null
+++ b/third_party/grpc/src/compiler/node_generator_helpers.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H
+#define GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H
+
+#include <algorithm>
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+
+namespace grpc_node_generator {
+
+inline grpc::string GetJSServiceFilename(const grpc::string& filename) {
+  return grpc_generator::StripProto(filename) + "_grpc_pb.js";
+}
+
+// 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 GetNodeComments(const DescriptorType* desc, bool leading) {
+  return grpc_generator::GetPrefixedComments(desc, leading, "//");
+}
+
+}  // namespace grpc_node_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H
diff --git a/third_party/grpc/src/compiler/node_plugin.cc b/third_party/grpc/src/compiler/node_plugin.cc
new file mode 100644
index 0000000..0d19d8e
--- /dev/null
+++ b/third_party/grpc/src/compiler/node_plugin.cc
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// Generates Node gRPC service interface out of Protobuf IDL.
+
+#include <memory>
+
+#include "src/compiler/config.h"
+#include "src/compiler/node_generator.h"
+#include "src/compiler/node_generator_helpers.h"
+
+using grpc_node_generator::GenerateFile;
+using grpc_node_generator::GetJSServiceFilename;
+
+class NodeGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
+ public:
+  NodeGrpcGenerator() {}
+  ~NodeGrpcGenerator() {}
+
+  bool Generate(const grpc::protobuf::FileDescriptor* file,
+                const grpc::string& parameter,
+                grpc::protobuf::compiler::GeneratorContext* context,
+                grpc::string* error) const {
+    grpc_node_generator::Parameters generator_parameters;
+    generator_parameters.minimum_node_version = 4;
+
+    if (!parameter.empty()) {
+      std::vector<grpc::string> parameters_list =
+          grpc_generator::tokenize(parameter, ",");
+      for (auto parameter_string = parameters_list.begin();
+           parameter_string != parameters_list.end(); parameter_string++) {
+        std::vector<grpc::string> param =
+            grpc_generator::tokenize(*parameter_string, "=");
+        if (param[0] == "minimum_node_version") {
+          sscanf(param[1].c_str(), "%d",
+                 &generator_parameters.minimum_node_version);
+        } else {
+          *error = grpc::string("Unknown parameter: ") + *parameter_string;
+          return false;
+        }
+      }
+    }
+
+    grpc::string code = GenerateFile(file, generator_parameters);
+    if (code.size() == 0) {
+      return true;
+    }
+
+    // Get output file name
+    grpc::string file_name = GetJSServiceFilename(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[]) {
+  NodeGrpcGenerator generator;
+  return grpc::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/third_party/grpc/src/compiler/objective_c_generator.cc b/third_party/grpc/src/compiler/objective_c_generator.cc
new file mode 100644
index 0000000..39f68cb
--- /dev/null
+++ b/third_party/grpc/src/compiler/objective_c_generator.cc
@@ -0,0 +1,309 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <map>
+#include <set>
+#include <sstream>
+
+#include "src/compiler/config.h"
+#include "src/compiler/objective_c_generator.h"
+#include "src/compiler/objective_c_generator_helpers.h"
+
+#include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
+
+using ::google::protobuf::compiler::objectivec::ClassName;
+using ::grpc::protobuf::FileDescriptor;
+using ::grpc::protobuf::FileDescriptor;
+using ::grpc::protobuf::MethodDescriptor;
+using ::grpc::protobuf::ServiceDescriptor;
+using ::grpc::protobuf::io::Printer;
+using ::std::map;
+using ::std::set;
+
+namespace grpc_objective_c_generator {
+namespace {
+
+void PrintProtoRpcDeclarationAsPragma(
+    Printer* printer, const MethodDescriptor* method,
+    map< ::grpc::string, ::grpc::string> vars) {
+  vars["client_stream"] = method->client_streaming() ? "stream " : "";
+  vars["server_stream"] = method->server_streaming() ? "stream " : "";
+
+  printer->Print(vars,
+                 "#pragma mark $method_name$($client_stream$$request_type$)"
+                 " returns ($server_stream$$response_type$)\n\n");
+}
+
+template <typename DescriptorType>
+static void PrintAllComments(const DescriptorType* desc, Printer* printer) {
+  std::vector<grpc::string> comments;
+  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
+                             &comments);
+  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
+                             &comments);
+  grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
+                             &comments);
+  if (comments.empty()) {
+    return;
+  }
+  printer->Print("/**\n");
+  for (auto it = comments.begin(); it != comments.end(); ++it) {
+    printer->Print(" * ");
+    size_t start_pos = it->find_first_not_of(' ');
+    if (start_pos != grpc::string::npos) {
+      printer->PrintRaw(it->c_str() + start_pos);
+    }
+    printer->Print("\n");
+  }
+  printer->Print(" */\n");
+}
+
+void PrintMethodSignature(Printer* printer, const MethodDescriptor* method,
+                          const map< ::grpc::string, ::grpc::string>& vars) {
+  // Print comment
+  PrintAllComments(method, printer);
+
+  printer->Print(vars, "- ($return_type$)$method_name$With");
+  if (method->client_streaming()) {
+    printer->Print("RequestsWriter:(GRXWriter *)requestWriter");
+  } else {
+    printer->Print(vars, "Request:($request_class$ *)request");
+  }
+
+  // TODO(jcanizales): Put this on a new line and align colons.
+  if (method->server_streaming()) {
+    printer->Print(vars,
+                   " eventHandler:(void(^)(BOOL done, "
+                   "$response_class$ *_Nullable response, NSError *_Nullable "
+                   "error))eventHandler");
+  } else {
+    printer->Print(vars,
+                   " handler:(void(^)($response_class$ *_Nullable response, "
+                   "NSError *_Nullable error))handler");
+  }
+}
+
+void PrintSimpleSignature(Printer* printer, const MethodDescriptor* method,
+                          map< ::grpc::string, ::grpc::string> vars) {
+  vars["method_name"] =
+      grpc_generator::LowercaseFirstLetter(vars["method_name"]);
+  vars["return_type"] = "void";
+  PrintMethodSignature(printer, method, vars);
+}
+
+void PrintAdvancedSignature(Printer* printer, const MethodDescriptor* method,
+                            map< ::grpc::string, ::grpc::string> vars) {
+  vars["method_name"] = "RPCTo" + vars["method_name"];
+  vars["return_type"] = "GRPCProtoCall *";
+  PrintMethodSignature(printer, method, vars);
+}
+
+inline map< ::grpc::string, ::grpc::string> GetMethodVars(
+    const MethodDescriptor* method) {
+  map< ::grpc::string, ::grpc::string> res;
+  res["method_name"] = method->name();
+  res["request_type"] = method->input_type()->name();
+  res["response_type"] = method->output_type()->name();
+  res["request_class"] = ClassName(method->input_type());
+  res["response_class"] = ClassName(method->output_type());
+  return res;
+}
+
+void PrintMethodDeclarations(Printer* printer, const MethodDescriptor* method) {
+  map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
+
+  PrintProtoRpcDeclarationAsPragma(printer, method, vars);
+
+  PrintSimpleSignature(printer, method, vars);
+  printer->Print(";\n\n");
+  PrintAdvancedSignature(printer, method, vars);
+  printer->Print(";\n\n\n");
+}
+
+void PrintSimpleImplementation(Printer* printer, const MethodDescriptor* method,
+                               map< ::grpc::string, ::grpc::string> vars) {
+  printer->Print("{\n");
+  printer->Print(vars, "  [[self RPCTo$method_name$With");
+  if (method->client_streaming()) {
+    printer->Print("RequestsWriter:requestWriter");
+  } else {
+    printer->Print("Request:request");
+  }
+  if (method->server_streaming()) {
+    printer->Print(" eventHandler:eventHandler] start];\n");
+  } else {
+    printer->Print(" handler:handler] start];\n");
+  }
+  printer->Print("}\n");
+}
+
+void PrintAdvancedImplementation(Printer* printer,
+                                 const MethodDescriptor* method,
+                                 map< ::grpc::string, ::grpc::string> vars) {
+  printer->Print("{\n");
+  printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
+
+  printer->Print("            requestsWriter:");
+  if (method->client_streaming()) {
+    printer->Print("requestWriter\n");
+  } else {
+    printer->Print("[GRXWriter writerWithValue:request]\n");
+  }
+
+  printer->Print(vars, "             responseClass:[$response_class$ class]\n");
+
+  printer->Print("        responsesWriteable:[GRXWriteable ");
+  if (method->server_streaming()) {
+    printer->Print("writeableWithEventHandler:eventHandler]];\n");
+  } else {
+    printer->Print("writeableWithSingleHandler:handler]];\n");
+  }
+
+  printer->Print("}\n");
+}
+
+void PrintMethodImplementations(Printer* printer,
+                                const MethodDescriptor* method) {
+  map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
+
+  PrintProtoRpcDeclarationAsPragma(printer, method, vars);
+
+  // TODO(jcanizales): Print documentation from the method.
+  PrintSimpleSignature(printer, method, vars);
+  PrintSimpleImplementation(printer, method, vars);
+
+  printer->Print("// Returns a not-yet-started RPC object.\n");
+  PrintAdvancedSignature(printer, method, vars);
+  PrintAdvancedImplementation(printer, method, vars);
+}
+
+}  // namespace
+
+::grpc::string GetAllMessageClasses(const FileDescriptor* file) {
+  ::grpc::string output;
+  set< ::grpc::string> classes;
+  for (int i = 0; i < file->service_count(); i++) {
+    const auto service = file->service(i);
+    for (int i = 0; i < service->method_count(); i++) {
+      const auto method = service->method(i);
+      classes.insert(ClassName(method->input_type()));
+      classes.insert(ClassName(method->output_type()));
+    }
+  }
+  for (auto one_class : classes) {
+    output += "@class " + one_class + ";\n";
+  }
+
+  return output;
+}
+
+::grpc::string GetProtocol(const ServiceDescriptor* service) {
+  ::grpc::string output;
+
+  // Scope the output stream so it closes and finalizes output to the string.
+  grpc::protobuf::io::StringOutputStream output_stream(&output);
+  Printer printer(&output_stream, '$');
+
+  map< ::grpc::string, ::grpc::string> vars = {
+      {"service_class", ServiceClassName(service)}};
+
+  printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
+  for (int i = 0; i < service->method_count(); i++) {
+    PrintMethodDeclarations(&printer, service->method(i));
+  }
+  printer.Print("@end\n\n");
+
+  return output;
+}
+
+::grpc::string GetInterface(const ServiceDescriptor* service) {
+  ::grpc::string output;
+
+  // Scope the output stream so it closes and finalizes output to the string.
+  grpc::protobuf::io::StringOutputStream output_stream(&output);
+  Printer printer(&output_stream, '$');
+
+  map< ::grpc::string, ::grpc::string> vars = {
+      {"service_class", ServiceClassName(service)}};
+
+  printer.Print(vars,
+                "/**\n"
+                " * Basic service implementation, over gRPC, that only does\n"
+                " * marshalling and parsing.\n"
+                " */\n");
+  printer.Print(vars,
+                "@interface $service_class$ :"
+                " GRPCProtoService<$service_class$>\n");
+  printer.Print(
+      "- (instancetype)initWithHost:(NSString *)host"
+      " NS_DESIGNATED_INITIALIZER;\n");
+  printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
+  printer.Print("@end\n");
+
+  return output;
+}
+
+::grpc::string GetSource(const ServiceDescriptor* service) {
+  ::grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    grpc::protobuf::io::StringOutputStream output_stream(&output);
+    Printer printer(&output_stream, '$');
+
+    map< ::grpc::string, ::grpc::string> vars = {
+        {"service_name", service->name()},
+        {"service_class", ServiceClassName(service)},
+        {"package", service->file()->package()}};
+
+    printer.Print(vars,
+                  "@implementation $service_class$\n\n"
+                  "// Designated initializer\n"
+                  "- (instancetype)initWithHost:(NSString *)host {\n"
+                  "  self = [super initWithHost:host\n"
+                  "                 packageName:@\"$package$\"\n"
+                  "                 serviceName:@\"$service_name$\"];\n"
+                  "  return self;\n"
+                  "}\n\n");
+
+    printer.Print(
+        "// Override superclass initializer to disallow different"
+        " package and service names.\n"
+        "- (instancetype)initWithHost:(NSString *)host\n"
+        "                 packageName:(NSString *)packageName\n"
+        "                 serviceName:(NSString *)serviceName {\n"
+        "  return [self initWithHost:host];\n"
+        "}\n\n");
+
+    printer.Print(
+        "#pragma mark - Class Methods\n\n"
+        "+ (instancetype)serviceWithHost:(NSString *)host {\n"
+        "  return [[self alloc] initWithHost:host];\n"
+        "}\n\n");
+
+    printer.Print("#pragma mark - Method Implementations\n\n");
+
+    for (int i = 0; i < service->method_count(); i++) {
+      PrintMethodImplementations(&printer, service->method(i));
+    }
+
+    printer.Print("@end\n");
+  }
+  return output;
+}
+
+}  // namespace grpc_objective_c_generator
diff --git a/third_party/grpc/src/compiler/objective_c_generator.h b/third_party/grpc/src/compiler/objective_c_generator.h
new file mode 100644
index 0000000..eb1c7ff
--- /dev/null
+++ b/third_party/grpc/src/compiler/objective_c_generator.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_H
+#define GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_H
+
+#include "src/compiler/config.h"
+
+namespace grpc_objective_c_generator {
+
+using ::grpc::protobuf::FileDescriptor;
+using ::grpc::protobuf::FileDescriptor;
+using ::grpc::protobuf::ServiceDescriptor;
+using ::grpc::string;
+
+// Returns forward declaration of classes in the generated header file.
+string GetAllMessageClasses(const FileDescriptor* file);
+
+// Returns the content to be included defining the @protocol segment at the
+// insertion point of the generated implementation file.
+string GetProtocol(const ServiceDescriptor* service);
+
+// Returns the content to be included defining the @interface segment at the
+// insertion point of the generated implementation file.
+string GetInterface(const ServiceDescriptor* service);
+
+// Returns the content to be included in the "global_scope" insertion point of
+// the generated implementation file.
+string GetSource(const ServiceDescriptor* service);
+
+}  // namespace grpc_objective_c_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_H
diff --git a/third_party/grpc/src/compiler/objective_c_generator_helpers.h b/third_party/grpc/src/compiler/objective_c_generator_helpers.h
new file mode 100644
index 0000000..a284da9
--- /dev/null
+++ b/third_party/grpc/src/compiler/objective_c_generator_helpers.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H
+#define GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H
+
+#include <map>
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+
+#include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
+
+namespace grpc_objective_c_generator {
+
+using ::grpc::protobuf::FileDescriptor;
+using ::grpc::protobuf::ServiceDescriptor;
+using ::grpc::string;
+
+inline string MessageHeaderName(const FileDescriptor* file) {
+  return google::protobuf::compiler::objectivec::FilePath(file) + ".pbobjc.h";
+}
+
+inline string ServiceClassName(const ServiceDescriptor* service) {
+  const FileDescriptor* file = service->file();
+  string prefix = file->options().objc_class_prefix();
+  return prefix + service->name();
+}
+
+inline ::grpc::string LocalImport(const ::grpc::string& import) {
+  return ::grpc::string("#import \"" + import + "\"\n");
+}
+
+inline ::grpc::string SystemImport(const ::grpc::string& import) {
+  return ::grpc::string("#import <" + import + ">\n");
+}
+
+inline ::grpc::string PreprocConditional(::grpc::string symbol, bool invert) {
+  return invert ? "!defined(" + symbol + ") || !" + symbol
+                : "defined(" + symbol + ") && " + symbol;
+}
+
+inline ::grpc::string PreprocIf(const ::grpc::string& symbol,
+                                const ::grpc::string& if_true) {
+  return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" +
+                        if_true + "#endif\n");
+}
+
+inline ::grpc::string PreprocIfNot(const ::grpc::string& symbol,
+                                   const ::grpc::string& if_true) {
+  return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" +
+                        if_true + "#endif\n");
+}
+
+inline ::grpc::string PreprocIfElse(const ::grpc::string& symbol,
+                                    const ::grpc::string& if_true,
+                                    const ::grpc::string& if_false) {
+  return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" +
+                        if_true + "#else\n" + if_false + "#endif\n");
+}
+
+inline ::grpc::string PreprocIfNotElse(const ::grpc::string& symbol,
+                                       const ::grpc::string& if_true,
+                                       const ::grpc::string& if_false) {
+  return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" +
+                        if_true + "#else\n" + if_false + "#endif\n");
+}
+
+}  // namespace grpc_objective_c_generator
+#endif  // GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H
diff --git a/third_party/grpc/src/compiler/objective_c_plugin.cc b/third_party/grpc/src/compiler/objective_c_plugin.cc
new file mode 100644
index 0000000..f0fe368
--- /dev/null
+++ b/third_party/grpc/src/compiler/objective_c_plugin.cc
@@ -0,0 +1,169 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// Generates Objective C gRPC service interface out of Protobuf IDL.
+
+#include <memory>
+
+#include "src/compiler/config.h"
+#include "src/compiler/objective_c_generator.h"
+#include "src/compiler/objective_c_generator_helpers.h"
+
+#include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
+
+using ::google::protobuf::compiler::objectivec::
+    IsProtobufLibraryBundledProtoFile;
+using ::google::protobuf::compiler::objectivec::ProtobufLibraryFrameworkName;
+using ::grpc_objective_c_generator::LocalImport;
+using ::grpc_objective_c_generator::PreprocIfElse;
+using ::grpc_objective_c_generator::PreprocIfNot;
+using ::grpc_objective_c_generator::SystemImport;
+
+namespace {
+
+inline ::grpc::string ImportProtoHeaders(
+    const grpc::protobuf::FileDescriptor* dep, const char* indent) {
+  ::grpc::string header = grpc_objective_c_generator::MessageHeaderName(dep);
+
+  if (!IsProtobufLibraryBundledProtoFile(dep)) {
+    return indent + LocalImport(header);
+  }
+
+  ::grpc::string base_name = header;
+  grpc_generator::StripPrefix(&base_name, "google/protobuf/");
+  // create the import code snippet
+  ::grpc::string framework_header =
+      ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name;
+
+  static const ::grpc::string kFrameworkImportsCondition =
+      "GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS";
+  return PreprocIfElse(kFrameworkImportsCondition,
+                       indent + SystemImport(framework_header),
+                       indent + LocalImport(header));
+}
+
+}  // namespace
+
+class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
+ public:
+  ObjectiveCGrpcGenerator() {}
+  virtual ~ObjectiveCGrpcGenerator() {}
+
+ public:
+  virtual bool Generate(const grpc::protobuf::FileDescriptor* file,
+                        const ::grpc::string& parameter,
+                        grpc::protobuf::compiler::GeneratorContext* context,
+                        ::grpc::string* error) const {
+    if (file->service_count() == 0) {
+      // No services.  Do nothing.
+      return true;
+    }
+
+    static const ::grpc::string kNonNullBegin = "NS_ASSUME_NONNULL_BEGIN\n";
+    static const ::grpc::string kNonNullEnd = "NS_ASSUME_NONNULL_END\n";
+    static const ::grpc::string kProtocolOnly = "GPB_GRPC_PROTOCOL_ONLY";
+    static const ::grpc::string kForwardDeclare =
+        "GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO";
+
+    ::grpc::string file_name =
+        google::protobuf::compiler::objectivec::FilePath(file);
+
+    {
+      // Generate .pbrpc.h
+
+      ::grpc::string imports = LocalImport(file_name + ".pbobjc.h");
+
+      ::grpc::string system_imports = SystemImport("ProtoRPC/ProtoService.h") +
+                                      SystemImport("ProtoRPC/ProtoRPC.h") +
+                                      SystemImport("RxLibrary/GRXWriteable.h") +
+                                      SystemImport("RxLibrary/GRXWriter.h");
+
+      ::grpc::string forward_declarations = "@class GRPCProtoCall;\n\n";
+
+      ::grpc::string class_declarations =
+          grpc_objective_c_generator::GetAllMessageClasses(file);
+
+      ::grpc::string class_imports;
+      for (int i = 0; i < file->dependency_count(); i++) {
+        class_imports += ImportProtoHeaders(file->dependency(i), "  ");
+      }
+
+      ::grpc::string protocols;
+      for (int i = 0; i < file->service_count(); i++) {
+        const grpc::protobuf::ServiceDescriptor* service = file->service(i);
+        protocols += grpc_objective_c_generator::GetProtocol(service);
+      }
+
+      ::grpc::string interfaces;
+      for (int i = 0; i < file->service_count(); i++) {
+        const grpc::protobuf::ServiceDescriptor* service = file->service(i);
+        interfaces += grpc_objective_c_generator::GetInterface(service);
+      }
+
+      Write(context, file_name + ".pbrpc.h",
+            PreprocIfNot(kForwardDeclare, imports) + "\n" +
+                PreprocIfNot(kProtocolOnly, system_imports) + "\n" +
+                class_declarations + "\n" +
+                PreprocIfNot(kForwardDeclare, class_imports) + "\n" +
+                forward_declarations + "\n" + kNonNullBegin + "\n" + protocols +
+                "\n" + PreprocIfNot(kProtocolOnly, interfaces) + "\n" +
+                kNonNullEnd + "\n");
+    }
+
+    {
+      // Generate .pbrpc.m
+
+      ::grpc::string imports = LocalImport(file_name + ".pbrpc.h") +
+                               LocalImport(file_name + ".pbobjc.h") +
+                               SystemImport("ProtoRPC/ProtoRPC.h") +
+                               SystemImport("RxLibrary/GRXWriter+Immediate.h");
+
+      ::grpc::string class_imports;
+      for (int i = 0; i < file->dependency_count(); i++) {
+        class_imports += ImportProtoHeaders(file->dependency(i), "");
+      }
+
+      ::grpc::string definitions;
+      for (int i = 0; i < file->service_count(); i++) {
+        const grpc::protobuf::ServiceDescriptor* service = file->service(i);
+        definitions += grpc_objective_c_generator::GetSource(service);
+      }
+
+      Write(context, file_name + ".pbrpc.m",
+            PreprocIfNot(kProtocolOnly,
+                         imports + "\n" + class_imports + "\n" + definitions));
+    }
+
+    return true;
+  }
+
+ private:
+  // Write the given code into the given file.
+  void Write(grpc::protobuf::compiler::GeneratorContext* context,
+             const ::grpc::string& filename, const ::grpc::string& code) const {
+    std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> output(
+        context->Open(filename));
+    grpc::protobuf::io::CodedOutputStream coded_out(output.get());
+    coded_out.WriteRaw(code.data(), code.size());
+  }
+};
+
+int main(int argc, char* argv[]) {
+  ObjectiveCGrpcGenerator generator;
+  return grpc::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/third_party/grpc/src/compiler/php_generator.cc b/third_party/grpc/src/compiler/php_generator.cc
new file mode 100644
index 0000000..ca084cc
--- /dev/null
+++ b/third_party/grpc/src/compiler/php_generator.cc
@@ -0,0 +1,184 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <map>
+
+#include <google/protobuf/compiler/php/php_generator.h>
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+#include "src/compiler/php_generator_helpers.h"
+
+using google::protobuf::compiler::php::GeneratedClassName;
+using grpc::protobuf::Descriptor;
+using grpc::protobuf::FileDescriptor;
+using grpc::protobuf::MethodDescriptor;
+using grpc::protobuf::ServiceDescriptor;
+using grpc::protobuf::io::Printer;
+using grpc::protobuf::io::StringOutputStream;
+using std::map;
+
+namespace grpc_php_generator {
+namespace {
+
+grpc::string ConvertToPhpNamespace(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();
+}
+
+grpc::string PackageName(const FileDescriptor* file) {
+  if (file->options().has_php_namespace()) {
+    return file->options().php_namespace();
+  } else {
+    return ConvertToPhpNamespace(file->package());
+  }
+}
+
+grpc::string MessageIdentifierName(const grpc::string& name,
+                                   const FileDescriptor* file) {
+  std::vector<grpc::string> tokens = grpc_generator::tokenize(name, ".");
+  std::ostringstream oss;
+  if (PackageName(file) != "") {
+    oss << PackageName(file) << "\\";
+  }
+  oss << grpc_generator::CapitalizeFirstLetter(tokens[tokens.size() - 1]);
+  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(GeneratedClassName(input_type), input_type->file());
+  vars["output_type_id"] = MessageIdentifierName(
+      GeneratedClassName(output_type), output_type->file());
+
+  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();
+    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();
+    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->Outdent();
+  out->Print("}\n\n");
+}
+
+// Prints out the service descriptor object
+void PrintService(const ServiceDescriptor* service,
+                  const grpc::string& class_suffix, Printer* out) {
+  map<grpc::string, grpc::string> vars;
+  out->Print("/**\n");
+  out->Print(GetPHPComments(service, " *").c_str());
+  out->Print(" */\n");
+  vars["name"] = GetPHPServiceClassname(service, class_suffix);
+  out->Print(vars, "class $name$ extends \\Grpc\\BaseStub {\n\n");
+  out->Indent();
+  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->Indent();
+  out->Print("parent::__construct($$hostname, $$opts, $$channel);\n");
+  out->Outdent();
+  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->Outdent();
+  out->Print("}\n");
+}
+}  // namespace
+
+grpc::string GenerateFile(const FileDescriptor* file,
+                          const ServiceDescriptor* service,
+                          const grpc::string& class_suffix) {
+  grpc::string output;
+  {
+    StringOutputStream output_stream(&output);
+    Printer out(&output_stream, '$');
+
+    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.PrintRaw(leading_comments.c_str());
+    }
+
+    map<grpc::string, grpc::string> vars;
+    grpc::string php_namespace = PackageName(file);
+    vars["package"] = php_namespace;
+    out.Print(vars, "namespace $package$;\n\n");
+
+    PrintService(service, class_suffix, &out);
+  }
+  return output;
+}
+
+}  // namespace grpc_php_generator
diff --git a/third_party/grpc/src/compiler/php_generator.h b/third_party/grpc/src/compiler/php_generator.h
new file mode 100644
index 0000000..46222b3
--- /dev/null
+++ b/third_party/grpc/src/compiler/php_generator.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#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,
+                          const grpc::protobuf::ServiceDescriptor* service,
+                          const grpc::string& class_suffix);
+
+}  // namespace grpc_php_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_PHP_GENERATOR_H
diff --git a/third_party/grpc/src/compiler/php_generator_helpers.h b/third_party/grpc/src/compiler/php_generator_helpers.h
new file mode 100644
index 0000000..3ad1997
--- /dev/null
+++ b/third_party/grpc/src/compiler/php_generator_helpers.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#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 GetPHPServiceClassname(
+    const grpc::protobuf::ServiceDescriptor* service,
+    const grpc::string& class_suffix) {
+  return service->name() + (class_suffix == "" ? "Client" : class_suffix);
+}
+
+// ReplaceAll replaces all instances of search with replace in s.
+inline grpc::string ReplaceAll(grpc::string s, const grpc::string& search,
+                               const grpc::string& replace) {
+  size_t pos = 0;
+  while ((pos = s.find(search, pos)) != grpc::string::npos) {
+    s.replace(pos, search.length(), replace);
+    pos += replace.length();
+  }
+  return s;
+}
+
+inline grpc::string GetPHPServiceFilename(
+    const grpc::protobuf::FileDescriptor* file,
+    const grpc::protobuf::ServiceDescriptor* service,
+    const grpc::string& class_suffix) {
+  std::ostringstream oss;
+  if (file->options().has_php_namespace()) {
+    oss << ReplaceAll(file->options().php_namespace(), "\\", "/");
+  } else {
+    std::vector<grpc::string> tokens =
+        grpc_generator::tokenize(file->package(), ".");
+    for (unsigned int i = 0; i < tokens.size(); i++) {
+      oss << (i == 0 ? "" : "/")
+          << grpc_generator::CapitalizeFirstLetter(tokens[i]);
+    }
+  }
+  return oss.str() + "/" + GetPHPServiceClassname(service, class_suffix) +
+         ".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 ReplaceAll(grpc_generator::GetPrefixedComments(desc, true, prefix),
+                    "*/", "&#42;/");
+}
+
+}  // namespace grpc_php_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_PHP_GENERATOR_HELPERS_H
diff --git a/third_party/grpc/src/compiler/php_plugin.cc b/third_party/grpc/src/compiler/php_plugin.cc
new file mode 100644
index 0000000..d01cbf8
--- /dev/null
+++ b/third_party/grpc/src/compiler/php_plugin.cc
@@ -0,0 +1,77 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// 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 google::protobuf::compiler::ParseGeneratorParameter;
+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 {
+    if (file->service_count() == 0) {
+      return true;
+    }
+
+    std::vector<std::pair<grpc::string, grpc::string> > options;
+    ParseGeneratorParameter(parameter, &options);
+
+    grpc::string class_suffix;
+    for (size_t i = 0; i < options.size(); ++i) {
+      if (options[i].first == "class_suffix") {
+        class_suffix = options[i].second;
+      } else {
+        *error = "unsupported options: " + options[i].first;
+        return false;
+      }
+    }
+
+    for (int i = 0; i < file->service_count(); i++) {
+      grpc::string code = GenerateFile(file, file->service(i), class_suffix);
+
+      // Get output file name
+      grpc::string file_name =
+          GetPHPServiceFilename(file, file->service(i), class_suffix);
+
+      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/third_party/grpc/src/compiler/protobuf_plugin.h b/third_party/grpc/src/compiler/protobuf_plugin.h
new file mode 100644
index 0000000..b971af1
--- /dev/null
+++ b/third_party/grpc/src/compiler/protobuf_plugin.h
@@ -0,0 +1,196 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_PROTOBUF_PLUGIN_H
+#define GRPC_INTERNAL_COMPILER_PROTOBUF_PLUGIN_H
+
+#include "src/compiler/config.h"
+#include "src/compiler/cpp_generator_helpers.h"
+#include "src/compiler/python_generator_helpers.h"
+#include "src/compiler/python_private_generator.h"
+#include "src/compiler/schema_interface.h"
+
+#include <vector>
+
+// Get leading or trailing comments in a string.
+template <typename DescriptorType>
+inline grpc::string GetCommentsHelper(const DescriptorType* desc, bool leading,
+                                      const grpc::string& prefix) {
+  return grpc_generator::GetPrefixedComments(desc, leading, prefix);
+}
+
+class ProtoBufMethod : public grpc_generator::Method {
+ public:
+  ProtoBufMethod(const grpc::protobuf::MethodDescriptor* method)
+      : method_(method) {}
+
+  grpc::string name() const { return method_->name(); }
+
+  grpc::string input_type_name() const {
+    return grpc_cpp_generator::ClassName(method_->input_type(), true);
+  }
+  grpc::string output_type_name() const {
+    return grpc_cpp_generator::ClassName(method_->output_type(), true);
+  }
+
+  grpc::string get_input_type_name() const {
+    return method_->input_type()->file()->name();
+  }
+  grpc::string get_output_type_name() const {
+    return method_->output_type()->file()->name();
+  }
+
+  bool get_module_and_message_path_input(grpc::string* str,
+                                         grpc::string generator_file_name,
+                                         bool generate_in_pb2_grpc,
+                                         grpc::string import_prefix) const {
+    return grpc_python_generator::GetModuleAndMessagePath(
+        method_->input_type(), str, generator_file_name, generate_in_pb2_grpc,
+        import_prefix);
+  }
+
+  bool get_module_and_message_path_output(grpc::string* str,
+                                          grpc::string generator_file_name,
+                                          bool generate_in_pb2_grpc,
+                                          grpc::string import_prefix) const {
+    return grpc_python_generator::GetModuleAndMessagePath(
+        method_->output_type(), str, generator_file_name, generate_in_pb2_grpc,
+        import_prefix);
+  }
+
+  bool NoStreaming() const {
+    return !method_->client_streaming() && !method_->server_streaming();
+  }
+
+  bool ClientStreaming() const { return method_->client_streaming(); }
+
+  bool ServerStreaming() const { return method_->server_streaming(); }
+
+  bool BidiStreaming() const {
+    return method_->client_streaming() && method_->server_streaming();
+  }
+
+  grpc::string GetLeadingComments(const grpc::string prefix) const {
+    return GetCommentsHelper(method_, true, prefix);
+  }
+
+  grpc::string GetTrailingComments(const grpc::string prefix) const {
+    return GetCommentsHelper(method_, false, prefix);
+  }
+
+  vector<grpc::string> GetAllComments() const {
+    return grpc_python_generator::get_all_comments(method_);
+  }
+
+ private:
+  const grpc::protobuf::MethodDescriptor* method_;
+};
+
+class ProtoBufService : public grpc_generator::Service {
+ public:
+  ProtoBufService(const grpc::protobuf::ServiceDescriptor* service)
+      : service_(service) {}
+
+  grpc::string name() const { return service_->name(); }
+
+  int method_count() const { return service_->method_count(); };
+  std::unique_ptr<const grpc_generator::Method> method(int i) const {
+    return std::unique_ptr<const grpc_generator::Method>(
+        new ProtoBufMethod(service_->method(i)));
+  };
+
+  grpc::string GetLeadingComments(const grpc::string prefix) const {
+    return GetCommentsHelper(service_, true, prefix);
+  }
+
+  grpc::string GetTrailingComments(const grpc::string prefix) const {
+    return GetCommentsHelper(service_, false, prefix);
+  }
+
+  vector<grpc::string> GetAllComments() const {
+    return grpc_python_generator::get_all_comments(service_);
+  }
+
+ private:
+  const grpc::protobuf::ServiceDescriptor* service_;
+};
+
+class ProtoBufPrinter : public grpc_generator::Printer {
+ public:
+  ProtoBufPrinter(grpc::string* str)
+      : output_stream_(str), printer_(&output_stream_, '$') {}
+
+  void Print(const std::map<grpc::string, grpc::string>& vars,
+             const char* string_template) {
+    printer_.Print(vars, string_template);
+  }
+
+  void Print(const char* string) { printer_.Print(string); }
+  void PrintRaw(const char* string) { printer_.PrintRaw(string); }
+  void Indent() { printer_.Indent(); }
+  void Outdent() { printer_.Outdent(); }
+
+ private:
+  grpc::protobuf::io::StringOutputStream output_stream_;
+  grpc::protobuf::io::Printer printer_;
+};
+
+class ProtoBufFile : public grpc_generator::File {
+ public:
+  ProtoBufFile(const grpc::protobuf::FileDescriptor* file) : file_(file) {}
+
+  grpc::string filename() const { return file_->name(); }
+  grpc::string filename_without_ext() const {
+    return grpc_generator::StripProto(filename());
+  }
+
+  grpc::string package() const { return file_->package(); }
+  std::vector<grpc::string> package_parts() const {
+    return grpc_generator::tokenize(package(), ".");
+  }
+
+  grpc::string additional_headers() const { return ""; }
+
+  int service_count() const { return file_->service_count(); };
+  std::unique_ptr<const grpc_generator::Service> service(int i) const {
+    return std::unique_ptr<const grpc_generator::Service>(
+        new ProtoBufService(file_->service(i)));
+  }
+
+  std::unique_ptr<grpc_generator::Printer> CreatePrinter(
+      grpc::string* str) const {
+    return std::unique_ptr<grpc_generator::Printer>(new ProtoBufPrinter(str));
+  }
+
+  grpc::string GetLeadingComments(const grpc::string prefix) const {
+    return GetCommentsHelper(file_, true, prefix);
+  }
+
+  grpc::string GetTrailingComments(const grpc::string prefix) const {
+    return GetCommentsHelper(file_, false, prefix);
+  }
+
+  vector<grpc::string> GetAllComments() const {
+    return grpc_python_generator::get_all_comments(file_);
+  }
+
+ private:
+  const grpc::protobuf::FileDescriptor* file_;
+};
+
+#endif  // GRPC_INTERNAL_COMPILER_PROTOBUF_PLUGIN_H
diff --git a/third_party/grpc/src/compiler/python_generator.cc b/third_party/grpc/src/compiler/python_generator.cc
new file mode 100644
index 0000000..8a0b889
--- /dev/null
+++ b/third_party/grpc/src/compiler/python_generator.cc
@@ -0,0 +1,781 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <tuple>
+#include <vector>
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+#include "src/compiler/protobuf_plugin.h"
+#include "src/compiler/python_generator.h"
+#include "src/compiler/python_generator_helpers.h"
+#include "src/compiler/python_private_generator.h"
+
+using grpc::protobuf::FileDescriptor;
+using grpc::protobuf::compiler::GeneratorContext;
+using grpc::protobuf::io::CodedOutputStream;
+using grpc::protobuf::io::ZeroCopyOutputStream;
+using std::make_pair;
+using std::map;
+using std::pair;
+using std::replace;
+using std::set;
+using std::tuple;
+using std::vector;
+
+namespace grpc_python_generator {
+
+grpc::string generator_file_name;
+
+namespace {
+
+typedef map<grpc::string, grpc::string> StringMap;
+typedef vector<grpc::string> StringVector;
+typedef tuple<grpc::string, grpc::string> StringPair;
+typedef set<StringPair> StringPairSet;
+
+// Provides RAII indentation handling. Use as:
+// {
+//   IndentScope raii_my_indent_var_name_here(my_py_printer);
+//   // constructor indented my_py_printer
+//   ...
+//   // destructor called at end of scope, un-indenting my_py_printer
+// }
+class IndentScope {
+ public:
+  explicit IndentScope(grpc_generator::Printer* printer) : printer_(printer) {
+    printer_->Indent();
+  }
+
+  ~IndentScope() { printer_->Outdent(); }
+
+ private:
+  grpc_generator::Printer* printer_;
+};
+
+PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config,
+                                   const grpc_generator::File* file)
+    : config(config), file(file) {}
+
+void PrivateGenerator::PrintAllComments(StringVector comments,
+                                        grpc_generator::Printer* out) {
+  if (comments.empty()) {
+    // Python requires code structures like class and def to have
+    // a body, even if it is just "pass" or a docstring.  We need
+    // to ensure not to generate empty bodies. We could do something
+    // smarter and more sophisticated, but at the moment, if there is
+    // no docstring to print, we simply emit "pass" to ensure validity
+    // of the generated code.
+    out->Print("# missing associated documentation comment in .proto file\n");
+    out->Print("pass\n");
+    return;
+  }
+  out->Print("\"\"\"");
+  for (StringVector::iterator it = comments.begin(); it != comments.end();
+       ++it) {
+    size_t start_pos = it->find_first_not_of(' ');
+    if (start_pos != grpc::string::npos) {
+      out->PrintRaw(it->c_str() + start_pos);
+    }
+    out->Print("\n");
+  }
+  out->Print("\"\"\"\n");
+}
+
+bool PrivateGenerator::PrintBetaServicer(const grpc_generator::Service* service,
+                                         grpc_generator::Printer* out) {
+  StringMap service_dict;
+  service_dict["Service"] = service->name();
+  out->Print("\n\n");
+  out->Print(service_dict, "class Beta$Service$Servicer(object):\n");
+  {
+    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");
+    StringVector service_comments = service->GetAllComments();
+    PrintAllComments(service_comments, out);
+    for (int i = 0; i < service->method_count(); ++i) {
+      auto method = service->method(i);
+      grpc::string arg_name =
+          method->ClientStreaming() ? "request_iterator" : "request";
+      StringMap method_dict;
+      method_dict["Method"] = method->name();
+      method_dict["ArgName"] = arg_name;
+      out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n");
+      {
+        IndentScope raii_method_indent(out);
+        StringVector method_comments = method->GetAllComments();
+        PrintAllComments(method_comments, out);
+        out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\n");
+      }
+    }
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintBetaStub(const grpc_generator::Service* service,
+                                     grpc_generator::Printer* out) {
+  StringMap service_dict;
+  service_dict["Service"] = service->name();
+  out->Print("\n\n");
+  out->Print(service_dict, "class Beta$Service$Stub(object):\n");
+  {
+    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");
+    StringVector service_comments = service->GetAllComments();
+    PrintAllComments(service_comments, out);
+    for (int i = 0; i < service->method_count(); ++i) {
+      auto method = service->method(i);
+      grpc::string arg_name =
+          method->ClientStreaming() ? "request_iterator" : "request";
+      StringMap method_dict;
+      method_dict["Method"] = method->name();
+      method_dict["ArgName"] = arg_name;
+      out->Print(method_dict,
+                 "def $Method$(self, $ArgName$, timeout, metadata=None, "
+                 "with_call=False, protocol_options=None):\n");
+      {
+        IndentScope raii_method_indent(out);
+        StringVector method_comments = method->GetAllComments();
+        PrintAllComments(method_comments, out);
+        out->Print("raise NotImplementedError()\n");
+      }
+      if (!method->ServerStreaming()) {
+        out->Print(method_dict, "$Method$.future = None\n");
+      }
+    }
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintBetaServerFactory(
+    const grpc::string& package_qualified_service_name,
+    const grpc_generator::Service* service, grpc_generator::Printer* out) {
+  StringMap service_dict;
+  service_dict["Service"] = service->name();
+  out->Print("\n\n");
+  out->Print(service_dict,
+             "def beta_create_$Service$_server(servicer, pool=None, "
+             "pool_size=None, default_timeout=None, maximum_timeout=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");
+    StringMap method_implementation_constructors;
+    StringMap input_message_modules_and_classes;
+    StringMap output_message_modules_and_classes;
+    for (int i = 0; i < service->method_count(); ++i) {
+      auto method = service->method(i);
+      const grpc::string method_implementation_constructor =
+          grpc::string(method->ClientStreaming() ? "stream_" : "unary_") +
+          grpc::string(method->ServerStreaming() ? "stream_" : "unary_") +
+          "inline";
+      grpc::string input_message_module_and_class;
+      if (!method->get_module_and_message_path_input(
+              &input_message_module_and_class, generator_file_name,
+              generate_in_pb2_grpc, config.import_prefix)) {
+        return false;
+      }
+      grpc::string output_message_module_and_class;
+      if (!method->get_module_and_message_path_output(
+              &output_message_module_and_class, generator_file_name,
+              generate_in_pb2_grpc, config.import_prefix)) {
+        return false;
+      }
+      method_implementation_constructors.insert(
+          make_pair(method->name(), method_implementation_constructor));
+      input_message_modules_and_classes.insert(
+          make_pair(method->name(), input_message_module_and_class));
+      output_message_modules_and_classes.insert(
+          make_pair(method->name(), output_message_module_and_class));
+    }
+    StringMap method_dict;
+    method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
+    out->Print("request_deserializers = {\n");
+    for (StringMap::iterator name_and_input_module_class_pair =
+             input_message_modules_and_classes.begin();
+         name_and_input_module_class_pair !=
+         input_message_modules_and_classes.end();
+         name_and_input_module_class_pair++) {
+      method_dict["MethodName"] = name_and_input_module_class_pair->first;
+      method_dict["InputTypeModuleAndClass"] =
+          name_and_input_module_class_pair->second;
+      IndentScope raii_indent(out);
+      out->Print(method_dict,
+                 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
+                 "$InputTypeModuleAndClass$.FromString,\n");
+    }
+    out->Print("}\n");
+    out->Print("response_serializers = {\n");
+    for (StringMap::iterator name_and_output_module_class_pair =
+             output_message_modules_and_classes.begin();
+         name_and_output_module_class_pair !=
+         output_message_modules_and_classes.end();
+         name_and_output_module_class_pair++) {
+      method_dict["MethodName"] = name_and_output_module_class_pair->first;
+      method_dict["OutputTypeModuleAndClass"] =
+          name_and_output_module_class_pair->second;
+      IndentScope raii_indent(out);
+      out->Print(method_dict,
+                 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
+                 "$OutputTypeModuleAndClass$.SerializeToString,\n");
+    }
+    out->Print("}\n");
+    out->Print("method_implementations = {\n");
+    for (StringMap::iterator name_and_implementation_constructor =
+             method_implementation_constructors.begin();
+         name_and_implementation_constructor !=
+         method_implementation_constructors.end();
+         name_and_implementation_constructor++) {
+      method_dict["Method"] = name_and_implementation_constructor->first;
+      method_dict["Constructor"] = name_and_implementation_constructor->second;
+      IndentScope raii_descriptions_indent(out);
+      const grpc::string method_name =
+          name_and_implementation_constructor->first;
+      out->Print(method_dict,
+                 "(\'$PackageQualifiedServiceName$\', \'$Method$\'): "
+                 "face_utilities.$Constructor$(servicer.$Method$),\n");
+    }
+    out->Print("}\n");
+    out->Print(
+        "server_options = beta_implementations.server_options("
+        "request_deserializers=request_deserializers, "
+        "response_serializers=response_serializers, "
+        "thread_pool=pool, thread_pool_size=pool_size, "
+        "default_timeout=default_timeout, "
+        "maximum_timeout=maximum_timeout)\n");
+    out->Print(
+        "return beta_implementations.server(method_implementations, "
+        "options=server_options)\n");
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintBetaStubFactory(
+    const grpc::string& package_qualified_service_name,
+    const grpc_generator::Service* service, grpc_generator::Printer* out) {
+  StringMap dict;
+  dict["Service"] = service->name();
+  out->Print("\n\n");
+  out->Print(dict,
+             "def beta_create_$Service$_stub(channel, host=None,"
+             " 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");
+    StringMap method_cardinalities;
+    StringMap input_message_modules_and_classes;
+    StringMap output_message_modules_and_classes;
+    for (int i = 0; i < service->method_count(); ++i) {
+      auto method = service->method(i);
+      const grpc::string method_cardinality =
+          grpc::string(method->ClientStreaming() ? "STREAM" : "UNARY") + "_" +
+          grpc::string(method->ServerStreaming() ? "STREAM" : "UNARY");
+      grpc::string input_message_module_and_class;
+      if (!method->get_module_and_message_path_input(
+              &input_message_module_and_class, generator_file_name,
+              generate_in_pb2_grpc, config.import_prefix)) {
+        return false;
+      }
+      grpc::string output_message_module_and_class;
+      if (!method->get_module_and_message_path_output(
+              &output_message_module_and_class, generator_file_name,
+              generate_in_pb2_grpc, config.import_prefix)) {
+        return false;
+      }
+      method_cardinalities.insert(
+          make_pair(method->name(), method_cardinality));
+      input_message_modules_and_classes.insert(
+          make_pair(method->name(), input_message_module_and_class));
+      output_message_modules_and_classes.insert(
+          make_pair(method->name(), output_message_module_and_class));
+    }
+    StringMap method_dict;
+    method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
+    out->Print("request_serializers = {\n");
+    for (StringMap::iterator name_and_input_module_class_pair =
+             input_message_modules_and_classes.begin();
+         name_and_input_module_class_pair !=
+         input_message_modules_and_classes.end();
+         name_and_input_module_class_pair++) {
+      method_dict["MethodName"] = name_and_input_module_class_pair->first;
+      method_dict["InputTypeModuleAndClass"] =
+          name_and_input_module_class_pair->second;
+      IndentScope raii_indent(out);
+      out->Print(method_dict,
+                 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
+                 "$InputTypeModuleAndClass$.SerializeToString,\n");
+    }
+    out->Print("}\n");
+    out->Print("response_deserializers = {\n");
+    for (StringMap::iterator name_and_output_module_class_pair =
+             output_message_modules_and_classes.begin();
+         name_and_output_module_class_pair !=
+         output_message_modules_and_classes.end();
+         name_and_output_module_class_pair++) {
+      method_dict["MethodName"] = name_and_output_module_class_pair->first;
+      method_dict["OutputTypeModuleAndClass"] =
+          name_and_output_module_class_pair->second;
+      IndentScope raii_indent(out);
+      out->Print(method_dict,
+                 "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): "
+                 "$OutputTypeModuleAndClass$.FromString,\n");
+    }
+    out->Print("}\n");
+    out->Print("cardinalities = {\n");
+    for (StringMap::iterator name_and_cardinality =
+             method_cardinalities.begin();
+         name_and_cardinality != method_cardinalities.end();
+         name_and_cardinality++) {
+      method_dict["Method"] = name_and_cardinality->first;
+      method_dict["Cardinality"] = name_and_cardinality->second;
+      IndentScope raii_descriptions_indent(out);
+      out->Print(method_dict,
+                 "\'$Method$\': cardinality.Cardinality.$Cardinality$,\n");
+    }
+    out->Print("}\n");
+    out->Print(
+        "stub_options = beta_implementations.stub_options("
+        "host=host, metadata_transformer=metadata_transformer, "
+        "request_serializers=request_serializers, "
+        "response_deserializers=response_deserializers, "
+        "thread_pool=pool, thread_pool_size=pool_size)\n");
+    out->Print(method_dict,
+               "return beta_implementations.dynamic_stub(channel, "
+               "\'$PackageQualifiedServiceName$\', "
+               "cardinalities, options=stub_options)\n");
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintStub(
+    const grpc::string& package_qualified_service_name,
+    const grpc_generator::Service* service, grpc_generator::Printer* out) {
+  StringMap dict;
+  dict["Service"] = service->name();
+  out->Print("\n\n");
+  out->Print(dict, "class $Service$Stub(object):\n");
+  {
+    IndentScope raii_class_indent(out);
+    StringVector service_comments = service->GetAllComments();
+    PrintAllComments(service_comments, out);
+    out->Print("\n");
+    out->Print("def __init__(self, channel):\n");
+    {
+      IndentScope raii_init_indent(out);
+      out->Print("\"\"\"Constructor.\n");
+      out->Print("\n");
+      out->Print("Args:\n");
+      {
+        IndentScope raii_args_indent(out);
+        out->Print("channel: A grpc.Channel.\n");
+      }
+      out->Print("\"\"\"\n");
+      for (int i = 0; i < service->method_count(); ++i) {
+        auto method = service->method(i);
+        grpc::string multi_callable_constructor =
+            grpc::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
+            grpc::string(method->ServerStreaming() ? "stream" : "unary");
+        grpc::string request_module_and_class;
+        if (!method->get_module_and_message_path_input(
+                &request_module_and_class, generator_file_name,
+                generate_in_pb2_grpc, config.import_prefix)) {
+          return false;
+        }
+        grpc::string response_module_and_class;
+        if (!method->get_module_and_message_path_output(
+                &response_module_and_class, generator_file_name,
+                generate_in_pb2_grpc, config.import_prefix)) {
+          return false;
+        }
+        StringMap method_dict;
+        method_dict["Method"] = method->name();
+        method_dict["MultiCallableConstructor"] = multi_callable_constructor;
+        out->Print(method_dict,
+                   "self.$Method$ = channel.$MultiCallableConstructor$(\n");
+        {
+          method_dict["PackageQualifiedService"] =
+              package_qualified_service_name;
+          method_dict["RequestModuleAndClass"] = request_module_and_class;
+          method_dict["ResponseModuleAndClass"] = response_module_and_class;
+          IndentScope raii_first_attribute_indent(out);
+          IndentScope raii_second_attribute_indent(out);
+          out->Print(method_dict, "'/$PackageQualifiedService$/$Method$',\n");
+          out->Print(method_dict,
+                     "request_serializer=$RequestModuleAndClass$."
+                     "SerializeToString,\n");
+          out->Print(
+              method_dict,
+              "response_deserializer=$ResponseModuleAndClass$.FromString,\n");
+          out->Print(")\n");
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintServicer(const grpc_generator::Service* service,
+                                     grpc_generator::Printer* out) {
+  StringMap service_dict;
+  service_dict["Service"] = service->name();
+  out->Print("\n\n");
+  out->Print(service_dict, "class $Service$Servicer(object):\n");
+  {
+    IndentScope raii_class_indent(out);
+    StringVector service_comments = service->GetAllComments();
+    PrintAllComments(service_comments, out);
+    for (int i = 0; i < service->method_count(); ++i) {
+      auto method = service->method(i);
+      grpc::string arg_name =
+          method->ClientStreaming() ? "request_iterator" : "request";
+      StringMap method_dict;
+      method_dict["Method"] = method->name();
+      method_dict["ArgName"] = arg_name;
+      out->Print("\n");
+      out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n");
+      {
+        IndentScope raii_method_indent(out);
+        StringVector method_comments = method->GetAllComments();
+        PrintAllComments(method_comments, out);
+        out->Print("context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n");
+        out->Print("context.set_details('Method not implemented!')\n");
+        out->Print("raise NotImplementedError('Method not implemented!')\n");
+      }
+    }
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintAddServicerToServer(
+    const grpc::string& package_qualified_service_name,
+    const grpc_generator::Service* service, grpc_generator::Printer* out) {
+  StringMap service_dict;
+  service_dict["Service"] = service->name();
+  out->Print("\n\n");
+  out->Print(service_dict,
+             "def add_$Service$Servicer_to_server(servicer, server):\n");
+  {
+    IndentScope raii_class_indent(out);
+    out->Print("rpc_method_handlers = {\n");
+    {
+      IndentScope raii_dict_first_indent(out);
+      IndentScope raii_dict_second_indent(out);
+      for (int i = 0; i < service->method_count(); ++i) {
+        auto method = service->method(i);
+        grpc::string method_handler_constructor =
+            grpc::string(method->ClientStreaming() ? "stream" : "unary") + "_" +
+            grpc::string(method->ServerStreaming() ? "stream" : "unary") +
+            "_rpc_method_handler";
+        grpc::string request_module_and_class;
+        if (!method->get_module_and_message_path_input(
+                &request_module_and_class, generator_file_name,
+                generate_in_pb2_grpc, config.import_prefix)) {
+          return false;
+        }
+        grpc::string response_module_and_class;
+        if (!method->get_module_and_message_path_output(
+                &response_module_and_class, generator_file_name,
+                generate_in_pb2_grpc, config.import_prefix)) {
+          return false;
+        }
+        StringMap method_dict;
+        method_dict["Method"] = method->name();
+        method_dict["MethodHandlerConstructor"] = method_handler_constructor;
+        method_dict["RequestModuleAndClass"] = request_module_and_class;
+        method_dict["ResponseModuleAndClass"] = response_module_and_class;
+        out->Print(method_dict,
+                   "'$Method$': grpc.$MethodHandlerConstructor$(\n");
+        {
+          IndentScope raii_call_first_indent(out);
+          IndentScope raii_call_second_indent(out);
+          out->Print(method_dict, "servicer.$Method$,\n");
+          out->Print(
+              method_dict,
+              "request_deserializer=$RequestModuleAndClass$.FromString,\n");
+          out->Print(
+              method_dict,
+              "response_serializer=$ResponseModuleAndClass$.SerializeToString,"
+              "\n");
+        }
+        out->Print("),\n");
+      }
+    }
+    StringMap method_dict;
+    method_dict["PackageQualifiedServiceName"] = package_qualified_service_name;
+    out->Print("}\n");
+    out->Print("generic_handler = grpc.method_handlers_generic_handler(\n");
+    {
+      IndentScope raii_call_first_indent(out);
+      IndentScope raii_call_second_indent(out);
+      out->Print(method_dict,
+                 "'$PackageQualifiedServiceName$', rpc_method_handlers)\n");
+    }
+    out->Print("server.add_generic_rpc_handlers((generic_handler,))\n");
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintBetaPreamble(grpc_generator::Printer* out) {
+  StringMap var;
+  var["Package"] = config.beta_package_root;
+  out->Print(var,
+             "from $Package$ import implementations as beta_implementations\n");
+  out->Print(var, "from $Package$ import interfaces as beta_interfaces\n");
+  out->Print("from grpc.framework.common import cardinality\n");
+  out->Print(
+      "from grpc.framework.interfaces.face import utilities as "
+      "face_utilities\n");
+  return true;
+}
+
+bool PrivateGenerator::PrintPreamble(grpc_generator::Printer* out) {
+  StringMap var;
+  var["Package"] = config.grpc_package_root;
+  out->Print(var, "import $Package$\n");
+  if (generate_in_pb2_grpc) {
+    out->Print("\n");
+    StringPairSet imports_set;
+    for (int i = 0; i < file->service_count(); ++i) {
+      auto service = file->service(i);
+      for (int j = 0; j < service->method_count(); ++j) {
+        auto method = service.get()->method(j);
+
+        grpc::string input_type_file_name = method->get_input_type_name();
+        grpc::string input_module_name =
+            ModuleName(input_type_file_name, config.import_prefix);
+        grpc::string input_module_alias =
+            ModuleAlias(input_type_file_name, config.import_prefix);
+        imports_set.insert(
+            std::make_tuple(input_module_name, input_module_alias));
+
+        grpc::string output_type_file_name = method->get_output_type_name();
+        grpc::string output_module_name =
+            ModuleName(output_type_file_name, config.import_prefix);
+        grpc::string output_module_alias =
+            ModuleAlias(output_type_file_name, config.import_prefix);
+        imports_set.insert(
+            std::make_tuple(output_module_name, output_module_alias));
+      }
+    }
+
+    for (StringPairSet::iterator it = imports_set.begin();
+         it != imports_set.end(); ++it) {
+      auto module_name = std::get<0>(*it);
+      var["ModuleAlias"] = std::get<1>(*it);
+      const size_t last_dot_pos = module_name.rfind('.');
+      if (last_dot_pos == grpc::string::npos) {
+        var["ImportStatement"] = "import " + module_name;
+      } else {
+        var["ImportStatement"] = "from " + module_name.substr(0, last_dot_pos) +
+                                 " import " +
+                                 module_name.substr(last_dot_pos + 1);
+      }
+      out->Print(var, "$ImportStatement$ as $ModuleAlias$\n");
+    }
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintGAServices(grpc_generator::Printer* out) {
+  grpc::string package = file->package();
+  if (!package.empty()) {
+    package = package.append(".");
+  }
+  for (int i = 0; i < file->service_count(); ++i) {
+    auto service = file->service(i);
+    grpc::string package_qualified_service_name = package + service->name();
+    if (!(PrintStub(package_qualified_service_name, service.get(), out) &&
+          PrintServicer(service.get(), out) &&
+          PrintAddServicerToServer(package_qualified_service_name,
+                                   service.get(), out))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool PrivateGenerator::PrintBetaServices(grpc_generator::Printer* out) {
+  grpc::string package = file->package();
+  if (!package.empty()) {
+    package = package.append(".");
+  }
+  for (int i = 0; i < file->service_count(); ++i) {
+    auto service = file->service(i);
+    grpc::string package_qualified_service_name = package + service->name();
+    if (!(PrintBetaServicer(service.get(), out) &&
+          PrintBetaStub(service.get(), out) &&
+          PrintBetaServerFactory(package_qualified_service_name, service.get(),
+                                 out) &&
+          PrintBetaStubFactory(package_qualified_service_name, service.get(),
+                               out))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+pair<bool, grpc::string> PrivateGenerator::GetGrpcServices() {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+    auto out = file->CreatePrinter(&output);
+    if (generate_in_pb2_grpc) {
+      out->Print(
+          "# Generated by the gRPC Python protocol compiler plugin. "
+          "DO NOT EDIT!\n");
+      if (!PrintPreamble(out.get())) {
+        return make_pair(false, "");
+      }
+      if (!PrintGAServices(out.get())) {
+        return make_pair(false, "");
+      }
+    } else {
+      out->Print("try:\n");
+      {
+        IndentScope raii_dict_try_indent(out.get());
+        out->Print(
+            "# THESE ELEMENTS WILL BE DEPRECATED.\n"
+            "# Please use the generated *_pb2_grpc.py files instead.\n");
+        if (!PrintPreamble(out.get())) {
+          return make_pair(false, "");
+        }
+        if (!PrintBetaPreamble(out.get())) {
+          return make_pair(false, "");
+        }
+        if (!PrintGAServices(out.get())) {
+          return make_pair(false, "");
+        }
+        if (!PrintBetaServices(out.get())) {
+          return make_pair(false, "");
+        }
+      }
+      out->Print("except ImportError:\n");
+      {
+        IndentScope raii_dict_except_indent(out.get());
+        out->Print("pass");
+      }
+    }
+  }
+  return make_pair(true, std::move(output));
+}
+
+}  // namespace
+
+GeneratorConfiguration::GeneratorConfiguration()
+    : grpc_package_root("grpc"),
+      beta_package_root("grpc.beta"),
+      import_prefix("") {}
+
+PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config)
+    : config_(config) {}
+
+PythonGrpcGenerator::~PythonGrpcGenerator() {}
+
+static bool GenerateGrpc(GeneratorContext* context, PrivateGenerator& generator,
+                         grpc::string file_name, bool generate_in_pb2_grpc) {
+  bool success;
+  std::unique_ptr<ZeroCopyOutputStream> output;
+  std::unique_ptr<CodedOutputStream> coded_output;
+  grpc::string grpc_code;
+
+  if (generate_in_pb2_grpc) {
+    output.reset(context->Open(file_name));
+    generator.generate_in_pb2_grpc = true;
+  } else {
+    output.reset(context->OpenForInsert(file_name, "module_scope"));
+    generator.generate_in_pb2_grpc = false;
+  }
+
+  coded_output.reset(new CodedOutputStream(output.get()));
+  tie(success, grpc_code) = generator.GetGrpcServices();
+
+  if (success) {
+    coded_output->WriteRaw(grpc_code.data(), grpc_code.size());
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
+                                   const grpc::string& parameter,
+                                   GeneratorContext* context,
+                                   grpc::string* error) const {
+  // Get output file name.
+  grpc::string pb2_file_name;
+  grpc::string pb2_grpc_file_name;
+  static const int proto_suffix_length = strlen(".proto");
+  if (file->name().size() > static_cast<size_t>(proto_suffix_length) &&
+      file->name().find_last_of(".proto") == file->name().size() - 1) {
+    grpc::string base =
+        file->name().substr(0, file->name().size() - proto_suffix_length);
+    std::replace(base.begin(), base.end(), '-', '_');
+    pb2_file_name = base + "_pb2.py";
+    pb2_grpc_file_name = base + "_pb2_grpc.py";
+  } else {
+    *error = "Invalid proto file name. Proto file must end with .proto";
+    return false;
+  }
+  generator_file_name = file->name();
+
+  ProtoBufFile pbfile(file);
+  PrivateGenerator generator(config_, &pbfile);
+  if (parameter == "" || parameter == "grpc_2_0") {
+    return GenerateGrpc(context, generator, pb2_grpc_file_name, true);
+  } else if (parameter == "grpc_1_0") {
+    return GenerateGrpc(context, generator, pb2_grpc_file_name, true) &&
+           GenerateGrpc(context, generator, pb2_file_name, false);
+  } else {
+    *error = "Invalid parameter '" + parameter + "'.";
+    return false;
+  }
+}
+
+}  // namespace grpc_python_generator
diff --git a/third_party/grpc/src/compiler/python_generator.h b/third_party/grpc/src/compiler/python_generator.h
new file mode 100644
index 0000000..9139307
--- /dev/null
+++ b/third_party/grpc/src/compiler/python_generator.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H
+#define GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H
+
+#include <utility>
+
+#include "src/compiler/config.h"
+#include "src/compiler/schema_interface.h"
+
+namespace grpc_python_generator {
+
+// Data pertaining to configuration of the generator with respect to anything
+// that may be used internally at Google.
+struct GeneratorConfiguration {
+  GeneratorConfiguration();
+  grpc::string grpc_package_root;
+  // TODO(https://github.com/grpc/grpc/issues/8622): Drop this.
+  grpc::string beta_package_root;
+  // TODO(https://github.com/google/protobuf/issues/888): Drop this.
+  grpc::string import_prefix;
+};
+
+class PythonGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
+ public:
+  PythonGrpcGenerator(const GeneratorConfiguration& config);
+  ~PythonGrpcGenerator();
+
+  bool Generate(const grpc::protobuf::FileDescriptor* file,
+                const grpc::string& parameter,
+                grpc::protobuf::compiler::GeneratorContext* context,
+                grpc::string* error) const;
+
+ private:
+  GeneratorConfiguration config_;
+};
+
+}  // namespace grpc_python_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_H
diff --git a/third_party/grpc/src/compiler/python_generator_helpers.h b/third_party/grpc/src/compiler/python_generator_helpers.h
new file mode 100644
index 0000000..b1b58be
--- /dev/null
+++ b/third_party/grpc/src/compiler/python_generator_helpers.h
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_HELPERS_H
+#define GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_HELPERS_H
+
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+#include "src/compiler/python_generator.h"
+#include "src/compiler/python_private_generator.h"
+
+using grpc::protobuf::Descriptor;
+using grpc::protobuf::FileDescriptor;
+using grpc::protobuf::MethodDescriptor;
+using grpc::protobuf::ServiceDescriptor;
+using grpc::protobuf::compiler::GeneratorContext;
+using grpc::protobuf::io::CodedOutputStream;
+using grpc::protobuf::io::Printer;
+using grpc::protobuf::io::StringOutputStream;
+using grpc::protobuf::io::ZeroCopyOutputStream;
+using grpc_generator::StringReplace;
+using grpc_generator::StripProto;
+using std::vector;
+
+namespace grpc_python_generator {
+
+namespace {
+
+typedef vector<const Descriptor*> DescriptorVector;
+typedef vector<grpc::string> StringVector;
+
+// TODO(https://github.com/google/protobuf/issues/888):
+// Export `ModuleName` from protobuf's
+// `src/google/protobuf/compiler/python/python_generator.cc` file.
+grpc::string ModuleName(const grpc::string& filename,
+                        const grpc::string& import_prefix) {
+  grpc::string basename = StripProto(filename);
+  basename = StringReplace(basename, "-", "_");
+  basename = StringReplace(basename, "/", ".");
+  return import_prefix + basename + "_pb2";
+}
+
+// TODO(https://github.com/google/protobuf/issues/888):
+// Export `ModuleAlias` from protobuf's
+// `src/google/protobuf/compiler/python/python_generator.cc` file.
+grpc::string ModuleAlias(const grpc::string& filename,
+                         const grpc::string& import_prefix) {
+  grpc::string module_name = ModuleName(filename, import_prefix);
+  // We can't have dots in the module name, so we replace each with _dot_.
+  // But that could lead to a collision between a.b and a_dot_b, so we also
+  // duplicate each underscore.
+  module_name = StringReplace(module_name, "_", "__");
+  module_name = StringReplace(module_name, ".", "_dot_");
+  return module_name;
+}
+
+bool GetModuleAndMessagePath(const Descriptor* type, grpc::string* out,
+                             grpc::string generator_file_name,
+                             bool generate_in_pb2_grpc,
+                             grpc::string& import_prefix) {
+  const Descriptor* path_elem_type = type;
+  DescriptorVector message_path;
+  do {
+    message_path.push_back(path_elem_type);
+    path_elem_type = path_elem_type->containing_type();
+  } while (path_elem_type);  // implicit nullptr comparison; don't be explicit
+  grpc::string file_name = type->file()->name();
+  static const int proto_suffix_length = strlen(".proto");
+  if (!(file_name.size() > static_cast<size_t>(proto_suffix_length) &&
+        file_name.find_last_of(".proto") == file_name.size() - 1)) {
+    return false;
+  }
+
+  grpc::string module;
+  if (generator_file_name != file_name || generate_in_pb2_grpc) {
+    module = ModuleAlias(file_name, import_prefix) + ".";
+  } else {
+    module = "";
+  }
+  grpc::string message_type;
+  for (DescriptorVector::reverse_iterator path_iter = message_path.rbegin();
+       path_iter != message_path.rend(); ++path_iter) {
+    message_type += (*path_iter)->name() + ".";
+  }
+  // no pop_back prior to C++11
+  message_type.resize(message_type.size() - 1);
+  *out = module + message_type;
+  return true;
+}
+
+template <typename DescriptorType>
+StringVector get_all_comments(const DescriptorType* descriptor) {
+  StringVector comments;
+  grpc_generator::GetComment(
+      descriptor, grpc_generator::COMMENTTYPE_LEADING_DETACHED, &comments);
+  grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_LEADING,
+                             &comments);
+  grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_TRAILING,
+                             &comments);
+  return comments;
+}
+
+}  // namespace
+
+}  // namespace grpc_python_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_HELPERS_H
diff --git a/third_party/grpc/src/compiler/python_plugin.cc b/third_party/grpc/src/compiler/python_plugin.cc
new file mode 100644
index 0000000..81eb1d4
--- /dev/null
+++ b/third_party/grpc/src/compiler/python_plugin.cc
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// Generates a Python gRPC service interface out of Protobuf IDL.
+
+#include "src/compiler/config.h"
+#include "src/compiler/protobuf_plugin.h"
+#include "src/compiler/python_generator.h"
+
+int main(int argc, char* argv[]) {
+  grpc_python_generator::GeneratorConfiguration config;
+  grpc_python_generator::PythonGrpcGenerator generator(config);
+  return grpc::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/third_party/grpc/src/compiler/python_private_generator.h b/third_party/grpc/src/compiler/python_private_generator.h
new file mode 100644
index 0000000..ee5cbbd
--- /dev/null
+++ b/third_party/grpc/src/compiler/python_private_generator.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H
+#define GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H
+
+#include <iostream>
+#include <vector>
+
+#include "src/compiler/python_generator.h"
+#include "src/compiler/schema_interface.h"
+
+namespace grpc_python_generator {
+
+namespace {
+
+// Tucks all generator state in an anonymous namespace away from
+// PythonGrpcGenerator and the header file, mostly to encourage future changes
+// to not require updates to the grpcio-tools C++ code part. Assumes that it is
+// only ever used from a single thread.
+struct PrivateGenerator {
+  const GeneratorConfiguration& config;
+  const grpc_generator::File* file;
+
+  bool generate_in_pb2_grpc;
+
+  PrivateGenerator(const GeneratorConfiguration& config,
+                   const grpc_generator::File* file);
+
+  std::pair<bool, grpc::string> GetGrpcServices();
+
+ private:
+  bool PrintPreamble(grpc_generator::Printer* out);
+  bool PrintBetaPreamble(grpc_generator::Printer* out);
+  bool PrintGAServices(grpc_generator::Printer* out);
+  bool PrintBetaServices(grpc_generator::Printer* out);
+
+  bool PrintAddServicerToServer(
+      const grpc::string& package_qualified_service_name,
+      const grpc_generator::Service* service, grpc_generator::Printer* out);
+  bool PrintServicer(const grpc_generator::Service* service,
+                     grpc_generator::Printer* out);
+  bool PrintStub(const grpc::string& package_qualified_service_name,
+                 const grpc_generator::Service* service,
+                 grpc_generator::Printer* out);
+
+  bool PrintBetaServicer(const grpc_generator::Service* service,
+                         grpc_generator::Printer* out);
+  bool PrintBetaServerFactory(
+      const grpc::string& package_qualified_service_name,
+      const grpc_generator::Service* service, grpc_generator::Printer* out);
+  bool PrintBetaStub(const grpc_generator::Service* service,
+                     grpc_generator::Printer* out);
+  bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name,
+                            const grpc_generator::Service* service,
+                            grpc_generator::Printer* out);
+
+  // Get all comments (leading, leading_detached, trailing) and print them as a
+  // docstring. Any leading space of a line will be removed, but the line
+  // wrapping will not be changed.
+  void PrintAllComments(std::vector<grpc::string> comments,
+                        grpc_generator::Printer* out);
+};
+
+}  // namespace
+
+}  // namespace grpc_python_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H
diff --git a/third_party/grpc/src/compiler/ruby_generator.cc b/third_party/grpc/src/compiler/ruby_generator.cc
new file mode 100644
index 0000000..e39d8be
--- /dev/null
+++ b/third_party/grpc/src/compiler/ruby_generator.cc
@@ -0,0 +1,224 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <cctype>
+#include <map>
+#include <vector>
+
+#include "src/compiler/config.h"
+#include "src/compiler/ruby_generator.h"
+#include "src/compiler/ruby_generator_helpers-inl.h"
+#include "src/compiler/ruby_generator_map-inl.h"
+#include "src/compiler/ruby_generator_string-inl.h"
+
+using grpc::protobuf::FileDescriptor;
+using grpc::protobuf::MethodDescriptor;
+using grpc::protobuf::ServiceDescriptor;
+using grpc::protobuf::io::Printer;
+using grpc::protobuf::io::StringOutputStream;
+using std::map;
+using std::vector;
+
+namespace grpc_ruby_generator {
+namespace {
+
+// Prints out the method using the ruby gRPC DSL.
+void PrintMethod(const MethodDescriptor* method, const grpc::string& package,
+                 Printer* out) {
+  grpc::string input_type =
+      RubyTypeOf(method->input_type()->full_name(), package);
+  if (method->client_streaming()) {
+    input_type = "stream(" + input_type + ")";
+  }
+  grpc::string output_type =
+      RubyTypeOf(method->output_type()->full_name(), package);
+  if (method->server_streaming()) {
+    output_type = "stream(" + output_type + ")";
+  }
+  std::map<grpc::string, grpc::string> method_vars = ListToDict({
+      "mth.name",
+      method->name(),
+      "input.type",
+      input_type,
+      "output.type",
+      output_type,
+  });
+  out->Print(GetRubyComments(method, true).c_str());
+  out->Print(method_vars, "rpc :$mth.name$, $input.type$, $output.type$\n");
+  out->Print(GetRubyComments(method, false).c_str());
+}
+
+// Prints out the service using the ruby gRPC DSL.
+void PrintService(const ServiceDescriptor* service, const grpc::string& package,
+                  Printer* out) {
+  if (service->method_count() == 0) {
+    return;
+  }
+
+  // Begin the service module
+  std::map<grpc::string, grpc::string> module_vars = ListToDict({
+      "module.name",
+      Modularize(service->name()),
+  });
+  out->Print(module_vars, "module $module.name$\n");
+  out->Indent();
+
+  out->Print(GetRubyComments(service, true).c_str());
+  out->Print("class Service\n");
+
+  // Write the indented class body.
+  out->Indent();
+  out->Print("\n");
+  out->Print("include GRPC::GenericService\n");
+  out->Print("\n");
+  out->Print("self.marshal_class_method = :encode\n");
+  out->Print("self.unmarshal_class_method = :decode\n");
+  std::map<grpc::string, grpc::string> pkg_vars =
+      ListToDict({"service_full_name", service->full_name()});
+  out->Print(pkg_vars, "self.service_name = '$service_full_name$'\n");
+  out->Print("\n");
+  for (int i = 0; i < service->method_count(); ++i) {
+    PrintMethod(service->method(i), package, out);
+  }
+  out->Outdent();
+
+  out->Print("end\n");
+  out->Print("\n");
+  out->Print("Stub = Service.rpc_stub_class\n");
+
+  // End the service module
+  out->Outdent();
+  out->Print("end\n");
+  out->Print(GetRubyComments(service, false).c_str());
+}
+
+}  // namespace
+
+// The following functions are copied directly from the source for the protoc
+// ruby generator
+// to ensure compatibility (with the exception of int and string type changes).
+// See
+// https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/ruby/ruby_generator.cc#L250
+// TODO: keep up to date with protoc code generation, though this behavior isn't
+// expected to change
+bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; }
+
+char ToUpper(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; }
+
+// Package names in protobuf are snake_case by convention, but Ruby module
+// names must be PascalCased.
+//
+//   foo_bar_baz -> FooBarBaz
+grpc::string PackageToModule(const grpc::string& name) {
+  bool next_upper = true;
+  grpc::string result;
+  result.reserve(name.size());
+
+  for (grpc::string::size_type i = 0; i < name.size(); i++) {
+    if (name[i] == '_') {
+      next_upper = true;
+    } else {
+      if (next_upper) {
+        result.push_back(ToUpper(name[i]));
+      } else {
+        result.push_back(name[i]);
+      }
+      next_upper = false;
+    }
+  }
+
+  return result;
+}
+// end copying of protoc generator for ruby code
+
+grpc::string GetServices(const FileDescriptor* file) {
+  grpc::string output;
+  {
+    // Scope the output stream so it closes and finalizes output to the string.
+
+    StringOutputStream output_stream(&output);
+    Printer out(&output_stream, '$');
+
+    // Don't write out any output if there no services, to avoid empty service
+    // files being generated for proto files that don't declare any.
+    if (file->service_count() == 0) {
+      return output;
+    }
+
+    std::string package_name;
+
+    if (file->options().has_ruby_package()) {
+      package_name = file->options().ruby_package();
+    } else {
+      package_name = file->package();
+    }
+
+    // Write out a file header.
+    std::map<grpc::string, grpc::string> header_comment_vars = ListToDict({
+        "file.name",
+        file->name(),
+        "file.package",
+        package_name,
+    });
+    out.Print("# Generated by the protocol buffer compiler.  DO NOT EDIT!\n");
+    out.Print(header_comment_vars,
+              "# Source: $file.name$ for package '$file.package$'\n");
+
+    grpc::string leading_comments = GetRubyComments(file, true);
+    if (!leading_comments.empty()) {
+      out.Print("# Original file comments:\n");
+      out.PrintRaw(leading_comments.c_str());
+    }
+
+    out.Print("\n");
+    out.Print("require 'grpc'\n");
+    // Write out require statemment to import the separately generated file
+    // that defines the messages used by the service. This is generated by the
+    // main ruby plugin.
+    std::map<grpc::string, grpc::string> dep_vars = ListToDict({
+        "dep.name",
+        MessagesRequireName(file),
+    });
+    out.Print(dep_vars, "require '$dep.name$'\n");
+
+    // Write out services within the modules
+    out.Print("\n");
+    std::vector<grpc::string> modules = Split(package_name, '.');
+    for (size_t i = 0; i < modules.size(); ++i) {
+      std::map<grpc::string, grpc::string> module_vars = ListToDict({
+          "module.name",
+          PackageToModule(modules[i]),
+      });
+      out.Print(module_vars, "module $module.name$\n");
+      out.Indent();
+    }
+    for (int i = 0; i < file->service_count(); ++i) {
+      auto service = file->service(i);
+      PrintService(service, file->package(), &out);
+    }
+    for (size_t i = 0; i < modules.size(); ++i) {
+      out.Outdent();
+      out.Print("end\n");
+    }
+
+    out.Print(GetRubyComments(file, false).c_str());
+  }
+  return output;
+}
+
+}  // namespace grpc_ruby_generator
diff --git a/third_party/grpc/src/compiler/ruby_generator.h b/third_party/grpc/src/compiler/ruby_generator.h
new file mode 100644
index 0000000..9a03e0d
--- /dev/null
+++ b/third_party/grpc/src/compiler/ruby_generator.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_H
+#define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_H
+
+#include "src/compiler/config.h"
+
+namespace grpc_ruby_generator {
+
+grpc::string GetServices(const grpc::protobuf::FileDescriptor* file);
+
+}  // namespace grpc_ruby_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_H
diff --git a/third_party/grpc/src/compiler/ruby_generator_helpers-inl.h b/third_party/grpc/src/compiler/ruby_generator_helpers-inl.h
new file mode 100644
index 0000000..2323770
--- /dev/null
+++ b/third_party/grpc/src/compiler/ruby_generator_helpers-inl.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H
+#define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H
+
+#include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
+#include "src/compiler/ruby_generator_string-inl.h"
+
+namespace grpc_ruby_generator {
+
+inline bool ServicesFilename(const grpc::protobuf::FileDescriptor* file,
+                             grpc::string* file_name_or_error) {
+  // Get output file name.
+  static const unsigned proto_suffix_length = 6;  // length of ".proto"
+  if (file->name().size() > proto_suffix_length &&
+      file->name().find_last_of(".proto") == file->name().size() - 1) {
+    *file_name_or_error =
+        file->name().substr(0, file->name().size() - proto_suffix_length) +
+        "_services_pb.rb";
+    return true;
+  } else {
+    *file_name_or_error = "Invalid proto file name:  must end with .proto";
+    return false;
+  }
+}
+
+inline grpc::string MessagesRequireName(
+    const grpc::protobuf::FileDescriptor* file) {
+  return Replace(file->name(), ".proto", "_pb");
+}
+
+// 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 GetRubyComments(const DescriptorType* desc, bool leading) {
+  return grpc_generator::GetPrefixedComments(desc, leading, "#");
+}
+
+}  // namespace grpc_ruby_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H
diff --git a/third_party/grpc/src/compiler/ruby_generator_map-inl.h b/third_party/grpc/src/compiler/ruby_generator_map-inl.h
new file mode 100644
index 0000000..89a74b1
--- /dev/null
+++ b/third_party/grpc/src/compiler/ruby_generator_map-inl.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_MAP_INL_H
+#define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_MAP_INL_H
+
+#include "src/compiler/config.h"
+
+#include <initializer_list>
+#include <iostream>
+#include <map>
+#include <ostream>  // NOLINT
+#include <vector>
+
+using std::initializer_list;
+using std::map;
+using std::vector;
+
+namespace grpc_ruby_generator {
+
+// Converts an initializer list of the form { key0, value0, key1, value1, ... }
+// into a map of key* to value*. Is merely a readability helper for later code.
+inline std::map<grpc::string, grpc::string> ListToDict(
+    const initializer_list<grpc::string>& values) {
+  if (values.size() % 2 != 0) {
+    std::cerr << "Not every 'key' has a value in `values`." << std::endl;
+  }
+  std::map<grpc::string, grpc::string> value_map;
+  auto value_iter = values.begin();
+  for (unsigned i = 0; i < values.size() / 2; ++i) {
+    grpc::string key = *value_iter;
+    ++value_iter;
+    grpc::string value = *value_iter;
+    value_map[key] = value;
+    ++value_iter;
+  }
+  return value_map;
+}
+
+}  // namespace grpc_ruby_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_MAP_INL_H
diff --git a/third_party/grpc/src/compiler/ruby_generator_string-inl.h b/third_party/grpc/src/compiler/ruby_generator_string-inl.h
new file mode 100644
index 0000000..ecfe796
--- /dev/null
+++ b/third_party/grpc/src/compiler/ruby_generator_string-inl.h
@@ -0,0 +1,130 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_STRING_INL_H
+#define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_STRING_INL_H
+
+#include "src/compiler/config.h"
+
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+using std::getline;
+using std::transform;
+
+namespace grpc_ruby_generator {
+
+// Split splits a string using char into elems.
+inline std::vector<grpc::string>& Split(const grpc::string& s, char delim,
+                                        std::vector<grpc::string>* elems) {
+  std::stringstream ss(s);
+  grpc::string item;
+  while (getline(ss, item, delim)) {
+    elems->push_back(item);
+  }
+  return *elems;
+}
+
+// Split splits a string using char, returning the result in a vector.
+inline std::vector<grpc::string> Split(const grpc::string& s, char delim) {
+  std::vector<grpc::string> elems;
+  Split(s, delim, &elems);
+  return elems;
+}
+
+// Replace replaces from with to in s.
+inline grpc::string Replace(grpc::string s, const grpc::string& from,
+                            const grpc::string& to) {
+  size_t start_pos = s.find(from);
+  if (start_pos == grpc::string::npos) {
+    return s;
+  }
+  s.replace(start_pos, from.length(), to);
+  return s;
+}
+
+// ReplaceAll replaces all instances of search with replace in s.
+inline grpc::string ReplaceAll(grpc::string s, const grpc::string& search,
+                               const grpc::string& replace) {
+  size_t pos = 0;
+  while ((pos = s.find(search, pos)) != grpc::string::npos) {
+    s.replace(pos, search.length(), replace);
+    pos += replace.length();
+  }
+  return s;
+}
+
+// ReplacePrefix replaces from with to in s if search is a prefix of s.
+inline bool ReplacePrefix(grpc::string* s, const grpc::string& from,
+                          const grpc::string& to) {
+  size_t start_pos = s->find(from);
+  if (start_pos == grpc::string::npos || start_pos != 0) {
+    return false;
+  }
+  s->replace(start_pos, from.length(), to);
+  return true;
+}
+
+// Modularize converts a string into a ruby module compatible name
+inline grpc::string Modularize(grpc::string s) {
+  if (s.empty()) {
+    return s;
+  }
+  grpc::string new_string = "";
+  bool was_last_underscore = false;
+  new_string.append(1, ::toupper(s[0]));
+  for (grpc::string::size_type i = 1; i < s.size(); ++i) {
+    if (was_last_underscore && s[i] != '_') {
+      new_string.append(1, ::toupper(s[i]));
+    } else if (s[i] != '_') {
+      new_string.append(1, s[i]);
+    }
+    was_last_underscore = s[i] == '_';
+  }
+  return new_string;
+}
+
+// RubyTypeOf updates a proto type to the required ruby equivalent.
+inline grpc::string RubyTypeOf(const grpc::string& a_type,
+                               const grpc::string& package) {
+  grpc::string res(a_type);
+  ReplacePrefix(&res, package, "");  // remove the leading package if present
+  ReplacePrefix(&res, ".", "");      // remove the leading . (no package)
+  if (res.find('.') == grpc::string::npos) {
+    return res;
+  } else {
+    std::vector<grpc::string> prefixes_and_type = Split(res, '.');
+    res.clear();
+    for (unsigned int i = 0; i < prefixes_and_type.size(); ++i) {
+      if (i != 0) {
+        res += "::";  // switch '.' to the ruby module delim
+      }
+      if (i < prefixes_and_type.size() - 1) {
+        res += Modularize(prefixes_and_type[i]);  // capitalize pkgs
+      } else {
+        res += prefixes_and_type[i];
+      }
+    }
+    return res;
+  }
+}
+
+}  // namespace grpc_ruby_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_STRING_INL_H
diff --git a/third_party/grpc/src/compiler/ruby_plugin.cc b/third_party/grpc/src/compiler/ruby_plugin.cc
new file mode 100644
index 0000000..9c234bd
--- /dev/null
+++ b/third_party/grpc/src/compiler/ruby_plugin.cc
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// Generates Ruby gRPC service interface out of Protobuf IDL.
+
+#include <memory>
+
+#include "src/compiler/config.h"
+#include "src/compiler/ruby_generator.h"
+#include "src/compiler/ruby_generator_helpers-inl.h"
+
+class RubyGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
+ public:
+  RubyGrpcGenerator() {}
+  ~RubyGrpcGenerator() {}
+
+  bool Generate(const grpc::protobuf::FileDescriptor* file,
+                const grpc::string& parameter,
+                grpc::protobuf::compiler::GeneratorContext* context,
+                grpc::string* error) const {
+    grpc::string code = grpc_ruby_generator::GetServices(file);
+    if (code.size() == 0) {
+      return true;  // don't generate a file if there are no services
+    }
+
+    // Get output file name.
+    grpc::string file_name;
+    if (!grpc_ruby_generator::ServicesFilename(file, &file_name)) {
+      return false;
+    }
+    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[]) {
+  RubyGrpcGenerator generator;
+  return grpc::protobuf::compiler::PluginMain(argc, argv, &generator);
+}
diff --git a/third_party/grpc/src/compiler/schema_interface.h b/third_party/grpc/src/compiler/schema_interface.h
new file mode 100644
index 0000000..c000478
--- /dev/null
+++ b/third_party/grpc/src/compiler/schema_interface.h
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H
+#define GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H
+
+#include "src/compiler/config.h"
+
+#include <memory>
+#include <vector>
+
+#ifndef GRPC_CUSTOM_STRING
+#include <string>
+#define GRPC_CUSTOM_STRING std::string
+#endif
+
+namespace grpc {
+
+typedef GRPC_CUSTOM_STRING string;
+
+}  // namespace grpc
+
+namespace grpc_generator {
+
+// A common interface for objects having comments in the source.
+// Return formatted comments to be inserted in generated code.
+struct CommentHolder {
+  virtual ~CommentHolder() {}
+  virtual grpc::string GetLeadingComments(const grpc::string prefix) const = 0;
+  virtual grpc::string GetTrailingComments(const grpc::string prefix) const = 0;
+  virtual std::vector<grpc::string> GetAllComments() const = 0;
+};
+
+// An abstract interface representing a method.
+struct Method : public CommentHolder {
+  virtual ~Method() {}
+
+  virtual grpc::string name() const = 0;
+
+  virtual grpc::string input_type_name() const = 0;
+  virtual grpc::string output_type_name() const = 0;
+
+  virtual bool get_module_and_message_path_input(
+      grpc::string* str, grpc::string generator_file_name,
+      bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0;
+  virtual bool get_module_and_message_path_output(
+      grpc::string* str, grpc::string generator_file_name,
+      bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0;
+
+  virtual grpc::string get_input_type_name() const = 0;
+  virtual grpc::string get_output_type_name() const = 0;
+  virtual bool NoStreaming() const = 0;
+  virtual bool ClientStreaming() const = 0;
+  virtual bool ServerStreaming() const = 0;
+  virtual bool BidiStreaming() const = 0;
+};
+
+// An abstract interface representing a service.
+struct Service : public CommentHolder {
+  virtual ~Service() {}
+
+  virtual grpc::string name() const = 0;
+
+  virtual int method_count() const = 0;
+  virtual std::unique_ptr<const Method> method(int i) const = 0;
+};
+
+struct Printer {
+  virtual ~Printer() {}
+
+  virtual void Print(const std::map<grpc::string, grpc::string>& vars,
+                     const char* template_string) = 0;
+  virtual void Print(const char* string) = 0;
+  virtual void PrintRaw(const char* string) = 0;
+  virtual void Indent() = 0;
+  virtual void Outdent() = 0;
+};
+
+// An interface that allows the source generated to be output using various
+// libraries/idls/serializers.
+struct File : public CommentHolder {
+  virtual ~File() {}
+
+  virtual grpc::string filename() const = 0;
+  virtual grpc::string filename_without_ext() const = 0;
+  virtual grpc::string package() const = 0;
+  virtual std::vector<grpc::string> package_parts() const = 0;
+  virtual grpc::string additional_headers() const = 0;
+
+  virtual int service_count() const = 0;
+  virtual std::unique_ptr<const Service> service(int i) const = 0;
+
+  virtual std::unique_ptr<Printer> CreatePrinter(grpc::string* str) const = 0;
+};
+}  // namespace grpc_generator
+
+#endif  // GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H
diff --git a/third_party/grpc/src/core/README.md b/third_party/grpc/src/core/README.md
new file mode 100644
index 0000000..130d265
--- /dev/null
+++ b/third_party/grpc/src/core/README.md
@@ -0,0 +1,4 @@
+# Overview
+
+This directory contains source code for C library (a.k.a the *gRPC C core*) that provides all gRPC's core functionality through a low level API. Libraries in other languages in this repository (C++, Ruby,
+Python, PHP, NodeJS, Objective-C) are layered on top of this library.
diff --git a/third_party/grpc/src/core/ext/README.md b/third_party/grpc/src/core/ext/README.md
new file mode 100644
index 0000000..0812b20
--- /dev/null
+++ b/third_party/grpc/src/core/ext/README.md
@@ -0,0 +1,5 @@
+Optional plugins for gRPC Core: Modules in this directory extend gRPC Core in
+useful ways. All optional code belongs here.
+
+NOTE: The movement of code between lib and ext is an ongoing effort, so this
+directory currently contains too much of the core library.
diff --git a/third_party/grpc/src/core/ext/filters/census/grpc_context.cc b/third_party/grpc/src/core/ext/filters/census/grpc_context.cc
new file mode 100644
index 0000000..599a798
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/census/grpc_context.cc
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/census.h>
+#include <grpc/grpc.h>
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+
+void grpc_census_call_set_context(grpc_call* call, census_context* context) {
+  GRPC_API_TRACE("grpc_census_call_set_context(call=%p, census_context=%p)", 2,
+                 (call, context));
+  if (context != nullptr) {
+    grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context, nullptr);
+  }
+}
+
+census_context* grpc_census_call_get_context(grpc_call* call) {
+  GRPC_API_TRACE("grpc_census_call_get_context(call=%p)", 1, (call));
+  return static_cast<census_context*>(
+      grpc_call_context_get(call, GRPC_CONTEXT_TRACING));
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/OWNERS b/third_party/grpc/src/core/ext/filters/client_channel/OWNERS
new file mode 100644
index 0000000..d38970e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/OWNERS
@@ -0,0 +1,4 @@
+set noparent
+@markdroth
+@apolcyn
+@AspirinSJL
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/README.md b/third_party/grpc/src/core/ext/filters/client_channel/README.md
new file mode 100644
index 0000000..9676a45
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/README.md
@@ -0,0 +1,49 @@
+Client Configuration Support for GRPC
+=====================================
+
+This library provides high level configuration machinery to construct client
+channels and load balance between them.
+
+Each grpc_channel is created with a grpc_resolver. It is the resolver's duty
+to resolve a name into a set of arguments for the channel. Such arguments
+might include:
+
+- a list of (ip, port) addresses to connect to
+- 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_channel_args objects to
+the channel. We represent arguments as a stream so that they can be changed
+by the resolver during execution, by reacting to external events (such as
+new service configuration data being pushed to some store).
+
+
+Load Balancing
+--------------
+
+Load balancing configuration is provided by a grpc_lb_policy object.
+
+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 object to the owning channel.
+
+
+Sub-Channels
+------------
+
+A sub-channel provides a connection to a server for a client channel. It has a
+connectivity state like a regular channel, and so can be connected or
+disconnected. This connectivity state can be used to inform load balancing
+decisions (for example, by avoiding disconnected backends).
+
+Configured sub-channels are fully setup to participate in the grpc data plane.
+Their behavior is specified by a set of grpc channel filters defined at their
+construction. To customize this behavior, resolvers build
+grpc_client_channel_factory objects, which use the decorator pattern to customize
+construction arguments for concrete grpc_subchannel instances.
+
+
+Naming for GRPC
+===============
+
+See [/doc/naming.md](gRPC name resolution).
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.cc b/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.cc
new file mode 100644
index 0000000..3e2faa5
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.cc
@@ -0,0 +1,174 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/backup_poller.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/completion_queue.h"
+
+#define DEFAULT_POLL_INTERVAL_MS 5000
+
+namespace {
+struct backup_poller {
+  grpc_timer polling_timer;
+  grpc_closure run_poller_closure;
+  grpc_closure shutdown_closure;
+  gpr_mu* pollset_mu;
+  grpc_pollset* pollset;  // guarded by pollset_mu
+  bool shutting_down;     // guarded by pollset_mu
+  gpr_refcount refs;
+  gpr_refcount shutdown_refs;
+};
+}  // namespace
+
+static gpr_once g_once = GPR_ONCE_INIT;
+static gpr_mu g_poller_mu;
+static backup_poller* g_poller = nullptr;  // guarded by g_poller_mu
+// g_poll_interval_ms is set only once at the first time
+// grpc_client_channel_start_backup_polling() is called, after that it is
+// treated as const.
+static int g_poll_interval_ms = DEFAULT_POLL_INTERVAL_MS;
+
+static void init_globals() {
+  gpr_mu_init(&g_poller_mu);
+  char* env = gpr_getenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS");
+  if (env != nullptr) {
+    int poll_interval_ms = gpr_parse_nonnegative_int(env);
+    if (poll_interval_ms == -1) {
+      gpr_log(GPR_ERROR,
+              "Invalid GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS: %s, "
+              "default value %d will be used.",
+              env, g_poll_interval_ms);
+    } else {
+      g_poll_interval_ms = poll_interval_ms;
+    }
+  }
+  gpr_free(env);
+}
+
+static void backup_poller_shutdown_unref(backup_poller* p) {
+  if (gpr_unref(&p->shutdown_refs)) {
+    grpc_pollset_destroy(p->pollset);
+    gpr_free(p->pollset);
+    gpr_free(p);
+  }
+}
+
+static void done_poller(void* arg, grpc_error* error) {
+  backup_poller_shutdown_unref(static_cast<backup_poller*>(arg));
+}
+
+static void g_poller_unref() {
+  gpr_mu_lock(&g_poller_mu);
+  if (gpr_unref(&g_poller->refs)) {
+    backup_poller* p = g_poller;
+    g_poller = nullptr;
+    gpr_mu_unlock(&g_poller_mu);
+    gpr_mu_lock(p->pollset_mu);
+    p->shutting_down = true;
+    grpc_pollset_shutdown(
+        p->pollset, GRPC_CLOSURE_INIT(&p->shutdown_closure, done_poller, p,
+                                      grpc_schedule_on_exec_ctx));
+    gpr_mu_unlock(p->pollset_mu);
+    grpc_timer_cancel(&p->polling_timer);
+  } else {
+    gpr_mu_unlock(&g_poller_mu);
+  }
+}
+
+static void run_poller(void* arg, grpc_error* error) {
+  backup_poller* p = static_cast<backup_poller*>(arg);
+  if (error != GRPC_ERROR_NONE) {
+    if (error != GRPC_ERROR_CANCELLED) {
+      GRPC_LOG_IF_ERROR("run_poller", GRPC_ERROR_REF(error));
+    }
+    backup_poller_shutdown_unref(p);
+    return;
+  }
+  gpr_mu_lock(p->pollset_mu);
+  if (p->shutting_down) {
+    gpr_mu_unlock(p->pollset_mu);
+    backup_poller_shutdown_unref(p);
+    return;
+  }
+  grpc_error* err =
+      grpc_pollset_work(p->pollset, nullptr, grpc_core::ExecCtx::Get()->Now());
+  gpr_mu_unlock(p->pollset_mu);
+  GRPC_LOG_IF_ERROR("Run client channel backup poller", err);
+  grpc_timer_init(&p->polling_timer,
+                  grpc_core::ExecCtx::Get()->Now() + g_poll_interval_ms,
+                  &p->run_poller_closure);
+}
+
+static void g_poller_init_locked() {
+  if (g_poller == nullptr) {
+    g_poller = static_cast<backup_poller*>(gpr_zalloc(sizeof(backup_poller)));
+    g_poller->pollset =
+        static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
+    g_poller->shutting_down = false;
+    grpc_pollset_init(g_poller->pollset, &g_poller->pollset_mu);
+    gpr_ref_init(&g_poller->refs, 0);
+    // one for timer cancellation, one for pollset shutdown
+    gpr_ref_init(&g_poller->shutdown_refs, 2);
+    GRPC_CLOSURE_INIT(&g_poller->run_poller_closure, run_poller, g_poller,
+                      grpc_schedule_on_exec_ctx);
+    grpc_timer_init(&g_poller->polling_timer,
+                    grpc_core::ExecCtx::Get()->Now() + g_poll_interval_ms,
+                    &g_poller->run_poller_closure);
+  }
+}
+
+void grpc_client_channel_start_backup_polling(
+    grpc_pollset_set* interested_parties) {
+  gpr_once_init(&g_once, init_globals);
+  if (g_poll_interval_ms == 0) {
+    return;
+  }
+  gpr_mu_lock(&g_poller_mu);
+  g_poller_init_locked();
+  gpr_ref(&g_poller->refs);
+  /* Get a reference to g_poller->pollset before releasing g_poller_mu to make
+   * TSAN happy. Otherwise, reading from g_poller (i.e g_poller->pollset) after
+   * releasing the lock and setting g_poller to NULL in g_poller_unref() is
+   * being flagged as a data-race by TSAN */
+  grpc_pollset* pollset = g_poller->pollset;
+  gpr_mu_unlock(&g_poller_mu);
+
+  grpc_pollset_set_add_pollset(interested_parties, pollset);
+}
+
+void grpc_client_channel_stop_backup_polling(
+    grpc_pollset_set* interested_parties) {
+  if (g_poll_interval_ms == 0) {
+    return;
+  }
+  grpc_pollset_set_del_pollset(interested_parties, g_poller->pollset);
+  g_poller_unref();
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.h b/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.h
new file mode 100644
index 0000000..8f132f9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include "src/core/lib/channel/channel_stack.h"
+
+/* Start polling \a interested_parties periodically in the timer thread  */
+void grpc_client_channel_start_backup_polling(
+    grpc_pollset_set* interested_parties);
+
+/* Stop polling \a interested_parties */
+void grpc_client_channel_stop_backup_polling(
+    grpc_pollset_set* interested_parties);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/channel_connectivity.cc b/third_party/grpc/src/core/ext/filters/client_channel/channel_connectivity.cc
new file mode 100644
index 0000000..c71d102
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/channel_connectivity.cc
@@ -0,0 +1,248 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/channel.h"
+
+#include <inttypes.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/completion_queue.h"
+
+grpc_connectivity_state grpc_channel_check_connectivity_state(
+    grpc_channel* channel, int try_to_connect) {
+  /* forward through to the underlying client channel */
+  grpc_channel_element* client_channel_elem =
+      grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
+  grpc_core::ExecCtx exec_ctx;
+  grpc_connectivity_state state;
+  GRPC_API_TRACE(
+      "grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2,
+      (channel, try_to_connect));
+  if (GPR_LIKELY(client_channel_elem->filter == &grpc_client_channel_filter)) {
+    state = grpc_client_channel_check_connectivity_state(client_channel_elem,
+                                                         try_to_connect);
+
+    return state;
+  }
+  gpr_log(GPR_ERROR,
+          "grpc_channel_check_connectivity_state called on something that is "
+          "not a client channel, but '%s'",
+          client_channel_elem->filter->name);
+
+  return GRPC_CHANNEL_SHUTDOWN;
+}
+
+typedef enum {
+  WAITING,
+  READY_TO_CALL_BACK,
+  CALLING_BACK_AND_FINISHED,
+} callback_phase;
+
+namespace {
+struct state_watcher {
+  gpr_mu mu;
+  callback_phase phase;
+  grpc_closure on_complete;
+  grpc_closure on_timeout;
+  grpc_closure watcher_timer_init;
+  grpc_timer alarm;
+  grpc_connectivity_state state;
+  grpc_completion_queue* cq;
+  grpc_cq_completion completion_storage;
+  grpc_channel* channel;
+  grpc_error* error;
+  void* tag;
+};
+}  // namespace
+
+static void delete_state_watcher(state_watcher* w) {
+  grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
+      grpc_channel_get_channel_stack(w->channel));
+  if (client_channel_elem->filter == &grpc_client_channel_filter) {
+    GRPC_CHANNEL_INTERNAL_UNREF(w->channel, "watch_channel_connectivity");
+  } else {
+    abort();
+  }
+  gpr_mu_destroy(&w->mu);
+  gpr_free(w);
+}
+
+static void finished_completion(void* pw, grpc_cq_completion* ignored) {
+  bool should_delete = false;
+  state_watcher* w = static_cast<state_watcher*>(pw);
+  gpr_mu_lock(&w->mu);
+  switch (w->phase) {
+    case WAITING:
+    case READY_TO_CALL_BACK:
+      GPR_UNREACHABLE_CODE(return );
+    case CALLING_BACK_AND_FINISHED:
+      should_delete = true;
+      break;
+  }
+  gpr_mu_unlock(&w->mu);
+
+  if (should_delete) {
+    delete_state_watcher(w);
+  }
+}
+
+static void partly_done(state_watcher* w, bool due_to_completion,
+                        grpc_error* error) {
+  if (due_to_completion) {
+    grpc_timer_cancel(&w->alarm);
+  } else {
+    grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
+        grpc_channel_get_channel_stack(w->channel));
+    grpc_client_channel_watch_connectivity_state(
+        client_channel_elem,
+        grpc_polling_entity_create_from_pollset(grpc_cq_pollset(w->cq)),
+        nullptr, &w->on_complete, nullptr);
+  }
+
+  gpr_mu_lock(&w->mu);
+
+  if (due_to_completion) {
+    if (grpc_trace_operation_failures.enabled()) {
+      GRPC_LOG_IF_ERROR("watch_completion_error", GRPC_ERROR_REF(error));
+    }
+    GRPC_ERROR_UNREF(error);
+    error = GRPC_ERROR_NONE;
+  } else {
+    if (error == GRPC_ERROR_NONE) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "Timed out waiting for connection state change");
+    } else if (error == GRPC_ERROR_CANCELLED) {
+      error = GRPC_ERROR_NONE;
+    }
+  }
+  switch (w->phase) {
+    case WAITING:
+      GRPC_ERROR_REF(error);
+      w->error = error;
+      w->phase = READY_TO_CALL_BACK;
+      break;
+    case READY_TO_CALL_BACK:
+      if (error != GRPC_ERROR_NONE) {
+        GPR_ASSERT(!due_to_completion);
+        GRPC_ERROR_UNREF(w->error);
+        GRPC_ERROR_REF(error);
+        w->error = error;
+      }
+      w->phase = CALLING_BACK_AND_FINISHED;
+      grpc_cq_end_op(w->cq, w->tag, w->error, finished_completion, w,
+                     &w->completion_storage);
+      break;
+    case CALLING_BACK_AND_FINISHED:
+      GPR_UNREACHABLE_CODE(return );
+      break;
+  }
+  gpr_mu_unlock(&w->mu);
+
+  GRPC_ERROR_UNREF(error);
+}
+
+static void watch_complete(void* pw, grpc_error* error) {
+  partly_done(static_cast<state_watcher*>(pw), true, GRPC_ERROR_REF(error));
+}
+
+static void timeout_complete(void* pw, grpc_error* error) {
+  partly_done(static_cast<state_watcher*>(pw), false, GRPC_ERROR_REF(error));
+}
+
+int grpc_channel_num_external_connectivity_watchers(grpc_channel* channel) {
+  grpc_channel_element* client_channel_elem =
+      grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
+  return grpc_client_channel_num_external_connectivity_watchers(
+      client_channel_elem);
+}
+
+typedef struct watcher_timer_init_arg {
+  state_watcher* w;
+  gpr_timespec deadline;
+} watcher_timer_init_arg;
+
+static void watcher_timer_init(void* arg, grpc_error* error_ignored) {
+  watcher_timer_init_arg* wa = static_cast<watcher_timer_init_arg*>(arg);
+
+  grpc_timer_init(&wa->w->alarm, grpc_timespec_to_millis_round_up(wa->deadline),
+                  &wa->w->on_timeout);
+  gpr_free(wa);
+}
+
+int grpc_channel_support_connectivity_watcher(grpc_channel* channel) {
+  grpc_channel_element* client_channel_elem =
+      grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
+  return client_channel_elem->filter != &grpc_client_channel_filter ? 0 : 1;
+}
+
+void grpc_channel_watch_connectivity_state(
+    grpc_channel* channel, grpc_connectivity_state last_observed_state,
+    gpr_timespec deadline, grpc_completion_queue* cq, void* tag) {
+  grpc_channel_element* client_channel_elem =
+      grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
+  grpc_core::ExecCtx exec_ctx;
+  state_watcher* w = static_cast<state_watcher*>(gpr_malloc(sizeof(*w)));
+
+  GRPC_API_TRACE(
+      "grpc_channel_watch_connectivity_state("
+      "channel=%p, last_observed_state=%d, "
+      "deadline=gpr_timespec { tv_sec: %" PRId64
+      ", tv_nsec: %d, clock_type: %d }, "
+      "cq=%p, tag=%p)",
+      7,
+      (channel, (int)last_observed_state, deadline.tv_sec, deadline.tv_nsec,
+       (int)deadline.clock_type, cq, tag));
+
+  GPR_ASSERT(grpc_cq_begin_op(cq, tag));
+
+  gpr_mu_init(&w->mu);
+  GRPC_CLOSURE_INIT(&w->on_complete, watch_complete, w,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&w->on_timeout, timeout_complete, w,
+                    grpc_schedule_on_exec_ctx);
+  w->phase = WAITING;
+  w->state = last_observed_state;
+  w->cq = cq;
+  w->tag = tag;
+  w->channel = channel;
+  w->error = nullptr;
+
+  watcher_timer_init_arg* wa = static_cast<watcher_timer_init_arg*>(
+      gpr_malloc(sizeof(watcher_timer_init_arg)));
+  wa->w = w;
+  wa->deadline = deadline;
+  GRPC_CLOSURE_INIT(&w->watcher_timer_init, watcher_timer_init, wa,
+                    grpc_schedule_on_exec_ctx);
+
+  if (client_channel_elem->filter == &grpc_client_channel_filter) {
+    GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity");
+    grpc_client_channel_watch_connectivity_state(
+        client_channel_elem,
+        grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)), &w->state,
+        &w->on_complete, &w->watcher_timer_init);
+  } else {
+    abort();
+  }
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel.cc b/third_party/grpc/src/core/ext/filters/client_channel/client_channel.cc
new file mode 100644
index 0000000..dd741f1
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel.cc
@@ -0,0 +1,2730 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.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/sync.h>
+
+#include "src/core/ext/filters/client_channel/backup_poller.h"
+#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
+#include "src/core/ext/filters/client_channel/request_routing.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
+#include "src/core/ext/filters/client_channel/retry_throttle.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/ext/filters/deadline/deadline_filter.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/iomgr/combiner.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/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/metadata_batch.h"
+#include "src/core/lib/transport/service_config.h"
+#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/status_metadata.h"
+
+using grpc_core::internal::ClientChannelMethodParams;
+using grpc_core::internal::ClientChannelMethodParamsTable;
+using grpc_core::internal::ProcessedResolverResult;
+using grpc_core::internal::ServerRetryThrottleData;
+
+/* Client channel implementation */
+
+// By default, we buffer 256 KiB per RPC for retries.
+// TODO(roth): Do we have any data to suggest a better value?
+#define DEFAULT_PER_RPC_RETRY_BUFFER_SIZE (256 << 10)
+
+// This value was picked arbitrarily.  It can be changed if there is
+// any even moderately compelling reason to do so.
+#define RETRY_BACKOFF_JITTER 0.2
+
+grpc_core::TraceFlag grpc_client_channel_trace(false, "client_channel");
+
+/*************************************************************************
+ * CHANNEL-WIDE FUNCTIONS
+ */
+
+struct external_connectivity_watcher;
+
+typedef struct client_channel_channel_data {
+  grpc_core::ManualConstructor<grpc_core::RequestRouter> request_router;
+
+  bool deadline_checking_enabled;
+  bool enable_retries;
+  size_t per_rpc_retry_buffer_size;
+
+  /** combiner protecting all variables below in this data structure */
+  grpc_combiner* combiner;
+  /** retry throttle data */
+  grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data;
+  /** maps method names to method_parameters structs */
+  grpc_core::RefCountedPtr<ClientChannelMethodParamsTable> method_params_table;
+  /** owning stack */
+  grpc_channel_stack* owning_stack;
+  /** interested parties (owned) */
+  grpc_pollset_set* interested_parties;
+
+  /* external_connectivity_watcher_list head is guarded by its own mutex, since
+   * counts need to be grabbed immediately without polling on a cq */
+  gpr_mu external_connectivity_watcher_list_mu;
+  struct external_connectivity_watcher* external_connectivity_watcher_list_head;
+
+  /* the following properties are guarded by a mutex since APIs require them
+     to be instantaneously available */
+  gpr_mu info_mu;
+  grpc_core::UniquePtr<char> info_lb_policy_name;
+  /** service config in JSON form */
+  grpc_core::UniquePtr<char> info_service_config_json;
+} channel_data;
+
+// Synchronous callback from chand->request_router to process a resolver
+// result update.
+static bool process_resolver_result_locked(void* arg,
+                                           const grpc_channel_args& args,
+                                           const char** lb_policy_name,
+                                           grpc_json** lb_policy_config) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  ProcessedResolverResult resolver_result(args, chand->enable_retries);
+  grpc_core::UniquePtr<char> service_config_json =
+      resolver_result.service_config_json();
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO, "chand=%p: resolver returned service config: \"%s\"",
+            chand, service_config_json.get());
+  }
+  // Update channel state.
+  chand->retry_throttle_data = resolver_result.retry_throttle_data();
+  chand->method_params_table = resolver_result.method_params_table();
+  // Swap out the data used by cc_get_channel_info().
+  gpr_mu_lock(&chand->info_mu);
+  chand->info_lb_policy_name = resolver_result.lb_policy_name();
+  const bool service_config_changed =
+      ((service_config_json == nullptr) !=
+       (chand->info_service_config_json == nullptr)) ||
+      (service_config_json != nullptr &&
+       strcmp(service_config_json.get(),
+              chand->info_service_config_json.get()) != 0);
+  chand->info_service_config_json = std::move(service_config_json);
+  gpr_mu_unlock(&chand->info_mu);
+  // Return results.
+  *lb_policy_name = chand->info_lb_policy_name.get();
+  *lb_policy_config = resolver_result.lb_policy_config();
+  return service_config_changed;
+}
+
+static void start_transport_op_locked(void* arg, grpc_error* error_ignored) {
+  grpc_transport_op* op = static_cast<grpc_transport_op*>(arg);
+  grpc_channel_element* elem =
+      static_cast<grpc_channel_element*>(op->handler_private.extra_arg);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+
+  if (op->on_connectivity_state_change != nullptr) {
+    chand->request_router->NotifyOnConnectivityStateChange(
+        op->connectivity_state, op->on_connectivity_state_change);
+    op->on_connectivity_state_change = nullptr;
+    op->connectivity_state = nullptr;
+  }
+
+  if (op->send_ping.on_initiate != nullptr || op->send_ping.on_ack != nullptr) {
+    if (chand->request_router->lb_policy() == nullptr) {
+      grpc_error* error =
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Ping with no load balancing");
+      GRPC_CLOSURE_SCHED(op->send_ping.on_initiate, GRPC_ERROR_REF(error));
+      GRPC_CLOSURE_SCHED(op->send_ping.on_ack, error);
+    } else {
+      grpc_error* error = GRPC_ERROR_NONE;
+      grpc_core::LoadBalancingPolicy::PickState pick_state;
+      // Pick must return synchronously, because pick_state.on_complete is null.
+      GPR_ASSERT(
+          chand->request_router->lb_policy()->PickLocked(&pick_state, &error));
+      if (pick_state.connected_subchannel != nullptr) {
+        pick_state.connected_subchannel->Ping(op->send_ping.on_initiate,
+                                              op->send_ping.on_ack);
+      } else {
+        if (error == GRPC_ERROR_NONE) {
+          error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "LB policy dropped call on ping");
+        }
+        GRPC_CLOSURE_SCHED(op->send_ping.on_initiate, GRPC_ERROR_REF(error));
+        GRPC_CLOSURE_SCHED(op->send_ping.on_ack, error);
+      }
+      op->bind_pollset = nullptr;
+    }
+    op->send_ping.on_initiate = nullptr;
+    op->send_ping.on_ack = nullptr;
+  }
+
+  if (op->disconnect_with_error != GRPC_ERROR_NONE) {
+    chand->request_router->ShutdownLocked(op->disconnect_with_error);
+  }
+
+  if (op->reset_connect_backoff) {
+    chand->request_router->ResetConnectionBackoffLocked();
+  }
+
+  GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "start_transport_op");
+  GRPC_CLOSURE_SCHED(op->on_consumed, GRPC_ERROR_NONE);
+}
+
+static void cc_start_transport_op(grpc_channel_element* elem,
+                                  grpc_transport_op* op) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+
+  GPR_ASSERT(op->set_accept_stream == false);
+  if (op->bind_pollset != nullptr) {
+    grpc_pollset_set_add_pollset(chand->interested_parties, op->bind_pollset);
+  }
+
+  op->handler_private.extra_arg = elem;
+  GRPC_CHANNEL_STACK_REF(chand->owning_stack, "start_transport_op");
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&op->handler_private.closure, start_transport_op_locked,
+                        op, grpc_combiner_scheduler(chand->combiner)),
+      GRPC_ERROR_NONE);
+}
+
+static void cc_get_channel_info(grpc_channel_element* elem,
+                                const grpc_channel_info* info) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  gpr_mu_lock(&chand->info_mu);
+  if (info->lb_policy_name != nullptr) {
+    *info->lb_policy_name = gpr_strdup(chand->info_lb_policy_name.get());
+  }
+  if (info->service_config_json != nullptr) {
+    *info->service_config_json =
+        gpr_strdup(chand->info_service_config_json.get());
+  }
+  gpr_mu_unlock(&chand->info_mu);
+}
+
+/* Constructor for channel_data */
+static grpc_error* cc_init_channel_elem(grpc_channel_element* elem,
+                                        grpc_channel_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  GPR_ASSERT(args->is_last);
+  GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
+  // Initialize data members.
+  chand->combiner = grpc_combiner_create();
+  gpr_mu_init(&chand->info_mu);
+  gpr_mu_init(&chand->external_connectivity_watcher_list_mu);
+
+  gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
+  chand->external_connectivity_watcher_list_head = nullptr;
+  gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
+
+  chand->owning_stack = args->channel_stack;
+  chand->deadline_checking_enabled =
+      grpc_deadline_checking_enabled(args->channel_args);
+  chand->interested_parties = grpc_pollset_set_create();
+  grpc_client_channel_start_backup_polling(chand->interested_parties);
+  // Record max per-RPC retry buffer size.
+  const grpc_arg* arg = grpc_channel_args_find(
+      args->channel_args, GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE);
+  chand->per_rpc_retry_buffer_size = (size_t)grpc_channel_arg_get_integer(
+      arg, {DEFAULT_PER_RPC_RETRY_BUFFER_SIZE, 0, INT_MAX});
+  // Record enable_retries.
+  arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_ENABLE_RETRIES);
+  chand->enable_retries = grpc_channel_arg_get_bool(arg, true);
+  // Record client channel factory.
+  arg = grpc_channel_args_find(args->channel_args,
+                               GRPC_ARG_CLIENT_CHANNEL_FACTORY);
+  if (arg == nullptr) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Missing client channel factory in args for client channel filter");
+  }
+  if (arg->type != GRPC_ARG_POINTER) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "client channel factory arg must be a pointer");
+  }
+  grpc_client_channel_factory* client_channel_factory =
+      static_cast<grpc_client_channel_factory*>(arg->value.pointer.p);
+  // Get server name to resolve, using proxy mapper if needed.
+  arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVER_URI);
+  if (arg == nullptr) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Missing server uri in args for client channel filter");
+  }
+  if (arg->type != GRPC_ARG_STRING) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "server uri arg must be a string");
+  }
+  char* proxy_name = nullptr;
+  grpc_channel_args* new_args = nullptr;
+  grpc_proxy_mappers_map_name(arg->value.string, args->channel_args,
+                              &proxy_name, &new_args);
+  // Instantiate request router.
+  grpc_client_channel_factory_ref(client_channel_factory);
+  grpc_error* error = GRPC_ERROR_NONE;
+  chand->request_router.Init(
+      chand->owning_stack, chand->combiner, client_channel_factory,
+      chand->interested_parties, &grpc_client_channel_trace,
+      process_resolver_result_locked, chand,
+      proxy_name != nullptr ? proxy_name : arg->value.string /* target_uri */,
+      new_args != nullptr ? new_args : args->channel_args, &error);
+  gpr_free(proxy_name);
+  grpc_channel_args_destroy(new_args);
+  return error;
+}
+
+/* Destructor for channel_data */
+static void cc_destroy_channel_elem(grpc_channel_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  chand->request_router.Destroy();
+  // TODO(roth): Once we convert the filter API to C++, there will no
+  // longer be any need to explicitly reset these smart pointer data members.
+  chand->info_lb_policy_name.reset();
+  chand->info_service_config_json.reset();
+  chand->retry_throttle_data.reset();
+  chand->method_params_table.reset();
+  grpc_client_channel_stop_backup_polling(chand->interested_parties);
+  grpc_pollset_set_destroy(chand->interested_parties);
+  GRPC_COMBINER_UNREF(chand->combiner, "client_channel");
+  gpr_mu_destroy(&chand->info_mu);
+  gpr_mu_destroy(&chand->external_connectivity_watcher_list_mu);
+}
+
+/*************************************************************************
+ * PER-CALL FUNCTIONS
+ */
+
+// Max number of batches that can be pending on a call at any given
+// time.  This includes one batch for each of the following ops:
+//   recv_initial_metadata
+//   send_initial_metadata
+//   recv_message
+//   send_message
+//   recv_trailing_metadata
+//   send_trailing_metadata
+#define MAX_PENDING_BATCHES 6
+
+// Retry support:
+//
+// In order to support retries, we act as a proxy for stream op batches.
+// When we get a batch from the surface, we add it to our list of pending
+// batches, and we then use those batches to construct separate "child"
+// batches to be started on the subchannel call.  When the child batches
+// return, we then decide which pending batches have been completed and
+// schedule their callbacks accordingly.  If a subchannel call fails and
+// we want to retry it, we do a new pick and start again, constructing
+// new "child" batches for the new subchannel call.
+//
+// Note that retries are committed when receiving data from the server
+// (except for Trailers-Only responses).  However, there may be many
+// send ops started before receiving any data, so we may have already
+// completed some number of send ops (and returned the completions up to
+// the surface) by the time we realize that we need to retry.  To deal
+// with this, we cache data for send ops, so that we can replay them on a
+// different subchannel call even after we have completed the original
+// batches.
+//
+// There are two sets of data to maintain:
+// - In call_data (in the parent channel), we maintain a list of pending
+//   ops and cached data for send ops.
+// - In the subchannel call, we maintain state to indicate what ops have
+//   already been sent down to that call.
+//
+// When constructing the "child" batches, we compare those two sets of
+// data to see which batches need to be sent to the subchannel call.
+
+// TODO(roth): In subsequent PRs:
+// - add support for transparent retries (including initial metadata)
+// - figure out how to record stats in census for retries
+//   (census filter is on top of this one)
+// - add census stats for retries
+
+namespace {
+
+struct call_data;
+
+// State used for starting a retryable batch on a subchannel call.
+// This provides its own grpc_transport_stream_op_batch and other data
+// structures needed to populate the ops in the batch.
+// We allocate one struct on the arena for each attempt at starting a
+// batch on a given subchannel call.
+struct subchannel_batch_data {
+  subchannel_batch_data(grpc_call_element* elem, call_data* calld, int refcount,
+                        bool set_on_complete);
+  // All dtor code must be added in `destroy`. This is because we may
+  // call closures in `subchannel_batch_data` after they are unrefed by
+  // `batch_data_unref`, and msan would complain about accessing this class
+  // after calling dtor. As a result we cannot call the `dtor` in
+  // `batch_data_unref`.
+  // TODO(soheil): We should try to call the dtor in `batch_data_unref`.
+  ~subchannel_batch_data() { destroy(); }
+  void destroy();
+
+  gpr_refcount refs;
+  grpc_call_element* elem;
+  grpc_subchannel_call* subchannel_call;  // Holds a ref.
+  // The batch to use in the subchannel call.
+  // Its payload field points to subchannel_call_retry_state.batch_payload.
+  grpc_transport_stream_op_batch batch;
+  // For intercepting on_complete.
+  grpc_closure on_complete;
+};
+
+// Retry state associated with a subchannel call.
+// Stored in the parent_data of the subchannel call object.
+struct subchannel_call_retry_state {
+  explicit subchannel_call_retry_state(grpc_call_context_element* context)
+      : batch_payload(context),
+        started_send_initial_metadata(false),
+        completed_send_initial_metadata(false),
+        started_send_trailing_metadata(false),
+        completed_send_trailing_metadata(false),
+        started_recv_initial_metadata(false),
+        completed_recv_initial_metadata(false),
+        started_recv_trailing_metadata(false),
+        completed_recv_trailing_metadata(false),
+        retry_dispatched(false) {}
+
+  // subchannel_batch_data.batch.payload points to this.
+  grpc_transport_stream_op_batch_payload batch_payload;
+  // For send_initial_metadata.
+  // Note that we need to make a copy of the initial metadata for each
+  // subchannel call instead of just referring to the copy in call_data,
+  // because filters in the subchannel stack will probably add entries,
+  // so we need to start in a pristine state for each attempt of the call.
+  grpc_linked_mdelem* send_initial_metadata_storage;
+  grpc_metadata_batch send_initial_metadata;
+  // For send_message.
+  grpc_core::ManualConstructor<grpc_core::ByteStreamCache::CachingByteStream>
+      send_message;
+  // For send_trailing_metadata.
+  grpc_linked_mdelem* send_trailing_metadata_storage;
+  grpc_metadata_batch send_trailing_metadata;
+  // For intercepting recv_initial_metadata.
+  grpc_metadata_batch recv_initial_metadata;
+  grpc_closure recv_initial_metadata_ready;
+  bool trailing_metadata_available = false;
+  // For intercepting recv_message.
+  grpc_closure recv_message_ready;
+  grpc_core::OrphanablePtr<grpc_core::ByteStream> recv_message;
+  // For intercepting recv_trailing_metadata.
+  grpc_metadata_batch recv_trailing_metadata;
+  grpc_transport_stream_stats collect_stats;
+  grpc_closure recv_trailing_metadata_ready;
+  // These fields indicate which ops have been started and completed on
+  // this subchannel call.
+  size_t started_send_message_count = 0;
+  size_t completed_send_message_count = 0;
+  size_t started_recv_message_count = 0;
+  size_t completed_recv_message_count = 0;
+  bool started_send_initial_metadata : 1;
+  bool completed_send_initial_metadata : 1;
+  bool started_send_trailing_metadata : 1;
+  bool completed_send_trailing_metadata : 1;
+  bool started_recv_initial_metadata : 1;
+  bool completed_recv_initial_metadata : 1;
+  bool started_recv_trailing_metadata : 1;
+  bool completed_recv_trailing_metadata : 1;
+  // State for callback processing.
+  subchannel_batch_data* recv_initial_metadata_ready_deferred_batch = nullptr;
+  grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
+  subchannel_batch_data* recv_message_ready_deferred_batch = nullptr;
+  grpc_error* recv_message_error = GRPC_ERROR_NONE;
+  subchannel_batch_data* recv_trailing_metadata_internal_batch = nullptr;
+  // NOTE: Do not move this next to the metadata bitfields above. That would
+  //       save space but will also result in a data race because compiler will
+  //       generate a 2 byte store which overwrites the meta-data fields upon
+  //       setting this field.
+  bool retry_dispatched : 1;
+};
+
+// Pending batches stored in call data.
+struct pending_batch {
+  // The pending batch.  If nullptr, this slot is empty.
+  grpc_transport_stream_op_batch* batch;
+  // Indicates whether payload for send ops has been cached in call data.
+  bool send_ops_cached;
+};
+
+/** 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. */
+struct call_data {
+  call_data(grpc_call_element* elem, const channel_data& chand,
+            const grpc_call_element_args& args)
+      : deadline_state(elem, args.call_stack, args.call_combiner,
+                       GPR_LIKELY(chand.deadline_checking_enabled)
+                           ? args.deadline
+                           : GRPC_MILLIS_INF_FUTURE),
+        path(grpc_slice_ref_internal(args.path)),
+        call_start_time(args.start_time),
+        deadline(args.deadline),
+        arena(args.arena),
+        owning_call(args.call_stack),
+        call_combiner(args.call_combiner),
+        pending_send_initial_metadata(false),
+        pending_send_message(false),
+        pending_send_trailing_metadata(false),
+        enable_retries(chand.enable_retries),
+        retry_committed(false),
+        last_attempt_got_server_pushback(false) {}
+
+  ~call_data() {
+    if (GPR_LIKELY(subchannel_call != nullptr)) {
+      GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call,
+                                 "client_channel_destroy_call");
+    }
+    grpc_slice_unref_internal(path);
+    GRPC_ERROR_UNREF(cancel_error);
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches); ++i) {
+      GPR_ASSERT(pending_batches[i].batch == nullptr);
+    }
+    if (have_request) {
+      request.Destroy();
+    }
+  }
+
+  // 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 pointers to the call stack
+  // and call combiner.  If/when we have time, find a way to avoid this
+  // without breaking the grpc_deadline_state abstraction.
+  grpc_deadline_state deadline_state;
+
+  grpc_slice path;  // Request path.
+  gpr_timespec call_start_time;
+  grpc_millis deadline;
+  gpr_arena* arena;
+  grpc_call_stack* owning_call;
+  grpc_call_combiner* call_combiner;
+
+  grpc_core::RefCountedPtr<ServerRetryThrottleData> retry_throttle_data;
+  grpc_core::RefCountedPtr<ClientChannelMethodParams> method_params;
+
+  grpc_subchannel_call* subchannel_call = nullptr;
+
+  // Set when we get a cancel_stream op.
+  grpc_error* cancel_error = GRPC_ERROR_NONE;
+
+  grpc_core::ManualConstructor<grpc_core::RequestRouter::Request> request;
+  bool have_request = false;
+  grpc_closure pick_closure;
+
+  grpc_polling_entity* pollent = nullptr;
+
+  // Batches are added to this list when received from above.
+  // They are removed when we are done handling the batch (i.e., when
+  // either we have invoked all of the batch's callbacks or we have
+  // passed the batch down to the subchannel call and are not
+  // intercepting any of its callbacks).
+  pending_batch pending_batches[MAX_PENDING_BATCHES] = {};
+  bool pending_send_initial_metadata : 1;
+  bool pending_send_message : 1;
+  bool pending_send_trailing_metadata : 1;
+
+  // Retry state.
+  bool enable_retries : 1;
+  bool retry_committed : 1;
+  bool last_attempt_got_server_pushback : 1;
+  int num_attempts_completed = 0;
+  size_t bytes_buffered_for_retry = 0;
+  grpc_core::ManualConstructor<grpc_core::BackOff> retry_backoff;
+  grpc_timer retry_timer;
+
+  // The number of pending retriable subchannel batches containing send ops.
+  // We hold a ref to the call stack while this is non-zero, since replay
+  // batches may not complete until after all callbacks have been returned
+  // to the surface, and we need to make sure that the call is not destroyed
+  // until all of these batches have completed.
+  // Note that we actually only need to track replay batches, but it's
+  // easier to track all batches with send ops.
+  int num_pending_retriable_subchannel_send_batches = 0;
+
+  // Cached data for retrying send ops.
+  // send_initial_metadata
+  bool seen_send_initial_metadata = false;
+  grpc_linked_mdelem* send_initial_metadata_storage = nullptr;
+  grpc_metadata_batch send_initial_metadata;
+  uint32_t send_initial_metadata_flags;
+  gpr_atm* peer_string;
+  // send_message
+  // When we get a send_message op, we replace the original byte stream
+  // with a CachingByteStream that caches the slices to a local buffer for
+  // use in retries.
+  // Note: We inline the cache for the first 3 send_message ops and use
+  // dynamic allocation after that.  This number was essentially picked
+  // at random; it could be changed in the future to tune performance.
+  grpc_core::InlinedVector<grpc_core::ByteStreamCache*, 3> send_messages;
+  // send_trailing_metadata
+  bool seen_send_trailing_metadata = false;
+  grpc_linked_mdelem* send_trailing_metadata_storage = nullptr;
+  grpc_metadata_batch send_trailing_metadata;
+};
+
+}  // namespace
+
+// Forward declarations.
+static void retry_commit(grpc_call_element* elem,
+                         subchannel_call_retry_state* retry_state);
+static void start_internal_recv_trailing_metadata(grpc_call_element* elem);
+static void on_complete(void* arg, grpc_error* error);
+static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored);
+static void start_pick_locked(void* arg, grpc_error* ignored);
+
+//
+// send op data caching
+//
+
+// Caches data for send ops so that it can be retried later, if not
+// already cached.
+static void maybe_cache_send_ops_for_batch(call_data* calld,
+                                           pending_batch* pending) {
+  if (pending->send_ops_cached) return;
+  pending->send_ops_cached = true;
+  grpc_transport_stream_op_batch* batch = pending->batch;
+  // Save a copy of metadata for send_initial_metadata ops.
+  if (batch->send_initial_metadata) {
+    calld->seen_send_initial_metadata = true;
+    GPR_ASSERT(calld->send_initial_metadata_storage == nullptr);
+    grpc_metadata_batch* send_initial_metadata =
+        batch->payload->send_initial_metadata.send_initial_metadata;
+    calld->send_initial_metadata_storage = (grpc_linked_mdelem*)gpr_arena_alloc(
+        calld->arena,
+        sizeof(grpc_linked_mdelem) * send_initial_metadata->list.count);
+    grpc_metadata_batch_copy(send_initial_metadata,
+                             &calld->send_initial_metadata,
+                             calld->send_initial_metadata_storage);
+    calld->send_initial_metadata_flags =
+        batch->payload->send_initial_metadata.send_initial_metadata_flags;
+    calld->peer_string = batch->payload->send_initial_metadata.peer_string;
+  }
+  // Set up cache for send_message ops.
+  if (batch->send_message) {
+    grpc_core::ByteStreamCache* cache =
+        static_cast<grpc_core::ByteStreamCache*>(
+            gpr_arena_alloc(calld->arena, sizeof(grpc_core::ByteStreamCache)));
+    new (cache) grpc_core::ByteStreamCache(
+        std::move(batch->payload->send_message.send_message));
+    calld->send_messages.push_back(cache);
+  }
+  // Save metadata batch for send_trailing_metadata ops.
+  if (batch->send_trailing_metadata) {
+    calld->seen_send_trailing_metadata = true;
+    GPR_ASSERT(calld->send_trailing_metadata_storage == nullptr);
+    grpc_metadata_batch* send_trailing_metadata =
+        batch->payload->send_trailing_metadata.send_trailing_metadata;
+    calld->send_trailing_metadata_storage =
+        (grpc_linked_mdelem*)gpr_arena_alloc(
+            calld->arena,
+            sizeof(grpc_linked_mdelem) * send_trailing_metadata->list.count);
+    grpc_metadata_batch_copy(send_trailing_metadata,
+                             &calld->send_trailing_metadata,
+                             calld->send_trailing_metadata_storage);
+  }
+}
+
+// Frees cached send_initial_metadata.
+static void free_cached_send_initial_metadata(channel_data* chand,
+                                              call_data* calld) {
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: destroying calld->send_initial_metadata", chand,
+            calld);
+  }
+  grpc_metadata_batch_destroy(&calld->send_initial_metadata);
+}
+
+// Frees cached send_message at index idx.
+static void free_cached_send_message(channel_data* chand, call_data* calld,
+                                     size_t idx) {
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: destroying calld->send_messages[%" PRIuPTR "]",
+            chand, calld, idx);
+  }
+  calld->send_messages[idx]->Destroy();
+}
+
+// Frees cached send_trailing_metadata.
+static void free_cached_send_trailing_metadata(channel_data* chand,
+                                               call_data* calld) {
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: destroying calld->send_trailing_metadata",
+            chand, calld);
+  }
+  grpc_metadata_batch_destroy(&calld->send_trailing_metadata);
+}
+
+// Frees cached send ops that have already been completed after
+// committing the call.
+static void free_cached_send_op_data_after_commit(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (retry_state->completed_send_initial_metadata) {
+    free_cached_send_initial_metadata(chand, calld);
+  }
+  for (size_t i = 0; i < retry_state->completed_send_message_count; ++i) {
+    free_cached_send_message(chand, calld, i);
+  }
+  if (retry_state->completed_send_trailing_metadata) {
+    free_cached_send_trailing_metadata(chand, calld);
+  }
+}
+
+// Frees cached send ops that were completed by the completed batch in
+// batch_data.  Used when batches are completed after the call is committed.
+static void free_cached_send_op_data_for_completed_batch(
+    grpc_call_element* elem, subchannel_batch_data* batch_data,
+    subchannel_call_retry_state* retry_state) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (batch_data->batch.send_initial_metadata) {
+    free_cached_send_initial_metadata(chand, calld);
+  }
+  if (batch_data->batch.send_message) {
+    free_cached_send_message(chand, calld,
+                             retry_state->completed_send_message_count - 1);
+  }
+  if (batch_data->batch.send_trailing_metadata) {
+    free_cached_send_trailing_metadata(chand, calld);
+  }
+}
+
+//
+// pending_batches management
+//
+
+// Returns the index into calld->pending_batches to be used for batch.
+static size_t get_batch_index(grpc_transport_stream_op_batch* batch) {
+  // Note: It is important the send_initial_metadata be the first entry
+  // here, since the code in pick_subchannel_locked() assumes it will be.
+  if (batch->send_initial_metadata) return 0;
+  if (batch->send_message) return 1;
+  if (batch->send_trailing_metadata) return 2;
+  if (batch->recv_initial_metadata) return 3;
+  if (batch->recv_message) return 4;
+  if (batch->recv_trailing_metadata) return 5;
+  GPR_UNREACHABLE_CODE(return (size_t)-1);
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+static void pending_batches_add(grpc_call_element* elem,
+                                grpc_transport_stream_op_batch* batch) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  const size_t idx = get_batch_index(batch);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: adding pending batch at index %" PRIuPTR, chand,
+            calld, idx);
+  }
+  pending_batch* pending = &calld->pending_batches[idx];
+  GPR_ASSERT(pending->batch == nullptr);
+  pending->batch = batch;
+  pending->send_ops_cached = false;
+  if (calld->enable_retries) {
+    // Update state in calld about pending batches.
+    // Also check if the batch takes us over the retry buffer limit.
+    // Note: We don't check the size of trailing metadata here, because
+    // gRPC clients do not send trailing metadata.
+    if (batch->send_initial_metadata) {
+      calld->pending_send_initial_metadata = true;
+      calld->bytes_buffered_for_retry += grpc_metadata_batch_size(
+          batch->payload->send_initial_metadata.send_initial_metadata);
+    }
+    if (batch->send_message) {
+      calld->pending_send_message = true;
+      calld->bytes_buffered_for_retry +=
+          batch->payload->send_message.send_message->length();
+    }
+    if (batch->send_trailing_metadata) {
+      calld->pending_send_trailing_metadata = true;
+    }
+    if (GPR_UNLIKELY(calld->bytes_buffered_for_retry >
+                     chand->per_rpc_retry_buffer_size)) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "chand=%p calld=%p: exceeded retry buffer size, committing",
+                chand, calld);
+      }
+      subchannel_call_retry_state* retry_state =
+          calld->subchannel_call == nullptr
+              ? nullptr
+              : static_cast<subchannel_call_retry_state*>(
+                    grpc_connected_subchannel_call_get_parent_data(
+                        calld->subchannel_call));
+      retry_commit(elem, retry_state);
+      // If we are not going to retry and have not yet started, pretend
+      // retries are disabled so that we don't bother with retry overhead.
+      if (calld->num_attempts_completed == 0) {
+        if (grpc_client_channel_trace.enabled()) {
+          gpr_log(GPR_INFO,
+                  "chand=%p calld=%p: disabling retries before first attempt",
+                  chand, calld);
+        }
+        calld->enable_retries = false;
+      }
+    }
+  }
+}
+
+static void pending_batch_clear(call_data* calld, pending_batch* pending) {
+  if (calld->enable_retries) {
+    if (pending->batch->send_initial_metadata) {
+      calld->pending_send_initial_metadata = false;
+    }
+    if (pending->batch->send_message) {
+      calld->pending_send_message = false;
+    }
+    if (pending->batch->send_trailing_metadata) {
+      calld->pending_send_trailing_metadata = false;
+    }
+  }
+  pending->batch = nullptr;
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+static void fail_pending_batch_in_call_combiner(void* arg, grpc_error* error) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  call_data* calld = static_cast<call_data*>(batch->handler_private.extra_arg);
+  // Note: This will release the call combiner.
+  grpc_transport_stream_op_batch_finish_with_failure(
+      batch, GRPC_ERROR_REF(error), calld->call_combiner);
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+// If yield_call_combiner is true, assumes responsibility for yielding
+// the call combiner.
+static void pending_batches_fail(grpc_call_element* elem, grpc_error* error,
+                                 bool yield_call_combiner) {
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    size_t num_batches = 0;
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+      if (calld->pending_batches[i].batch != nullptr) ++num_batches;
+    }
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s",
+            elem->channel_data, calld, num_batches, grpc_error_string(error));
+  }
+  grpc_core::CallCombinerClosureList closures;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    grpc_transport_stream_op_batch* batch = pending->batch;
+    if (batch != nullptr) {
+      batch->handler_private.extra_arg = calld;
+      GRPC_CLOSURE_INIT(&batch->handler_private.closure,
+                        fail_pending_batch_in_call_combiner, batch,
+                        grpc_schedule_on_exec_ctx);
+      closures.Add(&batch->handler_private.closure, GRPC_ERROR_REF(error),
+                   "pending_batches_fail");
+      pending_batch_clear(calld, pending);
+    }
+  }
+  if (yield_call_combiner) {
+    closures.RunClosures(calld->call_combiner);
+  } else {
+    closures.RunClosuresWithoutYielding(calld->call_combiner);
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+static void resume_pending_batch_in_call_combiner(void* arg,
+                                                  grpc_error* ignored) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  grpc_subchannel_call* subchannel_call =
+      static_cast<grpc_subchannel_call*>(batch->handler_private.extra_arg);
+  // Note: This will release the call combiner.
+  grpc_subchannel_call_process_op(subchannel_call, batch);
+}
+
+// This is called via the call combiner, so access to calld is synchronized.
+static void pending_batches_resume(grpc_call_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->enable_retries) {
+    start_retriable_subchannel_batches(elem, GRPC_ERROR_NONE);
+    return;
+  }
+  // Retries not enabled; send down batches as-is.
+  if (grpc_client_channel_trace.enabled()) {
+    size_t num_batches = 0;
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+      if (calld->pending_batches[i].batch != nullptr) ++num_batches;
+    }
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: starting %" PRIuPTR
+            " pending batches on subchannel_call=%p",
+            chand, calld, num_batches, calld->subchannel_call);
+  }
+  grpc_core::CallCombinerClosureList closures;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    grpc_transport_stream_op_batch* batch = pending->batch;
+    if (batch != nullptr) {
+      batch->handler_private.extra_arg = calld->subchannel_call;
+      GRPC_CLOSURE_INIT(&batch->handler_private.closure,
+                        resume_pending_batch_in_call_combiner, batch,
+                        grpc_schedule_on_exec_ctx);
+      closures.Add(&batch->handler_private.closure, GRPC_ERROR_NONE,
+                   "pending_batches_resume");
+      pending_batch_clear(calld, pending);
+    }
+  }
+  // Note: This will release the call combiner.
+  closures.RunClosures(calld->call_combiner);
+}
+
+static void maybe_clear_pending_batch(grpc_call_element* elem,
+                                      pending_batch* pending) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_transport_stream_op_batch* batch = pending->batch;
+  // We clear the pending batch if all of its callbacks have been
+  // scheduled and reset to nullptr.
+  if (batch->on_complete == nullptr &&
+      (!batch->recv_initial_metadata ||
+       batch->payload->recv_initial_metadata.recv_initial_metadata_ready ==
+           nullptr) &&
+      (!batch->recv_message ||
+       batch->payload->recv_message.recv_message_ready == nullptr) &&
+      (!batch->recv_trailing_metadata ||
+       batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready ==
+           nullptr)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: clearing pending batch", chand,
+              calld);
+    }
+    pending_batch_clear(calld, pending);
+  }
+}
+
+// Returns a pointer to the first pending batch for which predicate(batch)
+// returns true, or null if not found.
+template <typename Predicate>
+static pending_batch* pending_batch_find(grpc_call_element* elem,
+                                         const char* log_message,
+                                         Predicate predicate) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    grpc_transport_stream_op_batch* batch = pending->batch;
+    if (batch != nullptr && predicate(batch)) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "chand=%p calld=%p: %s pending batch at index %" PRIuPTR, chand,
+                calld, log_message, i);
+      }
+      return pending;
+    }
+  }
+  return nullptr;
+}
+
+//
+// retry code
+//
+
+// Commits the call so that no further retry attempts will be performed.
+static void retry_commit(grpc_call_element* elem,
+                         subchannel_call_retry_state* retry_state) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->retry_committed) return;
+  calld->retry_committed = true;
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p: committing retries", chand, calld);
+  }
+  if (retry_state != nullptr) {
+    free_cached_send_op_data_after_commit(elem, retry_state);
+  }
+}
+
+// Starts a retry after appropriate back-off.
+static void do_retry(grpc_call_element* elem,
+                     subchannel_call_retry_state* retry_state,
+                     grpc_millis server_pushback_ms) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  GPR_ASSERT(calld->method_params != nullptr);
+  const ClientChannelMethodParams::RetryPolicy* retry_policy =
+      calld->method_params->retry_policy();
+  GPR_ASSERT(retry_policy != nullptr);
+  // Reset subchannel call and connected subchannel.
+  if (calld->subchannel_call != nullptr) {
+    GRPC_SUBCHANNEL_CALL_UNREF(calld->subchannel_call,
+                               "client_channel_call_retry");
+    calld->subchannel_call = nullptr;
+  }
+  if (calld->have_request) {
+    calld->have_request = false;
+    calld->request.Destroy();
+  }
+  // Compute backoff delay.
+  grpc_millis next_attempt_time;
+  if (server_pushback_ms >= 0) {
+    next_attempt_time = grpc_core::ExecCtx::Get()->Now() + server_pushback_ms;
+    calld->last_attempt_got_server_pushback = true;
+  } else {
+    if (calld->num_attempts_completed == 1 ||
+        calld->last_attempt_got_server_pushback) {
+      calld->retry_backoff.Init(
+          grpc_core::BackOff::Options()
+              .set_initial_backoff(retry_policy->initial_backoff)
+              .set_multiplier(retry_policy->backoff_multiplier)
+              .set_jitter(RETRY_BACKOFF_JITTER)
+              .set_max_backoff(retry_policy->max_backoff));
+      calld->last_attempt_got_server_pushback = false;
+    }
+    next_attempt_time = calld->retry_backoff->NextAttemptTime();
+  }
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: retrying failed call in %" PRId64 " ms", chand,
+            calld, next_attempt_time - grpc_core::ExecCtx::Get()->Now());
+  }
+  // Schedule retry after computed delay.
+  GRPC_CLOSURE_INIT(&calld->pick_closure, start_pick_locked, elem,
+                    grpc_combiner_scheduler(chand->combiner));
+  grpc_timer_init(&calld->retry_timer, next_attempt_time, &calld->pick_closure);
+  // Update bookkeeping.
+  if (retry_state != nullptr) retry_state->retry_dispatched = true;
+}
+
+// Returns true if the call is being retried.
+static bool maybe_retry(grpc_call_element* elem,
+                        subchannel_batch_data* batch_data,
+                        grpc_status_code status,
+                        grpc_mdelem* server_pushback_md) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Get retry policy.
+  if (calld->method_params == nullptr) return false;
+  const ClientChannelMethodParams::RetryPolicy* retry_policy =
+      calld->method_params->retry_policy();
+  if (retry_policy == nullptr) return false;
+  // If we've already dispatched a retry from this call, return true.
+  // This catches the case where the batch has multiple callbacks
+  // (i.e., it includes either recv_message or recv_initial_metadata).
+  subchannel_call_retry_state* retry_state = nullptr;
+  if (batch_data != nullptr) {
+    retry_state = static_cast<subchannel_call_retry_state*>(
+        grpc_connected_subchannel_call_get_parent_data(
+            batch_data->subchannel_call));
+    if (retry_state->retry_dispatched) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_INFO, "chand=%p calld=%p: retry already dispatched", chand,
+                calld);
+      }
+      return true;
+    }
+  }
+  // Check status.
+  if (GPR_LIKELY(status == GRPC_STATUS_OK)) {
+    if (calld->retry_throttle_data != nullptr) {
+      calld->retry_throttle_data->RecordSuccess();
+    }
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: call succeeded", chand, calld);
+    }
+    return false;
+  }
+  // Status is not OK.  Check whether the status is retryable.
+  if (!retry_policy->retryable_status_codes.Contains(status)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: status %s not configured as retryable", chand,
+              calld, grpc_status_code_to_string(status));
+    }
+    return false;
+  }
+  // Record the failure and check whether retries are throttled.
+  // Note that it's important for this check to come after the status
+  // code check above, since we should only record failures whose statuses
+  // match the configured retryable status codes, so that we don't count
+  // things like failures due to malformed requests (INVALID_ARGUMENT).
+  // Conversely, it's important for this to come before the remaining
+  // checks, so that we don't fail to record failures due to other factors.
+  if (calld->retry_throttle_data != nullptr &&
+      !calld->retry_throttle_data->RecordFailure()) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: retries throttled", chand, calld);
+    }
+    return false;
+  }
+  // Check whether the call is committed.
+  if (calld->retry_committed) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: retries already committed", chand,
+              calld);
+    }
+    return false;
+  }
+  // Check whether we have retries remaining.
+  ++calld->num_attempts_completed;
+  if (calld->num_attempts_completed >= retry_policy->max_attempts) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: exceeded %d retry attempts", chand,
+              calld, retry_policy->max_attempts);
+    }
+    return false;
+  }
+  // If the call was cancelled from the surface, don't retry.
+  if (calld->cancel_error != GRPC_ERROR_NONE) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: call cancelled from surface, not retrying",
+              chand, calld);
+    }
+    return false;
+  }
+  // Check server push-back.
+  grpc_millis server_pushback_ms = -1;
+  if (server_pushback_md != nullptr) {
+    // If the value is "-1" or any other unparseable string, we do not retry.
+    uint32_t ms;
+    if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(*server_pushback_md), &ms)) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "chand=%p calld=%p: not retrying due to server push-back",
+                chand, calld);
+      }
+      return false;
+    } else {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_INFO, "chand=%p calld=%p: server push-back: retry in %u ms",
+                chand, calld, ms);
+      }
+      server_pushback_ms = (grpc_millis)ms;
+    }
+  }
+  do_retry(elem, retry_state, server_pushback_ms);
+  return true;
+}
+
+//
+// subchannel_batch_data
+//
+
+namespace {
+
+subchannel_batch_data::subchannel_batch_data(grpc_call_element* elem,
+                                             call_data* calld, int refcount,
+                                             bool set_on_complete)
+    : elem(elem),
+      subchannel_call(GRPC_SUBCHANNEL_CALL_REF(calld->subchannel_call,
+                                               "batch_data_create")) {
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              calld->subchannel_call));
+  batch.payload = &retry_state->batch_payload;
+  gpr_ref_init(&refs, refcount);
+  if (set_on_complete) {
+    GRPC_CLOSURE_INIT(&on_complete, ::on_complete, this,
+                      grpc_schedule_on_exec_ctx);
+    batch.on_complete = &on_complete;
+  }
+  GRPC_CALL_STACK_REF(calld->owning_call, "batch_data");
+}
+
+void subchannel_batch_data::destroy() {
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(subchannel_call));
+  if (batch.send_initial_metadata) {
+    grpc_metadata_batch_destroy(&retry_state->send_initial_metadata);
+  }
+  if (batch.send_trailing_metadata) {
+    grpc_metadata_batch_destroy(&retry_state->send_trailing_metadata);
+  }
+  if (batch.recv_initial_metadata) {
+    grpc_metadata_batch_destroy(&retry_state->recv_initial_metadata);
+  }
+  if (batch.recv_trailing_metadata) {
+    grpc_metadata_batch_destroy(&retry_state->recv_trailing_metadata);
+  }
+  GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "batch_data_unref");
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  GRPC_CALL_STACK_UNREF(calld->owning_call, "batch_data");
+}
+
+}  // namespace
+
+// Creates a subchannel_batch_data object on the call's arena with the
+// specified refcount.  If set_on_complete is true, the batch's
+// on_complete callback will be set to point to on_complete();
+// otherwise, the batch's on_complete callback will be null.
+static subchannel_batch_data* batch_data_create(grpc_call_element* elem,
+                                                int refcount,
+                                                bool set_on_complete) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  subchannel_batch_data* batch_data =
+      new (gpr_arena_alloc(calld->arena, sizeof(*batch_data)))
+          subchannel_batch_data(elem, calld, refcount, set_on_complete);
+  return batch_data;
+}
+
+static void batch_data_unref(subchannel_batch_data* batch_data) {
+  if (gpr_unref(&batch_data->refs)) {
+    batch_data->destroy();
+  }
+}
+
+//
+// recv_initial_metadata callback handling
+//
+
+// Invokes recv_initial_metadata_ready for a subchannel batch.
+static void invoke_recv_initial_metadata_callback(void* arg,
+                                                  grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  // Find pending batch.
+  pending_batch* pending = pending_batch_find(
+      batch_data->elem, "invoking recv_initial_metadata_ready for",
+      [](grpc_transport_stream_op_batch* batch) {
+        return batch->recv_initial_metadata &&
+               batch->payload->recv_initial_metadata
+                       .recv_initial_metadata_ready != nullptr;
+      });
+  GPR_ASSERT(pending != nullptr);
+  // Return metadata.
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  grpc_metadata_batch_move(
+      &retry_state->recv_initial_metadata,
+      pending->batch->payload->recv_initial_metadata.recv_initial_metadata);
+  // Update bookkeeping.
+  // Note: Need to do this before invoking the callback, since invoking
+  // the callback will result in yielding the call combiner.
+  grpc_closure* recv_initial_metadata_ready =
+      pending->batch->payload->recv_initial_metadata
+          .recv_initial_metadata_ready;
+  pending->batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
+      nullptr;
+  maybe_clear_pending_batch(batch_data->elem, pending);
+  batch_data_unref(batch_data);
+  // Invoke callback.
+  GRPC_CLOSURE_RUN(recv_initial_metadata_ready, GRPC_ERROR_REF(error));
+}
+
+// Intercepts recv_initial_metadata_ready callback for retries.
+// Commits the call and returns the initial metadata up the stack.
+static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  grpc_call_element* elem = batch_data->elem;
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: got recv_initial_metadata_ready, error=%s",
+            chand, calld, grpc_error_string(error));
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  retry_state->completed_recv_initial_metadata = true;
+  // If a retry was already dispatched, then we're not going to use the
+  // result of this recv_initial_metadata op, so do nothing.
+  if (retry_state->retry_dispatched) {
+    GRPC_CALL_COMBINER_STOP(
+        calld->call_combiner,
+        "recv_initial_metadata_ready after retry dispatched");
+    return;
+  }
+  // If we got an error or a Trailers-Only response and have not yet gotten
+  // the recv_trailing_metadata_ready callback, then defer propagating this
+  // callback back to the surface.  We can evaluate whether to retry when
+  // recv_trailing_metadata comes back.
+  if (GPR_UNLIKELY((retry_state->trailing_metadata_available ||
+                    error != GRPC_ERROR_NONE) &&
+                   !retry_state->completed_recv_trailing_metadata)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: deferring recv_initial_metadata_ready "
+              "(Trailers-Only)",
+              chand, calld);
+    }
+    retry_state->recv_initial_metadata_ready_deferred_batch = batch_data;
+    retry_state->recv_initial_metadata_error = GRPC_ERROR_REF(error);
+    if (!retry_state->started_recv_trailing_metadata) {
+      // recv_trailing_metadata not yet started by application; start it
+      // ourselves to get status.
+      start_internal_recv_trailing_metadata(elem);
+    } else {
+      GRPC_CALL_COMBINER_STOP(
+          calld->call_combiner,
+          "recv_initial_metadata_ready trailers-only or error");
+    }
+    return;
+  }
+  // Received valid initial metadata, so commit the call.
+  retry_commit(elem, retry_state);
+  // Invoke the callback to return the result to the surface.
+  // Manually invoking a callback function; it does not take ownership of error.
+  invoke_recv_initial_metadata_callback(batch_data, error);
+}
+
+//
+// recv_message callback handling
+//
+
+// Invokes recv_message_ready for a subchannel batch.
+static void invoke_recv_message_callback(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  // Find pending op.
+  pending_batch* pending = pending_batch_find(
+      batch_data->elem, "invoking recv_message_ready for",
+      [](grpc_transport_stream_op_batch* batch) {
+        return batch->recv_message &&
+               batch->payload->recv_message.recv_message_ready != nullptr;
+      });
+  GPR_ASSERT(pending != nullptr);
+  // Return payload.
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  *pending->batch->payload->recv_message.recv_message =
+      std::move(retry_state->recv_message);
+  // Update bookkeeping.
+  // Note: Need to do this before invoking the callback, since invoking
+  // the callback will result in yielding the call combiner.
+  grpc_closure* recv_message_ready =
+      pending->batch->payload->recv_message.recv_message_ready;
+  pending->batch->payload->recv_message.recv_message_ready = nullptr;
+  maybe_clear_pending_batch(batch_data->elem, pending);
+  batch_data_unref(batch_data);
+  // Invoke callback.
+  GRPC_CLOSURE_RUN(recv_message_ready, GRPC_ERROR_REF(error));
+}
+
+// Intercepts recv_message_ready callback for retries.
+// Commits the call and returns the message up the stack.
+static void recv_message_ready(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  grpc_call_element* elem = batch_data->elem;
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p: got recv_message_ready, error=%s",
+            chand, calld, grpc_error_string(error));
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  ++retry_state->completed_recv_message_count;
+  // If a retry was already dispatched, then we're not going to use the
+  // result of this recv_message op, so do nothing.
+  if (retry_state->retry_dispatched) {
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "recv_message_ready after retry dispatched");
+    return;
+  }
+  // If we got an error or the payload was nullptr and we have not yet gotten
+  // the recv_trailing_metadata_ready callback, then defer propagating this
+  // callback back to the surface.  We can evaluate whether to retry when
+  // recv_trailing_metadata comes back.
+  if (GPR_UNLIKELY(
+          (retry_state->recv_message == nullptr || error != GRPC_ERROR_NONE) &&
+          !retry_state->completed_recv_trailing_metadata)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: deferring recv_message_ready (nullptr "
+              "message and recv_trailing_metadata pending)",
+              chand, calld);
+    }
+    retry_state->recv_message_ready_deferred_batch = batch_data;
+    retry_state->recv_message_error = GRPC_ERROR_REF(error);
+    if (!retry_state->started_recv_trailing_metadata) {
+      // recv_trailing_metadata not yet started by application; start it
+      // ourselves to get status.
+      start_internal_recv_trailing_metadata(elem);
+    } else {
+      GRPC_CALL_COMBINER_STOP(calld->call_combiner, "recv_message_ready null");
+    }
+    return;
+  }
+  // Received a valid message, so commit the call.
+  retry_commit(elem, retry_state);
+  // Invoke the callback to return the result to the surface.
+  // Manually invoking a callback function; it does not take ownership of error.
+  invoke_recv_message_callback(batch_data, error);
+}
+
+//
+// recv_trailing_metadata handling
+//
+
+// Sets *status and *server_pushback_md based on md_batch and error.
+// Only sets *server_pushback_md if server_pushback_md != nullptr.
+static void get_call_status(grpc_call_element* elem,
+                            grpc_metadata_batch* md_batch, grpc_error* error,
+                            grpc_status_code* status,
+                            grpc_mdelem** server_pushback_md) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (error != GRPC_ERROR_NONE) {
+    grpc_error_get_status(error, calld->deadline, status, nullptr, nullptr,
+                          nullptr);
+  } else {
+    GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr);
+    *status =
+        grpc_get_status_code_from_metadata(md_batch->idx.named.grpc_status->md);
+    if (server_pushback_md != nullptr &&
+        md_batch->idx.named.grpc_retry_pushback_ms != nullptr) {
+      *server_pushback_md = &md_batch->idx.named.grpc_retry_pushback_ms->md;
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// Adds recv_trailing_metadata_ready closure to closures.
+static void add_closure_for_recv_trailing_metadata_ready(
+    grpc_call_element* elem, subchannel_batch_data* batch_data,
+    grpc_error* error, grpc_core::CallCombinerClosureList* closures) {
+  // Find pending batch.
+  pending_batch* pending = pending_batch_find(
+      elem, "invoking recv_trailing_metadata for",
+      [](grpc_transport_stream_op_batch* batch) {
+        return batch->recv_trailing_metadata &&
+               batch->payload->recv_trailing_metadata
+                       .recv_trailing_metadata_ready != nullptr;
+      });
+  // If we generated the recv_trailing_metadata op internally via
+  // start_internal_recv_trailing_metadata(), then there will be no
+  // pending batch.
+  if (pending == nullptr) {
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  // Return metadata.
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  grpc_metadata_batch_move(
+      &retry_state->recv_trailing_metadata,
+      pending->batch->payload->recv_trailing_metadata.recv_trailing_metadata);
+  // Add closure.
+  closures->Add(pending->batch->payload->recv_trailing_metadata
+                    .recv_trailing_metadata_ready,
+                error, "recv_trailing_metadata_ready for pending batch");
+  // Update bookkeeping.
+  pending->batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+      nullptr;
+  maybe_clear_pending_batch(elem, pending);
+}
+
+// Adds any necessary closures for deferred recv_initial_metadata and
+// recv_message callbacks to closures.
+static void add_closures_for_deferred_recv_callbacks(
+    subchannel_batch_data* batch_data, subchannel_call_retry_state* retry_state,
+    grpc_core::CallCombinerClosureList* closures) {
+  if (batch_data->batch.recv_trailing_metadata) {
+    // Add closure for deferred recv_initial_metadata_ready.
+    if (GPR_UNLIKELY(retry_state->recv_initial_metadata_ready_deferred_batch !=
+                     nullptr)) {
+      GRPC_CLOSURE_INIT(&retry_state->recv_initial_metadata_ready,
+                        invoke_recv_initial_metadata_callback,
+                        retry_state->recv_initial_metadata_ready_deferred_batch,
+                        grpc_schedule_on_exec_ctx);
+      closures->Add(&retry_state->recv_initial_metadata_ready,
+                    retry_state->recv_initial_metadata_error,
+                    "resuming recv_initial_metadata_ready");
+      retry_state->recv_initial_metadata_ready_deferred_batch = nullptr;
+    }
+    // Add closure for deferred recv_message_ready.
+    if (GPR_UNLIKELY(retry_state->recv_message_ready_deferred_batch !=
+                     nullptr)) {
+      GRPC_CLOSURE_INIT(&retry_state->recv_message_ready,
+                        invoke_recv_message_callback,
+                        retry_state->recv_message_ready_deferred_batch,
+                        grpc_schedule_on_exec_ctx);
+      closures->Add(&retry_state->recv_message_ready,
+                    retry_state->recv_message_error,
+                    "resuming recv_message_ready");
+      retry_state->recv_message_ready_deferred_batch = nullptr;
+    }
+  }
+}
+
+// Returns true if any op in the batch was not yet started.
+// Only looks at send ops, since recv ops are always started immediately.
+static bool pending_batch_is_unstarted(
+    pending_batch* pending, call_data* calld,
+    subchannel_call_retry_state* retry_state) {
+  if (pending->batch == nullptr || pending->batch->on_complete == nullptr) {
+    return false;
+  }
+  if (pending->batch->send_initial_metadata &&
+      !retry_state->started_send_initial_metadata) {
+    return true;
+  }
+  if (pending->batch->send_message &&
+      retry_state->started_send_message_count < calld->send_messages.size()) {
+    return true;
+  }
+  if (pending->batch->send_trailing_metadata &&
+      !retry_state->started_send_trailing_metadata) {
+    return true;
+  }
+  return false;
+}
+
+// For any pending batch containing an op that has not yet been started,
+// adds the pending batch's completion closures to closures.
+static void add_closures_to_fail_unstarted_pending_batches(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
+    grpc_error* error, grpc_core::CallCombinerClosureList* closures) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    if (pending_batch_is_unstarted(pending, calld, retry_state)) {
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "chand=%p calld=%p: failing unstarted pending batch at index "
+                "%" PRIuPTR,
+                chand, calld, i);
+      }
+      closures->Add(pending->batch->on_complete, GRPC_ERROR_REF(error),
+                    "failing on_complete for pending batch");
+      pending->batch->on_complete = nullptr;
+      maybe_clear_pending_batch(elem, pending);
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// Runs necessary closures upon completion of a call attempt.
+static void run_closures_for_completed_call(subchannel_batch_data* batch_data,
+                                            grpc_error* error) {
+  grpc_call_element* elem = batch_data->elem;
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  // Construct list of closures to execute.
+  grpc_core::CallCombinerClosureList closures;
+  // First, add closure for recv_trailing_metadata_ready.
+  add_closure_for_recv_trailing_metadata_ready(
+      elem, batch_data, GRPC_ERROR_REF(error), &closures);
+  // If there are deferred recv_initial_metadata_ready or recv_message_ready
+  // callbacks, add them to closures.
+  add_closures_for_deferred_recv_callbacks(batch_data, retry_state, &closures);
+  // Add closures to fail any pending batches that have not yet been started.
+  add_closures_to_fail_unstarted_pending_batches(
+      elem, retry_state, GRPC_ERROR_REF(error), &closures);
+  // Don't need batch_data anymore.
+  batch_data_unref(batch_data);
+  // Schedule all of the closures identified above.
+  // Note: This will release the call combiner.
+  closures.RunClosures(calld->call_combiner);
+  GRPC_ERROR_UNREF(error);
+}
+
+// Intercepts recv_trailing_metadata_ready callback for retries.
+// Commits the call and returns the trailing metadata up the stack.
+static void recv_trailing_metadata_ready(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  grpc_call_element* elem = batch_data->elem;
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: got recv_trailing_metadata_ready, error=%s",
+            chand, calld, grpc_error_string(error));
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  retry_state->completed_recv_trailing_metadata = true;
+  // Get the call's status and check for server pushback metadata.
+  grpc_status_code status = GRPC_STATUS_OK;
+  grpc_mdelem* server_pushback_md = nullptr;
+  grpc_metadata_batch* md_batch =
+      batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata;
+  get_call_status(elem, md_batch, GRPC_ERROR_REF(error), &status,
+                  &server_pushback_md);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p: call finished, status=%s", chand,
+            calld, grpc_status_code_to_string(status));
+  }
+  // Check if we should retry.
+  if (maybe_retry(elem, batch_data, status, server_pushback_md)) {
+    // Unref batch_data for deferred recv_initial_metadata_ready or
+    // recv_message_ready callbacks, if any.
+    if (retry_state->recv_initial_metadata_ready_deferred_batch != nullptr) {
+      batch_data_unref(batch_data);
+      GRPC_ERROR_UNREF(retry_state->recv_initial_metadata_error);
+    }
+    if (retry_state->recv_message_ready_deferred_batch != nullptr) {
+      batch_data_unref(batch_data);
+      GRPC_ERROR_UNREF(retry_state->recv_message_error);
+    }
+    batch_data_unref(batch_data);
+    return;
+  }
+  // Not retrying, so commit the call.
+  retry_commit(elem, retry_state);
+  // Run any necessary closures.
+  run_closures_for_completed_call(batch_data, GRPC_ERROR_REF(error));
+}
+
+//
+// on_complete callback handling
+//
+
+// Adds the on_complete closure for the pending batch completed in
+// batch_data to closures.
+static void add_closure_for_completed_pending_batch(
+    grpc_call_element* elem, subchannel_batch_data* batch_data,
+    subchannel_call_retry_state* retry_state, grpc_error* error,
+    grpc_core::CallCombinerClosureList* closures) {
+  pending_batch* pending = pending_batch_find(
+      elem, "completed", [batch_data](grpc_transport_stream_op_batch* batch) {
+        // Match the pending batch with the same set of send ops as the
+        // subchannel batch we've just completed.
+        return batch->on_complete != nullptr &&
+               batch_data->batch.send_initial_metadata ==
+                   batch->send_initial_metadata &&
+               batch_data->batch.send_message == batch->send_message &&
+               batch_data->batch.send_trailing_metadata ==
+                   batch->send_trailing_metadata;
+      });
+  // If batch_data is a replay batch, then there will be no pending
+  // batch to complete.
+  if (pending == nullptr) {
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  // Add closure.
+  closures->Add(pending->batch->on_complete, error,
+                "on_complete for pending batch");
+  pending->batch->on_complete = nullptr;
+  maybe_clear_pending_batch(elem, pending);
+}
+
+// If there are any cached ops to replay or pending ops to start on the
+// subchannel call, adds a closure to closures to invoke
+// start_retriable_subchannel_batches().
+static void add_closures_for_replay_or_pending_send_ops(
+    grpc_call_element* elem, subchannel_batch_data* batch_data,
+    subchannel_call_retry_state* retry_state,
+    grpc_core::CallCombinerClosureList* closures) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  bool have_pending_send_message_ops =
+      retry_state->started_send_message_count < calld->send_messages.size();
+  bool have_pending_send_trailing_metadata_op =
+      calld->seen_send_trailing_metadata &&
+      !retry_state->started_send_trailing_metadata;
+  if (!have_pending_send_message_ops &&
+      !have_pending_send_trailing_metadata_op) {
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+      pending_batch* pending = &calld->pending_batches[i];
+      grpc_transport_stream_op_batch* batch = pending->batch;
+      if (batch == nullptr || pending->send_ops_cached) continue;
+      if (batch->send_message) have_pending_send_message_ops = true;
+      if (batch->send_trailing_metadata) {
+        have_pending_send_trailing_metadata_op = true;
+      }
+    }
+  }
+  if (have_pending_send_message_ops || have_pending_send_trailing_metadata_op) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: starting next batch for pending send op(s)",
+              chand, calld);
+    }
+    GRPC_CLOSURE_INIT(&batch_data->batch.handler_private.closure,
+                      start_retriable_subchannel_batches, elem,
+                      grpc_schedule_on_exec_ctx);
+    closures->Add(&batch_data->batch.handler_private.closure, GRPC_ERROR_NONE,
+                  "starting next batch for send_* op(s)");
+  }
+}
+
+// Callback used to intercept on_complete from subchannel calls.
+// Called only when retries are enabled.
+static void on_complete(void* arg, grpc_error* error) {
+  subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg);
+  grpc_call_element* elem = batch_data->elem;
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    char* batch_str = grpc_transport_stream_op_batch_string(&batch_data->batch);
+    gpr_log(GPR_INFO, "chand=%p calld=%p: got on_complete, error=%s, batch=%s",
+            chand, calld, grpc_error_string(error), batch_str);
+    gpr_free(batch_str);
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              batch_data->subchannel_call));
+  // Update bookkeeping in retry_state.
+  if (batch_data->batch.send_initial_metadata) {
+    retry_state->completed_send_initial_metadata = true;
+  }
+  if (batch_data->batch.send_message) {
+    ++retry_state->completed_send_message_count;
+  }
+  if (batch_data->batch.send_trailing_metadata) {
+    retry_state->completed_send_trailing_metadata = true;
+  }
+  // If the call is committed, free cached data for send ops that we've just
+  // completed.
+  if (calld->retry_committed) {
+    free_cached_send_op_data_for_completed_batch(elem, batch_data, retry_state);
+  }
+  // Construct list of closures to execute.
+  grpc_core::CallCombinerClosureList closures;
+  // If a retry was already dispatched, that means we saw
+  // recv_trailing_metadata before this, so we do nothing here.
+  // Otherwise, invoke the callback to return the result to the surface.
+  if (!retry_state->retry_dispatched) {
+    // Add closure for the completed pending batch, if any.
+    add_closure_for_completed_pending_batch(elem, batch_data, retry_state,
+                                            GRPC_ERROR_REF(error), &closures);
+    // If needed, add a callback to start any replay or pending send ops on
+    // the subchannel call.
+    if (!retry_state->completed_recv_trailing_metadata) {
+      add_closures_for_replay_or_pending_send_ops(elem, batch_data, retry_state,
+                                                  &closures);
+    }
+  }
+  // Track number of pending subchannel send batches and determine if this
+  // was the last one.
+  --calld->num_pending_retriable_subchannel_send_batches;
+  const bool last_send_batch_complete =
+      calld->num_pending_retriable_subchannel_send_batches == 0;
+  // Don't need batch_data anymore.
+  batch_data_unref(batch_data);
+  // Schedule all of the closures identified above.
+  // Note: This yeilds the call combiner.
+  closures.RunClosures(calld->call_combiner);
+  // If this was the last subchannel send batch, unref the call stack.
+  if (last_send_batch_complete) {
+    GRPC_CALL_STACK_UNREF(calld->owning_call, "subchannel_send_batches");
+  }
+}
+
+//
+// subchannel batch construction
+//
+
+// Helper function used to start a subchannel batch in the call combiner.
+static void start_batch_in_call_combiner(void* arg, grpc_error* ignored) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  grpc_subchannel_call* subchannel_call =
+      static_cast<grpc_subchannel_call*>(batch->handler_private.extra_arg);
+  // Note: This will release the call combiner.
+  grpc_subchannel_call_process_op(subchannel_call, batch);
+}
+
+// Adds a closure to closures that will execute batch in the call combiner.
+static void add_closure_for_subchannel_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch,
+    grpc_core::CallCombinerClosureList* closures) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  batch->handler_private.extra_arg = calld->subchannel_call;
+  GRPC_CLOSURE_INIT(&batch->handler_private.closure,
+                    start_batch_in_call_combiner, batch,
+                    grpc_schedule_on_exec_ctx);
+  if (grpc_client_channel_trace.enabled()) {
+    char* batch_str = grpc_transport_stream_op_batch_string(batch);
+    gpr_log(GPR_INFO, "chand=%p calld=%p: starting subchannel batch: %s", chand,
+            calld, batch_str);
+    gpr_free(batch_str);
+  }
+  closures->Add(&batch->handler_private.closure, GRPC_ERROR_NONE,
+                "start_subchannel_batch");
+}
+
+// Adds retriable send_initial_metadata op to batch_data.
+static void add_retriable_send_initial_metadata_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  // Maps the number of retries to the corresponding metadata value slice.
+  static const grpc_slice* retry_count_strings[] = {
+      &GRPC_MDSTR_1, &GRPC_MDSTR_2, &GRPC_MDSTR_3, &GRPC_MDSTR_4};
+  // We need to make a copy of the metadata batch for each attempt, since
+  // the filters in the subchannel stack may modify this batch, and we don't
+  // want those modifications to be passed forward to subsequent attempts.
+  //
+  // If we've already completed one or more attempts, add the
+  // grpc-retry-attempts header.
+  retry_state->send_initial_metadata_storage =
+      static_cast<grpc_linked_mdelem*>(gpr_arena_alloc(
+          calld->arena, sizeof(grpc_linked_mdelem) *
+                            (calld->send_initial_metadata.list.count +
+                             (calld->num_attempts_completed > 0))));
+  grpc_metadata_batch_copy(&calld->send_initial_metadata,
+                           &retry_state->send_initial_metadata,
+                           retry_state->send_initial_metadata_storage);
+  if (GPR_UNLIKELY(retry_state->send_initial_metadata.idx.named
+                       .grpc_previous_rpc_attempts != nullptr)) {
+    grpc_metadata_batch_remove(&retry_state->send_initial_metadata,
+                               retry_state->send_initial_metadata.idx.named
+                                   .grpc_previous_rpc_attempts);
+  }
+  if (GPR_UNLIKELY(calld->num_attempts_completed > 0)) {
+    grpc_mdelem retry_md = grpc_mdelem_create(
+        GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS,
+        *retry_count_strings[calld->num_attempts_completed - 1], nullptr);
+    grpc_error* error = grpc_metadata_batch_add_tail(
+        &retry_state->send_initial_metadata,
+        &retry_state->send_initial_metadata_storage[calld->send_initial_metadata
+                                                        .list.count],
+        retry_md);
+    if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) {
+      gpr_log(GPR_ERROR, "error adding retry metadata: %s",
+              grpc_error_string(error));
+      GPR_ASSERT(false);
+    }
+  }
+  retry_state->started_send_initial_metadata = true;
+  batch_data->batch.send_initial_metadata = true;
+  batch_data->batch.payload->send_initial_metadata.send_initial_metadata =
+      &retry_state->send_initial_metadata;
+  batch_data->batch.payload->send_initial_metadata.send_initial_metadata_flags =
+      calld->send_initial_metadata_flags;
+  batch_data->batch.payload->send_initial_metadata.peer_string =
+      calld->peer_string;
+}
+
+// Adds retriable send_message op to batch_data.
+static void add_retriable_send_message_op(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: starting calld->send_messages[%" PRIuPTR "]",
+            chand, calld, retry_state->started_send_message_count);
+  }
+  grpc_core::ByteStreamCache* cache =
+      calld->send_messages[retry_state->started_send_message_count];
+  ++retry_state->started_send_message_count;
+  retry_state->send_message.Init(cache);
+  batch_data->batch.send_message = true;
+  batch_data->batch.payload->send_message.send_message.reset(
+      retry_state->send_message.get());
+}
+
+// Adds retriable send_trailing_metadata op to batch_data.
+static void add_retriable_send_trailing_metadata_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  // We need to make a copy of the metadata batch for each attempt, since
+  // the filters in the subchannel stack may modify this batch, and we don't
+  // want those modifications to be passed forward to subsequent attempts.
+  retry_state->send_trailing_metadata_storage =
+      static_cast<grpc_linked_mdelem*>(gpr_arena_alloc(
+          calld->arena, sizeof(grpc_linked_mdelem) *
+                            calld->send_trailing_metadata.list.count));
+  grpc_metadata_batch_copy(&calld->send_trailing_metadata,
+                           &retry_state->send_trailing_metadata,
+                           retry_state->send_trailing_metadata_storage);
+  retry_state->started_send_trailing_metadata = true;
+  batch_data->batch.send_trailing_metadata = true;
+  batch_data->batch.payload->send_trailing_metadata.send_trailing_metadata =
+      &retry_state->send_trailing_metadata;
+}
+
+// Adds retriable recv_initial_metadata op to batch_data.
+static void add_retriable_recv_initial_metadata_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  retry_state->started_recv_initial_metadata = true;
+  batch_data->batch.recv_initial_metadata = true;
+  grpc_metadata_batch_init(&retry_state->recv_initial_metadata);
+  batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata =
+      &retry_state->recv_initial_metadata;
+  batch_data->batch.payload->recv_initial_metadata.trailing_metadata_available =
+      &retry_state->trailing_metadata_available;
+  GRPC_CLOSURE_INIT(&retry_state->recv_initial_metadata_ready,
+                    recv_initial_metadata_ready, batch_data,
+                    grpc_schedule_on_exec_ctx);
+  batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata_ready =
+      &retry_state->recv_initial_metadata_ready;
+}
+
+// Adds retriable recv_message op to batch_data.
+static void add_retriable_recv_message_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  ++retry_state->started_recv_message_count;
+  batch_data->batch.recv_message = true;
+  batch_data->batch.payload->recv_message.recv_message =
+      &retry_state->recv_message;
+  GRPC_CLOSURE_INIT(&retry_state->recv_message_ready, recv_message_ready,
+                    batch_data, grpc_schedule_on_exec_ctx);
+  batch_data->batch.payload->recv_message.recv_message_ready =
+      &retry_state->recv_message_ready;
+}
+
+// Adds retriable recv_trailing_metadata op to batch_data.
+static void add_retriable_recv_trailing_metadata_op(
+    call_data* calld, subchannel_call_retry_state* retry_state,
+    subchannel_batch_data* batch_data) {
+  retry_state->started_recv_trailing_metadata = true;
+  batch_data->batch.recv_trailing_metadata = true;
+  grpc_metadata_batch_init(&retry_state->recv_trailing_metadata);
+  batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata =
+      &retry_state->recv_trailing_metadata;
+  batch_data->batch.payload->recv_trailing_metadata.collect_stats =
+      &retry_state->collect_stats;
+  GRPC_CLOSURE_INIT(&retry_state->recv_trailing_metadata_ready,
+                    recv_trailing_metadata_ready, batch_data,
+                    grpc_schedule_on_exec_ctx);
+  batch_data->batch.payload->recv_trailing_metadata
+      .recv_trailing_metadata_ready =
+      &retry_state->recv_trailing_metadata_ready;
+}
+
+// Helper function used to start a recv_trailing_metadata batch.  This
+// is used in the case where a recv_initial_metadata or recv_message
+// op fails in a way that we know the call is over but when the application
+// has not yet started its own recv_trailing_metadata op.
+static void start_internal_recv_trailing_metadata(grpc_call_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: call failed but recv_trailing_metadata not "
+            "started; starting it internally",
+            chand, calld);
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              calld->subchannel_call));
+  // Create batch_data with 2 refs, since this batch will be unreffed twice:
+  // once for the recv_trailing_metadata_ready callback when the subchannel
+  // batch returns, and again when we actually get a recv_trailing_metadata
+  // op from the surface.
+  subchannel_batch_data* batch_data =
+      batch_data_create(elem, 2, false /* set_on_complete */);
+  add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data);
+  retry_state->recv_trailing_metadata_internal_batch = batch_data;
+  // Note: This will release the call combiner.
+  grpc_subchannel_call_process_op(calld->subchannel_call, &batch_data->batch);
+}
+
+// If there are any cached send ops that need to be replayed on the
+// current subchannel call, creates and returns a new subchannel batch
+// to replay those ops.  Otherwise, returns nullptr.
+static subchannel_batch_data* maybe_create_subchannel_batch_for_replay(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  subchannel_batch_data* replay_batch_data = nullptr;
+  // send_initial_metadata.
+  if (calld->seen_send_initial_metadata &&
+      !retry_state->started_send_initial_metadata &&
+      !calld->pending_send_initial_metadata) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: replaying previously completed "
+              "send_initial_metadata op",
+              chand, calld);
+    }
+    replay_batch_data = batch_data_create(elem, 1, true /* set_on_complete */);
+    add_retriable_send_initial_metadata_op(calld, retry_state,
+                                           replay_batch_data);
+  }
+  // send_message.
+  // Note that we can only have one send_message op in flight at a time.
+  if (retry_state->started_send_message_count < calld->send_messages.size() &&
+      retry_state->started_send_message_count ==
+          retry_state->completed_send_message_count &&
+      !calld->pending_send_message) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: replaying previously completed "
+              "send_message op",
+              chand, calld);
+    }
+    if (replay_batch_data == nullptr) {
+      replay_batch_data =
+          batch_data_create(elem, 1, true /* set_on_complete */);
+    }
+    add_retriable_send_message_op(elem, retry_state, replay_batch_data);
+  }
+  // send_trailing_metadata.
+  // Note that we only add this op if we have no more send_message ops
+  // to start, since we can't send down any more send_message ops after
+  // send_trailing_metadata.
+  if (calld->seen_send_trailing_metadata &&
+      retry_state->started_send_message_count == calld->send_messages.size() &&
+      !retry_state->started_send_trailing_metadata &&
+      !calld->pending_send_trailing_metadata) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: replaying previously completed "
+              "send_trailing_metadata op",
+              chand, calld);
+    }
+    if (replay_batch_data == nullptr) {
+      replay_batch_data =
+          batch_data_create(elem, 1, true /* set_on_complete */);
+    }
+    add_retriable_send_trailing_metadata_op(calld, retry_state,
+                                            replay_batch_data);
+  }
+  return replay_batch_data;
+}
+
+// Adds subchannel batches for pending batches to batches, updating
+// *num_batches as needed.
+static void add_subchannel_batches_for_pending_batches(
+    grpc_call_element* elem, subchannel_call_retry_state* retry_state,
+    grpc_core::CallCombinerClosureList* closures) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) {
+    pending_batch* pending = &calld->pending_batches[i];
+    grpc_transport_stream_op_batch* batch = pending->batch;
+    if (batch == nullptr) continue;
+    // Skip any batch that either (a) has already been started on this
+    // subchannel call or (b) we can't start yet because we're still
+    // replaying send ops that need to be completed first.
+    // TODO(roth): Note that if any one op in the batch can't be sent
+    // yet due to ops that we're replaying, we don't start any of the ops
+    // in the batch.  This is probably okay, but it could conceivably
+    // lead to increased latency in some cases -- e.g., we could delay
+    // starting a recv op due to it being in the same batch with a send
+    // op.  If/when we revamp the callback protocol in
+    // transport_stream_op_batch, we may be able to fix this.
+    if (batch->send_initial_metadata &&
+        retry_state->started_send_initial_metadata) {
+      continue;
+    }
+    if (batch->send_message && retry_state->completed_send_message_count <
+                                   retry_state->started_send_message_count) {
+      continue;
+    }
+    // Note that we only start send_trailing_metadata if we have no more
+    // send_message ops to start, since we can't send down any more
+    // send_message ops after send_trailing_metadata.
+    if (batch->send_trailing_metadata &&
+        (retry_state->started_send_message_count + batch->send_message <
+             calld->send_messages.size() ||
+         retry_state->started_send_trailing_metadata)) {
+      continue;
+    }
+    if (batch->recv_initial_metadata &&
+        retry_state->started_recv_initial_metadata) {
+      continue;
+    }
+    if (batch->recv_message && retry_state->completed_recv_message_count <
+                                   retry_state->started_recv_message_count) {
+      continue;
+    }
+    if (batch->recv_trailing_metadata &&
+        retry_state->started_recv_trailing_metadata) {
+      // If we previously completed a recv_trailing_metadata op
+      // initiated by start_internal_recv_trailing_metadata(), use the
+      // result of that instead of trying to re-start this op.
+      if (GPR_UNLIKELY((retry_state->recv_trailing_metadata_internal_batch !=
+                        nullptr))) {
+        // If the batch completed, then trigger the completion callback
+        // directly, so that we return the previously returned results to
+        // the application.  Otherwise, just unref the internally
+        // started subchannel batch, since we'll propagate the
+        // completion when it completes.
+        if (retry_state->completed_recv_trailing_metadata) {
+          // Batches containing recv_trailing_metadata always succeed.
+          closures->Add(
+              &retry_state->recv_trailing_metadata_ready, GRPC_ERROR_NONE,
+              "re-executing recv_trailing_metadata_ready to propagate "
+              "internally triggered result");
+        } else {
+          batch_data_unref(retry_state->recv_trailing_metadata_internal_batch);
+        }
+        retry_state->recv_trailing_metadata_internal_batch = nullptr;
+      }
+      continue;
+    }
+    // If we're not retrying, just send the batch as-is.
+    if (calld->method_params == nullptr ||
+        calld->method_params->retry_policy() == nullptr ||
+        calld->retry_committed) {
+      add_closure_for_subchannel_batch(elem, batch, closures);
+      pending_batch_clear(calld, pending);
+      continue;
+    }
+    // Create batch with the right number of callbacks.
+    const bool has_send_ops = batch->send_initial_metadata ||
+                              batch->send_message ||
+                              batch->send_trailing_metadata;
+    const int num_callbacks = has_send_ops + batch->recv_initial_metadata +
+                              batch->recv_message +
+                              batch->recv_trailing_metadata;
+    subchannel_batch_data* batch_data = batch_data_create(
+        elem, num_callbacks, has_send_ops /* set_on_complete */);
+    // Cache send ops if needed.
+    maybe_cache_send_ops_for_batch(calld, pending);
+    // send_initial_metadata.
+    if (batch->send_initial_metadata) {
+      add_retriable_send_initial_metadata_op(calld, retry_state, batch_data);
+    }
+    // send_message.
+    if (batch->send_message) {
+      add_retriable_send_message_op(elem, retry_state, batch_data);
+    }
+    // send_trailing_metadata.
+    if (batch->send_trailing_metadata) {
+      add_retriable_send_trailing_metadata_op(calld, retry_state, batch_data);
+    }
+    // recv_initial_metadata.
+    if (batch->recv_initial_metadata) {
+      // recv_flags is only used on the server side.
+      GPR_ASSERT(batch->payload->recv_initial_metadata.recv_flags == nullptr);
+      add_retriable_recv_initial_metadata_op(calld, retry_state, batch_data);
+    }
+    // recv_message.
+    if (batch->recv_message) {
+      add_retriable_recv_message_op(calld, retry_state, batch_data);
+    }
+    // recv_trailing_metadata.
+    if (batch->recv_trailing_metadata) {
+      add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data);
+    }
+    add_closure_for_subchannel_batch(elem, &batch_data->batch, closures);
+    // Track number of pending subchannel send batches.
+    // If this is the first one, take a ref to the call stack.
+    if (batch->send_initial_metadata || batch->send_message ||
+        batch->send_trailing_metadata) {
+      if (calld->num_pending_retriable_subchannel_send_batches == 0) {
+        GRPC_CALL_STACK_REF(calld->owning_call, "subchannel_send_batches");
+      }
+      ++calld->num_pending_retriable_subchannel_send_batches;
+    }
+  }
+}
+
+// Constructs and starts whatever subchannel batches are needed on the
+// subchannel call.
+static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p: constructing retriable batches",
+            chand, calld);
+  }
+  subchannel_call_retry_state* retry_state =
+      static_cast<subchannel_call_retry_state*>(
+          grpc_connected_subchannel_call_get_parent_data(
+              calld->subchannel_call));
+  // Construct list of closures to execute, one for each pending batch.
+  grpc_core::CallCombinerClosureList closures;
+  // Replay previously-returned send_* ops if needed.
+  subchannel_batch_data* replay_batch_data =
+      maybe_create_subchannel_batch_for_replay(elem, retry_state);
+  if (replay_batch_data != nullptr) {
+    add_closure_for_subchannel_batch(elem, &replay_batch_data->batch,
+                                     &closures);
+    // Track number of pending subchannel send batches.
+    // If this is the first one, take a ref to the call stack.
+    if (calld->num_pending_retriable_subchannel_send_batches == 0) {
+      GRPC_CALL_STACK_REF(calld->owning_call, "subchannel_send_batches");
+    }
+    ++calld->num_pending_retriable_subchannel_send_batches;
+  }
+  // Now add pending batches.
+  add_subchannel_batches_for_pending_batches(elem, retry_state, &closures);
+  // Start batches on subchannel call.
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "chand=%p calld=%p: starting %" PRIuPTR
+            " retriable batches on subchannel_call=%p",
+            chand, calld, closures.size(), calld->subchannel_call);
+  }
+  // Note: This will yield the call combiner.
+  closures.RunClosures(calld->call_combiner);
+}
+
+//
+// LB pick
+//
+
+static void create_subchannel_call(grpc_call_element* elem, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  const size_t parent_data_size =
+      calld->enable_retries ? sizeof(subchannel_call_retry_state) : 0;
+  const grpc_core::ConnectedSubchannel::CallArgs call_args = {
+      calld->pollent,                                   // pollent
+      calld->path,                                      // path
+      calld->call_start_time,                           // start_time
+      calld->deadline,                                  // deadline
+      calld->arena,                                     // arena
+      calld->request->pick()->subchannel_call_context,  // context
+      calld->call_combiner,                             // call_combiner
+      parent_data_size                                  // parent_data_size
+  };
+  grpc_error* new_error =
+      calld->request->pick()->connected_subchannel->CreateCall(
+          call_args, &calld->subchannel_call);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p: create subchannel_call=%p: error=%s",
+            chand, calld, calld->subchannel_call, grpc_error_string(new_error));
+  }
+  if (GPR_UNLIKELY(new_error != GRPC_ERROR_NONE)) {
+    new_error = grpc_error_add_child(new_error, error);
+    pending_batches_fail(elem, new_error, true /* yield_call_combiner */);
+  } else {
+    if (parent_data_size > 0) {
+      new (grpc_connected_subchannel_call_get_parent_data(
+          calld->subchannel_call))
+          subchannel_call_retry_state(
+              calld->request->pick()->subchannel_call_context);
+    }
+    pending_batches_resume(elem);
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// Invoked when a pick is completed, on both success or failure.
+static void pick_done(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (GPR_UNLIKELY(calld->request->pick()->connected_subchannel == nullptr)) {
+    // Failed to create subchannel.
+    // If there was no error, this is an LB policy drop, in which case
+    // we return an error; otherwise, we may retry.
+    grpc_status_code status = GRPC_STATUS_OK;
+    grpc_error_get_status(error, calld->deadline, &status, nullptr, nullptr,
+                          nullptr);
+    if (error == GRPC_ERROR_NONE || !calld->enable_retries ||
+        !maybe_retry(elem, nullptr /* batch_data */, status,
+                     nullptr /* server_pushback_md */)) {
+      grpc_error* new_error =
+          error == GRPC_ERROR_NONE
+              ? GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                    "Call dropped by load balancing policy")
+              : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                    "Failed to create subchannel", &error, 1);
+      if (grpc_client_channel_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "chand=%p calld=%p: failed to create subchannel: error=%s",
+                chand, calld, grpc_error_string(new_error));
+      }
+      pending_batches_fail(elem, new_error, true /* yield_call_combiner */);
+    }
+  } else {
+    /* Create call on subchannel. */
+    create_subchannel_call(elem, GRPC_ERROR_REF(error));
+  }
+}
+
+// If the channel is in TRANSIENT_FAILURE and the call is not
+// wait_for_ready=true, fails the call and returns true.
+static bool fail_call_if_in_transient_failure(grpc_call_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_transport_stream_op_batch* batch = calld->pending_batches[0].batch;
+  if (chand->request_router->GetConnectivityState() ==
+          GRPC_CHANNEL_TRANSIENT_FAILURE &&
+      (batch->payload->send_initial_metadata.send_initial_metadata_flags &
+       GRPC_INITIAL_METADATA_WAIT_FOR_READY) == 0) {
+    pending_batches_fail(
+        elem,
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "channel is in state TRANSIENT_FAILURE"),
+                           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE),
+        true /* yield_call_combiner */);
+    return true;
+  }
+  return false;
+}
+
+// Applies service config to the call.  Must be invoked once we know
+// that the resolver has returned results to the channel.
+static void apply_service_config_to_call_locked(grpc_call_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (grpc_client_channel_trace.enabled()) {
+    gpr_log(GPR_INFO, "chand=%p calld=%p: applying service config to call",
+            chand, calld);
+  }
+  if (chand->retry_throttle_data != nullptr) {
+    calld->retry_throttle_data = chand->retry_throttle_data->Ref();
+  }
+  if (chand->method_params_table != nullptr) {
+    calld->method_params = grpc_core::ServiceConfig::MethodConfigTableLookup(
+        *chand->method_params_table, calld->path);
+    if (calld->method_params != nullptr) {
+      // If the deadline from the service config is shorter than the one
+      // from the client API, reset the deadline timer.
+      if (chand->deadline_checking_enabled &&
+          calld->method_params->timeout() != 0) {
+        const grpc_millis per_method_deadline =
+            grpc_timespec_to_millis_round_up(calld->call_start_time) +
+            calld->method_params->timeout();
+        if (per_method_deadline < calld->deadline) {
+          calld->deadline = per_method_deadline;
+          grpc_deadline_state_reset(elem, calld->deadline);
+        }
+      }
+      // If the service config set wait_for_ready and the application
+      // did not explicitly set it, use the value from the service config.
+      uint32_t* send_initial_metadata_flags =
+          &calld->pending_batches[0]
+               .batch->payload->send_initial_metadata
+               .send_initial_metadata_flags;
+      if (GPR_UNLIKELY(
+              calld->method_params->wait_for_ready() !=
+                  ClientChannelMethodParams::WAIT_FOR_READY_UNSET &&
+              !(*send_initial_metadata_flags &
+                GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET))) {
+        if (calld->method_params->wait_for_ready() ==
+            ClientChannelMethodParams::WAIT_FOR_READY_TRUE) {
+          *send_initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY;
+        } else {
+          *send_initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY;
+        }
+      }
+    }
+  }
+  // If no retry policy, disable retries.
+  // TODO(roth): Remove this when adding support for transparent retries.
+  if (calld->method_params == nullptr ||
+      calld->method_params->retry_policy() == nullptr) {
+    calld->enable_retries = false;
+  }
+}
+
+// Invoked once resolver results are available.
+static bool maybe_apply_service_config_to_call_locked(void* arg) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Only get service config data on the first attempt.
+  if (GPR_LIKELY(calld->num_attempts_completed == 0)) {
+    apply_service_config_to_call_locked(elem);
+    // Check this after applying service config, since it may have
+    // affected the call's wait_for_ready value.
+    if (fail_call_if_in_transient_failure(elem)) return false;
+  }
+  return true;
+}
+
+static void start_pick_locked(void* arg, grpc_error* ignored) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  GPR_ASSERT(!calld->have_request);
+  GPR_ASSERT(calld->subchannel_call == nullptr);
+  // Normally, we want to do this check until after we've processed the
+  // service config, so that we can honor the wait_for_ready setting in
+  // the service config.  However, if the channel is in TRANSIENT_FAILURE
+  // and we don't have an LB policy at this point, that means that the
+  // resolver has returned a failure, so we're not going to get a service
+  // config right away.  In that case, we fail the call now based on the
+  // wait_for_ready value passed in from the application.
+  if (chand->request_router->lb_policy() == nullptr &&
+      fail_call_if_in_transient_failure(elem)) {
+    return;
+  }
+  // If this is a retry, use the send_initial_metadata payload that
+  // we've cached; otherwise, use the pending batch.  The
+  // send_initial_metadata batch will be the first pending batch in the
+  // list, as set by get_batch_index() above.
+  // TODO(roth): What if the LB policy needs to add something to the
+  // call's initial metadata, and then there's a retry?  We don't want
+  // the new metadata to be added twice.  We might need to somehow
+  // allocate the subchannel batch earlier so that we can give the
+  // subchannel's copy of the metadata batch (which is copied for each
+  // attempt) to the LB policy instead the one from the parent channel.
+  grpc_metadata_batch* initial_metadata =
+      calld->seen_send_initial_metadata
+          ? &calld->send_initial_metadata
+          : calld->pending_batches[0]
+                .batch->payload->send_initial_metadata.send_initial_metadata;
+  uint32_t* initial_metadata_flags =
+      calld->seen_send_initial_metadata
+          ? &calld->send_initial_metadata_flags
+          : &calld->pending_batches[0]
+                 .batch->payload->send_initial_metadata
+                 .send_initial_metadata_flags;
+  GRPC_CLOSURE_INIT(&calld->pick_closure, pick_done, elem,
+                    grpc_schedule_on_exec_ctx);
+  calld->request.Init(calld->owning_call, calld->call_combiner, calld->pollent,
+                      initial_metadata, initial_metadata_flags,
+                      maybe_apply_service_config_to_call_locked, elem,
+                      &calld->pick_closure);
+  calld->have_request = true;
+  chand->request_router->RouteCallLocked(calld->request.get());
+}
+
+//
+// filter call vtable functions
+//
+
+static void cc_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  GPR_TIMER_SCOPE("cc_start_transport_stream_op_batch", 0);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  if (GPR_LIKELY(chand->deadline_checking_enabled)) {
+    grpc_deadline_state_client_start_transport_stream_op_batch(elem, batch);
+  }
+  // If we've previously been cancelled, immediately fail any new batches.
+  if (GPR_UNLIKELY(calld->cancel_error != GRPC_ERROR_NONE)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: failing batch with error: %s",
+              chand, calld, grpc_error_string(calld->cancel_error));
+    }
+    // Note: This will release the call combiner.
+    grpc_transport_stream_op_batch_finish_with_failure(
+        batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner);
+    return;
+  }
+  // Handle cancellation.
+  if (GPR_UNLIKELY(batch->cancel_stream)) {
+    // 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 batches 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 batch does get passed down.
+    GRPC_ERROR_UNREF(calld->cancel_error);
+    calld->cancel_error =
+        GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error);
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: recording cancel_error=%s", chand,
+              calld, grpc_error_string(calld->cancel_error));
+    }
+    // If we do not have a subchannel call (i.e., a pick has not yet
+    // been started), fail all pending batches.  Otherwise, send the
+    // cancellation down to the subchannel call.
+    if (calld->subchannel_call == nullptr) {
+      pending_batches_fail(elem, GRPC_ERROR_REF(calld->cancel_error),
+                           false /* yield_call_combiner */);
+      // Note: This will release the call combiner.
+      grpc_transport_stream_op_batch_finish_with_failure(
+          batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner);
+    } else {
+      // Note: This will release the call combiner.
+      grpc_subchannel_call_process_op(calld->subchannel_call, batch);
+    }
+    return;
+  }
+  // Add the batch to the pending list.
+  pending_batches_add(elem, batch);
+  // Check if we've already gotten a subchannel call.
+  // Note that once we have completed the pick, we do not need to enter
+  // the channel combiner, which is more efficient (especially for
+  // streaming calls).
+  if (calld->subchannel_call != nullptr) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: starting batch on subchannel_call=%p", chand,
+              calld, calld->subchannel_call);
+    }
+    pending_batches_resume(elem);
+    return;
+  }
+  // We do not yet have a subchannel call.
+  // For batches containing a send_initial_metadata op, enter the channel
+  // combiner to start a pick.
+  if (GPR_LIKELY(batch->send_initial_metadata)) {
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO, "chand=%p calld=%p: entering client_channel combiner",
+              chand, calld);
+    }
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_INIT(&batch->handler_private.closure, start_pick_locked,
+                          elem, grpc_combiner_scheduler(chand->combiner)),
+        GRPC_ERROR_NONE);
+  } else {
+    // For all other batches, release the call combiner.
+    if (grpc_client_channel_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "chand=%p calld=%p: saved batch, yielding call combiner", chand,
+              calld);
+    }
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "batch does not include send_initial_metadata");
+  }
+}
+
+/* Constructor for call_data */
+static grpc_error* cc_init_call_elem(grpc_call_element* elem,
+                                     const grpc_call_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  new (elem->call_data) call_data(elem, *chand, *args);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for call_data */
+static void cc_destroy_call_elem(grpc_call_element* elem,
+                                 const grpc_call_final_info* final_info,
+                                 grpc_closure* then_schedule_closure) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (GPR_LIKELY(calld->subchannel_call != nullptr)) {
+    grpc_subchannel_call_set_cleanup_closure(calld->subchannel_call,
+                                             then_schedule_closure);
+    then_schedule_closure = nullptr;
+  }
+  calld->~call_data();
+  GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE);
+}
+
+static void cc_set_pollset_or_pollset_set(grpc_call_element* elem,
+                                          grpc_polling_entity* pollent) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->pollent = pollent;
+}
+
+/*************************************************************************
+ * EXPORTED SYMBOLS
+ */
+
+const grpc_channel_filter grpc_client_channel_filter = {
+    cc_start_transport_stream_op_batch,
+    cc_start_transport_op,
+    sizeof(call_data),
+    cc_init_call_elem,
+    cc_set_pollset_or_pollset_set,
+    cc_destroy_call_elem,
+    sizeof(channel_data),
+    cc_init_channel_elem,
+    cc_destroy_channel_elem,
+    cc_get_channel_info,
+    "client-channel",
+};
+
+void grpc_client_channel_set_channelz_node(
+    grpc_channel_element* elem, grpc_core::channelz::ClientChannelNode* node) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  chand->request_router->set_channelz_node(node);
+}
+
+void grpc_client_channel_populate_child_refs(
+    grpc_channel_element* elem,
+    grpc_core::channelz::ChildRefsList* child_subchannels,
+    grpc_core::channelz::ChildRefsList* child_channels) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  if (chand->request_router->lb_policy() != nullptr) {
+    chand->request_router->lb_policy()->FillChildRefsForChannelz(
+        child_subchannels, child_channels);
+  }
+}
+
+static void try_to_connect_locked(void* arg, grpc_error* error_ignored) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  chand->request_router->ExitIdleLocked();
+  GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "try_to_connect");
+}
+
+grpc_connectivity_state grpc_client_channel_check_connectivity_state(
+    grpc_channel_element* elem, int try_to_connect) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  grpc_connectivity_state out = chand->request_router->GetConnectivityState();
+  if (out == GRPC_CHANNEL_IDLE && try_to_connect) {
+    GRPC_CHANNEL_STACK_REF(chand->owning_stack, "try_to_connect");
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_CREATE(try_to_connect_locked, chand,
+                            grpc_combiner_scheduler(chand->combiner)),
+        GRPC_ERROR_NONE);
+  }
+  return out;
+}
+
+typedef struct external_connectivity_watcher {
+  channel_data* chand;
+  grpc_polling_entity pollent;
+  grpc_closure* on_complete;
+  grpc_closure* watcher_timer_init;
+  grpc_connectivity_state* state;
+  grpc_closure my_closure;
+  struct external_connectivity_watcher* next;
+} external_connectivity_watcher;
+
+static external_connectivity_watcher* lookup_external_connectivity_watcher(
+    channel_data* chand, grpc_closure* on_complete) {
+  gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
+  external_connectivity_watcher* w =
+      chand->external_connectivity_watcher_list_head;
+  while (w != nullptr && w->on_complete != on_complete) {
+    w = w->next;
+  }
+  gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
+  return w;
+}
+
+static void external_connectivity_watcher_list_append(
+    channel_data* chand, external_connectivity_watcher* w) {
+  GPR_ASSERT(!lookup_external_connectivity_watcher(chand, w->on_complete));
+
+  gpr_mu_lock(&w->chand->external_connectivity_watcher_list_mu);
+  GPR_ASSERT(!w->next);
+  w->next = chand->external_connectivity_watcher_list_head;
+  chand->external_connectivity_watcher_list_head = w;
+  gpr_mu_unlock(&w->chand->external_connectivity_watcher_list_mu);
+}
+
+static void external_connectivity_watcher_list_remove(
+    channel_data* chand, external_connectivity_watcher* to_remove) {
+  GPR_ASSERT(
+      lookup_external_connectivity_watcher(chand, to_remove->on_complete));
+  gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
+  if (to_remove == chand->external_connectivity_watcher_list_head) {
+    chand->external_connectivity_watcher_list_head = to_remove->next;
+    gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
+    return;
+  }
+  external_connectivity_watcher* w =
+      chand->external_connectivity_watcher_list_head;
+  while (w != nullptr) {
+    if (w->next == to_remove) {
+      w->next = w->next->next;
+      gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
+      return;
+    }
+    w = w->next;
+  }
+  GPR_UNREACHABLE_CODE(return );
+}
+
+int grpc_client_channel_num_external_connectivity_watchers(
+    grpc_channel_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  int count = 0;
+
+  gpr_mu_lock(&chand->external_connectivity_watcher_list_mu);
+  external_connectivity_watcher* w =
+      chand->external_connectivity_watcher_list_head;
+  while (w != nullptr) {
+    count++;
+    w = w->next;
+  }
+  gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu);
+
+  return count;
+}
+
+static void on_external_watch_complete_locked(void* arg, grpc_error* error) {
+  external_connectivity_watcher* w =
+      static_cast<external_connectivity_watcher*>(arg);
+  grpc_closure* follow_up = w->on_complete;
+  grpc_polling_entity_del_from_pollset_set(&w->pollent,
+                                           w->chand->interested_parties);
+  GRPC_CHANNEL_STACK_UNREF(w->chand->owning_stack,
+                           "external_connectivity_watcher");
+  external_connectivity_watcher_list_remove(w->chand, w);
+  gpr_free(w);
+  GRPC_CLOSURE_SCHED(follow_up, GRPC_ERROR_REF(error));
+}
+
+static void watch_connectivity_state_locked(void* arg,
+                                            grpc_error* error_ignored) {
+  external_connectivity_watcher* w =
+      static_cast<external_connectivity_watcher*>(arg);
+  external_connectivity_watcher* found = nullptr;
+  if (w->state != nullptr) {
+    external_connectivity_watcher_list_append(w->chand, w);
+    // An assumption is being made that the closure is scheduled on the exec ctx
+    // scheduler and that GRPC_CLOSURE_RUN would run the closure immediately.
+    GRPC_CLOSURE_RUN(w->watcher_timer_init, GRPC_ERROR_NONE);
+    GRPC_CLOSURE_INIT(&w->my_closure, on_external_watch_complete_locked, w,
+                      grpc_combiner_scheduler(w->chand->combiner));
+    w->chand->request_router->NotifyOnConnectivityStateChange(w->state,
+                                                              &w->my_closure);
+  } else {
+    GPR_ASSERT(w->watcher_timer_init == nullptr);
+    found = lookup_external_connectivity_watcher(w->chand, w->on_complete);
+    if (found) {
+      GPR_ASSERT(found->on_complete == w->on_complete);
+      found->chand->request_router->NotifyOnConnectivityStateChange(
+          nullptr, &found->my_closure);
+    }
+    grpc_polling_entity_del_from_pollset_set(&w->pollent,
+                                             w->chand->interested_parties);
+    GRPC_CHANNEL_STACK_UNREF(w->chand->owning_stack,
+                             "external_connectivity_watcher");
+    gpr_free(w);
+  }
+}
+
+void grpc_client_channel_watch_connectivity_state(
+    grpc_channel_element* elem, grpc_polling_entity pollent,
+    grpc_connectivity_state* state, grpc_closure* closure,
+    grpc_closure* watcher_timer_init) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  external_connectivity_watcher* w =
+      static_cast<external_connectivity_watcher*>(gpr_zalloc(sizeof(*w)));
+  w->chand = chand;
+  w->pollent = pollent;
+  w->on_complete = closure;
+  w->state = state;
+  w->watcher_timer_init = watcher_timer_init;
+  grpc_polling_entity_add_to_pollset_set(&w->pollent,
+                                         chand->interested_parties);
+  GRPC_CHANNEL_STACK_REF(w->chand->owning_stack,
+                         "external_connectivity_watcher");
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&w->my_closure, watch_connectivity_state_locked, w,
+                        grpc_combiner_scheduler(chand->combiner)),
+      GRPC_ERROR_NONE);
+}
+
+grpc_subchannel_call* grpc_client_channel_get_subchannel_call(
+    grpc_call_element* elem) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  return calld->subchannel_call;
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel.h b/third_party/grpc/src/core/ext/filters/client_channel/client_channel.h
new file mode 100644
index 0000000..4935fd2
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
+#include "src/core/ext/filters/client_channel/client_channel_factory.h"
+#include "src/core/ext/filters/client_channel/resolver.h"
+#include "src/core/lib/channel/channel_stack.h"
+
+extern grpc_core::TraceFlag grpc_client_channel_trace;
+
+// Channel arg key for server URI string.
+#define GRPC_ARG_SERVER_URI "grpc.server_uri"
+
+/* A client channel is a channel that begins disconnected, and can connect
+   to some endpoint on demand. If that endpoint disconnects, it will be
+   connected to again later.
+
+   Calls on a disconnected client channel are queued until a connection is
+   established. */
+
+extern const grpc_channel_filter grpc_client_channel_filter;
+
+void grpc_client_channel_set_channelz_node(
+    grpc_channel_element* elem, grpc_core::channelz::ClientChannelNode* node);
+
+void grpc_client_channel_populate_child_refs(
+    grpc_channel_element* elem,
+    grpc_core::channelz::ChildRefsList* child_subchannels,
+    grpc_core::channelz::ChildRefsList* child_channels);
+
+grpc_connectivity_state grpc_client_channel_check_connectivity_state(
+    grpc_channel_element* elem, int try_to_connect);
+
+int grpc_client_channel_num_external_connectivity_watchers(
+    grpc_channel_element* elem);
+
+void grpc_client_channel_watch_connectivity_state(
+    grpc_channel_element* elem, grpc_polling_entity pollent,
+    grpc_connectivity_state* state, grpc_closure* on_complete,
+    grpc_closure* watcher_timer_init);
+
+/* Debug helper: pull the subchannel call from a call stack element */
+grpc_subchannel_call* grpc_client_channel_get_subchannel_call(
+    grpc_call_element* elem);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.cc b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.cc
new file mode 100644
index 0000000..8e54260
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.cc
@@ -0,0 +1,186 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
+#include "src/core/lib/channel/channelz_registry.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/connectivity_state.h"
+
+#include <grpc/support/string_util.h>
+
+namespace grpc_core {
+namespace channelz {
+namespace {
+
+void* client_channel_channelz_copy(void* p) { return p; }
+
+void client_channel_channelz_destroy(void* p) {}
+
+int client_channel_channelz_cmp(void* a, void* b) { return GPR_ICMP(a, b); }
+
+}  // namespace
+
+static const grpc_arg_pointer_vtable client_channel_channelz_vtable = {
+    client_channel_channelz_copy, client_channel_channelz_destroy,
+    client_channel_channelz_cmp};
+
+ClientChannelNode::ClientChannelNode(grpc_channel* channel,
+                                     size_t channel_tracer_max_nodes,
+                                     bool is_top_level_channel)
+    : ChannelNode(channel, channel_tracer_max_nodes, is_top_level_channel) {
+  client_channel_ =
+      grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
+  grpc_client_channel_set_channelz_node(client_channel_, this);
+  GPR_ASSERT(client_channel_->filter == &grpc_client_channel_filter);
+}
+
+void ClientChannelNode::PopulateConnectivityState(grpc_json* json) {
+  grpc_connectivity_state state;
+  if (ChannelIsDestroyed()) {
+    state = GRPC_CHANNEL_SHUTDOWN;
+  } else {
+    state =
+        grpc_client_channel_check_connectivity_state(client_channel_, false);
+  }
+  json = grpc_json_create_child(nullptr, json, "state", nullptr,
+                                GRPC_JSON_OBJECT, false);
+  grpc_json_create_child(nullptr, json, "state",
+                         grpc_connectivity_state_name(state), GRPC_JSON_STRING,
+                         false);
+}
+
+void ClientChannelNode::PopulateChildRefs(grpc_json* json) {
+  ChildRefsList child_subchannels;
+  ChildRefsList child_channels;
+  grpc_json* json_iterator = nullptr;
+  grpc_client_channel_populate_child_refs(client_channel_, &child_subchannels,
+                                          &child_channels);
+  if (!child_subchannels.empty()) {
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "subchannelRef", nullptr, GRPC_JSON_ARRAY, false);
+    for (size_t i = 0; i < child_subchannels.size(); ++i) {
+      json_iterator =
+          grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
+                                 GRPC_JSON_OBJECT, false);
+      grpc_json_add_number_string_child(json_iterator, nullptr, "subchannelId",
+                                        child_subchannels[i]);
+    }
+  }
+  if (!child_channels.empty()) {
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "channelRef", nullptr, GRPC_JSON_ARRAY, false);
+    json_iterator = nullptr;
+    for (size_t i = 0; i < child_channels.size(); ++i) {
+      json_iterator =
+          grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
+                                 GRPC_JSON_OBJECT, false);
+      grpc_json_add_number_string_child(json_iterator, nullptr, "channelId",
+                                        child_channels[i]);
+    }
+  }
+}
+
+grpc_arg ClientChannelNode::CreateChannelArg() {
+  return grpc_channel_arg_pointer_create(
+      const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC),
+      reinterpret_cast<void*>(MakeClientChannelNode),
+      &client_channel_channelz_vtable);
+}
+
+RefCountedPtr<ChannelNode> ClientChannelNode::MakeClientChannelNode(
+    grpc_channel* channel, size_t channel_tracer_max_nodes,
+    bool is_top_level_channel) {
+  return MakeRefCounted<ClientChannelNode>(channel, channel_tracer_max_nodes,
+                                           is_top_level_channel);
+}
+
+SubchannelNode::SubchannelNode(grpc_subchannel* subchannel,
+                               size_t channel_tracer_max_nodes)
+    : BaseNode(EntityType::kSubchannel),
+      subchannel_(subchannel),
+      target_(
+          UniquePtr<char>(gpr_strdup(grpc_subchannel_get_target(subchannel_)))),
+      trace_(channel_tracer_max_nodes) {}
+
+SubchannelNode::~SubchannelNode() {}
+
+void SubchannelNode::PopulateConnectivityState(grpc_json* json) {
+  grpc_connectivity_state state;
+  if (subchannel_ == nullptr) {
+    state = GRPC_CHANNEL_SHUTDOWN;
+  } else {
+    state = grpc_subchannel_check_connectivity(
+        subchannel_, nullptr, true /* inhibit_health_checking */);
+  }
+  json = grpc_json_create_child(nullptr, json, "state", nullptr,
+                                GRPC_JSON_OBJECT, false);
+  grpc_json_create_child(nullptr, json, "state",
+                         grpc_connectivity_state_name(state), GRPC_JSON_STRING,
+                         false);
+}
+
+grpc_json* SubchannelNode::RenderJson() {
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "subchannelId", uuid());
+  // reset json iterators to top level object
+  json = top_level_json;
+  json_iterator = nullptr;
+  // create and fill the data child.
+  grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
+                                           GRPC_JSON_OBJECT, false);
+  json = data;
+  json_iterator = nullptr;
+  PopulateConnectivityState(json);
+  GPR_ASSERT(target_.get() != nullptr);
+  grpc_json_create_child(nullptr, json, "target", target_.get(),
+                         GRPC_JSON_STRING, false);
+  // fill in the channel trace if applicable
+  grpc_json* trace_json = trace_.RenderJson();
+  if (trace_json != nullptr) {
+    trace_json->key = "trace";  // this object is named trace in channelz.proto
+    grpc_json_link_child(json, trace_json, nullptr);
+  }
+  // ask CallCountingHelper to populate trace and call count data.
+  call_counter_.PopulateCallCounts(json);
+  json = top_level_json;
+  // populate the child socket.
+  intptr_t socket_uuid = grpc_subchannel_get_child_socket_uuid(subchannel_);
+  if (socket_uuid != 0) {
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false);
+    json_iterator = grpc_json_create_child(json_iterator, array_parent, nullptr,
+                                           nullptr, GRPC_JSON_OBJECT, false);
+    grpc_json_add_number_string_child(json_iterator, nullptr, "socketId",
+                                      socket_uuid);
+  }
+  return top_level_json;
+}
+
+}  // namespace channelz
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.h b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.h
new file mode 100644
index 0000000..8a5c3e7
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.h
@@ -0,0 +1,98 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_CHANNELZ_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_CHANNELZ_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/channel/channelz.h"
+
+typedef struct grpc_subchannel grpc_subchannel;
+
+namespace grpc_core {
+namespace channelz {
+
+// Subtype of ChannelNode that overrides and provides client_channel specific
+// functionality like querying for connectivity_state and subchannel data.
+class ClientChannelNode : public ChannelNode {
+ public:
+  static RefCountedPtr<ChannelNode> MakeClientChannelNode(
+      grpc_channel* channel, size_t channel_tracer_max_nodes,
+      bool is_top_level_channel);
+
+  ClientChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
+                    bool is_top_level_channel);
+  virtual ~ClientChannelNode() {}
+
+  // Overriding template methods from ChannelNode to render information that
+  // only ClientChannelNode knows about.
+  void PopulateConnectivityState(grpc_json* json) override;
+  void PopulateChildRefs(grpc_json* json) override;
+
+  // Helper to create a channel arg to ensure this type of ChannelNode is
+  // created.
+  static grpc_arg CreateChannelArg();
+
+ private:
+  grpc_channel_element* client_channel_;
+};
+
+// Handles channelz bookkeeping for sockets
+class SubchannelNode : public BaseNode {
+ public:
+  SubchannelNode(grpc_subchannel* subchannel, size_t channel_tracer_max_nodes);
+  ~SubchannelNode() override;
+
+  void MarkSubchannelDestroyed() {
+    GPR_ASSERT(subchannel_ != nullptr);
+    subchannel_ = nullptr;
+  }
+
+  grpc_json* RenderJson() override;
+
+  // proxy methods to composed classes.
+  void AddTraceEvent(ChannelTrace::Severity severity, grpc_slice data) {
+    trace_.AddTraceEvent(severity, data);
+  }
+  void AddTraceEventWithReference(ChannelTrace::Severity severity,
+                                  grpc_slice data,
+                                  RefCountedPtr<BaseNode> referenced_channel) {
+    trace_.AddTraceEventWithReference(severity, data,
+                                      std::move(referenced_channel));
+  }
+  void RecordCallStarted() { call_counter_.RecordCallStarted(); }
+  void RecordCallFailed() { call_counter_.RecordCallFailed(); }
+  void RecordCallSucceeded() { call_counter_.RecordCallSucceeded(); }
+
+ private:
+  grpc_subchannel* subchannel_;
+  UniquePtr<char> target_;
+  CallCountingHelper call_counter_;
+  ChannelTrace trace_;
+
+  void PopulateConnectivityState(grpc_json* json);
+};
+
+}  // namespace channelz
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_CHANNELZ_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.cc b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.cc
new file mode 100644
index 0000000..172e9f0
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.cc
@@ -0,0 +1,67 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/client_channel_factory.h"
+#include "src/core/lib/channel/channel_args.h"
+
+void grpc_client_channel_factory_ref(grpc_client_channel_factory* factory) {
+  factory->vtable->ref(factory);
+}
+
+void grpc_client_channel_factory_unref(grpc_client_channel_factory* factory) {
+  factory->vtable->unref(factory);
+}
+
+grpc_subchannel* grpc_client_channel_factory_create_subchannel(
+    grpc_client_channel_factory* factory, const grpc_subchannel_args* args) {
+  return factory->vtable->create_subchannel(factory, args);
+}
+
+grpc_channel* grpc_client_channel_factory_create_channel(
+    grpc_client_channel_factory* factory, const char* target,
+    grpc_client_channel_type type, const grpc_channel_args* args) {
+  return factory->vtable->create_client_channel(factory, target, type, args);
+}
+
+static void* factory_arg_copy(void* factory) {
+  grpc_client_channel_factory_ref(
+      static_cast<grpc_client_channel_factory*>(factory));
+  return factory;
+}
+
+static void factory_arg_destroy(void* factory) {
+  grpc_client_channel_factory_unref(
+      static_cast<grpc_client_channel_factory*>(factory));
+}
+
+static int factory_arg_cmp(void* factory1, void* factory2) {
+  if (factory1 < factory2) return -1;
+  if (factory1 > factory2) return 1;
+  return 0;
+}
+
+static const grpc_arg_pointer_vtable factory_arg_vtable = {
+    factory_arg_copy, factory_arg_destroy, factory_arg_cmp};
+
+grpc_arg grpc_client_channel_factory_create_channel_arg(
+    grpc_client_channel_factory* factory) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_ARG_CLIENT_CHANNEL_FACTORY,
+                                         factory, &factory_arg_vtable);
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.h b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.h
new file mode 100644
index 0000000..601ec46
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/lib/channel/channel_stack.h"
+
+// Channel arg key for client channel factory.
+#define GRPC_ARG_CLIENT_CHANNEL_FACTORY "grpc.client_channel_factory"
+
+typedef struct grpc_client_channel_factory grpc_client_channel_factory;
+typedef struct grpc_client_channel_factory_vtable
+    grpc_client_channel_factory_vtable;
+
+typedef enum {
+  GRPC_CLIENT_CHANNEL_TYPE_REGULAR, /** for the user-level regular calls */
+  GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, /** for communication with a load
+                                              balancing service */
+} grpc_client_channel_type;
+
+/** Constructor for new configured channels.
+    Creating decorators around this type is encouraged to adapt behavior. */
+struct grpc_client_channel_factory {
+  const grpc_client_channel_factory_vtable* vtable;
+};
+
+struct grpc_client_channel_factory_vtable {
+  void (*ref)(grpc_client_channel_factory* factory);
+  void (*unref)(grpc_client_channel_factory* factory);
+  grpc_subchannel* (*create_subchannel)(grpc_client_channel_factory* factory,
+                                        const grpc_subchannel_args* args);
+  grpc_channel* (*create_client_channel)(grpc_client_channel_factory* factory,
+                                         const char* target,
+                                         grpc_client_channel_type type,
+                                         const grpc_channel_args* args);
+};
+
+void grpc_client_channel_factory_ref(grpc_client_channel_factory* factory);
+void grpc_client_channel_factory_unref(grpc_client_channel_factory* factory);
+
+/** Create a new grpc_subchannel */
+grpc_subchannel* grpc_client_channel_factory_create_subchannel(
+    grpc_client_channel_factory* factory, const grpc_subchannel_args* args);
+
+/** Create a new grpc_channel */
+grpc_channel* grpc_client_channel_factory_create_channel(
+    grpc_client_channel_factory* factory, const char* target,
+    grpc_client_channel_type type, const grpc_channel_args* args);
+
+grpc_arg grpc_client_channel_factory_create_channel_arg(
+    grpc_client_channel_factory* factory);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_plugin.cc b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_plugin.cc
new file mode 100644
index 0000000..e0784b7e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_plugin.cc
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <limits.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
+#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
+#include "src/core/ext/filters/client_channel/http_proxy.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/retry_throttle.h"
+#include "src/core/ext/filters/client_channel/subchannel_index.h"
+#include "src/core/lib/surface/channel_init.h"
+
+static bool append_filter(grpc_channel_stack_builder* builder, void* arg) {
+  const grpc_channel_args* args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  grpc_arg args_to_add[] = {
+      grpc_core::channelz::ClientChannelNode::CreateChannelArg()};
+  grpc_channel_args* new_args = grpc_channel_args_copy_and_add(
+      args, args_to_add, GPR_ARRAY_SIZE(args_to_add));
+  grpc_channel_stack_builder_set_channel_arguments(builder, new_args);
+  grpc_channel_args_destroy(new_args);
+  return grpc_channel_stack_builder_append_filter(
+      builder, static_cast<const grpc_channel_filter*>(arg), nullptr, nullptr);
+}
+
+void grpc_client_channel_init(void) {
+  grpc_core::LoadBalancingPolicyRegistry::Builder::InitRegistry();
+  grpc_core::ResolverRegistry::Builder::InitRegistry();
+  grpc_core::internal::ServerRetryThrottleMap::Init();
+  grpc_proxy_mapper_registry_init();
+  grpc_register_http_proxy_mapper();
+  grpc_subchannel_index_init();
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, append_filter,
+      (void*)&grpc_client_channel_filter);
+  grpc_http_connect_register_handshaker_factory();
+}
+
+void grpc_client_channel_shutdown(void) {
+  grpc_subchannel_index_shutdown();
+  grpc_channel_init_shutdown();
+  grpc_proxy_mapper_registry_shutdown();
+  grpc_core::internal::ServerRetryThrottleMap::Shutdown();
+  grpc_core::ResolverRegistry::Builder::ShutdownRegistry();
+  grpc_core::LoadBalancingPolicyRegistry::Builder::ShutdownRegistry();
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/connector.cc b/third_party/grpc/src/core/ext/filters/client_channel/connector.cc
new file mode 100644
index 0000000..5e04b3b
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/connector.cc
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/connector.h"
+
+grpc_connector* grpc_connector_ref(grpc_connector* connector) {
+  connector->vtable->ref(connector);
+  return connector;
+}
+
+void grpc_connector_unref(grpc_connector* connector) {
+  connector->vtable->unref(connector);
+}
+
+void grpc_connector_connect(grpc_connector* connector,
+                            const grpc_connect_in_args* in_args,
+                            grpc_connect_out_args* out_args,
+                            grpc_closure* notify) {
+  connector->vtable->connect(connector, in_args, out_args, notify);
+}
+
+void grpc_connector_shutdown(grpc_connector* connector, grpc_error* why) {
+  connector->vtable->shutdown(connector, why);
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/connector.h b/third_party/grpc/src/core/ext/filters/client_channel/connector.h
new file mode 100644
index 0000000..ea34dcd
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/connector.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/transport/transport.h"
+
+typedef struct grpc_connector grpc_connector;
+typedef struct grpc_connector_vtable grpc_connector_vtable;
+
+struct grpc_connector {
+  const grpc_connector_vtable* vtable;
+};
+
+typedef struct {
+  /** set of pollsets interested in this connection */
+  grpc_pollset_set* interested_parties;
+  /** deadline for connection */
+  grpc_millis deadline;
+  /** channel arguments (to be passed to transport) */
+  const grpc_channel_args* channel_args;
+} grpc_connect_in_args;
+
+typedef struct {
+  /** the connected transport */
+  grpc_transport* transport;
+
+  /** channel arguments (to be passed to the filters) */
+  grpc_channel_args* channel_args;
+
+  /** socket uuid of the connected transport. 0 if not available */
+  intptr_t socket_uuid;
+} grpc_connect_out_args;
+
+struct grpc_connector_vtable {
+  void (*ref)(grpc_connector* connector);
+  void (*unref)(grpc_connector* connector);
+  /** Implementation of grpc_connector_shutdown */
+  void (*shutdown)(grpc_connector* connector, grpc_error* why);
+  /** Implementation of grpc_connector_connect */
+  void (*connect)(grpc_connector* connector,
+                  const grpc_connect_in_args* in_args,
+                  grpc_connect_out_args* out_args, grpc_closure* notify);
+};
+
+grpc_connector* grpc_connector_ref(grpc_connector* connector);
+void grpc_connector_unref(grpc_connector* connector);
+/** Connect using the connector: max one outstanding call at a time */
+void grpc_connector_connect(grpc_connector* connector,
+                            const grpc_connect_in_args* in_args,
+                            grpc_connect_out_args* out_args,
+                            grpc_closure* notify);
+/** Cancel any pending connection */
+void grpc_connector_shutdown(grpc_connector* connector, grpc_error* why);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.c b/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.c
new file mode 100644
index 0000000..5499c54
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.c
@@ -0,0 +1,23 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/ext/filters/client_channel/health/health.pb.h"
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_health_v1_HealthCheckRequest_fields[2] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, STATIC  , FIRST, grpc_health_v1_HealthCheckRequest, service, service, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_health_v1_HealthCheckResponse_fields[2] = {
+    PB_FIELD(  1, UENUM   , OPTIONAL, STATIC  , FIRST, grpc_health_v1_HealthCheckResponse, status, status, 0),
+    PB_LAST_FIELD
+};
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.h b/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.h
new file mode 100644
index 0000000..9d54ccd
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.h
@@ -0,0 +1,73 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GRPC_HEALTH_V1_HEALTH_PB_H_INCLUDED
+#define PB_GRPC_HEALTH_V1_HEALTH_PB_H_INCLUDED
+#include "pb.h"
+/* @@protoc_insertion_point(includes) */
+#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 _grpc_health_v1_HealthCheckResponse_ServingStatus {
+    grpc_health_v1_HealthCheckResponse_ServingStatus_UNKNOWN = 0,
+    grpc_health_v1_HealthCheckResponse_ServingStatus_SERVING = 1,
+    grpc_health_v1_HealthCheckResponse_ServingStatus_NOT_SERVING = 2,
+    grpc_health_v1_HealthCheckResponse_ServingStatus_SERVICE_UNKNOWN = 3
+} grpc_health_v1_HealthCheckResponse_ServingStatus;
+#define _grpc_health_v1_HealthCheckResponse_ServingStatus_MIN grpc_health_v1_HealthCheckResponse_ServingStatus_UNKNOWN
+#define _grpc_health_v1_HealthCheckResponse_ServingStatus_MAX grpc_health_v1_HealthCheckResponse_ServingStatus_SERVICE_UNKNOWN
+#define _grpc_health_v1_HealthCheckResponse_ServingStatus_ARRAYSIZE ((grpc_health_v1_HealthCheckResponse_ServingStatus)(grpc_health_v1_HealthCheckResponse_ServingStatus_SERVICE_UNKNOWN+1))
+
+/* Struct definitions */
+typedef struct _grpc_health_v1_HealthCheckRequest {
+    bool has_service;
+    char service[200];
+/* @@protoc_insertion_point(struct:grpc_health_v1_HealthCheckRequest) */
+} grpc_health_v1_HealthCheckRequest;
+
+typedef struct _grpc_health_v1_HealthCheckResponse {
+    bool has_status;
+    grpc_health_v1_HealthCheckResponse_ServingStatus status;
+/* @@protoc_insertion_point(struct:grpc_health_v1_HealthCheckResponse) */
+} grpc_health_v1_HealthCheckResponse;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_health_v1_HealthCheckRequest_init_default {false, ""}
+#define grpc_health_v1_HealthCheckResponse_init_default {false, (grpc_health_v1_HealthCheckResponse_ServingStatus)0}
+#define grpc_health_v1_HealthCheckRequest_init_zero {false, ""}
+#define grpc_health_v1_HealthCheckResponse_init_zero {false, (grpc_health_v1_HealthCheckResponse_ServingStatus)0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_health_v1_HealthCheckRequest_service_tag 1
+#define grpc_health_v1_HealthCheckResponse_status_tag 1
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_health_v1_HealthCheckRequest_fields[2];
+extern const pb_field_t grpc_health_v1_HealthCheckResponse_fields[2];
+
+/* Maximum encoded size of messages (where known) */
+#define grpc_health_v1_HealthCheckRequest_size   203
+#define grpc_health_v1_HealthCheckResponse_size  2
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define HEALTH_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.cc b/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.cc
new file mode 100644
index 0000000..2232c57
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.cc
@@ -0,0 +1,650 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "src/core/ext/filters/client_channel/health/health_check_client.h"
+
+#include "pb_decode.h"
+#include "pb_encode.h"
+#include "src/core/ext/filters/client_channel/health/health.pb.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/transport/status_metadata.h"
+
+#define HEALTH_CHECK_INITIAL_CONNECT_BACKOFF_SECONDS 1
+#define HEALTH_CHECK_RECONNECT_BACKOFF_MULTIPLIER 1.6
+#define HEALTH_CHECK_RECONNECT_MAX_BACKOFF_SECONDS 120
+#define HEALTH_CHECK_RECONNECT_JITTER 0.2
+
+grpc_core::TraceFlag grpc_health_check_client_trace(false,
+                                                    "health_check_client");
+
+namespace grpc_core {
+
+//
+// HealthCheckClient
+//
+
+HealthCheckClient::HealthCheckClient(
+    const char* service_name,
+    RefCountedPtr<ConnectedSubchannel> connected_subchannel,
+    grpc_pollset_set* interested_parties,
+    grpc_core::RefCountedPtr<grpc_core::channelz::SubchannelNode> channelz_node)
+    : InternallyRefCounted<HealthCheckClient>(&grpc_health_check_client_trace),
+      service_name_(service_name),
+      connected_subchannel_(std::move(connected_subchannel)),
+      interested_parties_(interested_parties),
+      channelz_node_(std::move(channelz_node)),
+      retry_backoff_(
+          BackOff::Options()
+              .set_initial_backoff(
+                  HEALTH_CHECK_INITIAL_CONNECT_BACKOFF_SECONDS * 1000)
+              .set_multiplier(HEALTH_CHECK_RECONNECT_BACKOFF_MULTIPLIER)
+              .set_jitter(HEALTH_CHECK_RECONNECT_JITTER)
+              .set_max_backoff(HEALTH_CHECK_RECONNECT_MAX_BACKOFF_SECONDS *
+                               1000)) {
+  if (grpc_health_check_client_trace.enabled()) {
+    gpr_log(GPR_INFO, "created HealthCheckClient %p", this);
+  }
+  GRPC_CLOSURE_INIT(&retry_timer_callback_, OnRetryTimer, this,
+                    grpc_schedule_on_exec_ctx);
+  gpr_mu_init(&mu_);
+  StartCall();
+}
+
+HealthCheckClient::~HealthCheckClient() {
+  if (grpc_health_check_client_trace.enabled()) {
+    gpr_log(GPR_INFO, "destroying HealthCheckClient %p", this);
+  }
+  GRPC_ERROR_UNREF(error_);
+  gpr_mu_destroy(&mu_);
+}
+
+void HealthCheckClient::NotifyOnHealthChange(grpc_connectivity_state* state,
+                                             grpc_closure* closure) {
+  MutexLock lock(&mu_);
+  GPR_ASSERT(notify_state_ == nullptr);
+  if (*state != state_) {
+    *state = state_;
+    GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_REF(error_));
+    return;
+  }
+  notify_state_ = state;
+  on_health_changed_ = closure;
+}
+
+void HealthCheckClient::SetHealthStatus(grpc_connectivity_state state,
+                                        grpc_error* error) {
+  MutexLock lock(&mu_);
+  SetHealthStatusLocked(state, error);
+}
+
+void HealthCheckClient::SetHealthStatusLocked(grpc_connectivity_state state,
+                                              grpc_error* error) {
+  if (grpc_health_check_client_trace.enabled()) {
+    gpr_log(GPR_INFO, "HealthCheckClient %p: setting state=%d error=%s", this,
+            state, grpc_error_string(error));
+  }
+  if (notify_state_ != nullptr && *notify_state_ != state) {
+    *notify_state_ = state;
+    notify_state_ = nullptr;
+    GRPC_CLOSURE_SCHED(on_health_changed_, GRPC_ERROR_REF(error));
+    on_health_changed_ = nullptr;
+  }
+  state_ = state;
+  GRPC_ERROR_UNREF(error_);
+  error_ = error;
+}
+
+void HealthCheckClient::Orphan() {
+  if (grpc_health_check_client_trace.enabled()) {
+    gpr_log(GPR_INFO, "HealthCheckClient %p: shutting down", this);
+  }
+  {
+    MutexLock lock(&mu_);
+    if (on_health_changed_ != nullptr) {
+      *notify_state_ = GRPC_CHANNEL_SHUTDOWN;
+      notify_state_ = nullptr;
+      GRPC_CLOSURE_SCHED(on_health_changed_, GRPC_ERROR_NONE);
+      on_health_changed_ = nullptr;
+    }
+    shutting_down_ = true;
+    call_state_.reset();
+    if (retry_timer_callback_pending_) {
+      grpc_timer_cancel(&retry_timer_);
+    }
+  }
+  Unref(DEBUG_LOCATION, "orphan");
+}
+
+void HealthCheckClient::StartCall() {
+  MutexLock lock(&mu_);
+  StartCallLocked();
+}
+
+void HealthCheckClient::StartCallLocked() {
+  if (shutting_down_) return;
+  GPR_ASSERT(call_state_ == nullptr);
+  SetHealthStatusLocked(GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE);
+  call_state_ = MakeOrphanable<CallState>(Ref(), interested_parties_);
+  if (grpc_health_check_client_trace.enabled()) {
+    gpr_log(GPR_INFO, "HealthCheckClient %p: created CallState %p", this,
+            call_state_.get());
+  }
+  call_state_->StartCall();
+}
+
+void HealthCheckClient::StartRetryTimer() {
+  MutexLock lock(&mu_);
+  SetHealthStatusLocked(
+      GRPC_CHANNEL_TRANSIENT_FAILURE,
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "health check call failed; will retry after backoff"));
+  grpc_millis next_try = retry_backoff_.NextAttemptTime();
+  if (grpc_health_check_client_trace.enabled()) {
+    gpr_log(GPR_INFO, "HealthCheckClient %p: health check call lost...", this);
+    grpc_millis timeout = next_try - ExecCtx::Get()->Now();
+    if (timeout > 0) {
+      gpr_log(GPR_INFO,
+              "HealthCheckClient %p: ... will retry in %" PRId64 "ms.", this,
+              timeout);
+    } else {
+      gpr_log(GPR_INFO, "HealthCheckClient %p: ... retrying immediately.",
+              this);
+    }
+  }
+  // Ref for callback, tracked manually.
+  Ref(DEBUG_LOCATION, "health_retry_timer").release();
+  retry_timer_callback_pending_ = true;
+  grpc_timer_init(&retry_timer_, next_try, &retry_timer_callback_);
+}
+
+void HealthCheckClient::OnRetryTimer(void* arg, grpc_error* error) {
+  HealthCheckClient* self = static_cast<HealthCheckClient*>(arg);
+  {
+    MutexLock lock(&self->mu_);
+    self->retry_timer_callback_pending_ = false;
+    if (!self->shutting_down_ && error == GRPC_ERROR_NONE &&
+        self->call_state_ == nullptr) {
+      if (grpc_health_check_client_trace.enabled()) {
+        gpr_log(GPR_INFO, "HealthCheckClient %p: restarting health check call",
+                self);
+      }
+      self->StartCallLocked();
+    }
+  }
+  self->Unref(DEBUG_LOCATION, "health_retry_timer");
+}
+
+//
+// protobuf helpers
+//
+
+namespace {
+
+void EncodeRequest(const char* service_name,
+                   ManualConstructor<SliceBufferByteStream>* send_message) {
+  grpc_health_v1_HealthCheckRequest request_struct;
+  request_struct.has_service = true;
+  snprintf(request_struct.service, sizeof(request_struct.service), "%s",
+           service_name);
+  pb_ostream_t ostream;
+  memset(&ostream, 0, sizeof(ostream));
+  pb_encode(&ostream, grpc_health_v1_HealthCheckRequest_fields,
+            &request_struct);
+  grpc_slice request_slice = GRPC_SLICE_MALLOC(ostream.bytes_written);
+  ostream = pb_ostream_from_buffer(GRPC_SLICE_START_PTR(request_slice),
+                                   GRPC_SLICE_LENGTH(request_slice));
+  GPR_ASSERT(pb_encode(&ostream, grpc_health_v1_HealthCheckRequest_fields,
+                       &request_struct) != 0);
+  grpc_slice_buffer slice_buffer;
+  grpc_slice_buffer_init(&slice_buffer);
+  grpc_slice_buffer_add(&slice_buffer, request_slice);
+  send_message->Init(&slice_buffer, 0);
+  grpc_slice_buffer_destroy_internal(&slice_buffer);
+}
+
+// Returns true if healthy.
+// If there was an error parsing the response, sets *error and returns false.
+bool DecodeResponse(grpc_slice_buffer* slice_buffer, grpc_error** error) {
+  // If message is empty, assume unhealthy.
+  if (slice_buffer->length == 0) {
+    *error =
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("health check response was empty");
+    return false;
+  }
+  // Concatenate the slices to form a single string.
+  UniquePtr<uint8_t> recv_message_deleter;
+  uint8_t* recv_message;
+  if (slice_buffer->count == 1) {
+    recv_message = GRPC_SLICE_START_PTR(slice_buffer->slices[0]);
+  } else {
+    recv_message = static_cast<uint8_t*>(gpr_malloc(slice_buffer->length));
+    recv_message_deleter.reset(recv_message);
+    size_t offset = 0;
+    for (size_t i = 0; i < slice_buffer->count; ++i) {
+      memcpy(recv_message + offset,
+             GRPC_SLICE_START_PTR(slice_buffer->slices[i]),
+             GRPC_SLICE_LENGTH(slice_buffer->slices[i]));
+      offset += GRPC_SLICE_LENGTH(slice_buffer->slices[i]);
+    }
+  }
+  // Deserialize message.
+  grpc_health_v1_HealthCheckResponse response_struct;
+  pb_istream_t istream =
+      pb_istream_from_buffer(recv_message, slice_buffer->length);
+  if (!pb_decode(&istream, grpc_health_v1_HealthCheckResponse_fields,
+                 &response_struct)) {
+    // Can't parse message; assume unhealthy.
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "cannot parse health check response");
+    return false;
+  }
+  if (!response_struct.has_status) {
+    // Field not present; assume unhealthy.
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "status field not present in health check response");
+    return false;
+  }
+  return response_struct.status ==
+         grpc_health_v1_HealthCheckResponse_ServingStatus_SERVING;
+}
+
+}  // namespace
+
+//
+// HealthCheckClient::CallState
+//
+
+HealthCheckClient::CallState::CallState(
+    RefCountedPtr<HealthCheckClient> health_check_client,
+    grpc_pollset_set* interested_parties)
+    : InternallyRefCounted<CallState>(&grpc_health_check_client_trace),
+      health_check_client_(std::move(health_check_client)),
+      pollent_(grpc_polling_entity_create_from_pollset_set(interested_parties)),
+      arena_(gpr_arena_create(health_check_client_->connected_subchannel_
+                                  ->GetInitialCallSizeEstimate(0))),
+      payload_(context_) {
+  grpc_call_combiner_init(&call_combiner_);
+  gpr_atm_rel_store(&seen_response_, static_cast<gpr_atm>(0));
+}
+
+HealthCheckClient::CallState::~CallState() {
+  if (grpc_health_check_client_trace.enabled()) {
+    gpr_log(GPR_INFO, "HealthCheckClient %p: destroying CallState %p",
+            health_check_client_.get(), this);
+  }
+  if (call_ != nullptr) GRPC_SUBCHANNEL_CALL_UNREF(call_, "call_ended");
+  for (size_t i = 0; i < GRPC_CONTEXT_COUNT; i++) {
+    if (context_[i].destroy != nullptr) {
+      context_[i].destroy(context_[i].value);
+    }
+  }
+  // Unset the call combiner cancellation closure.  This has the
+  // effect of scheduling the previously set cancellation closure, if
+  // any, so that it can release any internal references it may be
+  // holding to the call stack. Also flush the closures on exec_ctx so that
+  // filters that schedule cancel notification closures on exec_ctx do not
+  // need to take a ref of the call stack to guarantee closure liveness.
+  grpc_call_combiner_set_notify_on_cancel(&call_combiner_, nullptr);
+  grpc_core::ExecCtx::Get()->Flush();
+  grpc_call_combiner_destroy(&call_combiner_);
+  gpr_arena_destroy(arena_);
+}
+
+void HealthCheckClient::CallState::Orphan() {
+  grpc_call_combiner_cancel(&call_combiner_, GRPC_ERROR_CANCELLED);
+  Cancel();
+}
+
+void HealthCheckClient::CallState::StartCall() {
+  ConnectedSubchannel::CallArgs args = {
+      &pollent_,
+      GRPC_MDSTR_SLASH_GRPC_DOT_HEALTH_DOT_V1_DOT_HEALTH_SLASH_WATCH,
+      gpr_now(GPR_CLOCK_MONOTONIC),  // start_time
+      GRPC_MILLIS_INF_FUTURE,        // deadline
+      arena_,
+      context_,
+      &call_combiner_,
+      0,  // parent_data_size
+  };
+  grpc_error* error =
+      health_check_client_->connected_subchannel_->CreateCall(args, &call_);
+  if (error != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR,
+            "HealthCheckClient %p CallState %p: error creating health "
+            "checking call on subchannel (%s); will retry",
+            health_check_client_.get(), this, grpc_error_string(error));
+    GRPC_ERROR_UNREF(error);
+    // Schedule instead of running directly, since we must not be
+    // holding health_check_client_->mu_ when CallEnded() is called.
+    Ref(DEBUG_LOCATION, "call_end_closure").release();
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_INIT(&batch_.handler_private.closure, CallEndedRetry, this,
+                          grpc_schedule_on_exec_ctx),
+        GRPC_ERROR_NONE);
+    return;
+  }
+  // Initialize payload and batch.
+  memset(&batch_, 0, sizeof(batch_));
+  payload_.context = context_;
+  batch_.payload = &payload_;
+  // on_complete callback takes ref, handled manually.
+  Ref(DEBUG_LOCATION, "on_complete").release();
+  batch_.on_complete = GRPC_CLOSURE_INIT(&on_complete_, OnComplete, this,
+                                         grpc_schedule_on_exec_ctx);
+  // Add send_initial_metadata op.
+  grpc_metadata_batch_init(&send_initial_metadata_);
+  error = grpc_metadata_batch_add_head(
+      &send_initial_metadata_, &path_metadata_storage_,
+      grpc_mdelem_from_slices(
+          GRPC_MDSTR_PATH,
+          GRPC_MDSTR_SLASH_GRPC_DOT_HEALTH_DOT_V1_DOT_HEALTH_SLASH_WATCH));
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  payload_.send_initial_metadata.send_initial_metadata =
+      &send_initial_metadata_;
+  payload_.send_initial_metadata.send_initial_metadata_flags = 0;
+  payload_.send_initial_metadata.peer_string = nullptr;
+  batch_.send_initial_metadata = true;
+  // Add send_message op.
+  EncodeRequest(health_check_client_->service_name_, &send_message_);
+  payload_.send_message.send_message.reset(send_message_.get());
+  batch_.send_message = true;
+  // Add send_trailing_metadata op.
+  grpc_metadata_batch_init(&send_trailing_metadata_);
+  payload_.send_trailing_metadata.send_trailing_metadata =
+      &send_trailing_metadata_;
+  batch_.send_trailing_metadata = true;
+  // Add recv_initial_metadata op.
+  grpc_metadata_batch_init(&recv_initial_metadata_);
+  payload_.recv_initial_metadata.recv_initial_metadata =
+      &recv_initial_metadata_;
+  payload_.recv_initial_metadata.recv_flags = nullptr;
+  payload_.recv_initial_metadata.trailing_metadata_available = nullptr;
+  payload_.recv_initial_metadata.peer_string = nullptr;
+  // recv_initial_metadata_ready callback takes ref, handled manually.
+  Ref(DEBUG_LOCATION, "recv_initial_metadata_ready").release();
+  payload_.recv_initial_metadata.recv_initial_metadata_ready =
+      GRPC_CLOSURE_INIT(&recv_initial_metadata_ready_, RecvInitialMetadataReady,
+                        this, grpc_schedule_on_exec_ctx);
+  batch_.recv_initial_metadata = true;
+  // Add recv_message op.
+  payload_.recv_message.recv_message = &recv_message_;
+  // recv_message callback takes ref, handled manually.
+  Ref(DEBUG_LOCATION, "recv_message_ready").release();
+  payload_.recv_message.recv_message_ready = GRPC_CLOSURE_INIT(
+      &recv_message_ready_, RecvMessageReady, this, grpc_schedule_on_exec_ctx);
+  batch_.recv_message = true;
+  // Start batch.
+  StartBatch(&batch_);
+  // Initialize recv_trailing_metadata batch.
+  memset(&recv_trailing_metadata_batch_, 0,
+         sizeof(recv_trailing_metadata_batch_));
+  recv_trailing_metadata_batch_.payload = &payload_;
+  // Add recv_trailing_metadata op.
+  grpc_metadata_batch_init(&recv_trailing_metadata_);
+  payload_.recv_trailing_metadata.recv_trailing_metadata =
+      &recv_trailing_metadata_;
+  payload_.recv_trailing_metadata.collect_stats = &collect_stats_;
+  // This callback signals the end of the call, so it relies on the
+  // initial ref instead of taking a new ref.  When it's invoked, the
+  // initial ref is released.
+  payload_.recv_trailing_metadata.recv_trailing_metadata_ready =
+      GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready_,
+                        RecvTrailingMetadataReady, this,
+                        grpc_schedule_on_exec_ctx);
+  recv_trailing_metadata_batch_.recv_trailing_metadata = true;
+  // Start recv_trailing_metadata batch.
+  StartBatch(&recv_trailing_metadata_batch_);
+}
+
+void HealthCheckClient::CallState::StartBatchInCallCombiner(void* arg,
+                                                            grpc_error* error) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  grpc_subchannel_call* call =
+      static_cast<grpc_subchannel_call*>(batch->handler_private.extra_arg);
+  grpc_subchannel_call_process_op(call, batch);
+}
+
+void HealthCheckClient::CallState::StartBatch(
+    grpc_transport_stream_op_batch* batch) {
+  batch->handler_private.extra_arg = call_;
+  GRPC_CLOSURE_INIT(&batch->handler_private.closure, StartBatchInCallCombiner,
+                    batch, grpc_schedule_on_exec_ctx);
+  GRPC_CALL_COMBINER_START(&call_combiner_, &batch->handler_private.closure,
+                           GRPC_ERROR_NONE, "start_subchannel_batch");
+}
+
+void HealthCheckClient::CallState::OnCancelComplete(void* arg,
+                                                    grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "health_cancel");
+  self->Unref(DEBUG_LOCATION, "cancel");
+}
+
+void HealthCheckClient::CallState::StartCancel(void* arg, grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  auto* batch = grpc_make_transport_stream_op(
+      GRPC_CLOSURE_CREATE(OnCancelComplete, self, grpc_schedule_on_exec_ctx));
+  batch->cancel_stream = true;
+  batch->payload->cancel_stream.cancel_error = GRPC_ERROR_CANCELLED;
+  grpc_subchannel_call_process_op(self->call_, batch);
+}
+
+void HealthCheckClient::CallState::Cancel() {
+  if (call_ != nullptr) {
+    Ref(DEBUG_LOCATION, "cancel").release();
+    GRPC_CALL_COMBINER_START(
+        &call_combiner_,
+        GRPC_CLOSURE_CREATE(StartCancel, this, grpc_schedule_on_exec_ctx),
+        GRPC_ERROR_NONE, "health_cancel");
+  }
+}
+
+void HealthCheckClient::CallState::OnComplete(void* arg, grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "on_complete");
+  grpc_metadata_batch_destroy(&self->send_initial_metadata_);
+  grpc_metadata_batch_destroy(&self->send_trailing_metadata_);
+  self->Unref(DEBUG_LOCATION, "on_complete");
+}
+
+void HealthCheckClient::CallState::RecvInitialMetadataReady(void* arg,
+                                                            grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "recv_initial_metadata_ready");
+  grpc_metadata_batch_destroy(&self->recv_initial_metadata_);
+  self->Unref(DEBUG_LOCATION, "recv_initial_metadata_ready");
+}
+
+void HealthCheckClient::CallState::DoneReadingRecvMessage(grpc_error* error) {
+  recv_message_.reset();
+  if (error != GRPC_ERROR_NONE) {
+    GRPC_ERROR_UNREF(error);
+    Cancel();
+    grpc_slice_buffer_destroy_internal(&recv_message_buffer_);
+    Unref(DEBUG_LOCATION, "recv_message_ready");
+    return;
+  }
+  const bool healthy = DecodeResponse(&recv_message_buffer_, &error);
+  const grpc_connectivity_state state =
+      healthy ? GRPC_CHANNEL_READY : GRPC_CHANNEL_TRANSIENT_FAILURE;
+  if (error == GRPC_ERROR_NONE && !healthy) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("backend unhealthy");
+  }
+  health_check_client_->SetHealthStatus(state, error);
+  gpr_atm_rel_store(&seen_response_, static_cast<gpr_atm>(1));
+  grpc_slice_buffer_destroy_internal(&recv_message_buffer_);
+  // Start another recv_message batch.
+  // This re-uses the ref we're holding.
+  // Note: Can't just reuse batch_ here, since we don't know that all
+  // callbacks from the original batch have completed yet.
+  memset(&recv_message_batch_, 0, sizeof(recv_message_batch_));
+  recv_message_batch_.payload = &payload_;
+  payload_.recv_message.recv_message = &recv_message_;
+  payload_.recv_message.recv_message_ready = GRPC_CLOSURE_INIT(
+      &recv_message_ready_, RecvMessageReady, this, grpc_schedule_on_exec_ctx);
+  recv_message_batch_.recv_message = true;
+  StartBatch(&recv_message_batch_);
+}
+
+grpc_error* HealthCheckClient::CallState::PullSliceFromRecvMessage() {
+  grpc_slice slice;
+  grpc_error* error = recv_message_->Pull(&slice);
+  if (error == GRPC_ERROR_NONE) {
+    grpc_slice_buffer_add(&recv_message_buffer_, slice);
+  }
+  return error;
+}
+
+void HealthCheckClient::CallState::ContinueReadingRecvMessage() {
+  while (recv_message_->Next(SIZE_MAX, &recv_message_ready_)) {
+    grpc_error* error = PullSliceFromRecvMessage();
+    if (error != GRPC_ERROR_NONE) {
+      DoneReadingRecvMessage(error);
+      return;
+    }
+    if (recv_message_buffer_.length == recv_message_->length()) {
+      DoneReadingRecvMessage(GRPC_ERROR_NONE);
+      break;
+    }
+  }
+}
+
+void HealthCheckClient::CallState::OnByteStreamNext(void* arg,
+                                                    grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  if (error != GRPC_ERROR_NONE) {
+    self->DoneReadingRecvMessage(GRPC_ERROR_REF(error));
+    return;
+  }
+  error = self->PullSliceFromRecvMessage();
+  if (error != GRPC_ERROR_NONE) {
+    self->DoneReadingRecvMessage(error);
+    return;
+  }
+  if (self->recv_message_buffer_.length == self->recv_message_->length()) {
+    self->DoneReadingRecvMessage(GRPC_ERROR_NONE);
+  } else {
+    self->ContinueReadingRecvMessage();
+  }
+}
+
+void HealthCheckClient::CallState::RecvMessageReady(void* arg,
+                                                    grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "recv_message_ready");
+  if (self->recv_message_ == nullptr) {
+    self->Unref(DEBUG_LOCATION, "recv_message_ready");
+    return;
+  }
+  grpc_slice_buffer_init(&self->recv_message_buffer_);
+  GRPC_CLOSURE_INIT(&self->recv_message_ready_, OnByteStreamNext, self,
+                    grpc_schedule_on_exec_ctx);
+  self->ContinueReadingRecvMessage();
+  // Ref will continue to be held until we finish draining the byte stream.
+}
+
+void HealthCheckClient::CallState::RecvTrailingMetadataReady(
+    void* arg, grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  GRPC_CALL_COMBINER_STOP(&self->call_combiner_,
+                          "recv_trailing_metadata_ready");
+  // Get call status.
+  grpc_status_code status = GRPC_STATUS_UNKNOWN;
+  if (error != GRPC_ERROR_NONE) {
+    grpc_error_get_status(error, GRPC_MILLIS_INF_FUTURE, &status,
+                          nullptr /* slice */, nullptr /* http_error */,
+                          nullptr /* error_string */);
+  } else if (self->recv_trailing_metadata_.idx.named.grpc_status != nullptr) {
+    status = grpc_get_status_code_from_metadata(
+        self->recv_trailing_metadata_.idx.named.grpc_status->md);
+  }
+  if (grpc_health_check_client_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "HealthCheckClient %p CallState %p: health watch failed with "
+            "status %d",
+            self->health_check_client_.get(), self, status);
+  }
+  // Clean up.
+  grpc_metadata_batch_destroy(&self->recv_trailing_metadata_);
+  // For status UNIMPLEMENTED, give up and assume always healthy.
+  bool retry = true;
+  if (status == GRPC_STATUS_UNIMPLEMENTED) {
+    static const char kErrorMessage[] =
+        "health checking Watch method returned UNIMPLEMENTED; "
+        "disabling health checks but assuming server is healthy";
+    gpr_log(GPR_ERROR, kErrorMessage);
+    if (self->health_check_client_->channelz_node_ != nullptr) {
+      self->health_check_client_->channelz_node_->AddTraceEvent(
+          channelz::ChannelTrace::Error,
+          grpc_slice_from_static_string(kErrorMessage));
+    }
+    self->health_check_client_->SetHealthStatus(GRPC_CHANNEL_READY,
+                                                GRPC_ERROR_NONE);
+    retry = false;
+  }
+  self->CallEnded(retry);
+}
+
+void HealthCheckClient::CallState::CallEndedRetry(void* arg,
+                                                  grpc_error* error) {
+  HealthCheckClient::CallState* self =
+      static_cast<HealthCheckClient::CallState*>(arg);
+  self->CallEnded(true /* retry */);
+  self->Unref(DEBUG_LOCATION, "call_end_closure");
+}
+
+void HealthCheckClient::CallState::CallEnded(bool retry) {
+  // If this CallState is still in use, this call ended because of a failure,
+  // so we need to stop using it and optionally create a new one.
+  // Otherwise, we have deliberately ended this call, and no further action
+  // is required.
+  if (this == health_check_client_->call_state_.get()) {
+    health_check_client_->call_state_.reset();
+    if (retry) {
+      GPR_ASSERT(!health_check_client_->shutting_down_);
+      if (static_cast<bool>(gpr_atm_acq_load(&seen_response_))) {
+        // If the call fails after we've gotten a successful response, reset
+        // the backoff and restart the call immediately.
+        health_check_client_->retry_backoff_.Reset();
+        health_check_client_->StartCall();
+      } else {
+        // If the call failed without receiving any messages, retry later.
+        health_check_client_->StartRetryTimer();
+      }
+    }
+  }
+  Unref(DEBUG_LOCATION, "call_ended");
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.h b/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.h
new file mode 100644
index 0000000..2369b73
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.h
@@ -0,0 +1,172 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HEALTH_HEALTH_CHECK_CLIENT_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HEALTH_HEALTH_CHECK_CLIENT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/call_combiner.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/transport/byte_stream.h"
+#include "src/core/lib/transport/metadata_batch.h"
+#include "src/core/lib/transport/transport.h"
+
+namespace grpc_core {
+
+class HealthCheckClient : public InternallyRefCounted<HealthCheckClient> {
+ public:
+  HealthCheckClient(const char* service_name,
+                    RefCountedPtr<ConnectedSubchannel> connected_subchannel,
+                    grpc_pollset_set* interested_parties,
+                    RefCountedPtr<channelz::SubchannelNode> channelz_node);
+
+  ~HealthCheckClient();
+
+  // When the health state changes from *state, sets *state to the new
+  // value and schedules closure.
+  // Only one closure can be outstanding at a time.
+  void NotifyOnHealthChange(grpc_connectivity_state* state,
+                            grpc_closure* closure);
+
+  void Orphan() override;
+
+ private:
+  // Contains a call to the backend and all the data related to the call.
+  class CallState : public InternallyRefCounted<CallState> {
+   public:
+    CallState(RefCountedPtr<HealthCheckClient> health_check_client,
+              grpc_pollset_set* interested_parties_);
+    ~CallState();
+
+    void Orphan() override;
+
+    void StartCall();
+
+   private:
+    void Cancel();
+
+    void StartBatch(grpc_transport_stream_op_batch* batch);
+    static void StartBatchInCallCombiner(void* arg, grpc_error* error);
+
+    static void CallEndedRetry(void* arg, grpc_error* error);
+    void CallEnded(bool retry);
+
+    static void OnComplete(void* arg, grpc_error* error);
+    static void RecvInitialMetadataReady(void* arg, grpc_error* error);
+    static void RecvMessageReady(void* arg, grpc_error* error);
+    static void RecvTrailingMetadataReady(void* arg, grpc_error* error);
+    static void StartCancel(void* arg, grpc_error* error);
+    static void OnCancelComplete(void* arg, grpc_error* error);
+
+    static void OnByteStreamNext(void* arg, grpc_error* error);
+    void ContinueReadingRecvMessage();
+    grpc_error* PullSliceFromRecvMessage();
+    void DoneReadingRecvMessage(grpc_error* error);
+
+    RefCountedPtr<HealthCheckClient> health_check_client_;
+    grpc_polling_entity pollent_;
+
+    gpr_arena* arena_;
+    grpc_call_combiner call_combiner_;
+    grpc_call_context_element context_[GRPC_CONTEXT_COUNT] = {};
+
+    // The streaming call to the backend. Always non-NULL.
+    grpc_subchannel_call* call_;
+
+    grpc_transport_stream_op_batch_payload payload_;
+    grpc_transport_stream_op_batch batch_;
+    grpc_transport_stream_op_batch recv_message_batch_;
+    grpc_transport_stream_op_batch recv_trailing_metadata_batch_;
+
+    grpc_closure on_complete_;
+
+    // send_initial_metadata
+    grpc_metadata_batch send_initial_metadata_;
+    grpc_linked_mdelem path_metadata_storage_;
+
+    // send_message
+    ManualConstructor<SliceBufferByteStream> send_message_;
+
+    // send_trailing_metadata
+    grpc_metadata_batch send_trailing_metadata_;
+
+    // recv_initial_metadata
+    grpc_metadata_batch recv_initial_metadata_;
+    grpc_closure recv_initial_metadata_ready_;
+
+    // recv_message
+    OrphanablePtr<ByteStream> recv_message_;
+    grpc_closure recv_message_ready_;
+    grpc_slice_buffer recv_message_buffer_;
+    gpr_atm seen_response_;
+
+    // recv_trailing_metadata
+    grpc_metadata_batch recv_trailing_metadata_;
+    grpc_transport_stream_stats collect_stats_;
+    grpc_closure recv_trailing_metadata_ready_;
+  };
+
+  void StartCall();
+  void StartCallLocked();  // Requires holding mu_.
+
+  void StartRetryTimer();
+  static void OnRetryTimer(void* arg, grpc_error* error);
+
+  void SetHealthStatus(grpc_connectivity_state state, grpc_error* error);
+  void SetHealthStatusLocked(grpc_connectivity_state state,
+                             grpc_error* error);  // Requires holding mu_.
+
+  const char* service_name_;  // Do not own.
+  RefCountedPtr<ConnectedSubchannel> connected_subchannel_;
+  grpc_pollset_set* interested_parties_;  // Do not own.
+  RefCountedPtr<channelz::SubchannelNode> channelz_node_;
+
+  gpr_mu mu_;
+  grpc_connectivity_state state_ = GRPC_CHANNEL_CONNECTING;
+  grpc_error* error_ = GRPC_ERROR_NONE;
+  grpc_connectivity_state* notify_state_ = nullptr;
+  grpc_closure* on_health_changed_ = nullptr;
+  bool shutting_down_ = false;
+
+  // The data associated with the current health check call.  It holds a ref
+  // to this HealthCheckClient object.
+  OrphanablePtr<CallState> call_state_;
+
+  // Call retry state.
+  BackOff retry_backoff_;
+  grpc_timer retry_timer_;
+  grpc_closure retry_timer_callback_;
+  bool retry_timer_callback_pending_ = false;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HEALTH_HEALTH_CHECK_CLIENT_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.cc b/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.cc
new file mode 100644
index 0000000..0716e46
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.cc
@@ -0,0 +1,371 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
+
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/http/format_request.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+typedef struct http_connect_handshaker {
+  // Base class.  Must be first.
+  grpc_handshaker base;
+
+  gpr_refcount refcount;
+  gpr_mu mu;
+
+  bool shutdown;
+  // Endpoint and read buffer to destroy after a shutdown.
+  grpc_endpoint* endpoint_to_destroy;
+  grpc_slice_buffer* read_buffer_to_destroy;
+
+  // State saved while performing the handshake.
+  grpc_handshaker_args* args;
+  grpc_closure* on_handshake_done;
+
+  // Objects for processing the HTTP CONNECT request and response.
+  grpc_slice_buffer write_buffer;
+  grpc_closure request_done_closure;
+  grpc_closure response_read_closure;
+  grpc_http_parser http_parser;
+  grpc_http_response http_response;
+} http_connect_handshaker;
+
+// Unref and clean up handshaker.
+static void http_connect_handshaker_unref(http_connect_handshaker* handshaker) {
+  if (gpr_unref(&handshaker->refcount)) {
+    gpr_mu_destroy(&handshaker->mu);
+    if (handshaker->endpoint_to_destroy != nullptr) {
+      grpc_endpoint_destroy(handshaker->endpoint_to_destroy);
+    }
+    if (handshaker->read_buffer_to_destroy != nullptr) {
+      grpc_slice_buffer_destroy_internal(handshaker->read_buffer_to_destroy);
+      gpr_free(handshaker->read_buffer_to_destroy);
+    }
+    grpc_slice_buffer_destroy_internal(&handshaker->write_buffer);
+    grpc_http_parser_destroy(&handshaker->http_parser);
+    grpc_http_response_destroy(&handshaker->http_response);
+    gpr_free(handshaker);
+  }
+}
+
+// Set args fields to nullptr, saving the endpoint and read buffer for
+// later destruction.
+static void cleanup_args_for_failure_locked(
+    http_connect_handshaker* handshaker) {
+  handshaker->endpoint_to_destroy = handshaker->args->endpoint;
+  handshaker->args->endpoint = nullptr;
+  handshaker->read_buffer_to_destroy = handshaker->args->read_buffer;
+  handshaker->args->read_buffer = nullptr;
+  grpc_channel_args_destroy(handshaker->args->args);
+  handshaker->args->args = nullptr;
+}
+
+// If the handshake failed or we're shutting down, clean up and invoke the
+// callback with the error.
+static void handshake_failed_locked(http_connect_handshaker* handshaker,
+                                    grpc_error* error) {
+  if (error == GRPC_ERROR_NONE) {
+    // If we were shut down after an endpoint operation succeeded but
+    // before the endpoint callback was invoked, we need to generate our
+    // own error.
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown");
+  }
+  if (!handshaker->shutdown) {
+    // TODO(ctiller): It is currently necessary to shutdown endpoints
+    // before destroying them, even if we know that there are no
+    // pending read/write callbacks.  This should be fixed, at which
+    // point this can be removed.
+    grpc_endpoint_shutdown(handshaker->args->endpoint, GRPC_ERROR_REF(error));
+    // Not shutting down, so the handshake failed.  Clean up before
+    // invoking the callback.
+    cleanup_args_for_failure_locked(handshaker);
+    // Set shutdown to true so that subsequent calls to
+    // http_connect_handshaker_shutdown() do nothing.
+    handshaker->shutdown = true;
+  }
+  // Invoke callback.
+  GRPC_CLOSURE_SCHED(handshaker->on_handshake_done, error);
+}
+
+// Callback invoked when finished writing HTTP CONNECT request.
+static void on_write_done(void* arg, grpc_error* error) {
+  http_connect_handshaker* handshaker =
+      static_cast<http_connect_handshaker*>(arg);
+  gpr_mu_lock(&handshaker->mu);
+  if (error != GRPC_ERROR_NONE || handshaker->shutdown) {
+    // If the write failed or we're shutting down, clean up and invoke the
+    // callback with the error.
+    handshake_failed_locked(handshaker, GRPC_ERROR_REF(error));
+    gpr_mu_unlock(&handshaker->mu);
+    http_connect_handshaker_unref(handshaker);
+  } else {
+    // Otherwise, read the response.
+    // The read callback inherits our ref to the handshaker.
+    grpc_endpoint_read(handshaker->args->endpoint,
+                       handshaker->args->read_buffer,
+                       &handshaker->response_read_closure);
+    gpr_mu_unlock(&handshaker->mu);
+  }
+}
+
+// Callback invoked for reading HTTP CONNECT response.
+static void on_read_done(void* arg, grpc_error* error) {
+  http_connect_handshaker* handshaker =
+      static_cast<http_connect_handshaker*>(arg);
+  gpr_mu_lock(&handshaker->mu);
+  if (error != GRPC_ERROR_NONE || handshaker->shutdown) {
+    // If the read failed or we're shutting down, clean up and invoke the
+    // callback with the error.
+    handshake_failed_locked(handshaker, GRPC_ERROR_REF(error));
+    goto done;
+  }
+  // Add buffer to parser.
+  for (size_t i = 0; i < handshaker->args->read_buffer->count; ++i) {
+    if (GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i]) > 0) {
+      size_t body_start_offset = 0;
+      error = grpc_http_parser_parse(&handshaker->http_parser,
+                                     handshaker->args->read_buffer->slices[i],
+                                     &body_start_offset);
+      if (error != GRPC_ERROR_NONE) {
+        handshake_failed_locked(handshaker, error);
+        goto done;
+      }
+      if (handshaker->http_parser.state == GRPC_HTTP_BODY) {
+        // Remove the data we've already read from the read buffer,
+        // leaving only the leftover bytes (if any).
+        grpc_slice_buffer tmp_buffer;
+        grpc_slice_buffer_init(&tmp_buffer);
+        if (body_start_offset <
+            GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i])) {
+          grpc_slice_buffer_add(
+              &tmp_buffer,
+              grpc_slice_split_tail(&handshaker->args->read_buffer->slices[i],
+                                    body_start_offset));
+        }
+        grpc_slice_buffer_addn(&tmp_buffer,
+                               &handshaker->args->read_buffer->slices[i + 1],
+                               handshaker->args->read_buffer->count - i - 1);
+        grpc_slice_buffer_swap(handshaker->args->read_buffer, &tmp_buffer);
+        grpc_slice_buffer_destroy_internal(&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) {
+    grpc_slice_buffer_reset_and_unref_internal(handshaker->args->read_buffer);
+    grpc_endpoint_read(handshaker->args->endpoint,
+                       handshaker->args->read_buffer,
+                       &handshaker->response_read_closure);
+    gpr_mu_unlock(&handshaker->mu);
+    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_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    handshake_failed_locked(handshaker, error);
+    goto done;
+  }
+  // Success.  Invoke handshake-done callback.
+  GRPC_CLOSURE_SCHED(handshaker->on_handshake_done, error);
+done:
+  // Set shutdown to true so that subsequent calls to
+  // http_connect_handshaker_shutdown() do nothing.
+  handshaker->shutdown = true;
+  gpr_mu_unlock(&handshaker->mu);
+  http_connect_handshaker_unref(handshaker);
+}
+
+//
+// Public handshaker methods
+//
+
+static void http_connect_handshaker_destroy(grpc_handshaker* handshaker_in) {
+  http_connect_handshaker* handshaker =
+      reinterpret_cast<http_connect_handshaker*>(handshaker_in);
+  http_connect_handshaker_unref(handshaker);
+}
+
+static void http_connect_handshaker_shutdown(grpc_handshaker* handshaker_in,
+                                             grpc_error* why) {
+  http_connect_handshaker* handshaker =
+      reinterpret_cast<http_connect_handshaker*>(handshaker_in);
+  gpr_mu_lock(&handshaker->mu);
+  if (!handshaker->shutdown) {
+    handshaker->shutdown = true;
+    grpc_endpoint_shutdown(handshaker->args->endpoint, GRPC_ERROR_REF(why));
+    cleanup_args_for_failure_locked(handshaker);
+  }
+  gpr_mu_unlock(&handshaker->mu);
+  GRPC_ERROR_UNREF(why);
+}
+
+static void http_connect_handshaker_do_handshake(
+    grpc_handshaker* handshaker_in, grpc_tcp_server_acceptor* acceptor,
+    grpc_closure* on_handshake_done, grpc_handshaker_args* args) {
+  http_connect_handshaker* handshaker =
+      reinterpret_cast<http_connect_handshaker*>(handshaker_in);
+  // Check for HTTP CONNECT channel arg.
+  // If not found, invoke on_handshake_done without doing anything.
+  const grpc_arg* arg =
+      grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_SERVER);
+  char* server_name = grpc_channel_arg_get_string(arg);
+  if (server_name == nullptr) {
+    // Set shutdown to true so that subsequent calls to
+    // http_connect_handshaker_shutdown() do nothing.
+    gpr_mu_lock(&handshaker->mu);
+    handshaker->shutdown = true;
+    gpr_mu_unlock(&handshaker->mu);
+    GRPC_CLOSURE_SCHED(on_handshake_done, GRPC_ERROR_NONE);
+    return;
+  }
+  // Get headers from channel args.
+  arg = grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_HEADERS);
+  char* arg_header_string = grpc_channel_arg_get_string(arg);
+  grpc_http_header* headers = nullptr;
+  size_t num_headers = 0;
+  char** header_strings = nullptr;
+  size_t num_header_strings = 0;
+  if (arg_header_string != nullptr) {
+    gpr_string_split(arg_header_string, "\n", &header_strings,
+                     &num_header_strings);
+    headers = static_cast<grpc_http_header*>(
+        gpr_malloc(sizeof(grpc_http_header) * num_header_strings));
+    for (size_t i = 0; i < num_header_strings; ++i) {
+      char* sep = strchr(header_strings[i], ':');
+      if (sep == nullptr) {
+        gpr_log(GPR_ERROR, "skipping unparseable HTTP CONNECT header: %s",
+                header_strings[i]);
+        continue;
+      }
+      *sep = '\0';
+      headers[num_headers].key = header_strings[i];
+      headers[num_headers].value = sep + 1;
+      ++num_headers;
+    }
+  }
+  // Save state in the handshaker object.
+  gpr_mu_lock(&handshaker->mu);
+  handshaker->args = args;
+  handshaker->on_handshake_done = on_handshake_done;
+  // Log connection via proxy.
+  char* proxy_name = grpc_endpoint_get_peer(args->endpoint);
+  gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s", server_name,
+          proxy_name);
+  gpr_free(proxy_name);
+  // Construct HTTP CONNECT request.
+  grpc_httpcli_request request;
+  memset(&request, 0, sizeof(request));
+  request.host = server_name;
+  request.http.method = (char*)"CONNECT";
+  request.http.path = server_name;
+  request.http.hdrs = headers;
+  request.http.hdr_count = num_headers;
+  request.handshaker = &grpc_httpcli_plaintext;
+  grpc_slice request_slice = grpc_httpcli_format_connect_request(&request);
+  grpc_slice_buffer_add(&handshaker->write_buffer, request_slice);
+  // Clean up.
+  gpr_free(headers);
+  for (size_t i = 0; i < num_header_strings; ++i) {
+    gpr_free(header_strings[i]);
+  }
+  gpr_free(header_strings);
+  // Take a new ref to be held by the write callback.
+  gpr_ref(&handshaker->refcount);
+  grpc_endpoint_write(args->endpoint, &handshaker->write_buffer,
+                      &handshaker->request_done_closure, nullptr);
+  gpr_mu_unlock(&handshaker->mu);
+}
+
+static const grpc_handshaker_vtable http_connect_handshaker_vtable = {
+    http_connect_handshaker_destroy, http_connect_handshaker_shutdown,
+    http_connect_handshaker_do_handshake, "http_connect"};
+
+static grpc_handshaker* grpc_http_connect_handshaker_create() {
+  http_connect_handshaker* handshaker =
+      static_cast<http_connect_handshaker*>(gpr_malloc(sizeof(*handshaker)));
+  memset(handshaker, 0, sizeof(*handshaker));
+  grpc_handshaker_init(&http_connect_handshaker_vtable, &handshaker->base);
+  gpr_mu_init(&handshaker->mu);
+  gpr_ref_init(&handshaker->refcount, 1);
+  grpc_slice_buffer_init(&handshaker->write_buffer);
+  GRPC_CLOSURE_INIT(&handshaker->request_done_closure, on_write_done,
+                    handshaker, grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&handshaker->response_read_closure, on_read_done,
+                    handshaker, grpc_schedule_on_exec_ctx);
+  grpc_http_parser_init(&handshaker->http_parser, GRPC_HTTP_RESPONSE,
+                        &handshaker->http_response);
+  return &handshaker->base;
+}
+
+//
+// handshaker factory
+//
+
+static void handshaker_factory_add_handshakers(
+    grpc_handshaker_factory* factory, const grpc_channel_args* args,
+    grpc_pollset_set* interested_parties,
+    grpc_handshake_manager* handshake_mgr) {
+  grpc_handshake_manager_add(handshake_mgr,
+                             grpc_http_connect_handshaker_create());
+}
+
+static void handshaker_factory_destroy(grpc_handshaker_factory* factory) {}
+
+static const grpc_handshaker_factory_vtable handshaker_factory_vtable = {
+    handshaker_factory_add_handshakers, handshaker_factory_destroy};
+
+static grpc_handshaker_factory handshaker_factory = {
+    &handshaker_factory_vtable};
+
+void grpc_http_connect_register_handshaker_factory() {
+  grpc_handshaker_factory_register(true /* at_start */, HANDSHAKER_CLIENT,
+                                   &handshaker_factory);
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.h b/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.h
new file mode 100644
index 0000000..928a23d
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H
+
+/// Channel arg indicating the server in HTTP CONNECT request (string).
+/// The presence of this arg triggers the use of HTTP CONNECT.
+#define GRPC_ARG_HTTP_CONNECT_SERVER "grpc.http_connect_server"
+
+/// Channel arg indicating HTTP CONNECT headers (string).
+/// Multiple headers are separated by newlines.  Key/value pairs are
+/// seperated by colons.
+#define GRPC_ARG_HTTP_CONNECT_HEADERS "grpc.http_connect_headers"
+
+/// Registers handshaker factory.
+void grpc_http_connect_register_handshaker_factory();
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.cc b/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.cc
new file mode 100644
index 0000000..8951a29
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.cc
@@ -0,0 +1,212 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/http_proxy.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
+#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/slice/b64.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+/**
+ * Parses the 'https_proxy' env var (fallback on 'http_proxy') and returns the
+ * proxy hostname to resolve or nullptr on error. Also sets 'user_cred' to user
+ * credentials if present in the 'http_proxy' env var, otherwise leaves it
+ * unchanged. It is caller's responsibility to gpr_free user_cred.
+ */
+static char* get_http_proxy_server(char** user_cred) {
+  GPR_ASSERT(user_cred != nullptr);
+  char* proxy_name = nullptr;
+  char** authority_strs = nullptr;
+  size_t authority_nstrs;
+  /* Prefer using 'https_proxy'. Fallback on 'http_proxy' if it is not set. The
+   * fallback behavior can be removed if there's a demand for it.
+   */
+  char* uri_str = gpr_getenv("https_proxy");
+  if (uri_str == nullptr) uri_str = gpr_getenv("http_proxy");
+  if (uri_str == nullptr) return nullptr;
+  grpc_uri* uri = grpc_uri_parse(uri_str, false /* suppress_errors */);
+  if (uri == nullptr || uri->authority == nullptr) {
+    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;
+  }
+  /* Split on '@' to separate user credentials from host */
+  gpr_string_split(uri->authority, "@", &authority_strs, &authority_nstrs);
+  GPR_ASSERT(authority_nstrs != 0); /* should have at least 1 string */
+  if (authority_nstrs == 1) {
+    /* User cred not present in authority */
+    proxy_name = authority_strs[0];
+  } else if (authority_nstrs == 2) {
+    /* User cred found */
+    *user_cred = authority_strs[0];
+    proxy_name = authority_strs[1];
+    gpr_log(GPR_DEBUG, "userinfo found in proxy URI");
+  } else {
+    /* Bad authority */
+    for (size_t i = 0; i < authority_nstrs; i++) {
+      gpr_free(authority_strs[i]);
+    }
+    proxy_name = nullptr;
+  }
+  gpr_free(authority_strs);
+done:
+  gpr_free(uri_str);
+  grpc_uri_destroy(uri);
+  return proxy_name;
+}
+
+/**
+ * Checks the value of GRPC_ARG_ENABLE_HTTP_PROXY to determine if http_proxy
+ * should be used.
+ */
+bool http_proxy_enabled(const grpc_channel_args* args) {
+  const grpc_arg* arg =
+      grpc_channel_args_find(args, GRPC_ARG_ENABLE_HTTP_PROXY);
+  return grpc_channel_arg_get_bool(arg, true);
+}
+
+static bool proxy_mapper_map_name(grpc_proxy_mapper* mapper,
+                                  const char* server_uri,
+                                  const grpc_channel_args* args,
+                                  char** name_to_resolve,
+                                  grpc_channel_args** new_args) {
+  if (!http_proxy_enabled(args)) {
+    return false;
+  }
+  char* user_cred = nullptr;
+  *name_to_resolve = get_http_proxy_server(&user_cred);
+  if (*name_to_resolve == nullptr) return false;
+  char* no_proxy_str = nullptr;
+  grpc_uri* uri = grpc_uri_parse(server_uri, false /* suppress_errors */);
+  if (uri == nullptr || uri->path[0] == '\0') {
+    gpr_log(GPR_ERROR,
+            "'http_proxy' environment variable set, but cannot "
+            "parse server URI '%s' -- not using proxy",
+            server_uri);
+    goto no_use_proxy;
+  }
+  if (strcmp(uri->scheme, "unix") == 0) {
+    gpr_log(GPR_INFO, "not using proxy for Unix domain socket '%s'",
+            server_uri);
+    goto no_use_proxy;
+  }
+  no_proxy_str = gpr_getenv("no_proxy");
+  if (no_proxy_str != nullptr) {
+    static const char* NO_PROXY_SEPARATOR = ",";
+    bool use_proxy = true;
+    char* server_host;
+    char* server_port;
+    if (!gpr_split_host_port(uri->path[0] == '/' ? uri->path + 1 : uri->path,
+                             &server_host, &server_port)) {
+      gpr_log(GPR_INFO,
+              "unable to split host and port, not checking no_proxy list for "
+              "host '%s'",
+              server_uri);
+      gpr_free(no_proxy_str);
+    } else {
+      size_t uri_len = strlen(server_host);
+      char** no_proxy_hosts;
+      size_t num_no_proxy_hosts;
+      gpr_string_split(no_proxy_str, NO_PROXY_SEPARATOR, &no_proxy_hosts,
+                       &num_no_proxy_hosts);
+      for (size_t i = 0; i < num_no_proxy_hosts; i++) {
+        char* no_proxy_entry = no_proxy_hosts[i];
+        size_t no_proxy_len = strlen(no_proxy_entry);
+        if (no_proxy_len <= uri_len &&
+            gpr_stricmp(no_proxy_entry, &server_host[uri_len - no_proxy_len]) ==
+                0) {
+          gpr_log(GPR_INFO, "not using proxy for host in no_proxy list '%s'",
+                  server_uri);
+          use_proxy = false;
+          break;
+        }
+      }
+      for (size_t i = 0; i < num_no_proxy_hosts; i++) {
+        gpr_free(no_proxy_hosts[i]);
+      }
+      gpr_free(no_proxy_hosts);
+      gpr_free(server_host);
+      gpr_free(server_port);
+      gpr_free(no_proxy_str);
+      if (!use_proxy) goto no_use_proxy;
+    }
+  }
+  grpc_arg args_to_add[2];
+  args_to_add[0] = grpc_channel_arg_string_create(
+      (char*)GRPC_ARG_HTTP_CONNECT_SERVER,
+      uri->path[0] == '/' ? uri->path + 1 : uri->path);
+  if (user_cred != nullptr) {
+    /* Use base64 encoding for user credentials as stated in RFC 7617 */
+    char* encoded_user_cred =
+        grpc_base64_encode(user_cred, strlen(user_cred), 0, 0);
+    char* header;
+    gpr_asprintf(&header, "Proxy-Authorization:Basic %s", encoded_user_cred);
+    gpr_free(encoded_user_cred);
+    args_to_add[1] = grpc_channel_arg_string_create(
+        (char*)GRPC_ARG_HTTP_CONNECT_HEADERS, header);
+    *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 2);
+    gpr_free(header);
+  } else {
+    *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 1);
+  }
+  grpc_uri_destroy(uri);
+  gpr_free(user_cred);
+  return true;
+no_use_proxy:
+  if (uri != nullptr) grpc_uri_destroy(uri);
+  gpr_free(*name_to_resolve);
+  *name_to_resolve = nullptr;
+  gpr_free(user_cred);
+  return false;
+}
+
+static bool proxy_mapper_map_address(grpc_proxy_mapper* mapper,
+                                     const grpc_resolved_address* address,
+                                     const grpc_channel_args* args,
+                                     grpc_resolved_address** new_address,
+                                     grpc_channel_args** new_args) {
+  return false;
+}
+
+static void proxy_mapper_destroy(grpc_proxy_mapper* mapper) {}
+
+static const grpc_proxy_mapper_vtable proxy_mapper_vtable = {
+    proxy_mapper_map_name, proxy_mapper_map_address, proxy_mapper_destroy};
+
+static grpc_proxy_mapper proxy_mapper = {&proxy_mapper_vtable};
+
+void grpc_register_http_proxy_mapper() {
+  grpc_proxy_mapper_register(true /* at_start */, &proxy_mapper);
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.h b/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.h
new file mode 100644
index 0000000..3469493
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H
+
+void grpc_register_http_proxy_mapper();
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.cc
new file mode 100644
index 0000000..b4e8036
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.cc
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy.h"
+#include "src/core/lib/iomgr/combiner.h"
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount(
+    false, "lb_policy_refcount");
+
+namespace grpc_core {
+
+LoadBalancingPolicy::LoadBalancingPolicy(const Args& args)
+    : InternallyRefCounted(&grpc_trace_lb_policy_refcount),
+      combiner_(GRPC_COMBINER_REF(args.combiner, "lb_policy")),
+      client_channel_factory_(args.client_channel_factory),
+      interested_parties_(grpc_pollset_set_create()),
+      request_reresolution_(nullptr) {}
+
+LoadBalancingPolicy::~LoadBalancingPolicy() {
+  grpc_pollset_set_destroy(interested_parties_);
+  GRPC_COMBINER_UNREF(combiner_, "lb_policy");
+}
+
+void LoadBalancingPolicy::TryReresolutionLocked(
+    grpc_core::TraceFlag* grpc_lb_trace, grpc_error* error) {
+  if (request_reresolution_ != nullptr) {
+    GRPC_CLOSURE_SCHED(request_reresolution_, error);
+    request_reresolution_ = nullptr;
+    if (grpc_lb_trace->enabled()) {
+      gpr_log(GPR_INFO,
+              "%s %p: scheduling re-resolution closure with error=%s.",
+              grpc_lb_trace->name(), this, grpc_error_string(error));
+    }
+  } else {
+    if (grpc_lb_trace->enabled()) {
+      gpr_log(GPR_INFO, "%s %p: no available re-resolution closure.",
+              grpc_lb_trace->name(), this);
+    }
+  }
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.h
new file mode 100644
index 0000000..293d8e9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.h
@@ -0,0 +1,215 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
+#include "src/core/ext/filters/client_channel/client_channel_factory.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/transport/connectivity_state.h"
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount;
+
+namespace grpc_core {
+
+/// Interface for load balancing policies.
+///
+/// Note: All methods with a "Locked" suffix must be called from the
+/// combiner passed to the constructor.
+///
+/// Any I/O done by the LB policy should be done under the pollset_set
+/// returned by \a interested_parties().
+class LoadBalancingPolicy : public InternallyRefCounted<LoadBalancingPolicy> {
+ public:
+  struct Args {
+    /// The combiner under which all LB policy calls will be run.
+    /// Policy does NOT take ownership of the reference to the combiner.
+    // TODO(roth): Once we have a C++-like interface for combiners, this
+    // API should change to take a smart pointer that does pass ownership
+    // of a reference.
+    grpc_combiner* combiner = nullptr;
+    /// Used to create channels and subchannels.
+    grpc_client_channel_factory* client_channel_factory = nullptr;
+    /// Channel args from the resolver.
+    /// Note that the LB policy gets the set of addresses from the
+    /// GRPC_ARG_SERVER_ADDRESS_LIST channel arg.
+    grpc_channel_args* args = nullptr;
+    /// Load balancing config from the resolver.
+    grpc_json* lb_config = nullptr;
+  };
+
+  /// State used for an LB pick.
+  struct PickState {
+    /// Initial metadata associated with the picking call.
+    grpc_metadata_batch* initial_metadata = nullptr;
+    /// Pointer to bitmask used for selective cancelling. See
+    /// \a CancelMatchingPicksLocked() and \a GRPC_INITIAL_METADATA_* in
+    /// grpc_types.h.
+    uint32_t* initial_metadata_flags = nullptr;
+    /// Storage for LB token in \a initial_metadata, or nullptr if not used.
+    grpc_linked_mdelem lb_token_mdelem_storage;
+    /// Closure to run when pick is complete, if not completed synchronously.
+    /// If null, pick will fail if a result is not available synchronously.
+    grpc_closure* on_complete = nullptr;
+    /// Will be set to the selected subchannel, or nullptr on failure or when
+    /// the LB policy decides to drop the call.
+    RefCountedPtr<ConnectedSubchannel> connected_subchannel;
+    /// Will be populated with context to pass to the subchannel call, if
+    /// needed.
+    grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT] = {};
+    /// Next pointer.  For internal use by LB policy.
+    PickState* next = nullptr;
+  };
+
+  // Not copyable nor movable.
+  LoadBalancingPolicy(const LoadBalancingPolicy&) = delete;
+  LoadBalancingPolicy& operator=(const LoadBalancingPolicy&) = delete;
+
+  /// Returns the name of the LB policy.
+  virtual const char* name() const GRPC_ABSTRACT;
+
+  /// Updates the policy with a new set of \a args and a new \a lb_config from
+  /// the resolver. Note that the LB policy gets the set of addresses from the
+  /// GRPC_ARG_SERVER_ADDRESS_LIST channel arg.
+  virtual void UpdateLocked(const grpc_channel_args& args,
+                            grpc_json* lb_config) GRPC_ABSTRACT;
+
+  /// Finds an appropriate subchannel for a call, based on data in \a pick.
+  /// \a pick must remain alive until the pick is complete.
+  ///
+  /// If a result is known immediately, returns true, setting \a *error
+  /// upon failure.  Otherwise, \a pick->on_complete will be invoked once
+  /// the pick is complete with its error argument set to indicate success
+  /// or failure.
+  ///
+  /// If \a pick->on_complete is null and no result is known immediately,
+  /// a synchronous failure will be returned (i.e., \a *error will be
+  /// set and true will be returned).
+  virtual bool PickLocked(PickState* pick, grpc_error** error) GRPC_ABSTRACT;
+
+  /// Cancels \a pick.
+  /// The \a on_complete callback of the pending pick will be invoked with
+  /// \a pick->connected_subchannel set to null.
+  virtual void CancelPickLocked(PickState* pick,
+                                grpc_error* error) GRPC_ABSTRACT;
+
+  /// Cancels all pending picks for which their \a initial_metadata_flags (as
+  /// given in the call to \a PickLocked()) matches
+  /// \a initial_metadata_flags_eq when ANDed with
+  /// \a initial_metadata_flags_mask.
+  virtual void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                         uint32_t initial_metadata_flags_eq,
+                                         grpc_error* error) GRPC_ABSTRACT;
+
+  /// Requests a notification when the connectivity state of the policy
+  /// changes from \a *state.  When that happens, sets \a *state to the
+  /// new state and schedules \a closure.
+  virtual void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                         grpc_closure* closure) GRPC_ABSTRACT;
+
+  /// Returns the policy's current connectivity state.  Sets \a error to
+  /// the associated error, if any.
+  virtual grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) GRPC_ABSTRACT;
+
+  /// Hands off pending picks to \a new_policy.
+  virtual void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy)
+      GRPC_ABSTRACT;
+
+  /// Tries to enter a READY connectivity state.
+  /// TODO(roth): As part of restructuring how we handle IDLE state,
+  /// consider whether this method is still needed.
+  virtual void ExitIdleLocked() GRPC_ABSTRACT;
+
+  /// Resets connection backoff.
+  virtual void ResetBackoffLocked() GRPC_ABSTRACT;
+
+  /// Populates child_subchannels and child_channels with the uuids of this
+  /// LB policy's referenced children. This is not invoked from the
+  /// client_channel's combiner. The implementation is responsible for
+  /// providing its own synchronization.
+  virtual void FillChildRefsForChannelz(
+      channelz::ChildRefsList* child_subchannels,
+      channelz::ChildRefsList* child_channels) GRPC_ABSTRACT;
+
+  void Orphan() override {
+    // Invoke ShutdownAndUnrefLocked() inside of the combiner.
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_CREATE(&LoadBalancingPolicy::ShutdownAndUnrefLocked, this,
+                            grpc_combiner_scheduler(combiner_)),
+        GRPC_ERROR_NONE);
+  }
+
+  /// Sets the re-resolution closure to \a request_reresolution.
+  void SetReresolutionClosureLocked(grpc_closure* request_reresolution) {
+    GPR_ASSERT(request_reresolution_ == nullptr);
+    request_reresolution_ = request_reresolution;
+  }
+
+  grpc_pollset_set* interested_parties() const { return interested_parties_; }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+  explicit LoadBalancingPolicy(const Args& args);
+  virtual ~LoadBalancingPolicy();
+
+  grpc_combiner* combiner() const { return combiner_; }
+  grpc_client_channel_factory* client_channel_factory() const {
+    return client_channel_factory_;
+  }
+
+  /// Shuts down the policy.  Any pending picks that have not been
+  /// handed off to a new policy via HandOffPendingPicksLocked() will be
+  /// failed.
+  virtual void ShutdownLocked() GRPC_ABSTRACT;
+
+  /// Tries to request a re-resolution.
+  void TryReresolutionLocked(grpc_core::TraceFlag* grpc_lb_trace,
+                             grpc_error* error);
+
+ private:
+  static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored) {
+    LoadBalancingPolicy* policy = static_cast<LoadBalancingPolicy*>(arg);
+    policy->ShutdownLocked();
+    policy->Unref();
+  }
+
+  /// Combiner under which LB policy actions take place.
+  grpc_combiner* combiner_;
+  /// Client channel factory, used to create channels and subchannels.
+  grpc_client_channel_factory* client_channel_factory_;
+  /// Owned pointer to interested parties in load balancing decisions.
+  grpc_pollset_set* interested_parties_;
+  /// Callback to force a re-resolution.
+  grpc_closure* request_reresolution_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
new file mode 100644
index 0000000..399bb45
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h"
+
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/profiling/timers.h"
+
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_channel_elem(grpc_channel_element* elem) {}
+
+namespace {
+
+struct call_data {
+  call_data(const grpc_call_element_args& args) {
+    if (args.context[GRPC_GRPCLB_CLIENT_STATS].value != nullptr) {
+      // Get stats object from context and take a ref.
+      client_stats = static_cast<grpc_core::GrpcLbClientStats*>(
+                         args.context[GRPC_GRPCLB_CLIENT_STATS].value)
+                         ->Ref();
+      // Record call started.
+      client_stats->AddCallStarted();
+    }
+  }
+
+  // Stats object to update.
+  grpc_core::RefCountedPtr<grpc_core::GrpcLbClientStats> client_stats;
+  // State for intercepting send_initial_metadata.
+  grpc_closure on_complete_for_send;
+  grpc_closure* original_on_complete_for_send;
+  bool send_initial_metadata_succeeded = false;
+  // State for intercepting recv_initial_metadata.
+  grpc_closure recv_initial_metadata_ready;
+  grpc_closure* original_recv_initial_metadata_ready;
+  bool recv_initial_metadata_succeeded = false;
+};
+
+}  // namespace
+
+static void on_complete_for_send(void* arg, grpc_error* error) {
+  call_data* calld = static_cast<call_data*>(arg);
+  if (error == GRPC_ERROR_NONE) {
+    calld->send_initial_metadata_succeeded = true;
+  }
+  GRPC_CLOSURE_RUN(calld->original_on_complete_for_send, GRPC_ERROR_REF(error));
+}
+
+static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
+  call_data* calld = static_cast<call_data*>(arg);
+  if (error == GRPC_ERROR_NONE) {
+    calld->recv_initial_metadata_succeeded = true;
+  }
+  GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready,
+                   GRPC_ERROR_REF(error));
+}
+
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  GPR_ASSERT(args->context != nullptr);
+  new (elem->call_data) call_data(*args);
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->client_stats != nullptr) {
+    // Record call finished, optionally setting client_failed_to_send and
+    // received.
+    calld->client_stats->AddCallFinished(
+        !calld->send_initial_metadata_succeeded /* client_failed_to_send */,
+        calld->recv_initial_metadata_succeeded /* known_received */);
+    // All done, so unref the stats object.
+    // TODO(roth): Eliminate this once filter stack is converted to C++.
+    calld->client_stats.reset();
+  }
+  calld->~call_data();
+}
+
+static void start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  GPR_TIMER_SCOPE("clr_start_transport_stream_op_batch", 0);
+  if (calld->client_stats != nullptr) {
+    // Intercept send_initial_metadata.
+    if (batch->send_initial_metadata) {
+      calld->original_on_complete_for_send = batch->on_complete;
+      GRPC_CLOSURE_INIT(&calld->on_complete_for_send, on_complete_for_send,
+                        calld, grpc_schedule_on_exec_ctx);
+      batch->on_complete = &calld->on_complete_for_send;
+    }
+    // Intercept recv_initial_metadata.
+    if (batch->recv_initial_metadata) {
+      calld->original_recv_initial_metadata_ready =
+          batch->payload->recv_initial_metadata.recv_initial_metadata_ready;
+      GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready,
+                        recv_initial_metadata_ready, calld,
+                        grpc_schedule_on_exec_ctx);
+      batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
+          &calld->recv_initial_metadata_ready;
+    }
+  }
+  // Chain to next filter.
+  grpc_call_next_op(elem, batch);
+}
+
+const grpc_channel_filter grpc_client_load_reporting_filter = {
+    start_transport_stream_op_batch,
+    grpc_channel_next_op,
+    sizeof(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_channel_next_get_info,
+    "client_load_reporting"};
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
new file mode 100644
index 0000000..838e2ef
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_client_load_reporting_filter;
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
new file mode 100644
index 0000000..ba40feb
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
@@ -0,0 +1,1867 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/// Implementation of the gRPC LB policy.
+///
+/// This policy takes as input a list of resolved addresses, which must
+/// include at least one balancer address.
+///
+/// An internal channel (\a lb_channel_) is created for the addresses
+/// from that are balancers.  This channel behaves just like a regular
+/// channel that uses pick_first to select from the list of balancer
+/// addresses.
+///
+/// The first time the policy gets a request for a pick, a ping, or to exit
+/// the idle state, \a StartPickingLocked() is called. This method is
+/// responsible for instantiating the internal *streaming* call to the LB
+/// server (whichever address pick_first chose).  The call will be complete
+/// when either the balancer sends status or when we cancel the call (e.g.,
+/// because we are shutting down).  In needed, we retry the call.  If we
+/// received at least one valid message from the server, a new call attempt
+/// will be made immediately; otherwise, we apply back-off delays between
+/// attempts.
+///
+/// We maintain an internal round_robin policy instance for distributing
+/// requests across backends.  Whenever we receive a new serverlist from
+/// the balancer, we update the round_robin policy with the new list of
+/// addresses.  If we cannot communicate with the balancer on startup,
+/// however, we may enter fallback mode, in which case we will populate
+/// the RR policy's addresses from the backend addresses returned by the
+/// resolver.
+///
+/// Once an RR policy instance is in place (and getting updated as described),
+/// calls 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 (i.e., right after the creation of the gRPCLB policy),
+/// pick and ping requests are added to a list of pending picks and pings
+/// to be flushed and serviced when 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.
+
+// With the addition of a libuv endpoint, sockaddr.h now includes uv.h when
+// using that endpoint. Because of various transitive includes in uv.h,
+// including windows.h on Windows, uv.h must be included before other system
+// headers. Therefore, sockaddr.h must always be included first.
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/socket_utils.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/byte_buffer_reader.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/client_channel_factory.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h"
+#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/ext/filters/client_channel/subchannel_index.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_hash_table.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#define GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS 1
+#define GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER 1.6
+#define GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS 120
+#define GRPC_GRPCLB_RECONNECT_JITTER 0.2
+#define GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS 10000
+
+#define GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN "grpc.grpclb_address_lb_token"
+
+namespace grpc_core {
+
+TraceFlag grpc_lb_glb_trace(false, "glb");
+
+namespace {
+
+constexpr char kGrpclb[] = "grpclb";
+
+class GrpcLb : public LoadBalancingPolicy {
+ public:
+  explicit GrpcLb(const Args& args);
+
+  const char* name() const override { return kGrpclb; }
+
+  void UpdateLocked(const grpc_channel_args& args,
+                    grpc_json* lb_config) override;
+  bool PickLocked(PickState* pick, grpc_error** error) override;
+  void CancelPickLocked(PickState* pick, grpc_error* error) override;
+  void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error* error) override;
+  void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                 grpc_closure* closure) override;
+  grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) override;
+  void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
+  void ExitIdleLocked() override;
+  void ResetBackoffLocked() override;
+  void FillChildRefsForChannelz(
+      channelz::ChildRefsList* child_subchannels,
+      channelz::ChildRefsList* child_channels) override;
+
+ private:
+  /// 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.
+  ///
+  /// Note that when a pick is sent to the RR policy, we inject our own
+  /// on_complete callback, so that we can intercept the result before
+  /// invoking the original on_complete callback.  This allows us to set the
+  /// LB token metadata and add client_stats to the call context.
+  /// See \a pending_pick_complete() for details.
+  struct PendingPick {
+    // The grpclb instance that created the wrapping. This instance is not
+    // owned; reference counts are untouched. It's used only for logging
+    // purposes.
+    GrpcLb* grpclb_policy;
+    // The original pick.
+    PickState* pick;
+    // Our on_complete closure and the original one.
+    grpc_closure on_complete;
+    grpc_closure* original_on_complete;
+    // Stats for client-side load reporting.
+    RefCountedPtr<GrpcLbClientStats> client_stats;
+    // Next pending pick.
+    PendingPick* next = nullptr;
+  };
+
+  /// Contains a call to the LB server and all the data related to the call.
+  class BalancerCallState : public InternallyRefCounted<BalancerCallState> {
+   public:
+    explicit BalancerCallState(
+        RefCountedPtr<LoadBalancingPolicy> parent_grpclb_policy);
+
+    // It's the caller's responsibility to ensure that Orphan() is called from
+    // inside the combiner.
+    void Orphan() override;
+
+    void StartQuery();
+
+    GrpcLbClientStats* client_stats() const { return client_stats_.get(); }
+
+    bool seen_initial_response() const { return seen_initial_response_; }
+
+   private:
+    // So Delete() can access our private dtor.
+    template <typename T>
+    friend void grpc_core::Delete(T*);
+
+    ~BalancerCallState();
+
+    GrpcLb* grpclb_policy() const {
+      return static_cast<GrpcLb*>(grpclb_policy_.get());
+    }
+
+    void ScheduleNextClientLoadReportLocked();
+    void SendClientLoadReportLocked();
+
+    static bool LoadReportCountersAreZero(grpc_grpclb_request* request);
+
+    static void MaybeSendClientLoadReportLocked(void* arg, grpc_error* error);
+    static void ClientLoadReportDoneLocked(void* arg, grpc_error* error);
+    static void OnInitialRequestSentLocked(void* arg, grpc_error* error);
+    static void OnBalancerMessageReceivedLocked(void* arg, grpc_error* error);
+    static void OnBalancerStatusReceivedLocked(void* arg, grpc_error* error);
+
+    // The owning LB policy.
+    RefCountedPtr<LoadBalancingPolicy> grpclb_policy_;
+
+    // The streaming call to the LB server. Always non-NULL.
+    grpc_call* lb_call_ = nullptr;
+
+    // recv_initial_metadata
+    grpc_metadata_array lb_initial_metadata_recv_;
+
+    // send_message
+    grpc_byte_buffer* send_message_payload_ = nullptr;
+    grpc_closure lb_on_initial_request_sent_;
+
+    // recv_message
+    grpc_byte_buffer* recv_message_payload_ = nullptr;
+    grpc_closure lb_on_balancer_message_received_;
+    bool seen_initial_response_ = false;
+
+    // recv_trailing_metadata
+    grpc_closure lb_on_balancer_status_received_;
+    grpc_metadata_array lb_trailing_metadata_recv_;
+    grpc_status_code lb_call_status_;
+    grpc_slice lb_call_status_details_;
+
+    // The stats for client-side load reporting associated with this LB call.
+    // Created after the first serverlist is received.
+    RefCountedPtr<GrpcLbClientStats> client_stats_;
+    grpc_millis client_stats_report_interval_ = 0;
+    grpc_timer client_load_report_timer_;
+    bool client_load_report_timer_callback_pending_ = false;
+    bool last_client_load_report_counters_were_zero_ = false;
+    bool client_load_report_is_due_ = false;
+    // The closure used for either the load report timer or the callback for
+    // completion of sending the load report.
+    grpc_closure client_load_report_closure_;
+  };
+
+  ~GrpcLb();
+
+  void ShutdownLocked() override;
+
+  // Helper function used in ctor and UpdateLocked().
+  void ProcessChannelArgsLocked(const grpc_channel_args& args);
+
+  // Methods for dealing with the balancer channel and call.
+  void StartPickingLocked();
+  void StartBalancerCallLocked();
+  static void OnFallbackTimerLocked(void* arg, grpc_error* error);
+  void StartBalancerCallRetryTimerLocked();
+  static void OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error);
+  static void OnBalancerChannelConnectivityChangedLocked(void* arg,
+                                                         grpc_error* error);
+
+  // Pending pick methods.
+  static void PendingPickSetMetadataAndContext(PendingPick* pp);
+  PendingPick* PendingPickCreate(PickState* pick);
+  void AddPendingPick(PendingPick* pp);
+  static void OnPendingPickComplete(void* arg, grpc_error* error);
+
+  // Methods for dealing with the RR policy.
+  void CreateOrUpdateRoundRobinPolicyLocked();
+  grpc_channel_args* CreateRoundRobinPolicyArgsLocked();
+  void CreateRoundRobinPolicyLocked(const Args& args);
+  bool PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp,
+                                      grpc_error** error);
+  void UpdateConnectivityStateFromRoundRobinPolicyLocked(
+      grpc_error* rr_state_error);
+  static void OnRoundRobinConnectivityChangedLocked(void* arg,
+                                                    grpc_error* error);
+  static void OnRoundRobinRequestReresolutionLocked(void* arg,
+                                                    grpc_error* error);
+
+  // Who the client is trying to communicate with.
+  const char* server_name_ = nullptr;
+
+  // Current channel args from the resolver.
+  grpc_channel_args* args_ = nullptr;
+
+  // Internal state.
+  bool started_picking_ = false;
+  bool shutting_down_ = false;
+  grpc_connectivity_state_tracker state_tracker_;
+
+  // The channel for communicating with the LB server.
+  grpc_channel* lb_channel_ = nullptr;
+  // Mutex to protect the channel to the LB server. This is used when
+  // processing a channelz request.
+  gpr_mu lb_channel_mu_;
+  grpc_connectivity_state lb_channel_connectivity_;
+  grpc_closure lb_channel_on_connectivity_changed_;
+  // Are we already watching the LB channel's connectivity?
+  bool watching_lb_channel_ = false;
+  // Response generator to inject address updates into lb_channel_.
+  RefCountedPtr<FakeResolverResponseGenerator> response_generator_;
+
+  // The data associated with the current LB call. It holds a ref to this LB
+  // policy. It's initialized every time we query for backends. It's reset to
+  // NULL whenever the current LB call is no longer needed (e.g., the LB policy
+  // is shutting down, or the LB call has ended). A non-NULL lb_calld_ always
+  // contains a non-NULL lb_call_.
+  OrphanablePtr<BalancerCallState> lb_calld_;
+  // Timeout in milliseconds for the LB call. 0 means no deadline.
+  int lb_call_timeout_ms_ = 0;
+  // Balancer call retry state.
+  BackOff lb_call_backoff_;
+  bool retry_timer_callback_pending_ = false;
+  grpc_timer lb_call_retry_timer_;
+  grpc_closure lb_on_call_retry_;
+
+  // The deserialized response from the balancer. May be nullptr until one
+  // such response has arrived.
+  grpc_grpclb_serverlist* serverlist_ = nullptr;
+  // Index into serverlist for next pick.
+  // If the server at this index is a drop, we return a drop.
+  // Otherwise, we delegate to the RR policy.
+  size_t serverlist_index_ = 0;
+
+  // Timeout in milliseconds for before using fallback backend addresses.
+  // 0 means not using fallback.
+  int lb_fallback_timeout_ms_ = 0;
+  // The backend addresses from the resolver.
+  UniquePtr<ServerAddressList> fallback_backend_addresses_;
+  // Fallback timer.
+  bool fallback_timer_callback_pending_ = false;
+  grpc_timer lb_fallback_timer_;
+  grpc_closure lb_on_fallback_;
+
+  // Pending picks that are waiting on the RR policy's connectivity.
+  PendingPick* pending_picks_ = nullptr;
+
+  // The RR policy to use for the backends.
+  OrphanablePtr<LoadBalancingPolicy> rr_policy_;
+  grpc_connectivity_state rr_connectivity_state_;
+  grpc_closure on_rr_connectivity_changed_;
+  grpc_closure on_rr_request_reresolution_;
+};
+
+//
+// serverlist parsing code
+//
+
+// vtable for LB token channel arg.
+void* lb_token_copy(void* token) {
+  return token == nullptr
+             ? nullptr
+             : (void*)GRPC_MDELEM_REF(grpc_mdelem{(uintptr_t)token}).payload;
+}
+void lb_token_destroy(void* token) {
+  if (token != nullptr) {
+    GRPC_MDELEM_UNREF(grpc_mdelem{(uintptr_t)token});
+  }
+}
+int lb_token_cmp(void* token1, void* token2) {
+  // Always indicate a match, since we don't want this channel arg to
+  // affect the subchannel's key in the index.
+  return 0;
+}
+const grpc_arg_pointer_vtable lb_token_arg_vtable = {
+    lb_token_copy, lb_token_destroy, lb_token_cmp};
+
+bool IsServerValid(const grpc_grpclb_server* server, size_t idx, bool log) {
+  if (server->drop) return false;
+  const grpc_grpclb_ip_address* ip = &server->ip_address;
+  if (GPR_UNLIKELY(server->port >> 16 != 0)) {
+    if (log) {
+      gpr_log(GPR_ERROR,
+              "Invalid port '%d' at index %lu of serverlist. Ignoring.",
+              server->port, (unsigned long)idx);
+    }
+    return false;
+  }
+  if (GPR_UNLIKELY(ip->size != 4 && ip->size != 16)) {
+    if (log) {
+      gpr_log(GPR_ERROR,
+              "Expected IP to be 4 or 16 bytes, got %d at index %lu of "
+              "serverlist. Ignoring",
+              ip->size, (unsigned long)idx);
+    }
+    return false;
+  }
+  return true;
+}
+
+void ParseServer(const grpc_grpclb_server* server,
+                 grpc_resolved_address* addr) {
+  memset(addr, 0, sizeof(*addr));
+  if (server->drop) return;
+  const uint16_t netorder_port = grpc_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;
+  if (ip->size == 4) {
+    addr->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in));
+    grpc_sockaddr_in* addr4 = reinterpret_cast<grpc_sockaddr_in*>(&addr->addr);
+    addr4->sin_family = GRPC_AF_INET;
+    memcpy(&addr4->sin_addr, ip->bytes, ip->size);
+    addr4->sin_port = netorder_port;
+  } else if (ip->size == 16) {
+    addr->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in6));
+    grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)&addr->addr;
+    addr6->sin6_family = GRPC_AF_INET6;
+    memcpy(&addr6->sin6_addr, ip->bytes, ip->size);
+    addr6->sin6_port = netorder_port;
+  }
+}
+
+// Returns addresses extracted from \a serverlist.
+ServerAddressList ProcessServerlist(const grpc_grpclb_serverlist* serverlist) {
+  ServerAddressList addresses;
+  for (size_t i = 0; i < serverlist->num_servers; ++i) {
+    const grpc_grpclb_server* server = serverlist->servers[i];
+    if (!IsServerValid(serverlist->servers[i], i, false)) continue;
+    // Address processing.
+    grpc_resolved_address addr;
+    ParseServer(server, &addr);
+    // LB token processing.
+    grpc_mdelem lb_token;
+    if (server->has_load_balance_token) {
+      const size_t lb_token_max_length =
+          GPR_ARRAY_SIZE(server->load_balance_token);
+      const size_t lb_token_length =
+          strnlen(server->load_balance_token, lb_token_max_length);
+      grpc_slice lb_token_mdstr = grpc_slice_from_copied_buffer(
+          server->load_balance_token, lb_token_length);
+      lb_token = grpc_mdelem_from_slices(GRPC_MDSTR_LB_TOKEN, lb_token_mdstr);
+    } else {
+      char* uri = grpc_sockaddr_to_uri(&addr);
+      gpr_log(GPR_INFO,
+              "Missing LB token for backend address '%s'. The empty token will "
+              "be used instead",
+              uri);
+      gpr_free(uri);
+      lb_token = GRPC_MDELEM_LB_TOKEN_EMPTY;
+    }
+    // Add address.
+    grpc_arg arg = grpc_channel_arg_pointer_create(
+        const_cast<char*>(GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN),
+        (void*)lb_token.payload, &lb_token_arg_vtable);
+    grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1);
+    addresses.emplace_back(addr, args);
+    // Clean up.
+    GRPC_MDELEM_UNREF(lb_token);
+  }
+  return addresses;
+}
+
+//
+// GrpcLb::BalancerCallState
+//
+
+GrpcLb::BalancerCallState::BalancerCallState(
+    RefCountedPtr<LoadBalancingPolicy> parent_grpclb_policy)
+    : InternallyRefCounted<BalancerCallState>(&grpc_lb_glb_trace),
+      grpclb_policy_(std::move(parent_grpclb_policy)) {
+  GPR_ASSERT(grpclb_policy_ != nullptr);
+  GPR_ASSERT(!grpclb_policy()->shutting_down_);
+  // Init the LB call. Note that the LB call will progress every time there's
+  // activity in grpclb_policy_->interested_parties(), which is comprised of
+  // the polling entities from client_channel.
+  GPR_ASSERT(grpclb_policy()->server_name_ != nullptr);
+  GPR_ASSERT(grpclb_policy()->server_name_[0] != '\0');
+  const grpc_millis deadline =
+      grpclb_policy()->lb_call_timeout_ms_ == 0
+          ? GRPC_MILLIS_INF_FUTURE
+          : ExecCtx::Get()->Now() + grpclb_policy()->lb_call_timeout_ms_;
+  lb_call_ = grpc_channel_create_pollset_set_call(
+      grpclb_policy()->lb_channel_, nullptr, GRPC_PROPAGATE_DEFAULTS,
+      grpclb_policy_->interested_parties(),
+      GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD,
+      nullptr, deadline, nullptr);
+  // Init the LB call request payload.
+  grpc_grpclb_request* request =
+      grpc_grpclb_request_create(grpclb_policy()->server_name_);
+  grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
+  send_message_payload_ =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_slice_unref_internal(request_payload_slice);
+  grpc_grpclb_request_destroy(request);
+  // Init other data associated with the LB call.
+  grpc_metadata_array_init(&lb_initial_metadata_recv_);
+  grpc_metadata_array_init(&lb_trailing_metadata_recv_);
+  GRPC_CLOSURE_INIT(&lb_on_initial_request_sent_, OnInitialRequestSentLocked,
+                    this, grpc_combiner_scheduler(grpclb_policy()->combiner()));
+  GRPC_CLOSURE_INIT(&lb_on_balancer_message_received_,
+                    OnBalancerMessageReceivedLocked, this,
+                    grpc_combiner_scheduler(grpclb_policy()->combiner()));
+  GRPC_CLOSURE_INIT(&lb_on_balancer_status_received_,
+                    OnBalancerStatusReceivedLocked, this,
+                    grpc_combiner_scheduler(grpclb_policy()->combiner()));
+}
+
+GrpcLb::BalancerCallState::~BalancerCallState() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  grpc_call_unref(lb_call_);
+  grpc_metadata_array_destroy(&lb_initial_metadata_recv_);
+  grpc_metadata_array_destroy(&lb_trailing_metadata_recv_);
+  grpc_byte_buffer_destroy(send_message_payload_);
+  grpc_byte_buffer_destroy(recv_message_payload_);
+  grpc_slice_unref_internal(lb_call_status_details_);
+}
+
+void GrpcLb::BalancerCallState::Orphan() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  // If we are here because grpclb_policy wants to cancel the call,
+  // lb_on_balancer_status_received_ will complete the cancellation and clean
+  // up. Otherwise, we are here because grpclb_policy has to orphan a failed
+  // call, then the following cancellation will be a no-op.
+  grpc_call_cancel(lb_call_, nullptr);
+  if (client_load_report_timer_callback_pending_) {
+    grpc_timer_cancel(&client_load_report_timer_);
+  }
+  // Note that the initial ref is hold by lb_on_balancer_status_received_
+  // instead of the caller of this function. So the corresponding unref happens
+  // in lb_on_balancer_status_received_ instead of here.
+}
+
+void GrpcLb::BalancerCallState::StartQuery() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_INFO, "[grpclb %p] lb_calld=%p: Starting LB call %p",
+            grpclb_policy_.get(), this, lb_call_);
+  }
+  // Create the ops.
+  grpc_call_error call_error;
+  grpc_op ops[3];
+  memset(ops, 0, sizeof(ops));
+  // Op: send initial metadata.
+  grpc_op* op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // Op: send request message.
+  GPR_ASSERT(send_message_payload_ != nullptr);
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = send_message_payload_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  auto self = Ref(DEBUG_LOCATION, "on_initial_request_sent");
+  self.release();
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_initial_request_sent_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+  // Op: recv initial metadata.
+  op = ops;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &lb_initial_metadata_recv_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // Op: recv response.
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &recv_message_payload_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  self = Ref(DEBUG_LOCATION, "on_message_received");
+  self.release();
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_message_received_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+  // Op: recv server status.
+  op = ops;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata =
+      &lb_trailing_metadata_recv_;
+  op->data.recv_status_on_client.status = &lb_call_status_;
+  op->data.recv_status_on_client.status_details = &lb_call_status_details_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // This callback signals the end of the LB call, so it relies on the initial
+  // ref instead of a new ref. When it's invoked, it's the initial ref that is
+  // unreffed.
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_status_received_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+};
+
+void GrpcLb::BalancerCallState::ScheduleNextClientLoadReportLocked() {
+  const grpc_millis next_client_load_report_time =
+      ExecCtx::Get()->Now() + client_stats_report_interval_;
+  GRPC_CLOSURE_INIT(&client_load_report_closure_,
+                    MaybeSendClientLoadReportLocked, this,
+                    grpc_combiner_scheduler(grpclb_policy()->combiner()));
+  grpc_timer_init(&client_load_report_timer_, next_client_load_report_time,
+                  &client_load_report_closure_);
+  client_load_report_timer_callback_pending_ = true;
+}
+
+void GrpcLb::BalancerCallState::MaybeSendClientLoadReportLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
+  lb_calld->client_load_report_timer_callback_pending_ = false;
+  if (error != GRPC_ERROR_NONE || lb_calld != grpclb_policy->lb_calld_.get()) {
+    lb_calld->Unref(DEBUG_LOCATION, "client_load_report");
+    return;
+  }
+  // If we've already sent the initial request, then we can go ahead and send
+  // the load report. Otherwise, we need to wait until the initial request has
+  // been sent to send this (see OnInitialRequestSentLocked()).
+  if (lb_calld->send_message_payload_ == nullptr) {
+    lb_calld->SendClientLoadReportLocked();
+  } else {
+    lb_calld->client_load_report_is_due_ = true;
+  }
+}
+
+bool GrpcLb::BalancerCallState::LoadReportCountersAreZero(
+    grpc_grpclb_request* request) {
+  GrpcLbClientStats::DroppedCallCounts* drop_entries =
+      static_cast<GrpcLbClientStats::DroppedCallCounts*>(
+          request->client_stats.calls_finished_with_drop.arg);
+  return request->client_stats.num_calls_started == 0 &&
+         request->client_stats.num_calls_finished == 0 &&
+         request->client_stats.num_calls_finished_with_client_failed_to_send ==
+             0 &&
+         request->client_stats.num_calls_finished_known_received == 0 &&
+         (drop_entries == nullptr || drop_entries->size() == 0);
+}
+
+void GrpcLb::BalancerCallState::SendClientLoadReportLocked() {
+  // Construct message payload.
+  GPR_ASSERT(send_message_payload_ == nullptr);
+  grpc_grpclb_request* request =
+      grpc_grpclb_load_report_request_create_locked(client_stats_.get());
+  // Skip client load report if the counters were all zero in the last
+  // report and they are still zero in this one.
+  if (LoadReportCountersAreZero(request)) {
+    if (last_client_load_report_counters_were_zero_) {
+      grpc_grpclb_request_destroy(request);
+      ScheduleNextClientLoadReportLocked();
+      return;
+    }
+    last_client_load_report_counters_were_zero_ = true;
+  } else {
+    last_client_load_report_counters_were_zero_ = false;
+  }
+  grpc_slice request_payload_slice = grpc_grpclb_request_encode(request);
+  send_message_payload_ =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_slice_unref_internal(request_payload_slice);
+  grpc_grpclb_request_destroy(request);
+  // Send the report.
+  grpc_op op;
+  memset(&op, 0, sizeof(op));
+  op.op = GRPC_OP_SEND_MESSAGE;
+  op.data.send_message.send_message = send_message_payload_;
+  GRPC_CLOSURE_INIT(&client_load_report_closure_, ClientLoadReportDoneLocked,
+                    this, grpc_combiner_scheduler(grpclb_policy()->combiner()));
+  grpc_call_error call_error = grpc_call_start_batch_and_execute(
+      lb_call_, &op, 1, &client_load_report_closure_);
+  if (GPR_UNLIKELY(call_error != GRPC_CALL_OK)) {
+    gpr_log(GPR_ERROR,
+            "[grpclb %p] lb_calld=%p call_error=%d sending client load report",
+            grpclb_policy_.get(), this, call_error);
+    GPR_ASSERT(GRPC_CALL_OK == call_error);
+  }
+}
+
+void GrpcLb::BalancerCallState::ClientLoadReportDoneLocked(void* arg,
+                                                           grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
+  grpc_byte_buffer_destroy(lb_calld->send_message_payload_);
+  lb_calld->send_message_payload_ = nullptr;
+  if (error != GRPC_ERROR_NONE || lb_calld != grpclb_policy->lb_calld_.get()) {
+    lb_calld->Unref(DEBUG_LOCATION, "client_load_report");
+    return;
+  }
+  lb_calld->ScheduleNextClientLoadReportLocked();
+}
+
+void GrpcLb::BalancerCallState::OnInitialRequestSentLocked(void* arg,
+                                                           grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  grpc_byte_buffer_destroy(lb_calld->send_message_payload_);
+  lb_calld->send_message_payload_ = nullptr;
+  // If we attempted to send a client load report before the initial request was
+  // sent (and this lb_calld is still in use), send the load report now.
+  if (lb_calld->client_load_report_is_due_ &&
+      lb_calld == lb_calld->grpclb_policy()->lb_calld_.get()) {
+    lb_calld->SendClientLoadReportLocked();
+    lb_calld->client_load_report_is_due_ = false;
+  }
+  lb_calld->Unref(DEBUG_LOCATION, "on_initial_request_sent");
+}
+
+void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
+  // Null payload means the LB call was cancelled.
+  if (lb_calld != grpclb_policy->lb_calld_.get() ||
+      lb_calld->recv_message_payload_ == nullptr) {
+    lb_calld->Unref(DEBUG_LOCATION, "on_message_received");
+    return;
+  }
+  grpc_byte_buffer_reader bbr;
+  grpc_byte_buffer_reader_init(&bbr, lb_calld->recv_message_payload_);
+  grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
+  grpc_byte_buffer_reader_destroy(&bbr);
+  grpc_byte_buffer_destroy(lb_calld->recv_message_payload_);
+  lb_calld->recv_message_payload_ = nullptr;
+  grpc_grpclb_initial_response* initial_response;
+  grpc_grpclb_serverlist* serverlist;
+  if (!lb_calld->seen_initial_response_ &&
+      (initial_response = grpc_grpclb_initial_response_parse(response_slice)) !=
+          nullptr) {
+    // Have NOT seen initial response, look for initial response.
+    if (initial_response->has_client_stats_report_interval) {
+      lb_calld->client_stats_report_interval_ = GPR_MAX(
+          GPR_MS_PER_SEC, grpc_grpclb_duration_to_millis(
+                              &initial_response->client_stats_report_interval));
+      if (grpc_lb_glb_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "[grpclb %p] lb_calld=%p: Received initial LB response "
+                "message; client load reporting interval = %" PRId64
+                " milliseconds",
+                grpclb_policy, lb_calld,
+                lb_calld->client_stats_report_interval_);
+      }
+    } else if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[grpclb %p] lb_calld=%p: Received initial LB response message; "
+              "client load reporting NOT enabled",
+              grpclb_policy, lb_calld);
+    }
+    grpc_grpclb_initial_response_destroy(initial_response);
+    lb_calld->seen_initial_response_ = true;
+  } else if ((serverlist = grpc_grpclb_response_parse_serverlist(
+                  response_slice)) != nullptr) {
+    // Have seen initial response, look for serverlist.
+    GPR_ASSERT(lb_calld->lb_call_ != nullptr);
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[grpclb %p] lb_calld=%p: Serverlist with %" PRIuPTR
+              " servers received",
+              grpclb_policy, lb_calld, serverlist->num_servers);
+      for (size_t i = 0; i < serverlist->num_servers; ++i) {
+        grpc_resolved_address addr;
+        ParseServer(serverlist->servers[i], &addr);
+        char* ipport;
+        grpc_sockaddr_to_string(&ipport, &addr, false);
+        gpr_log(GPR_INFO,
+                "[grpclb %p] lb_calld=%p: Serverlist[%" PRIuPTR "]: %s",
+                grpclb_policy, lb_calld, i, ipport);
+        gpr_free(ipport);
+      }
+    }
+    // Start sending client load report only after we start using the
+    // serverlist returned from the current LB call.
+    if (lb_calld->client_stats_report_interval_ > 0 &&
+        lb_calld->client_stats_ == nullptr) {
+      lb_calld->client_stats_.reset(New<GrpcLbClientStats>());
+      // TODO(roth): We currently track this ref manually.  Once the
+      // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+      // with the callback.
+      auto self = lb_calld->Ref(DEBUG_LOCATION, "client_load_report");
+      self.release();
+      lb_calld->ScheduleNextClientLoadReportLocked();
+    }
+    // Check if the serverlist differs from the previous one.
+    if (grpc_grpclb_serverlist_equals(grpclb_policy->serverlist_, serverlist)) {
+      if (grpc_lb_glb_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "[grpclb %p] lb_calld=%p: Incoming server list identical to "
+                "current, ignoring.",
+                grpclb_policy, lb_calld);
+      }
+      grpc_grpclb_destroy_serverlist(serverlist);
+    } else {  // New serverlist.
+      if (grpclb_policy->serverlist_ != nullptr) {
+        // Dispose of the old serverlist.
+        grpc_grpclb_destroy_serverlist(grpclb_policy->serverlist_);
+      } else {
+        // Dispose of the fallback.
+        grpclb_policy->fallback_backend_addresses_.reset();
+        if (grpclb_policy->fallback_timer_callback_pending_) {
+          grpc_timer_cancel(&grpclb_policy->lb_fallback_timer_);
+        }
+      }
+      // Update the serverlist in the GrpcLb instance. This serverlist
+      // instance will be destroyed either upon the next update or when the
+      // GrpcLb instance is destroyed.
+      grpclb_policy->serverlist_ = serverlist;
+      grpclb_policy->serverlist_index_ = 0;
+      grpclb_policy->CreateOrUpdateRoundRobinPolicyLocked();
+    }
+  } else {
+    // No valid initial response or serverlist found.
+    char* response_slice_str =
+        grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX);
+    gpr_log(GPR_ERROR,
+            "[grpclb %p] lb_calld=%p: Invalid LB response received: '%s'. "
+            "Ignoring.",
+            grpclb_policy, lb_calld, response_slice_str);
+    gpr_free(response_slice_str);
+  }
+  grpc_slice_unref_internal(response_slice);
+  if (!grpclb_policy->shutting_down_) {
+    // Keep listening for serverlist updates.
+    grpc_op op;
+    memset(&op, 0, sizeof(op));
+    op.op = GRPC_OP_RECV_MESSAGE;
+    op.data.recv_message.recv_message = &lb_calld->recv_message_payload_;
+    op.flags = 0;
+    op.reserved = nullptr;
+    // Reuse the "OnBalancerMessageReceivedLocked" ref taken in StartQuery().
+    const grpc_call_error call_error = grpc_call_start_batch_and_execute(
+        lb_calld->lb_call_, &op, 1,
+        &lb_calld->lb_on_balancer_message_received_);
+    GPR_ASSERT(GRPC_CALL_OK == call_error);
+  } else {
+    lb_calld->Unref(DEBUG_LOCATION, "on_message_received+grpclb_shutdown");
+  }
+}
+
+void GrpcLb::BalancerCallState::OnBalancerStatusReceivedLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  GrpcLb* grpclb_policy = lb_calld->grpclb_policy();
+  GPR_ASSERT(lb_calld->lb_call_ != nullptr);
+  if (grpc_lb_glb_trace.enabled()) {
+    char* status_details =
+        grpc_slice_to_c_string(lb_calld->lb_call_status_details_);
+    gpr_log(GPR_INFO,
+            "[grpclb %p] lb_calld=%p: Status from LB server received. "
+            "Status = %d, details = '%s', (lb_call: %p), error '%s'",
+            grpclb_policy, lb_calld, lb_calld->lb_call_status_, status_details,
+            lb_calld->lb_call_, grpc_error_string(error));
+    gpr_free(status_details);
+  }
+  grpclb_policy->TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_NONE);
+  // If this lb_calld is still in use, this call ended because of a failure so
+  // we want to retry connecting. Otherwise, we have deliberately ended this
+  // call and no further action is required.
+  if (lb_calld == grpclb_policy->lb_calld_.get()) {
+    grpclb_policy->lb_calld_.reset();
+    GPR_ASSERT(!grpclb_policy->shutting_down_);
+    if (lb_calld->seen_initial_response_) {
+      // If we lose connection to the LB server, reset the backoff and restart
+      // the LB call immediately.
+      grpclb_policy->lb_call_backoff_.Reset();
+      grpclb_policy->StartBalancerCallLocked();
+    } else {
+      // If this LB call fails establishing any connection to the LB server,
+      // retry later.
+      grpclb_policy->StartBalancerCallRetryTimerLocked();
+    }
+  }
+  lb_calld->Unref(DEBUG_LOCATION, "lb_call_ended");
+}
+
+//
+// helper code for creating balancer channel
+//
+
+ServerAddressList ExtractBalancerAddresses(const ServerAddressList& addresses) {
+  ServerAddressList balancer_addresses;
+  for (size_t i = 0; i < addresses.size(); ++i) {
+    if (addresses[i].IsBalancer()) {
+      // Strip out the is_balancer channel arg, since we don't want to
+      // recursively use the grpclb policy in the channel used to talk to
+      // the balancers.  Note that we do NOT strip out the balancer_name
+      // channel arg, since we need that to set the authority correctly
+      // to talk to the balancers.
+      static const char* args_to_remove[] = {
+          GRPC_ARG_ADDRESS_IS_BALANCER,
+      };
+      balancer_addresses.emplace_back(
+          addresses[i].address(),
+          grpc_channel_args_copy_and_remove(addresses[i].args(), args_to_remove,
+                                            GPR_ARRAY_SIZE(args_to_remove)));
+    }
+  }
+  return balancer_addresses;
+}
+
+/* Returns the channel args for the LB channel, used to create a bidirectional
+ * stream for the reception of load balancing updates.
+ *
+ * Inputs:
+ *   - \a addresses: corresponding to the balancers.
+ *   - \a response_generator: in order to propagate updates from the resolver
+ *   above the grpclb policy.
+ *   - \a args: other args inherited from the grpclb policy. */
+grpc_channel_args* BuildBalancerChannelArgs(
+    const ServerAddressList& addresses,
+    FakeResolverResponseGenerator* response_generator,
+    const grpc_channel_args* args) {
+  ServerAddressList balancer_addresses = ExtractBalancerAddresses(addresses);
+  // Channel args to remove.
+  static const char* args_to_remove[] = {
+      // LB policy name, since we want to use the default (pick_first) in
+      // the LB channel.
+      GRPC_ARG_LB_POLICY_NAME,
+      // The channel arg for the server URI, since that will be different for
+      // the LB channel than for the parent channel.  The client channel
+      // factory will re-add this arg with the right value.
+      GRPC_ARG_SERVER_URI,
+      // The resolved addresses, which will be generated by the name resolver
+      // used in the LB channel.  Note that the LB channel will use the fake
+      // resolver, so this won't actually generate a query to DNS (or some
+      // other name service).  However, the addresses returned by the fake
+      // resolver will have is_balancer=false, whereas our own addresses have
+      // is_balancer=true.  We need the LB channel to return addresses with
+      // is_balancer=false so that it does not wind up recursively using the
+      // grpclb LB policy, as per the special case logic in client_channel.c.
+      GRPC_ARG_SERVER_ADDRESS_LIST,
+      // The fake resolver response generator, because we are replacing it
+      // with the one from the grpclb policy, used to propagate updates to
+      // the LB channel.
+      GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
+      // The LB channel should use the authority indicated by the target
+      // authority table (see \a grpc_lb_policy_grpclb_modify_lb_channel_args),
+      // as opposed to the authority from the parent channel.
+      GRPC_ARG_DEFAULT_AUTHORITY,
+      // Just as for \a GRPC_ARG_DEFAULT_AUTHORITY, the LB channel should be
+      // treated as a stand-alone channel and not inherit this argument from the
+      // args of the parent channel.
+      GRPC_SSL_TARGET_NAME_OVERRIDE_ARG,
+  };
+  // Channel args to add.
+  const grpc_arg args_to_add[] = {
+      // New address list.
+      // Note that we pass these in both when creating the LB channel
+      // and via the fake resolver.  The latter is what actually gets used.
+      CreateServerAddressListChannelArg(&balancer_addresses),
+      // The fake resolver response generator, which we use to inject
+      // address updates into the LB channel.
+      grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
+          response_generator),
+      // A channel arg indicating the target is a grpclb load balancer.
+      grpc_channel_arg_integer_create(
+          const_cast<char*>(GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER), 1),
+      // A channel arg indicating this is an internal channels, aka it is
+      // owned by components in Core, not by the user application.
+      grpc_channel_arg_integer_create(
+          const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), 1),
+  };
+  // Construct channel args.
+  grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove(
+      args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), args_to_add,
+      GPR_ARRAY_SIZE(args_to_add));
+  // Make any necessary modifications for security.
+  return grpc_lb_policy_grpclb_modify_lb_channel_args(new_args);
+}
+
+//
+// ctor and dtor
+//
+
+GrpcLb::GrpcLb(const LoadBalancingPolicy::Args& args)
+    : LoadBalancingPolicy(args),
+      response_generator_(MakeRefCounted<FakeResolverResponseGenerator>()),
+      lb_call_backoff_(
+          BackOff::Options()
+              .set_initial_backoff(GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS *
+                                   1000)
+              .set_multiplier(GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER)
+              .set_jitter(GRPC_GRPCLB_RECONNECT_JITTER)
+              .set_max_backoff(GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS *
+                               1000)) {
+  // Initialization.
+  gpr_mu_init(&lb_channel_mu_);
+  grpc_subchannel_index_ref();
+  GRPC_CLOSURE_INIT(&lb_channel_on_connectivity_changed_,
+                    &GrpcLb::OnBalancerChannelConnectivityChangedLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  GRPC_CLOSURE_INIT(&on_rr_connectivity_changed_,
+                    &GrpcLb::OnRoundRobinConnectivityChangedLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  GRPC_CLOSURE_INIT(&on_rr_request_reresolution_,
+                    &GrpcLb::OnRoundRobinRequestReresolutionLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, "grpclb");
+  // Record server name.
+  const grpc_arg* arg = grpc_channel_args_find(args.args, GRPC_ARG_SERVER_URI);
+  const char* server_uri = grpc_channel_arg_get_string(arg);
+  GPR_ASSERT(server_uri != nullptr);
+  grpc_uri* uri = grpc_uri_parse(server_uri, true);
+  GPR_ASSERT(uri->path[0] != '\0');
+  server_name_ = gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[grpclb %p] Will use '%s' as the server name for LB request.",
+            this, server_name_);
+  }
+  grpc_uri_destroy(uri);
+  // Record LB call timeout.
+  arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS);
+  lb_call_timeout_ms_ = grpc_channel_arg_get_integer(arg, {0, 0, INT_MAX});
+  // Record fallback timeout.
+  arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS);
+  lb_fallback_timeout_ms_ = grpc_channel_arg_get_integer(
+      arg, {GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS, 0, INT_MAX});
+  // Process channel args.
+  ProcessChannelArgsLocked(*args.args);
+}
+
+GrpcLb::~GrpcLb() {
+  GPR_ASSERT(pending_picks_ == nullptr);
+  gpr_mu_destroy(&lb_channel_mu_);
+  gpr_free((void*)server_name_);
+  grpc_channel_args_destroy(args_);
+  grpc_connectivity_state_destroy(&state_tracker_);
+  if (serverlist_ != nullptr) {
+    grpc_grpclb_destroy_serverlist(serverlist_);
+  }
+  grpc_subchannel_index_unref();
+}
+
+void GrpcLb::ShutdownLocked() {
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
+  shutting_down_ = true;
+  lb_calld_.reset();
+  if (retry_timer_callback_pending_) {
+    grpc_timer_cancel(&lb_call_retry_timer_);
+  }
+  if (fallback_timer_callback_pending_) {
+    grpc_timer_cancel(&lb_fallback_timer_);
+  }
+  rr_policy_.reset();
+  TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_CANCELLED);
+  // We destroy the LB channel here instead of in our destructor because
+  // destroying the channel triggers a last callback to
+  // OnBalancerChannelConnectivityChangedLocked(), and we need to be
+  // alive when that callback is invoked.
+  if (lb_channel_ != nullptr) {
+    gpr_mu_lock(&lb_channel_mu_);
+    grpc_channel_destroy(lb_channel_);
+    lb_channel_ = nullptr;
+    gpr_mu_unlock(&lb_channel_mu_);
+  }
+  grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN,
+                              GRPC_ERROR_REF(error), "grpclb_shutdown");
+  // Clear pending picks.
+  PendingPick* pp;
+  while ((pp = pending_picks_) != nullptr) {
+    pending_picks_ = pp->next;
+    pp->pick->connected_subchannel.reset();
+    // Note: pp is deleted in this callback.
+    GRPC_CLOSURE_SCHED(&pp->on_complete, GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+//
+// public methods
+//
+
+void GrpcLb::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
+  PendingPick* pp;
+  while ((pp = pending_picks_) != nullptr) {
+    pending_picks_ = pp->next;
+    pp->pick->on_complete = pp->original_on_complete;
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (new_policy->PickLocked(pp->pick, &error)) {
+      // Synchronous return; schedule closure.
+      GRPC_CLOSURE_SCHED(pp->pick->on_complete, error);
+    }
+    Delete(pp);
+  }
+}
+
+// Cancel a specific pending pick.
+//
+// A grpclb pick progresses as follows:
+// - If there's a Round Robin policy (rr_policy_) available, it'll be
+//   handed over to the RR policy (in CreateRoundRobinPolicyLocked()). From
+//   that point onwards, it'll be RR's responsibility. For cancellations, that
+//   implies the pick needs also be cancelled by the RR instance.
+// - Otherwise, without an RR instance, picks stay pending at this policy's
+//   level (grpclb), inside the pending_picks_ list. To cancel these,
+//   we invoke the completion closure and set the pick's connected
+//   subchannel to nullptr right here.
+void GrpcLb::CancelPickLocked(PickState* pick, grpc_error* error) {
+  PendingPick* pp = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pp != nullptr) {
+    PendingPick* next = pp->next;
+    if (pp->pick == pick) {
+      pick->connected_subchannel.reset();
+      // Note: pp is deleted in this callback.
+      GRPC_CLOSURE_SCHED(&pp->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
+    }
+    pp = next;
+  }
+  if (rr_policy_ != nullptr) {
+    rr_policy_->CancelPickLocked(pick, GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// Cancel all pending picks.
+//
+// A grpclb pick progresses as follows:
+// - If there's a Round Robin policy (rr_policy_) available, it'll be
+//   handed over to the RR policy (in CreateRoundRobinPolicyLocked()). From
+//   that point onwards, it'll be RR's responsibility. For cancellations, that
+//   implies the pick needs also be cancelled by the RR instance.
+// - Otherwise, without an RR instance, picks stay pending at this policy's
+//   level (grpclb), inside the pending_picks_ list. To cancel these,
+//   we invoke the completion closure and set the pick's connected
+//   subchannel to nullptr right here.
+void GrpcLb::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                       uint32_t initial_metadata_flags_eq,
+                                       grpc_error* error) {
+  PendingPick* pp = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pp != nullptr) {
+    PendingPick* next = pp->next;
+    if ((*pp->pick->initial_metadata_flags & initial_metadata_flags_mask) ==
+        initial_metadata_flags_eq) {
+      // Note: pp is deleted in this callback.
+      GRPC_CLOSURE_SCHED(&pp->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
+    }
+    pp = next;
+  }
+  if (rr_policy_ != nullptr) {
+    rr_policy_->CancelMatchingPicksLocked(initial_metadata_flags_mask,
+                                          initial_metadata_flags_eq,
+                                          GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void GrpcLb::ExitIdleLocked() {
+  if (!started_picking_) {
+    StartPickingLocked();
+  }
+}
+
+void GrpcLb::ResetBackoffLocked() {
+  if (lb_channel_ != nullptr) {
+    grpc_channel_reset_connect_backoff(lb_channel_);
+  }
+  if (rr_policy_ != nullptr) {
+    rr_policy_->ResetBackoffLocked();
+  }
+}
+
+bool GrpcLb::PickLocked(PickState* pick, grpc_error** error) {
+  PendingPick* pp = PendingPickCreate(pick);
+  bool pick_done = false;
+  if (rr_policy_ != nullptr) {
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO, "[grpclb %p] about to PICK from RR %p", this,
+              rr_policy_.get());
+    }
+    pick_done =
+        PickFromRoundRobinPolicyLocked(false /* force_async */, pp, error);
+  } else {  // rr_policy_ == NULL
+    if (pick->on_complete == nullptr) {
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "No pick result available but synchronous result required.");
+      pick_done = true;
+    } else {
+      if (grpc_lb_glb_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "[grpclb %p] No RR policy. Adding to grpclb's pending picks",
+                this);
+      }
+      AddPendingPick(pp);
+      if (!started_picking_) {
+        StartPickingLocked();
+      }
+      pick_done = false;
+    }
+  }
+  return pick_done;
+}
+
+void GrpcLb::FillChildRefsForChannelz(
+    channelz::ChildRefsList* child_subchannels,
+    channelz::ChildRefsList* child_channels) {
+  // delegate to the RoundRobin to fill the children subchannels.
+  rr_policy_->FillChildRefsForChannelz(child_subchannels, child_channels);
+  MutexLock lock(&lb_channel_mu_);
+  if (lb_channel_ != nullptr) {
+    grpc_core::channelz::ChannelNode* channel_node =
+        grpc_channel_get_channelz_node(lb_channel_);
+    if (channel_node != nullptr) {
+      child_channels->push_back(channel_node->uuid());
+    }
+  }
+}
+
+grpc_connectivity_state GrpcLb::CheckConnectivityLocked(
+    grpc_error** connectivity_error) {
+  return grpc_connectivity_state_get(&state_tracker_, connectivity_error);
+}
+
+void GrpcLb::NotifyOnStateChangeLocked(grpc_connectivity_state* current,
+                                       grpc_closure* notify) {
+  grpc_connectivity_state_notify_on_state_change(&state_tracker_, current,
+                                                 notify);
+}
+
+// Returns the backend addresses extracted from the given addresses.
+UniquePtr<ServerAddressList> ExtractBackendAddresses(
+    const ServerAddressList& addresses) {
+  void* lb_token = (void*)GRPC_MDELEM_LB_TOKEN_EMPTY.payload;
+  grpc_arg arg = grpc_channel_arg_pointer_create(
+      const_cast<char*>(GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN), lb_token,
+      &lb_token_arg_vtable);
+  auto backend_addresses = MakeUnique<ServerAddressList>();
+  for (size_t i = 0; i < addresses.size(); ++i) {
+    if (!addresses[i].IsBalancer()) {
+      backend_addresses->emplace_back(
+          addresses[i].address(),
+          grpc_channel_args_copy_and_add(addresses[i].args(), &arg, 1));
+    }
+  }
+  return backend_addresses;
+}
+
+void GrpcLb::ProcessChannelArgsLocked(const grpc_channel_args& args) {
+  const ServerAddressList* addresses = FindServerAddressListChannelArg(&args);
+  if (addresses == nullptr) {
+    // Ignore this update.
+    gpr_log(
+        GPR_ERROR,
+        "[grpclb %p] No valid LB addresses channel arg in update, ignoring.",
+        this);
+    return;
+  }
+  // Update fallback address list.
+  fallback_backend_addresses_ = ExtractBackendAddresses(*addresses);
+  // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args,
+  // since we use this to trigger the client_load_reporting filter.
+  static const char* args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME};
+  grpc_arg new_arg = grpc_channel_arg_string_create(
+      (char*)GRPC_ARG_LB_POLICY_NAME, (char*)"grpclb");
+  grpc_channel_args_destroy(args_);
+  args_ = grpc_channel_args_copy_and_add_and_remove(
+      &args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1);
+  // Construct args for balancer channel.
+  grpc_channel_args* lb_channel_args =
+      BuildBalancerChannelArgs(*addresses, response_generator_.get(), &args);
+  // Create balancer channel if needed.
+  if (lb_channel_ == nullptr) {
+    char* uri_str;
+    gpr_asprintf(&uri_str, "fake:///%s", server_name_);
+    gpr_mu_lock(&lb_channel_mu_);
+    lb_channel_ = grpc_client_channel_factory_create_channel(
+        client_channel_factory(), uri_str,
+        GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, lb_channel_args);
+    gpr_mu_unlock(&lb_channel_mu_);
+    GPR_ASSERT(lb_channel_ != nullptr);
+    gpr_free(uri_str);
+  }
+  // Propagate updates to the LB channel (pick_first) through the fake
+  // resolver.
+  response_generator_->SetResponse(lb_channel_args);
+  grpc_channel_args_destroy(lb_channel_args);
+}
+
+void GrpcLb::UpdateLocked(const grpc_channel_args& args, grpc_json* lb_config) {
+  ProcessChannelArgsLocked(args);
+  // Update the existing RR policy.
+  if (rr_policy_ != nullptr) CreateOrUpdateRoundRobinPolicyLocked();
+  // Start watching the LB channel connectivity for connection, if not
+  // already doing so.
+  if (!watching_lb_channel_) {
+    lb_channel_connectivity_ = grpc_channel_check_connectivity_state(
+        lb_channel_, true /* try to connect */);
+    grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
+        grpc_channel_get_channel_stack(lb_channel_));
+    GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+    watching_lb_channel_ = true;
+    // TODO(roth): We currently track this ref manually.  Once the
+    // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+    // with the callback.
+    auto self = Ref(DEBUG_LOCATION, "watch_lb_channel_connectivity");
+    self.release();
+    grpc_client_channel_watch_connectivity_state(
+        client_channel_elem,
+        grpc_polling_entity_create_from_pollset_set(interested_parties()),
+        &lb_channel_connectivity_, &lb_channel_on_connectivity_changed_,
+        nullptr);
+  }
+}
+
+//
+// code for balancer channel and call
+//
+
+void GrpcLb::StartPickingLocked() {
+  // Start a timer to fall back.
+  if (lb_fallback_timeout_ms_ > 0 && serverlist_ == nullptr &&
+      !fallback_timer_callback_pending_) {
+    grpc_millis deadline = ExecCtx::Get()->Now() + lb_fallback_timeout_ms_;
+    // TODO(roth): We currently track this ref manually.  Once the
+    // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+    // with the callback.
+    auto self = Ref(DEBUG_LOCATION, "on_fallback_timer");
+    self.release();
+    GRPC_CLOSURE_INIT(&lb_on_fallback_, &GrpcLb::OnFallbackTimerLocked, this,
+                      grpc_combiner_scheduler(combiner()));
+    fallback_timer_callback_pending_ = true;
+    grpc_timer_init(&lb_fallback_timer_, deadline, &lb_on_fallback_);
+  }
+  started_picking_ = true;
+  StartBalancerCallLocked();
+}
+
+void GrpcLb::StartBalancerCallLocked() {
+  GPR_ASSERT(lb_channel_ != nullptr);
+  if (shutting_down_) return;
+  // Init the LB call data.
+  GPR_ASSERT(lb_calld_ == nullptr);
+  lb_calld_ = MakeOrphanable<BalancerCallState>(Ref());
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[grpclb %p] Query for backends (lb_channel: %p, lb_calld: %p)",
+            this, lb_channel_, lb_calld_.get());
+  }
+  lb_calld_->StartQuery();
+}
+
+void GrpcLb::OnFallbackTimerLocked(void* arg, grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  grpclb_policy->fallback_timer_callback_pending_ = false;
+  // If we receive a serverlist after the timer fires but before this callback
+  // actually runs, don't fall back.
+  if (grpclb_policy->serverlist_ == nullptr && !grpclb_policy->shutting_down_ &&
+      error == GRPC_ERROR_NONE) {
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[grpclb %p] Falling back to use backends from resolver",
+              grpclb_policy);
+    }
+    GPR_ASSERT(grpclb_policy->fallback_backend_addresses_ != nullptr);
+    grpclb_policy->CreateOrUpdateRoundRobinPolicyLocked();
+  }
+  grpclb_policy->Unref(DEBUG_LOCATION, "on_fallback_timer");
+}
+
+void GrpcLb::StartBalancerCallRetryTimerLocked() {
+  grpc_millis next_try = lb_call_backoff_.NextAttemptTime();
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_INFO, "[grpclb %p] Connection to LB server lost...", this);
+    grpc_millis timeout = next_try - ExecCtx::Get()->Now();
+    if (timeout > 0) {
+      gpr_log(GPR_INFO, "[grpclb %p] ... retry_timer_active in %" PRId64 "ms.",
+              this, timeout);
+    } else {
+      gpr_log(GPR_INFO, "[grpclb %p] ... retry_timer_active immediately.",
+              this);
+    }
+  }
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  auto self = Ref(DEBUG_LOCATION, "on_balancer_call_retry_timer");
+  self.release();
+  GRPC_CLOSURE_INIT(&lb_on_call_retry_, &GrpcLb::OnBalancerCallRetryTimerLocked,
+                    this, grpc_combiner_scheduler(combiner()));
+  retry_timer_callback_pending_ = true;
+  grpc_timer_init(&lb_call_retry_timer_, next_try, &lb_on_call_retry_);
+}
+
+void GrpcLb::OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  grpclb_policy->retry_timer_callback_pending_ = false;
+  if (!grpclb_policy->shutting_down_ && error == GRPC_ERROR_NONE &&
+      grpclb_policy->lb_calld_ == nullptr) {
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO, "[grpclb %p] Restarting call to LB server",
+              grpclb_policy);
+    }
+    grpclb_policy->StartBalancerCallLocked();
+  }
+  grpclb_policy->Unref(DEBUG_LOCATION, "on_balancer_call_retry_timer");
+}
+
+// Invoked as part of the update process. It continues watching the LB channel
+// until it shuts down or becomes READY. It's invoked even if the LB channel
+// stayed READY throughout the update (for example if the update is identical).
+void GrpcLb::OnBalancerChannelConnectivityChangedLocked(void* arg,
+                                                        grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  if (grpclb_policy->shutting_down_) goto done;
+  // Re-initialize the lb_call. This should also take care of updating the
+  // embedded RR policy. Note that the current RR policy, if any, will stay in
+  // effect until an update from the new lb_call is received.
+  switch (grpclb_policy->lb_channel_connectivity_) {
+    case GRPC_CHANNEL_CONNECTING:
+    case GRPC_CHANNEL_TRANSIENT_FAILURE: {
+      // Keep watching the LB channel.
+      grpc_channel_element* client_channel_elem =
+          grpc_channel_stack_last_element(
+              grpc_channel_get_channel_stack(grpclb_policy->lb_channel_));
+      GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+      grpc_client_channel_watch_connectivity_state(
+          client_channel_elem,
+          grpc_polling_entity_create_from_pollset_set(
+              grpclb_policy->interested_parties()),
+          &grpclb_policy->lb_channel_connectivity_,
+          &grpclb_policy->lb_channel_on_connectivity_changed_, nullptr);
+      break;
+    }
+      // The LB channel may be IDLE because it's shut down before the update.
+      // Restart the LB call to kick the LB channel into gear.
+    case GRPC_CHANNEL_IDLE:
+    case GRPC_CHANNEL_READY:
+      grpclb_policy->lb_calld_.reset();
+      if (grpclb_policy->started_picking_) {
+        if (grpclb_policy->retry_timer_callback_pending_) {
+          grpc_timer_cancel(&grpclb_policy->lb_call_retry_timer_);
+        }
+        grpclb_policy->lb_call_backoff_.Reset();
+        grpclb_policy->StartBalancerCallLocked();
+      }
+      // fallthrough
+    case GRPC_CHANNEL_SHUTDOWN:
+    done:
+      grpclb_policy->watching_lb_channel_ = false;
+      grpclb_policy->Unref(DEBUG_LOCATION,
+                           "watch_lb_channel_connectivity_cb_shutdown");
+  }
+}
+
+//
+// PendingPick
+//
+
+// Adds lb_token of selected subchannel (address) to the call's initial
+// metadata.
+grpc_error* AddLbTokenToInitialMetadata(
+    grpc_mdelem lb_token, grpc_linked_mdelem* lb_token_mdelem_storage,
+    grpc_metadata_batch* initial_metadata) {
+  GPR_ASSERT(lb_token_mdelem_storage != nullptr);
+  GPR_ASSERT(!GRPC_MDISNULL(lb_token));
+  return grpc_metadata_batch_add_tail(initial_metadata, lb_token_mdelem_storage,
+                                      lb_token);
+}
+
+// Destroy function used when embedding client stats in call context.
+void DestroyClientStats(void* arg) {
+  static_cast<GrpcLbClientStats*>(arg)->Unref();
+}
+
+void GrpcLb::PendingPickSetMetadataAndContext(PendingPick* pp) {
+  // If connected_subchannel is nullptr, no pick has been made by the RR
+  // policy (e.g., all addresses failed to connect). There won't be any
+  // LB token available.
+  if (pp->pick->connected_subchannel != nullptr) {
+    const grpc_arg* arg =
+        grpc_channel_args_find(pp->pick->connected_subchannel->args(),
+                               GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN);
+    if (arg != nullptr) {
+      grpc_mdelem lb_token = {
+          reinterpret_cast<uintptr_t>(arg->value.pointer.p)};
+      AddLbTokenToInitialMetadata(GRPC_MDELEM_REF(lb_token),
+                                  &pp->pick->lb_token_mdelem_storage,
+                                  pp->pick->initial_metadata);
+    } else {
+      gpr_log(GPR_ERROR,
+              "[grpclb %p] No LB token for connected subchannel pick %p",
+              pp->grpclb_policy, pp->pick);
+      abort();
+    }
+    // Pass on client stats via context. Passes ownership of the reference.
+    if (pp->client_stats != nullptr) {
+      pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].value =
+          pp->client_stats.release();
+      pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].destroy =
+          DestroyClientStats;
+    }
+  } else {
+    pp->client_stats.reset();
+  }
+}
+
+/* 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 */
+void GrpcLb::OnPendingPickComplete(void* arg, grpc_error* error) {
+  PendingPick* pp = static_cast<PendingPick*>(arg);
+  PendingPickSetMetadataAndContext(pp);
+  GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_REF(error));
+  Delete(pp);
+}
+
+GrpcLb::PendingPick* GrpcLb::PendingPickCreate(PickState* pick) {
+  PendingPick* pp = New<PendingPick>();
+  pp->grpclb_policy = this;
+  pp->pick = pick;
+  GRPC_CLOSURE_INIT(&pp->on_complete, &GrpcLb::OnPendingPickComplete, pp,
+                    grpc_schedule_on_exec_ctx);
+  pp->original_on_complete = pick->on_complete;
+  pick->on_complete = &pp->on_complete;
+  return pp;
+}
+
+void GrpcLb::AddPendingPick(PendingPick* pp) {
+  pp->next = pending_picks_;
+  pending_picks_ = pp;
+}
+
+//
+// code for interacting with the RR policy
+//
+
+// Performs a pick over \a rr_policy_. Given that a pick can return
+// immediately (ignoring its completion callback), we need to perform the
+// cleanups this callback would otherwise be responsible for.
+// If \a force_async is true, then we will manually schedule the
+// completion callback even if the pick is available immediately.
+bool GrpcLb::PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp,
+                                            grpc_error** error) {
+  // Check for drops if we are not using fallback backend addresses.
+  if (serverlist_ != nullptr && serverlist_->num_servers > 0) {
+    // Look at the index into the serverlist to see if we should drop this call.
+    grpc_grpclb_server* server = serverlist_->servers[serverlist_index_++];
+    if (serverlist_index_ == serverlist_->num_servers) {
+      serverlist_index_ = 0;  // Wrap-around.
+    }
+    if (server->drop) {
+      // Update client load reporting stats to indicate the number of
+      // dropped calls.  Note that we have to do this here instead of in
+      // the client_load_reporting filter, because we do not create a
+      // subchannel call (and therefore no client_load_reporting filter)
+      // for dropped calls.
+      if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) {
+        lb_calld_->client_stats()->AddCallDroppedLocked(
+            server->load_balance_token);
+      }
+      if (force_async) {
+        GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_NONE);
+        Delete(pp);
+        return false;
+      }
+      Delete(pp);
+      return true;
+    }
+  }
+  // Set client_stats.
+  if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) {
+    pp->client_stats = lb_calld_->client_stats()->Ref();
+  }
+  // Pick via the RR policy.
+  bool pick_done = rr_policy_->PickLocked(pp->pick, error);
+  if (pick_done) {
+    PendingPickSetMetadataAndContext(pp);
+    if (force_async) {
+      GRPC_CLOSURE_SCHED(pp->original_on_complete, *error);
+      *error = GRPC_ERROR_NONE;
+      pick_done = false;
+    }
+    Delete(pp);
+  }
+  // else, the pending pick will be registered and taken care of by the
+  // pending pick list inside the RR policy.  Eventually,
+  // OnPendingPickComplete() will be called, which will (among other
+  // things) add the LB token to the call's initial metadata.
+  return pick_done;
+}
+
+void GrpcLb::CreateRoundRobinPolicyLocked(const Args& args) {
+  GPR_ASSERT(rr_policy_ == nullptr);
+  rr_policy_ = LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
+      "round_robin", args);
+  if (GPR_UNLIKELY(rr_policy_ == nullptr)) {
+    gpr_log(GPR_ERROR, "[grpclb %p] Failure creating a RoundRobin policy",
+            this);
+    return;
+  }
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(GPR_INFO, "[grpclb %p] Created new RR policy %p", this,
+            rr_policy_.get());
+  }
+  // TODO(roth): We currently track this ref manually.  Once the new
+  // ClosureRef API is done, pass the RefCountedPtr<> along with the closure.
+  auto self = Ref(DEBUG_LOCATION, "on_rr_reresolution_requested");
+  self.release();
+  rr_policy_->SetReresolutionClosureLocked(&on_rr_request_reresolution_);
+  grpc_error* rr_state_error = nullptr;
+  rr_connectivity_state_ = rr_policy_->CheckConnectivityLocked(&rr_state_error);
+  // Connectivity state is a function of the RR policy updated/created.
+  UpdateConnectivityStateFromRoundRobinPolicyLocked(rr_state_error);
+  // Add the gRPC LB's interested_parties pollset_set to that of the newly
+  // created RR policy. This will make the RR policy progress upon activity on
+  // gRPC LB, which in turn is tied to the application's call.
+  grpc_pollset_set_add_pollset_set(rr_policy_->interested_parties(),
+                                   interested_parties());
+  // Subscribe to changes to the connectivity of the new RR.
+  // TODO(roth): We currently track this ref manually.  Once the new
+  // ClosureRef API is done, pass the RefCountedPtr<> along with the closure.
+  self = Ref(DEBUG_LOCATION, "on_rr_connectivity_changed");
+  self.release();
+  rr_policy_->NotifyOnStateChangeLocked(&rr_connectivity_state_,
+                                        &on_rr_connectivity_changed_);
+  rr_policy_->ExitIdleLocked();
+  // Send pending picks to RR policy.
+  PendingPick* pp;
+  while ((pp = pending_picks_)) {
+    pending_picks_ = pp->next;
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[grpclb %p] Pending pick about to (async) PICK from RR %p", this,
+              rr_policy_.get());
+    }
+    grpc_error* error = GRPC_ERROR_NONE;
+    PickFromRoundRobinPolicyLocked(true /* force_async */, pp, &error);
+  }
+}
+
+grpc_channel_args* GrpcLb::CreateRoundRobinPolicyArgsLocked() {
+  ServerAddressList tmp_addresses;
+  ServerAddressList* addresses = &tmp_addresses;
+  bool is_backend_from_grpclb_load_balancer = false;
+  if (serverlist_ != nullptr) {
+    tmp_addresses = ProcessServerlist(serverlist_);
+    is_backend_from_grpclb_load_balancer = true;
+  } else {
+    // If CreateOrUpdateRoundRobinPolicyLocked() is invoked when we haven't
+    // received any serverlist from the balancer, we use the fallback backends
+    // returned by the resolver. Note that the fallback backend list may be
+    // empty, in which case the new round_robin policy will keep the requested
+    // picks pending.
+    GPR_ASSERT(fallback_backend_addresses_ != nullptr);
+    addresses = fallback_backend_addresses_.get();
+  }
+  GPR_ASSERT(addresses != nullptr);
+  // Replace the server address list in the channel args that we pass down to
+  // the subchannel.
+  static const char* keys_to_remove[] = {GRPC_ARG_SERVER_ADDRESS_LIST};
+  grpc_arg args_to_add[3] = {
+      CreateServerAddressListChannelArg(addresses),
+      // A channel arg indicating if the target is a backend inferred from a
+      // grpclb load balancer.
+      grpc_channel_arg_integer_create(
+          const_cast<char*>(
+              GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER),
+          is_backend_from_grpclb_load_balancer),
+  };
+  size_t num_args_to_add = 2;
+  if (is_backend_from_grpclb_load_balancer) {
+    args_to_add[2] = grpc_channel_arg_integer_create(
+        const_cast<char*>(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1);
+    ++num_args_to_add;
+  }
+  grpc_channel_args* args = grpc_channel_args_copy_and_add_and_remove(
+      args_, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), args_to_add,
+      num_args_to_add);
+  return args;
+}
+
+void GrpcLb::CreateOrUpdateRoundRobinPolicyLocked() {
+  if (shutting_down_) return;
+  grpc_channel_args* args = CreateRoundRobinPolicyArgsLocked();
+  GPR_ASSERT(args != nullptr);
+  if (rr_policy_ != nullptr) {
+    if (grpc_lb_glb_trace.enabled()) {
+      gpr_log(GPR_INFO, "[grpclb %p] Updating RR policy %p", this,
+              rr_policy_.get());
+    }
+    rr_policy_->UpdateLocked(*args, nullptr);
+  } else {
+    LoadBalancingPolicy::Args lb_policy_args;
+    lb_policy_args.combiner = combiner();
+    lb_policy_args.client_channel_factory = client_channel_factory();
+    lb_policy_args.args = args;
+    CreateRoundRobinPolicyLocked(lb_policy_args);
+  }
+  grpc_channel_args_destroy(args);
+}
+
+void GrpcLb::OnRoundRobinRequestReresolutionLocked(void* arg,
+                                                   grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  if (grpclb_policy->shutting_down_ || error != GRPC_ERROR_NONE) {
+    grpclb_policy->Unref(DEBUG_LOCATION, "on_rr_reresolution_requested");
+    return;
+  }
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(
+        GPR_INFO,
+        "[grpclb %p] Re-resolution requested from the internal RR policy (%p).",
+        grpclb_policy, grpclb_policy->rr_policy_.get());
+  }
+  // If we are talking to a balancer, we expect to get updated addresses form
+  // the balancer, so we can ignore the re-resolution request from the RR
+  // policy. Otherwise, handle the re-resolution request using the
+  // grpclb policy's original re-resolution closure.
+  if (grpclb_policy->lb_calld_ == nullptr ||
+      !grpclb_policy->lb_calld_->seen_initial_response()) {
+    grpclb_policy->TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_NONE);
+  }
+  // Give back the wrapper closure to the RR policy.
+  grpclb_policy->rr_policy_->SetReresolutionClosureLocked(
+      &grpclb_policy->on_rr_request_reresolution_);
+}
+
+void GrpcLb::UpdateConnectivityStateFromRoundRobinPolicyLocked(
+    grpc_error* rr_state_error) {
+  const grpc_connectivity_state curr_glb_state =
+      grpc_connectivity_state_check(&state_tracker_);
+  /* The new connectivity status is a function of the previous one and the new
+   * input coming from the status of the RR policy.
+   *
+   *  current state (grpclb's)
+   *  |
+   *  v  || I  |  C  |  R  |  TF  |  SD  |  <- new state (RR's)
+   *  ===++====+=====+=====+======+======+
+   *   I || I  |  C  |  R  | [I]  | [I]  |
+   *  ---++----+-----+-----+------+------+
+   *   C || I  |  C  |  R  | [C]  | [C]  |
+   *  ---++----+-----+-----+------+------+
+   *   R || I  |  C  |  R  | [R]  | [R]  |
+   *  ---++----+-----+-----+------+------+
+   *  TF || I  |  C  |  R  | [TF] | [TF] |
+   *  ---++----+-----+-----+------+------+
+   *  SD || NA |  NA |  NA |  NA  |  NA  | (*)
+   *  ---++----+-----+-----+------+------+
+   *
+   * A [STATE] indicates that the old RR policy is kept. In those cases, STATE
+   * is the current state of grpclb, which is left untouched.
+   *
+   *  In summary, if the new state is TRANSIENT_FAILURE or SHUTDOWN, stick to
+   *  the previous RR instance.
+   *
+   *  Note that the status is never updated to SHUTDOWN as a result of calling
+   *  this function. Only glb_shutdown() has the power to set that state.
+   *
+   *  (*) This function mustn't be called during shutting down. */
+  GPR_ASSERT(curr_glb_state != GRPC_CHANNEL_SHUTDOWN);
+  switch (rr_connectivity_state_) {
+    case GRPC_CHANNEL_TRANSIENT_FAILURE:
+    case GRPC_CHANNEL_SHUTDOWN:
+      GPR_ASSERT(rr_state_error != GRPC_ERROR_NONE);
+      break;
+    case GRPC_CHANNEL_IDLE:
+    case GRPC_CHANNEL_CONNECTING:
+    case GRPC_CHANNEL_READY:
+      GPR_ASSERT(rr_state_error == GRPC_ERROR_NONE);
+  }
+  if (grpc_lb_glb_trace.enabled()) {
+    gpr_log(
+        GPR_INFO,
+        "[grpclb %p] Setting grpclb's state to %s from new RR policy %p state.",
+        this, grpc_connectivity_state_name(rr_connectivity_state_),
+        rr_policy_.get());
+  }
+  grpc_connectivity_state_set(&state_tracker_, rr_connectivity_state_,
+                              rr_state_error,
+                              "update_lb_connectivity_status_locked");
+}
+
+void GrpcLb::OnRoundRobinConnectivityChangedLocked(void* arg,
+                                                   grpc_error* error) {
+  GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg);
+  if (grpclb_policy->shutting_down_) {
+    grpclb_policy->Unref(DEBUG_LOCATION, "on_rr_connectivity_changed");
+    return;
+  }
+  grpclb_policy->UpdateConnectivityStateFromRoundRobinPolicyLocked(
+      GRPC_ERROR_REF(error));
+  // Resubscribe. Reuse the "on_rr_connectivity_changed" ref.
+  grpclb_policy->rr_policy_->NotifyOnStateChangeLocked(
+      &grpclb_policy->rr_connectivity_state_,
+      &grpclb_policy->on_rr_connectivity_changed_);
+}
+
+//
+// factory
+//
+
+class GrpcLbFactory : public LoadBalancingPolicyFactory {
+ public:
+  OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const override {
+    /* Count the number of gRPC-LB addresses. There must be at least one. */
+    const ServerAddressList* addresses =
+        FindServerAddressListChannelArg(args.args);
+    if (addresses == nullptr) return nullptr;
+    bool found_balancer = false;
+    for (size_t i = 0; i < addresses->size(); ++i) {
+      if ((*addresses)[i].IsBalancer()) {
+        found_balancer = true;
+        break;
+      }
+    }
+    if (!found_balancer) return nullptr;
+    return OrphanablePtr<LoadBalancingPolicy>(New<GrpcLb>(args));
+  }
+
+  const char* name() const override { return kGrpclb; }
+};
+
+}  // namespace
+
+}  // namespace grpc_core
+
+//
+// Plugin registration
+//
+
+namespace {
+
+// Only add client_load_reporting filter if the grpclb LB policy is used.
+bool maybe_add_client_load_reporting_filter(grpc_channel_stack_builder* builder,
+                                            void* arg) {
+  const grpc_channel_args* args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  const grpc_arg* channel_arg =
+      grpc_channel_args_find(args, GRPC_ARG_LB_POLICY_NAME);
+  if (channel_arg != nullptr && channel_arg->type == GRPC_ARG_STRING &&
+      strcmp(channel_arg->value.string, "grpclb") == 0) {
+    return grpc_channel_stack_builder_append_filter(
+        builder, (const grpc_channel_filter*)arg, nullptr, nullptr);
+  }
+  return true;
+}
+
+}  // namespace
+
+void grpc_lb_policy_grpclb_init() {
+  grpc_core::LoadBalancingPolicyRegistry::Builder::
+      RegisterLoadBalancingPolicyFactory(
+          grpc_core::UniquePtr<grpc_core::LoadBalancingPolicyFactory>(
+              grpc_core::New<grpc_core::GrpcLbFactory>()));
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_client_load_reporting_filter,
+                                   (void*)&grpc_client_load_reporting_filter);
+}
+
+void grpc_lb_policy_grpclb_shutdown() {}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h
new file mode 100644
index 0000000..4d39c4d
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H
+
+#include <grpc/support/port_platform.h>
+
+/** Channel arg indicating if a target corresponding to the address is grpclb
+ * loadbalancer. The type of this arg is an integer and the value is treated as
+ * a bool. */
+#define GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER \
+  "grpc.address_is_grpclb_load_balancer"
+/** Channel arg indicating if a target corresponding to the address is a backend
+ * received from a balancer. The type of this arg is an integer and the value is
+ * treated as a bool. */
+#define GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER \
+  "grpc.address_is_backend_from_grpclb_load_balancer"
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc
new file mode 100644
index 0000000..fd873f0
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
+
+grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args(
+    grpc_channel_args* args) {
+  return args;
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
new file mode 100644
index 0000000..3b2dc37
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+/// Makes any necessary modifications to \a args for use in the grpclb
+/// balancer channel.
+///
+/// Takes ownership of \a args.
+///
+/// Caller takes ownership of the returned args.
+grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args(
+    grpc_channel_args* args);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc
new file mode 100644
index 0000000..657ff69
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+namespace grpc_core {
+namespace {
+
+int BalancerNameCmp(const grpc_core::UniquePtr<char>& a,
+                    const grpc_core::UniquePtr<char>& b) {
+  return strcmp(a.get(), b.get());
+}
+
+RefCountedPtr<TargetAuthorityTable> CreateTargetAuthorityTable(
+    const ServerAddressList& addresses) {
+  TargetAuthorityTable::Entry* target_authority_entries =
+      static_cast<TargetAuthorityTable::Entry*>(
+          gpr_zalloc(sizeof(*target_authority_entries) * addresses.size()));
+  for (size_t i = 0; i < addresses.size(); ++i) {
+    char* addr_str;
+    GPR_ASSERT(
+        grpc_sockaddr_to_string(&addr_str, &addresses[i].address(), true) > 0);
+    target_authority_entries[i].key = grpc_slice_from_copied_string(addr_str);
+    gpr_free(addr_str);
+    char* balancer_name = grpc_channel_arg_get_string(grpc_channel_args_find(
+        addresses[i].args(), GRPC_ARG_ADDRESS_BALANCER_NAME));
+    target_authority_entries[i].value.reset(gpr_strdup(balancer_name));
+  }
+  RefCountedPtr<TargetAuthorityTable> target_authority_table =
+      TargetAuthorityTable::Create(addresses.size(), target_authority_entries,
+                                   BalancerNameCmp);
+  gpr_free(target_authority_entries);
+  return target_authority_table;
+}
+
+}  // namespace
+}  // namespace grpc_core
+
+grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args(
+    grpc_channel_args* args) {
+  const char* args_to_remove[1];
+  size_t num_args_to_remove = 0;
+  grpc_arg args_to_add[2];
+  size_t num_args_to_add = 0;
+  // Add arg for targets info table.
+  grpc_core::ServerAddressList* addresses =
+      grpc_core::FindServerAddressListChannelArg(args);
+  GPR_ASSERT(addresses != nullptr);
+  grpc_core::RefCountedPtr<grpc_core::TargetAuthorityTable>
+      target_authority_table =
+          grpc_core::CreateTargetAuthorityTable(*addresses);
+  args_to_add[num_args_to_add++] =
+      grpc_core::CreateTargetAuthorityTableChannelArg(
+          target_authority_table.get());
+  // Substitute the channel credentials with a version without call
+  // credentials: the load balancer is not necessarily trusted to handle
+  // bearer token credentials.
+  grpc_channel_credentials* channel_credentials =
+      grpc_channel_credentials_find_in_args(args);
+  grpc_core::RefCountedPtr<grpc_channel_credentials> creds_sans_call_creds;
+  if (channel_credentials != nullptr) {
+    creds_sans_call_creds =
+        channel_credentials->duplicate_without_call_credentials();
+    GPR_ASSERT(creds_sans_call_creds != nullptr);
+    args_to_remove[num_args_to_remove++] = GRPC_ARG_CHANNEL_CREDENTIALS;
+    args_to_add[num_args_to_add++] =
+        grpc_channel_credentials_to_arg(creds_sans_call_creds.get());
+  }
+  grpc_channel_args* result = grpc_channel_args_copy_and_add_and_remove(
+      args, args_to_remove, num_args_to_remove, args_to_add, num_args_to_add);
+  // Clean up.
+  grpc_channel_args_destroy(args);
+  return result;
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc
new file mode 100644
index 0000000..087cd8f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc
@@ -0,0 +1,86 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
+
+#include <string.h>
+
+#include <grpc/support/atm.h>
+#include <grpc/support/string_util.h>
+
+namespace grpc_core {
+
+void GrpcLbClientStats::AddCallStarted() {
+  gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1);
+}
+
+void GrpcLbClientStats::AddCallFinished(
+    bool finished_with_client_failed_to_send, bool finished_known_received) {
+  gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1);
+  if (finished_with_client_failed_to_send) {
+    gpr_atm_full_fetch_add(&num_calls_finished_with_client_failed_to_send_,
+                           (gpr_atm)1);
+  }
+  if (finished_known_received) {
+    gpr_atm_full_fetch_add(&num_calls_finished_known_received_, (gpr_atm)1);
+  }
+}
+
+void GrpcLbClientStats::AddCallDroppedLocked(char* token) {
+  // Increment num_calls_started and num_calls_finished.
+  gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1);
+  gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1);
+  // Record the drop.
+  if (drop_token_counts_ == nullptr) {
+    drop_token_counts_.reset(New<DroppedCallCounts>());
+  }
+  for (size_t i = 0; i < drop_token_counts_->size(); ++i) {
+    if (strcmp((*drop_token_counts_)[i].token.get(), token) == 0) {
+      ++(*drop_token_counts_)[i].count;
+      return;
+    }
+  }
+  // Not found, so add a new entry.
+  drop_token_counts_->emplace_back(UniquePtr<char>(gpr_strdup(token)), 1);
+}
+
+namespace {
+
+void AtomicGetAndResetCounter(int64_t* value, gpr_atm* counter) {
+  *value = static_cast<int64_t>(gpr_atm_full_xchg(counter, (gpr_atm)0));
+}
+
+}  // namespace
+
+void GrpcLbClientStats::GetLocked(
+    int64_t* num_calls_started, int64_t* num_calls_finished,
+    int64_t* num_calls_finished_with_client_failed_to_send,
+    int64_t* num_calls_finished_known_received,
+    UniquePtr<DroppedCallCounts>* drop_token_counts) {
+  AtomicGetAndResetCounter(num_calls_started, &num_calls_started_);
+  AtomicGetAndResetCounter(num_calls_finished, &num_calls_finished_);
+  AtomicGetAndResetCounter(num_calls_finished_with_client_failed_to_send,
+                           &num_calls_finished_with_client_failed_to_send_);
+  AtomicGetAndResetCounter(num_calls_finished_known_received,
+                           &num_calls_finished_known_received_);
+  *drop_token_counts = std::move(drop_token_counts_);
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
new file mode 100644
index 0000000..18ab2c9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+
+namespace grpc_core {
+
+class GrpcLbClientStats : public RefCounted<GrpcLbClientStats> {
+ public:
+  struct DropTokenCount {
+    UniquePtr<char> token;
+    int64_t count;
+
+    DropTokenCount(UniquePtr<char> token, int64_t count)
+        : token(std::move(token)), count(count) {}
+  };
+
+  typedef InlinedVector<DropTokenCount, 10> DroppedCallCounts;
+
+  GrpcLbClientStats() {}
+
+  void AddCallStarted();
+  void AddCallFinished(bool finished_with_client_failed_to_send,
+                       bool finished_known_received);
+
+  // This method is not thread-safe; caller must synchronize.
+  void AddCallDroppedLocked(char* token);
+
+  // This method is not thread-safe; caller must synchronize.
+  void GetLocked(int64_t* num_calls_started, int64_t* num_calls_finished,
+                 int64_t* num_calls_finished_with_client_failed_to_send,
+                 int64_t* num_calls_finished_known_received,
+                 UniquePtr<DroppedCallCounts>* drop_token_counts);
+
+ private:
+  // This field must only be accessed via *_locked() methods.
+  UniquePtr<DroppedCallCounts> drop_token_counts_;
+  // These fields may be accessed from multiple threads at a time.
+  gpr_atm num_calls_started_ = 0;
+  gpr_atm num_calls_finished_ = 0;
+  gpr_atm num_calls_finished_with_client_failed_to_send_ = 0;
+  gpr_atm num_calls_finished_known_received_ = 0;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
new file mode 100644
index 0000000..f24281a
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc
@@ -0,0 +1,307 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "pb_decode.h"
+#include "pb_encode.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h"
+
+#include <grpc/support/alloc.h>
+
+/* invoked once for every Server in ServerList */
+static bool count_serverlist(pb_istream_t* stream, const pb_field_t* field,
+                             void** arg) {
+  grpc_grpclb_serverlist* sl = static_cast<grpc_grpclb_serverlist*>(*arg);
+  grpc_grpclb_server server;
+  if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, &server))) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream));
+    return false;
+  }
+  ++sl->num_servers;
+  return true;
+}
+
+typedef struct decode_serverlist_arg {
+  /* The decoding callback is invoked once per server in serverlist. Remember
+   * which index of the serverlist are we currently decoding */
+  size_t decoding_idx;
+  /* The decoded serverlist */
+  grpc_grpclb_serverlist* serverlist;
+} decode_serverlist_arg;
+
+/* invoked once for every Server in ServerList */
+static bool decode_serverlist(pb_istream_t* stream, const pb_field_t* field,
+                              void** arg) {
+  decode_serverlist_arg* dec_arg = static_cast<decode_serverlist_arg*>(*arg);
+  GPR_ASSERT(dec_arg->serverlist->num_servers >= dec_arg->decoding_idx);
+  grpc_grpclb_server* server =
+      static_cast<grpc_grpclb_server*>(gpr_zalloc(sizeof(grpc_grpclb_server)));
+  if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, server))) {
+    gpr_free(server);
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream));
+    return false;
+  }
+  dec_arg->serverlist->servers[dec_arg->decoding_idx++] = server;
+  return true;
+}
+
+grpc_grpclb_request* grpc_grpclb_request_create(const char* lb_service_name) {
+  grpc_grpclb_request* req = static_cast<grpc_grpclb_request*>(
+      gpr_malloc(sizeof(grpc_grpclb_request)));
+  req->has_client_stats = false;
+  req->has_initial_request = true;
+  req->initial_request.has_name = true;
+  strncpy(req->initial_request.name, lb_service_name,
+          GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH);
+  return req;
+}
+
+static void populate_timestamp(gpr_timespec timestamp,
+                               grpc_grpclb_timestamp* timestamp_pb) {
+  timestamp_pb->has_seconds = true;
+  timestamp_pb->seconds = timestamp.tv_sec;
+  timestamp_pb->has_nanos = true;
+  timestamp_pb->nanos = timestamp.tv_nsec;
+}
+
+static bool encode_string(pb_ostream_t* stream, const pb_field_t* field,
+                          void* const* arg) {
+  char* str = static_cast<char*>(*arg);
+  if (!pb_encode_tag_for_field(stream, field)) return false;
+  return pb_encode_string(stream, reinterpret_cast<uint8_t*>(str), strlen(str));
+}
+
+static bool encode_drops(pb_ostream_t* stream, const pb_field_t* field,
+                         void* const* arg) {
+  grpc_core::GrpcLbClientStats::DroppedCallCounts* drop_entries =
+      static_cast<grpc_core::GrpcLbClientStats::DroppedCallCounts*>(*arg);
+  if (drop_entries == nullptr) return true;
+  for (size_t i = 0; i < drop_entries->size(); ++i) {
+    if (!pb_encode_tag_for_field(stream, field)) return false;
+    grpc_lb_v1_ClientStatsPerToken drop_message;
+    drop_message.load_balance_token.funcs.encode = encode_string;
+    drop_message.load_balance_token.arg = (*drop_entries)[i].token.get();
+    drop_message.has_num_calls = true;
+    drop_message.num_calls = (*drop_entries)[i].count;
+    if (!pb_encode_submessage(stream, grpc_lb_v1_ClientStatsPerToken_fields,
+                              &drop_message)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+grpc_grpclb_request* grpc_grpclb_load_report_request_create_locked(
+    grpc_core::GrpcLbClientStats* client_stats) {
+  grpc_grpclb_request* req = static_cast<grpc_grpclb_request*>(
+      gpr_zalloc(sizeof(grpc_grpclb_request)));
+  req->has_client_stats = true;
+  req->client_stats.has_timestamp = true;
+  populate_timestamp(gpr_now(GPR_CLOCK_REALTIME), &req->client_stats.timestamp);
+  req->client_stats.has_num_calls_started = true;
+  req->client_stats.has_num_calls_finished = true;
+  req->client_stats.has_num_calls_finished_with_client_failed_to_send = true;
+  req->client_stats.has_num_calls_finished_with_client_failed_to_send = true;
+  req->client_stats.has_num_calls_finished_known_received = true;
+  req->client_stats.calls_finished_with_drop.funcs.encode = encode_drops;
+  grpc_core::UniquePtr<grpc_core::GrpcLbClientStats::DroppedCallCounts>
+      drop_counts;
+  client_stats->GetLocked(
+      &req->client_stats.num_calls_started,
+      &req->client_stats.num_calls_finished,
+      &req->client_stats.num_calls_finished_with_client_failed_to_send,
+      &req->client_stats.num_calls_finished_known_received, &drop_counts);
+  // Will be deleted in grpc_grpclb_request_destroy().
+  req->client_stats.calls_finished_with_drop.arg = drop_counts.release();
+  return req;
+}
+
+grpc_slice grpc_grpclb_request_encode(const grpc_grpclb_request* request) {
+  size_t encoded_length;
+  pb_ostream_t sizestream;
+  pb_ostream_t outputstream;
+  grpc_slice slice;
+  memset(&sizestream, 0, sizeof(pb_ostream_t));
+  pb_encode(&sizestream, grpc_lb_v1_LoadBalanceRequest_fields, request);
+  encoded_length = sizestream.bytes_written;
+
+  slice = GRPC_SLICE_MALLOC(encoded_length);
+  outputstream =
+      pb_ostream_from_buffer(GRPC_SLICE_START_PTR(slice), encoded_length);
+  GPR_ASSERT(pb_encode(&outputstream, grpc_lb_v1_LoadBalanceRequest_fields,
+                       request) != 0);
+  return slice;
+}
+
+void grpc_grpclb_request_destroy(grpc_grpclb_request* request) {
+  if (request->has_client_stats) {
+    grpc_core::GrpcLbClientStats::DroppedCallCounts* drop_entries =
+        static_cast<grpc_core::GrpcLbClientStats::DroppedCallCounts*>(
+            request->client_stats.calls_finished_with_drop.arg);
+    grpc_core::Delete(drop_entries);
+  }
+  gpr_free(request);
+}
+
+typedef grpc_lb_v1_LoadBalanceResponse grpc_grpclb_response;
+grpc_grpclb_initial_response* grpc_grpclb_initial_response_parse(
+    grpc_slice encoded_grpc_grpclb_response) {
+  pb_istream_t stream =
+      pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response),
+                             GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response));
+  grpc_grpclb_response res;
+  memset(&res, 0, sizeof(grpc_grpclb_response));
+  if (GPR_UNLIKELY(
+          !pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res))) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return nullptr;
+  }
+
+  if (!res.has_initial_response) return nullptr;
+
+  grpc_grpclb_initial_response* initial_res =
+      static_cast<grpc_grpclb_initial_response*>(
+          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(
+    grpc_slice encoded_grpc_grpclb_response) {
+  pb_istream_t stream =
+      pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response),
+                             GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response));
+  pb_istream_t stream_at_start = stream;
+  grpc_grpclb_serverlist* sl = static_cast<grpc_grpclb_serverlist*>(
+      gpr_zalloc(sizeof(grpc_grpclb_serverlist)));
+  grpc_grpclb_response res;
+  memset(&res, 0, sizeof(grpc_grpclb_response));
+  // First pass: count number of servers.
+  res.server_list.servers.funcs.decode = count_serverlist;
+  res.server_list.servers.arg = sl;
+  bool status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res);
+  if (GPR_UNLIKELY(!status)) {
+    gpr_free(sl);
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return nullptr;
+  }
+  // Second pass: populate servers.
+  if (sl->num_servers > 0) {
+    sl->servers = static_cast<grpc_grpclb_server**>(
+        gpr_zalloc(sizeof(grpc_grpclb_server*) * sl->num_servers));
+    decode_serverlist_arg decode_arg;
+    memset(&decode_arg, 0, sizeof(decode_arg));
+    decode_arg.serverlist = sl;
+    res.server_list.servers.funcs.decode = decode_serverlist;
+    res.server_list.servers.arg = &decode_arg;
+    status = pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields,
+                       &res);
+    if (GPR_UNLIKELY(!status)) {
+      grpc_grpclb_destroy_serverlist(sl);
+      gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+      return nullptr;
+    }
+  }
+  return sl;
+}
+
+void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist* serverlist) {
+  if (serverlist == nullptr) {
+    return;
+  }
+  for (size_t i = 0; i < serverlist->num_servers; i++) {
+    gpr_free(serverlist->servers[i]);
+  }
+  gpr_free(serverlist->servers);
+  gpr_free(serverlist);
+}
+
+grpc_grpclb_serverlist* grpc_grpclb_serverlist_copy(
+    const grpc_grpclb_serverlist* sl) {
+  grpc_grpclb_serverlist* copy = static_cast<grpc_grpclb_serverlist*>(
+      gpr_zalloc(sizeof(grpc_grpclb_serverlist)));
+  copy->num_servers = sl->num_servers;
+  copy->servers = static_cast<grpc_grpclb_server**>(
+      gpr_malloc(sizeof(grpc_grpclb_server*) * sl->num_servers));
+  for (size_t i = 0; i < sl->num_servers; i++) {
+    copy->servers[i] = static_cast<grpc_grpclb_server*>(
+        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 == nullptr || rhs == nullptr) {
+    return false;
+  }
+  if (lhs->num_servers != rhs->num_servers) {
+    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;
+}
+
+grpc_millis grpc_grpclb_duration_to_millis(grpc_grpclb_duration* duration_pb) {
+  return static_cast<grpc_millis>(
+      (duration_pb->has_seconds ? duration_pb->seconds : 0) * GPR_MS_PER_SEC +
+      (duration_pb->has_nanos ? duration_pb->nanos : 0) / GPR_NS_PER_MS);
+}
+
+void grpc_grpclb_initial_response_destroy(
+    grpc_grpclb_initial_response* response) {
+  gpr_free(response);
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
new file mode 100644
index 0000000..71d371c
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+#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_InitialLoadBalanceResponse grpc_grpclb_initial_response;
+typedef grpc_lb_v1_Server grpc_grpclb_server;
+typedef google_protobuf_Duration grpc_grpclb_duration;
+typedef google_protobuf_Timestamp grpc_grpclb_timestamp;
+
+typedef struct {
+  grpc_grpclb_server** servers;
+  size_t num_servers;
+} grpc_grpclb_serverlist;
+
+/** Create a request for a gRPC LB service under \a lb_service_name */
+grpc_grpclb_request* grpc_grpclb_request_create(const char* lb_service_name);
+grpc_grpclb_request* grpc_grpclb_load_report_request_create_locked(
+    grpc_core::GrpcLbClientStats* client_stats);
+
+/** Protocol Buffers v3-encode \a request */
+grpc_slice grpc_grpclb_request_encode(const grpc_grpclb_request* request);
+
+/** Destroy \a request */
+void grpc_grpclb_request_destroy(grpc_grpclb_request* request);
+
+/** 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(
+    grpc_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(
+    grpc_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);
+
+/** 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);
+
+grpc_millis grpc_grpclb_duration_to_millis(grpc_grpclb_duration* duration_pb);
+
+/** Destroy \a initial_response */
+void grpc_grpclb_initial_response_destroy(
+    grpc_grpclb_initial_response* response);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c
new file mode 100644
index 0000000..131d9b7
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c
@@ -0,0 +1,19 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h"
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t google_protobuf_Duration_fields[3] = {
+    PB_FIELD(  1, INT64   , OPTIONAL, STATIC  , FIRST, google_protobuf_Duration, seconds, seconds, 0),
+    PB_FIELD(  2, INT32   , OPTIONAL, STATIC  , OTHER, google_protobuf_Duration, nanos, seconds, 0),
+    PB_LAST_FIELD
+};
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h
new file mode 100644
index 0000000..93070c6
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h
@@ -0,0 +1,54 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GOOGLE_PROTOBUF_DURATION_PB_H_INCLUDED
+#define PB_GOOGLE_PROTOBUF_DURATION_PB_H_INCLUDED
+#include "pb.h"
+/* @@protoc_insertion_point(includes) */
+#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_protobuf_Duration {
+    bool has_seconds;
+    int64_t seconds;
+    bool has_nanos;
+    int32_t nanos;
+/* @@protoc_insertion_point(struct:google_protobuf_Duration) */
+} google_protobuf_Duration;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define google_protobuf_Duration_init_default    {false, 0, false, 0}
+#define google_protobuf_Duration_init_zero       {false, 0, false, 0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define google_protobuf_Duration_seconds_tag     1
+#define google_protobuf_Duration_nanos_tag       2
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t google_protobuf_Duration_fields[3];
+
+/* Maximum encoded size of messages (where known) */
+#define google_protobuf_Duration_size            22
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define DURATION_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c
new file mode 100644
index 0000000..b6f8ffc
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c
@@ -0,0 +1,19 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h"
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t google_protobuf_Timestamp_fields[3] = {
+    PB_FIELD(  1, INT64   , OPTIONAL, STATIC  , FIRST, google_protobuf_Timestamp, seconds, seconds, 0),
+    PB_FIELD(  2, INT32   , OPTIONAL, STATIC  , OTHER, google_protobuf_Timestamp, nanos, seconds, 0),
+    PB_LAST_FIELD
+};
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h
new file mode 100644
index 0000000..7f48481
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h
@@ -0,0 +1,54 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GOOGLE_PROTOBUF_TIMESTAMP_PB_H_INCLUDED
+#define PB_GOOGLE_PROTOBUF_TIMESTAMP_PB_H_INCLUDED
+#include "pb.h"
+/* @@protoc_insertion_point(includes) */
+#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_protobuf_Timestamp {
+    bool has_seconds;
+    int64_t seconds;
+    bool has_nanos;
+    int32_t nanos;
+/* @@protoc_insertion_point(struct:google_protobuf_Timestamp) */
+} google_protobuf_Timestamp;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define google_protobuf_Timestamp_init_default   {false, 0, false, 0}
+#define google_protobuf_Timestamp_init_zero      {false, 0, false, 0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define google_protobuf_Timestamp_seconds_tag    1
+#define google_protobuf_Timestamp_nanos_tag      2
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t google_protobuf_Timestamp_fields[3];
+
+/* Maximum encoded size of messages (where known) */
+#define google_protobuf_Timestamp_size           22
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define TIMESTAMP_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
new file mode 100644
index 0000000..f6538e1
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c
@@ -0,0 +1,89 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/ext/filters/client_channel/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
+
+
+
+const pb_field_t grpc_lb_v1_LoadBalanceRequest_fields[3] = {
+    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, grpc_lb_v1_LoadBalanceRequest, initial_request, initial_request, &grpc_lb_v1_InitialLoadBalanceRequest_fields),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_LoadBalanceRequest, client_stats, initial_request, &grpc_lb_v1_ClientStats_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v1_InitialLoadBalanceRequest_fields[2] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, STATIC  , FIRST, grpc_lb_v1_InitialLoadBalanceRequest, name, name, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v1_ClientStatsPerToken_fields[3] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_lb_v1_ClientStatsPerToken, load_balance_token, load_balance_token, 0),
+    PB_FIELD(  2, INT64   , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_ClientStatsPerToken, num_calls, load_balance_token, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v1_ClientStats_fields[7] = {
+    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, grpc_lb_v1_ClientStats, timestamp, timestamp, &google_protobuf_Timestamp_fields),
+    PB_FIELD(  2, INT64   , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_ClientStats, num_calls_started, timestamp, 0),
+    PB_FIELD(  3, INT64   , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_ClientStats, num_calls_finished, num_calls_started, 0),
+    PB_FIELD(  6, INT64   , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_ClientStats, num_calls_finished_with_client_failed_to_send, num_calls_finished, 0),
+    PB_FIELD(  7, INT64   , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_ClientStats, num_calls_finished_known_received, num_calls_finished_with_client_failed_to_send, 0),
+    PB_FIELD(  8, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_lb_v1_ClientStats, calls_finished_with_drop, num_calls_finished_known_received, &grpc_lb_v1_ClientStatsPerToken_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v1_LoadBalanceResponse_fields[3] = {
+    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, grpc_lb_v1_LoadBalanceResponse, initial_response, initial_response, &grpc_lb_v1_InitialLoadBalanceResponse_fields),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_lb_v1_LoadBalanceResponse, server_list, initial_response, &grpc_lb_v1_ServerList_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v1_InitialLoadBalanceResponse_fields[3] = {
+    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, &google_protobuf_Duration_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v1_ServerList_fields[2] = {
+    PB_FIELD(  1, MESSAGE , REPEATED, CALLBACK, FIRST, grpc_lb_v1_ServerList, servers, servers, &grpc_lb_v1_Server_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v1_Server_fields[5] = {
+    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, load_balance_token, 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(grpc_lb_v1_LoadBalanceRequest, initial_request) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceRequest, client_stats) < 65536 && pb_membersize(grpc_lb_v1_ClientStats, timestamp) < 65536 && pb_membersize(grpc_lb_v1_ClientStats, calls_finished_with_drop) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, initial_response) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, server_list) < 65536 && pb_membersize(grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval) < 65536 && pb_membersize(grpc_lb_v1_ServerList, servers) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_lb_v1_LoadBalanceRequest_grpc_lb_v1_InitialLoadBalanceRequest_grpc_lb_v1_ClientStatsPerToken_grpc_lb_v1_ClientStats_grpc_lb_v1_LoadBalanceResponse_grpc_lb_v1_InitialLoadBalanceResponse_grpc_lb_v1_ServerList_grpc_lb_v1_Server)
+#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(grpc_lb_v1_LoadBalanceRequest, initial_request) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceRequest, client_stats) < 256 && pb_membersize(grpc_lb_v1_ClientStats, timestamp) < 256 && pb_membersize(grpc_lb_v1_ClientStats, calls_finished_with_drop) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, initial_response) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, server_list) < 256 && pb_membersize(grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval) < 256 && pb_membersize(grpc_lb_v1_ServerList, servers) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_lb_v1_LoadBalanceRequest_grpc_lb_v1_InitialLoadBalanceRequest_grpc_lb_v1_ClientStatsPerToken_grpc_lb_v1_ClientStats_grpc_lb_v1_LoadBalanceResponse_grpc_lb_v1_InitialLoadBalanceResponse_grpc_lb_v1_ServerList_grpc_lb_v1_Server)
+#endif
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
new file mode 100644
index 0000000..a4ff516
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h
@@ -0,0 +1,164 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GRPC_LB_V1_LOAD_BALANCER_PB_H_INCLUDED
+#define PB_GRPC_LB_V1_LOAD_BALANCER_PB_H_INCLUDED
+#include "pb.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h"
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h"
+/* @@protoc_insertion_point(includes) */
+#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 _grpc_lb_v1_ServerList {
+    pb_callback_t servers;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_ServerList) */
+} grpc_lb_v1_ServerList;
+
+typedef struct _grpc_lb_v1_ClientStats {
+    bool has_timestamp;
+    google_protobuf_Timestamp timestamp;
+    bool has_num_calls_started;
+    int64_t num_calls_started;
+    bool has_num_calls_finished;
+    int64_t num_calls_finished;
+    bool has_num_calls_finished_with_client_failed_to_send;
+    int64_t num_calls_finished_with_client_failed_to_send;
+    bool has_num_calls_finished_known_received;
+    int64_t num_calls_finished_known_received;
+    pb_callback_t calls_finished_with_drop;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_ClientStats) */
+} grpc_lb_v1_ClientStats;
+
+typedef struct _grpc_lb_v1_ClientStatsPerToken {
+    pb_callback_t load_balance_token;
+    bool has_num_calls;
+    int64_t num_calls;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_ClientStatsPerToken) */
+} grpc_lb_v1_ClientStatsPerToken;
+
+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 struct _grpc_lb_v1_InitialLoadBalanceResponse {
+    bool has_load_balancer_delegate;
+    char load_balancer_delegate[64];
+    bool has_client_stats_report_interval;
+    google_protobuf_Duration client_stats_report_interval;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_InitialLoadBalanceResponse) */
+} grpc_lb_v1_InitialLoadBalanceResponse;
+
+typedef PB_BYTES_ARRAY_T(16) grpc_lb_v1_Server_ip_address_t;
+typedef struct _grpc_lb_v1_Server {
+    bool has_ip_address;
+    grpc_lb_v1_Server_ip_address_t ip_address;
+    bool has_port;
+    int32_t port;
+    bool has_load_balance_token;
+    char load_balance_token[50];
+    bool has_drop;
+    bool drop;
+/* @@protoc_insertion_point(struct:grpc_lb_v1_Server) */
+} grpc_lb_v1_Server;
+
+typedef struct _grpc_lb_v1_LoadBalanceRequest {
+    bool has_initial_request;
+    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_LoadBalanceResponse {
+    bool has_initial_response;
+    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 */
+
+/* Initializer values for message structs */
+#define grpc_lb_v1_LoadBalanceRequest_init_default {false, grpc_lb_v1_InitialLoadBalanceRequest_init_default, false, grpc_lb_v1_ClientStats_init_default}
+#define grpc_lb_v1_InitialLoadBalanceRequest_init_default {false, ""}
+#define grpc_lb_v1_ClientStatsPerToken_init_default {{{NULL}, NULL}, false, 0}
+#define grpc_lb_v1_ClientStats_init_default      {false, google_protobuf_Timestamp_init_default, false, 0, false, 0, false, 0, false, 0, {{NULL}, NULL}}
+#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, google_protobuf_Duration_init_default}
+#define grpc_lb_v1_ServerList_init_default       {{{NULL}, NULL}}
+#define grpc_lb_v1_Server_init_default           {false, {0, {0}}, false, 0, false, "", 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, ""}
+#define grpc_lb_v1_ClientStatsPerToken_init_zero {{{NULL}, NULL}, false, 0}
+#define grpc_lb_v1_ClientStats_init_zero         {false, google_protobuf_Timestamp_init_zero, false, 0, false, 0, false, 0, false, 0, {{NULL}, NULL}}
+#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, google_protobuf_Duration_init_zero}
+#define grpc_lb_v1_ServerList_init_zero          {{{NULL}, NULL}}
+#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_ServerList_servers_tag        1
+#define grpc_lb_v1_ClientStats_timestamp_tag     1
+#define grpc_lb_v1_ClientStats_num_calls_started_tag 2
+#define grpc_lb_v1_ClientStats_num_calls_finished_tag 3
+#define grpc_lb_v1_ClientStats_num_calls_finished_with_client_failed_to_send_tag 6
+#define grpc_lb_v1_ClientStats_num_calls_finished_known_received_tag 7
+#define grpc_lb_v1_ClientStats_calls_finished_with_drop_tag 8
+#define grpc_lb_v1_ClientStatsPerToken_load_balance_token_tag 1
+#define grpc_lb_v1_ClientStatsPerToken_num_calls_tag 2
+#define grpc_lb_v1_InitialLoadBalanceRequest_name_tag 1
+#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_Server_ip_address_tag         1
+#define grpc_lb_v1_Server_port_tag               2
+#define grpc_lb_v1_Server_load_balance_token_tag 3
+#define grpc_lb_v1_Server_drop_tag               4
+#define grpc_lb_v1_LoadBalanceRequest_initial_request_tag 1
+#define grpc_lb_v1_LoadBalanceRequest_client_stats_tag 2
+#define grpc_lb_v1_LoadBalanceResponse_initial_response_tag 1
+#define grpc_lb_v1_LoadBalanceResponse_server_list_tag 2
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_lb_v1_LoadBalanceRequest_fields[3];
+extern const pb_field_t grpc_lb_v1_InitialLoadBalanceRequest_fields[2];
+extern const pb_field_t grpc_lb_v1_ClientStatsPerToken_fields[3];
+extern const pb_field_t grpc_lb_v1_ClientStats_fields[7];
+extern const pb_field_t grpc_lb_v1_LoadBalanceResponse_fields[3];
+extern const pb_field_t grpc_lb_v1_InitialLoadBalanceResponse_fields[3];
+extern const pb_field_t grpc_lb_v1_ServerList_fields[2];
+extern const pb_field_t grpc_lb_v1_Server_fields[5];
+
+/* Maximum encoded size of messages (where known) */
+#define grpc_lb_v1_LoadBalanceRequest_size       (140 + grpc_lb_v1_ClientStats_size)
+#define grpc_lb_v1_InitialLoadBalanceRequest_size 131
+/* grpc_lb_v1_ClientStatsPerToken_size depends on runtime parameters */
+/* grpc_lb_v1_ClientStats_size depends on runtime parameters */
+#define grpc_lb_v1_LoadBalanceResponse_size      (98 + grpc_lb_v1_ServerList_size)
+#define grpc_lb_v1_InitialLoadBalanceResponse_size 90
+/* grpc_lb_v1_ServerList_size depends on runtime parameters */
+#define grpc_lb_v1_Server_size                   83
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define LOAD_BALANCER_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
new file mode 100644
index 0000000..d6ff74e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
@@ -0,0 +1,643 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/subchannel_list.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/ext/filters/client_channel/subchannel_index.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/transport/connectivity_state.h"
+
+namespace grpc_core {
+
+TraceFlag grpc_lb_pick_first_trace(false, "pick_first");
+
+namespace {
+
+//
+// pick_first LB policy
+//
+
+constexpr char kPickFirst[] = "pick_first";
+
+class PickFirst : public LoadBalancingPolicy {
+ public:
+  explicit PickFirst(const Args& args);
+
+  const char* name() const override { return kPickFirst; }
+
+  void UpdateLocked(const grpc_channel_args& args,
+                    grpc_json* lb_config) override;
+  bool PickLocked(PickState* pick, grpc_error** error) override;
+  void CancelPickLocked(PickState* pick, grpc_error* error) override;
+  void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error* error) override;
+  void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                 grpc_closure* closure) override;
+  grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) override;
+  void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
+  void ExitIdleLocked() override;
+  void ResetBackoffLocked() override;
+  void FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels,
+                                channelz::ChildRefsList* ignored) override;
+
+ private:
+  ~PickFirst();
+
+  class PickFirstSubchannelList;
+
+  class PickFirstSubchannelData
+      : public SubchannelData<PickFirstSubchannelList,
+                              PickFirstSubchannelData> {
+   public:
+    PickFirstSubchannelData(
+        SubchannelList<PickFirstSubchannelList, PickFirstSubchannelData>*
+            subchannel_list,
+        const ServerAddress& address, grpc_subchannel* subchannel,
+        grpc_combiner* combiner)
+        : SubchannelData(subchannel_list, address, subchannel, combiner) {}
+
+    void ProcessConnectivityChangeLocked(
+        grpc_connectivity_state connectivity_state, grpc_error* error) override;
+
+    // Processes the connectivity change to READY for an unselected subchannel.
+    void ProcessUnselectedReadyLocked();
+
+    void CheckConnectivityStateAndStartWatchingLocked();
+  };
+
+  class PickFirstSubchannelList
+      : public SubchannelList<PickFirstSubchannelList,
+                              PickFirstSubchannelData> {
+   public:
+    PickFirstSubchannelList(PickFirst* policy, TraceFlag* tracer,
+                            const ServerAddressList& addresses,
+                            grpc_combiner* combiner,
+                            grpc_client_channel_factory* client_channel_factory,
+                            const grpc_channel_args& args)
+        : SubchannelList(policy, tracer, addresses, combiner,
+                         client_channel_factory, args) {
+      // Need to maintain a ref to the LB policy as long as we maintain
+      // any references to subchannels, since the subchannels'
+      // pollset_sets will include the LB policy's pollset_set.
+      policy->Ref(DEBUG_LOCATION, "subchannel_list").release();
+    }
+
+    ~PickFirstSubchannelList() {
+      PickFirst* p = static_cast<PickFirst*>(policy());
+      p->Unref(DEBUG_LOCATION, "subchannel_list");
+    }
+  };
+
+  // Helper class to ensure that any function that modifies the child refs
+  // data structures will update the channelz snapshot data structures before
+  // returning.
+  class AutoChildRefsUpdater {
+   public:
+    explicit AutoChildRefsUpdater(PickFirst* pf) : pf_(pf) {}
+    ~AutoChildRefsUpdater() { pf_->UpdateChildRefsLocked(); }
+
+   private:
+    PickFirst* pf_;
+  };
+
+  void ShutdownLocked() override;
+
+  void StartPickingLocked();
+  void UpdateChildRefsLocked();
+
+  // All our subchannels.
+  OrphanablePtr<PickFirstSubchannelList> subchannel_list_;
+  // Latest pending subchannel list.
+  OrphanablePtr<PickFirstSubchannelList> latest_pending_subchannel_list_;
+  // Selected subchannel in \a subchannel_list_.
+  PickFirstSubchannelData* selected_ = nullptr;
+  // Have we started picking?
+  bool started_picking_ = false;
+  // Are we shut down?
+  bool shutdown_ = false;
+  // List of picks that are waiting on connectivity.
+  PickState* pending_picks_ = nullptr;
+  // Our connectivity state tracker.
+  grpc_connectivity_state_tracker state_tracker_;
+
+  /// Lock and data used to capture snapshots of this channels child
+  /// channels and subchannels. This data is consumed by channelz.
+  gpr_mu child_refs_mu_;
+  channelz::ChildRefsList child_subchannels_;
+  channelz::ChildRefsList child_channels_;
+};
+
+PickFirst::PickFirst(const Args& args) : LoadBalancingPolicy(args) {
+  GPR_ASSERT(args.client_channel_factory != nullptr);
+  gpr_mu_init(&child_refs_mu_);
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
+                               "pick_first");
+  if (grpc_lb_pick_first_trace.enabled()) {
+    gpr_log(GPR_INFO, "Pick First %p created.", this);
+  }
+  UpdateLocked(*args.args, args.lb_config);
+  grpc_subchannel_index_ref();
+}
+
+PickFirst::~PickFirst() {
+  if (grpc_lb_pick_first_trace.enabled()) {
+    gpr_log(GPR_INFO, "Destroying Pick First %p", this);
+  }
+  gpr_mu_destroy(&child_refs_mu_);
+  GPR_ASSERT(subchannel_list_ == nullptr);
+  GPR_ASSERT(latest_pending_subchannel_list_ == nullptr);
+  GPR_ASSERT(pending_picks_ == nullptr);
+  grpc_connectivity_state_destroy(&state_tracker_);
+  grpc_subchannel_index_unref();
+}
+
+void PickFirst::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
+  PickState* pick;
+  while ((pick = pending_picks_) != nullptr) {
+    pending_picks_ = pick->next;
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (new_policy->PickLocked(pick, &error)) {
+      // Synchronous return, schedule closure.
+      GRPC_CLOSURE_SCHED(pick->on_complete, error);
+    }
+  }
+}
+
+void PickFirst::ShutdownLocked() {
+  AutoChildRefsUpdater guard(this);
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
+  if (grpc_lb_pick_first_trace.enabled()) {
+    gpr_log(GPR_INFO, "Pick First %p Shutting down", this);
+  }
+  shutdown_ = true;
+  PickState* pick;
+  while ((pick = pending_picks_) != nullptr) {
+    pending_picks_ = pick->next;
+    pick->connected_subchannel.reset();
+    GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_REF(error));
+  }
+  grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN,
+                              GRPC_ERROR_REF(error), "shutdown");
+  subchannel_list_.reset();
+  latest_pending_subchannel_list_.reset();
+  TryReresolutionLocked(&grpc_lb_pick_first_trace, GRPC_ERROR_CANCELLED);
+  GRPC_ERROR_UNREF(error);
+}
+
+void PickFirst::CancelPickLocked(PickState* pick, grpc_error* error) {
+  PickState* pp = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pp != nullptr) {
+    PickState* next = pp->next;
+    if (pp == pick) {
+      pick->connected_subchannel.reset();
+      GRPC_CLOSURE_SCHED(pick->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
+    }
+    pp = next;
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void PickFirst::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                          uint32_t initial_metadata_flags_eq,
+                                          grpc_error* error) {
+  PickState* pick = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pick != nullptr) {
+    PickState* next = pick->next;
+    if ((*pick->initial_metadata_flags & initial_metadata_flags_mask) ==
+        initial_metadata_flags_eq) {
+      GRPC_CLOSURE_SCHED(pick->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pick->next = pending_picks_;
+      pending_picks_ = pick;
+    }
+    pick = next;
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void PickFirst::StartPickingLocked() {
+  started_picking_ = true;
+  if (subchannel_list_ != nullptr && subchannel_list_->num_subchannels() > 0) {
+    subchannel_list_->subchannel(0)
+        ->CheckConnectivityStateAndStartWatchingLocked();
+  }
+}
+
+void PickFirst::ExitIdleLocked() {
+  if (!started_picking_) {
+    StartPickingLocked();
+  }
+}
+
+void PickFirst::ResetBackoffLocked() {
+  subchannel_list_->ResetBackoffLocked();
+  if (latest_pending_subchannel_list_ != nullptr) {
+    latest_pending_subchannel_list_->ResetBackoffLocked();
+  }
+}
+
+bool PickFirst::PickLocked(PickState* pick, grpc_error** error) {
+  // If we have a selected subchannel already, return synchronously.
+  if (selected_ != nullptr) {
+    pick->connected_subchannel = selected_->connected_subchannel()->Ref();
+    return true;
+  }
+  // No subchannel selected yet, so handle asynchronously.
+  if (pick->on_complete == nullptr) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "No pick result available but synchronous result required.");
+    return true;
+  }
+  pick->next = pending_picks_;
+  pending_picks_ = pick;
+  if (!started_picking_) {
+    StartPickingLocked();
+  }
+  return false;
+}
+
+grpc_connectivity_state PickFirst::CheckConnectivityLocked(grpc_error** error) {
+  return grpc_connectivity_state_get(&state_tracker_, error);
+}
+
+void PickFirst::NotifyOnStateChangeLocked(grpc_connectivity_state* current,
+                                          grpc_closure* notify) {
+  grpc_connectivity_state_notify_on_state_change(&state_tracker_, current,
+                                                 notify);
+}
+
+void PickFirst::FillChildRefsForChannelz(
+    channelz::ChildRefsList* child_subchannels_to_fill,
+    channelz::ChildRefsList* ignored) {
+  MutexLock lock(&child_refs_mu_);
+  for (size_t i = 0; i < child_subchannels_.size(); ++i) {
+    // TODO(ncteisen): implement a de dup loop that is not O(n^2). Might
+    // have to implement lightweight set. For now, we don't care about
+    // performance when channelz requests are made.
+    bool found = false;
+    for (size_t j = 0; j < child_subchannels_to_fill->size(); ++j) {
+      if ((*child_subchannels_to_fill)[j] == child_subchannels_[i]) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      child_subchannels_to_fill->push_back(child_subchannels_[i]);
+    }
+  }
+}
+
+void PickFirst::UpdateChildRefsLocked() {
+  channelz::ChildRefsList cs;
+  if (subchannel_list_ != nullptr) {
+    subchannel_list_->PopulateChildRefsList(&cs);
+  }
+  if (latest_pending_subchannel_list_ != nullptr) {
+    latest_pending_subchannel_list_->PopulateChildRefsList(&cs);
+  }
+  // atomically update the data that channelz will actually be looking at.
+  MutexLock lock(&child_refs_mu_);
+  child_subchannels_ = std::move(cs);
+}
+
+void PickFirst::UpdateLocked(const grpc_channel_args& args,
+                             grpc_json* lb_config) {
+  AutoChildRefsUpdater guard(this);
+  const ServerAddressList* addresses = FindServerAddressListChannelArg(&args);
+  if (addresses == nullptr) {
+    if (subchannel_list_ == nullptr) {
+      // If we don't have a current subchannel list, go into TRANSIENT FAILURE.
+      grpc_connectivity_state_set(
+          &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"),
+          "pf_update_missing");
+    } else {
+      // otherwise, keep using the current subchannel list (ignore this update).
+      gpr_log(GPR_ERROR,
+              "No valid LB addresses channel arg for Pick First %p update, "
+              "ignoring.",
+              this);
+    }
+    return;
+  }
+  if (grpc_lb_pick_first_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "Pick First %p received update with %" PRIuPTR " addresses", this,
+            addresses->size());
+  }
+  grpc_arg new_arg = grpc_channel_arg_integer_create(
+      const_cast<char*>(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1);
+  grpc_channel_args* new_args =
+      grpc_channel_args_copy_and_add(&args, &new_arg, 1);
+  auto subchannel_list = MakeOrphanable<PickFirstSubchannelList>(
+      this, &grpc_lb_pick_first_trace, *addresses, combiner(),
+      client_channel_factory(), *new_args);
+  grpc_channel_args_destroy(new_args);
+  if (subchannel_list->num_subchannels() == 0) {
+    // Empty update or no valid subchannels. Unsubscribe from all current
+    // subchannels and put the channel in TRANSIENT_FAILURE.
+    grpc_connectivity_state_set(
+        &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"),
+        "pf_update_empty");
+    subchannel_list_ = std::move(subchannel_list);  // Empty list.
+    selected_ = nullptr;
+    return;
+  }
+  // If one of the subchannels in the new list is already in state
+  // READY, then select it immediately.  This can happen when the
+  // currently selected subchannel is also present in the update.  It
+  // can also happen if one of the subchannels in the update is already
+  // in the subchannel index because it's in use by another channel.
+  for (size_t i = 0; i < subchannel_list->num_subchannels(); ++i) {
+    PickFirstSubchannelData* sd = subchannel_list->subchannel(i);
+    grpc_error* error = GRPC_ERROR_NONE;
+    grpc_connectivity_state state = sd->CheckConnectivityStateLocked(&error);
+    GRPC_ERROR_UNREF(error);
+    if (state == GRPC_CHANNEL_READY) {
+      subchannel_list_ = std::move(subchannel_list);
+      sd->ProcessUnselectedReadyLocked();
+      sd->StartConnectivityWatchLocked();
+      // If there was a previously pending update (which may or may
+      // not have contained the currently selected subchannel), drop
+      // it, so that it doesn't override what we've done here.
+      latest_pending_subchannel_list_.reset();
+      // Make sure that subsequent calls to ExitIdleLocked() don't cause
+      // us to start watching a subchannel other than the one we've
+      // selected.
+      started_picking_ = true;
+      return;
+    }
+  }
+  if (selected_ == nullptr) {
+    // We don't yet have a selected subchannel, so replace the current
+    // subchannel list immediately.
+    subchannel_list_ = std::move(subchannel_list);
+    // If we've started picking, start trying to connect to the first
+    // subchannel in the new list.
+    if (started_picking_) {
+      // Note: No need to use CheckConnectivityStateAndStartWatchingLocked()
+      // here, since we've already checked the initial connectivity
+      // state of all subchannels above.
+      subchannel_list_->subchannel(0)->StartConnectivityWatchLocked();
+    }
+  } else {
+    // We do have a selected subchannel, so keep using it until one of
+    // the subchannels in the new list reports READY.
+    if (latest_pending_subchannel_list_ != nullptr) {
+      if (grpc_lb_pick_first_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "Pick First %p Shutting down latest pending subchannel list "
+                "%p, about to be replaced by newer latest %p",
+                this, latest_pending_subchannel_list_.get(),
+                subchannel_list.get());
+      }
+    }
+    latest_pending_subchannel_list_ = std::move(subchannel_list);
+    // If we've started picking, start trying to connect to the first
+    // subchannel in the new list.
+    if (started_picking_) {
+      // Note: No need to use CheckConnectivityStateAndStartWatchingLocked()
+      // here, since we've already checked the initial connectivity
+      // state of all subchannels above.
+      latest_pending_subchannel_list_->subchannel(0)
+          ->StartConnectivityWatchLocked();
+    }
+  }
+}
+
+void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked(
+    grpc_connectivity_state connectivity_state, grpc_error* error) {
+  PickFirst* p = static_cast<PickFirst*>(subchannel_list()->policy());
+  AutoChildRefsUpdater guard(p);
+  // The notification must be for a subchannel in either the current or
+  // latest pending subchannel lists.
+  GPR_ASSERT(subchannel_list() == p->subchannel_list_.get() ||
+             subchannel_list() == p->latest_pending_subchannel_list_.get());
+  GPR_ASSERT(connectivity_state != GRPC_CHANNEL_SHUTDOWN);
+  // Handle updates for the currently selected subchannel.
+  if (p->selected_ == this) {
+    if (grpc_lb_pick_first_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "Pick First %p connectivity changed for selected subchannel", p);
+    }
+    // If the new state is anything other than READY and there is a
+    // pending update, switch to the pending update.
+    if (connectivity_state != GRPC_CHANNEL_READY &&
+        p->latest_pending_subchannel_list_ != nullptr) {
+      if (grpc_lb_pick_first_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "Pick First %p promoting pending subchannel list %p to "
+                "replace %p",
+                p, p->latest_pending_subchannel_list_.get(),
+                p->subchannel_list_.get());
+      }
+      p->selected_ = nullptr;
+      StopConnectivityWatchLocked();
+      p->subchannel_list_ = std::move(p->latest_pending_subchannel_list_);
+      grpc_connectivity_state_set(
+          &p->state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          error != GRPC_ERROR_NONE
+              ? GRPC_ERROR_REF(error)
+              : GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                    "selected subchannel not ready; switching to pending "
+                    "update"),
+          "selected_not_ready+switch_to_update");
+    } else {
+      if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+        // If the selected subchannel goes bad, request a re-resolution. We also
+        // set the channel state to IDLE and reset started_picking_. The reason
+        // is that if the new state is TRANSIENT_FAILURE due to a GOAWAY
+        // reception we don't want to connect to the re-resolved backends until
+        // we leave the IDLE state.
+        grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_IDLE,
+                                    GRPC_ERROR_NONE,
+                                    "selected_changed+reresolve");
+        p->started_picking_ = false;
+        p->TryReresolutionLocked(&grpc_lb_pick_first_trace, GRPC_ERROR_NONE);
+        // In transient failure. Rely on re-resolution to recover.
+        p->selected_ = nullptr;
+        StopConnectivityWatchLocked();
+      } else {
+        grpc_connectivity_state_set(&p->state_tracker_, connectivity_state,
+                                    GRPC_ERROR_REF(error), "selected_changed");
+        // Renew notification.
+        RenewConnectivityWatchLocked();
+      }
+    }
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  // If we get here, there are two possible cases:
+  // 1. We do not currently have a selected subchannel, and the update is
+  //    for a subchannel in p->subchannel_list_ that we're trying to
+  //    connect to.  The goal here is to find a subchannel that we can
+  //    select.
+  // 2. We do currently have a selected subchannel, and the update is
+  //    for a subchannel in p->latest_pending_subchannel_list_.  The
+  //    goal here is to find a subchannel from the update that we can
+  //    select in place of the current one.
+  switch (connectivity_state) {
+    case GRPC_CHANNEL_READY: {
+      ProcessUnselectedReadyLocked();
+      // Renew notification.
+      RenewConnectivityWatchLocked();
+      break;
+    }
+    case GRPC_CHANNEL_TRANSIENT_FAILURE: {
+      StopConnectivityWatchLocked();
+      PickFirstSubchannelData* sd = this;
+      size_t next_index =
+          (sd->Index() + 1) % subchannel_list()->num_subchannels();
+      sd = subchannel_list()->subchannel(next_index);
+      // Case 1: Only set state to TRANSIENT_FAILURE if we've tried
+      // all subchannels.
+      if (sd->Index() == 0 && subchannel_list() == p->subchannel_list_.get()) {
+        p->TryReresolutionLocked(&grpc_lb_pick_first_trace, GRPC_ERROR_NONE);
+        grpc_connectivity_state_set(
+            &p->state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
+            GRPC_ERROR_REF(error), "exhausted_subchannels");
+      }
+      sd->CheckConnectivityStateAndStartWatchingLocked();
+      break;
+    }
+    case GRPC_CHANNEL_CONNECTING:
+    case GRPC_CHANNEL_IDLE: {
+      // Only update connectivity state in case 1.
+      if (subchannel_list() == p->subchannel_list_.get()) {
+        grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_CONNECTING,
+                                    GRPC_ERROR_REF(error),
+                                    "connecting_changed");
+      }
+      // Renew notification.
+      RenewConnectivityWatchLocked();
+      break;
+    }
+    case GRPC_CHANNEL_SHUTDOWN:
+      GPR_UNREACHABLE_CODE(break);
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void PickFirst::PickFirstSubchannelData::ProcessUnselectedReadyLocked() {
+  PickFirst* p = static_cast<PickFirst*>(subchannel_list()->policy());
+  // If we get here, there are two possible cases:
+  // 1. We do not currently have a selected subchannel, and the update is
+  //    for a subchannel in p->subchannel_list_ that we're trying to
+  //    connect to.  The goal here is to find a subchannel that we can
+  //    select.
+  // 2. We do currently have a selected subchannel, and the update is
+  //    for a subchannel in p->latest_pending_subchannel_list_.  The
+  //    goal here is to find a subchannel from the update that we can
+  //    select in place of the current one.
+  GPR_ASSERT(subchannel_list() == p->subchannel_list_.get() ||
+             subchannel_list() == p->latest_pending_subchannel_list_.get());
+  // Case 2.  Promote p->latest_pending_subchannel_list_ to p->subchannel_list_.
+  if (subchannel_list() == p->latest_pending_subchannel_list_.get()) {
+    if (grpc_lb_pick_first_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "Pick First %p promoting pending subchannel list %p to "
+              "replace %p",
+              p, p->latest_pending_subchannel_list_.get(),
+              p->subchannel_list_.get());
+    }
+    p->subchannel_list_ = std::move(p->latest_pending_subchannel_list_);
+  }
+  // Cases 1 and 2.
+  grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_READY,
+                              GRPC_ERROR_NONE, "subchannel_ready");
+  p->selected_ = this;
+  if (grpc_lb_pick_first_trace.enabled()) {
+    gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", p, subchannel());
+  }
+  // Update any calls that were waiting for a pick.
+  PickState* pick;
+  while ((pick = p->pending_picks_)) {
+    p->pending_picks_ = pick->next;
+    pick->connected_subchannel = p->selected_->connected_subchannel()->Ref();
+    if (grpc_lb_pick_first_trace.enabled()) {
+      gpr_log(GPR_INFO, "Servicing pending pick with selected subchannel %p",
+              p->selected_->subchannel());
+    }
+    GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE);
+  }
+}
+
+void PickFirst::PickFirstSubchannelData::
+    CheckConnectivityStateAndStartWatchingLocked() {
+  PickFirst* p = static_cast<PickFirst*>(subchannel_list()->policy());
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (p->selected_ != this &&
+      CheckConnectivityStateLocked(&error) == GRPC_CHANNEL_READY) {
+    // We must process the READY subchannel before we start watching it.
+    // Otherwise, we won't know it's READY because we will be waiting for its
+    // connectivity state to change from READY.
+    ProcessUnselectedReadyLocked();
+  }
+  GRPC_ERROR_UNREF(error);
+  StartConnectivityWatchLocked();
+}
+
+//
+// factory
+//
+
+class PickFirstFactory : public LoadBalancingPolicyFactory {
+ public:
+  OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const override {
+    return OrphanablePtr<LoadBalancingPolicy>(New<PickFirst>(args));
+  }
+
+  const char* name() const override { return kPickFirst; }
+};
+
+}  // namespace
+
+}  // namespace grpc_core
+
+void grpc_lb_policy_pick_first_init() {
+  grpc_core::LoadBalancingPolicyRegistry::Builder::
+      RegisterLoadBalancingPolicyFactory(
+          grpc_core::UniquePtr<grpc_core::LoadBalancingPolicyFactory>(
+              grpc_core::New<grpc_core::PickFirstFactory>()));
+}
+
+void grpc_lb_policy_pick_first_shutdown() {}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
new file mode 100644
index 0000000..3bcb33e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
@@ -0,0 +1,721 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/** Round Robin Policy.
+ *
+ * Before every pick, the \a get_next_ready_subchannel_index_locked function
+ * returns the p->subchannel_list->subchannels index for next subchannel,
+ * respecting the relative order of the addresses provided upon creation or
+ * updates. Note however that updates will start picking from the beginning of
+ * the updated list. */
+
+#include <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/subchannel_list.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/ext/filters/client_channel/subchannel_index.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+namespace grpc_core {
+
+TraceFlag grpc_lb_round_robin_trace(false, "round_robin");
+
+namespace {
+
+//
+// round_robin LB policy
+//
+
+constexpr char kRoundRobin[] = "round_robin";
+
+class RoundRobin : public LoadBalancingPolicy {
+ public:
+  explicit RoundRobin(const Args& args);
+
+  const char* name() const override { return kRoundRobin; }
+
+  void UpdateLocked(const grpc_channel_args& args,
+                    grpc_json* lb_config) override;
+  bool PickLocked(PickState* pick, grpc_error** error) override;
+  void CancelPickLocked(PickState* pick, grpc_error* error) override;
+  void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error* error) override;
+  void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                 grpc_closure* closure) override;
+  grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) override;
+  void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
+  void ExitIdleLocked() override;
+  void ResetBackoffLocked() override;
+  void FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels,
+                                channelz::ChildRefsList* ignored) override;
+
+ private:
+  ~RoundRobin();
+
+  // Forward declaration.
+  class RoundRobinSubchannelList;
+
+  // Data for a particular subchannel in a subchannel list.
+  // This subclass adds the following functionality:
+  // - Tracks the previous connectivity state of the subchannel, so that
+  //   we know how many subchannels are in each state.
+  class RoundRobinSubchannelData
+      : public SubchannelData<RoundRobinSubchannelList,
+                              RoundRobinSubchannelData> {
+   public:
+    RoundRobinSubchannelData(
+        SubchannelList<RoundRobinSubchannelList, RoundRobinSubchannelData>*
+            subchannel_list,
+        const ServerAddress& address, grpc_subchannel* subchannel,
+        grpc_combiner* combiner)
+        : SubchannelData(subchannel_list, address, subchannel, combiner) {}
+
+    grpc_connectivity_state connectivity_state() const {
+      return last_connectivity_state_;
+    }
+
+    void UpdateConnectivityStateLocked(
+        grpc_connectivity_state connectivity_state, grpc_error* error);
+
+   private:
+    void ProcessConnectivityChangeLocked(
+        grpc_connectivity_state connectivity_state, grpc_error* error) override;
+
+    grpc_connectivity_state last_connectivity_state_ = GRPC_CHANNEL_IDLE;
+  };
+
+  // A list of subchannels.
+  class RoundRobinSubchannelList
+      : public SubchannelList<RoundRobinSubchannelList,
+                              RoundRobinSubchannelData> {
+   public:
+    RoundRobinSubchannelList(
+        RoundRobin* policy, TraceFlag* tracer,
+        const ServerAddressList& addresses, grpc_combiner* combiner,
+        grpc_client_channel_factory* client_channel_factory,
+        const grpc_channel_args& args)
+        : SubchannelList(policy, tracer, addresses, combiner,
+                         client_channel_factory, args),
+          last_ready_index_(num_subchannels() - 1) {
+      // Need to maintain a ref to the LB policy as long as we maintain
+      // any references to subchannels, since the subchannels'
+      // pollset_sets will include the LB policy's pollset_set.
+      policy->Ref(DEBUG_LOCATION, "subchannel_list").release();
+    }
+
+    ~RoundRobinSubchannelList() {
+      GRPC_ERROR_UNREF(last_transient_failure_error_);
+      RoundRobin* p = static_cast<RoundRobin*>(policy());
+      p->Unref(DEBUG_LOCATION, "subchannel_list");
+    }
+
+    // Starts watching the subchannels in this list.
+    void StartWatchingLocked();
+
+    // Updates the counters of subchannels in each state when a
+    // subchannel transitions from old_state to new_state.
+    // transient_failure_error is the error that is reported when
+    // new_state is TRANSIENT_FAILURE.
+    void UpdateStateCountersLocked(grpc_connectivity_state old_state,
+                                   grpc_connectivity_state new_state,
+                                   grpc_error* transient_failure_error);
+
+    // If this subchannel list is the RR policy's current subchannel
+    // list, updates the RR policy's connectivity state based on the
+    // subchannel list's state counters.
+    void MaybeUpdateRoundRobinConnectivityStateLocked();
+
+    // Updates the RR policy's overall state based on the counters of
+    // subchannels in each state.
+    void UpdateRoundRobinStateFromSubchannelStateCountsLocked();
+
+    size_t GetNextReadySubchannelIndexLocked();
+    void UpdateLastReadySubchannelIndexLocked(size_t last_ready_index);
+
+   private:
+    size_t num_ready_ = 0;
+    size_t num_connecting_ = 0;
+    size_t num_transient_failure_ = 0;
+    grpc_error* last_transient_failure_error_ = GRPC_ERROR_NONE;
+    size_t last_ready_index_;  // Index into list of last pick.
+  };
+
+  // Helper class to ensure that any function that modifies the child refs
+  // data structures will update the channelz snapshot data structures before
+  // returning.
+  class AutoChildRefsUpdater {
+   public:
+    explicit AutoChildRefsUpdater(RoundRobin* rr) : rr_(rr) {}
+    ~AutoChildRefsUpdater() { rr_->UpdateChildRefsLocked(); }
+
+   private:
+    RoundRobin* rr_;
+  };
+
+  void ShutdownLocked() override;
+
+  void StartPickingLocked();
+  bool DoPickLocked(PickState* pick);
+  void DrainPendingPicksLocked();
+  void UpdateChildRefsLocked();
+
+  /** list of subchannels */
+  OrphanablePtr<RoundRobinSubchannelList> subchannel_list_;
+  /** Latest version of the subchannel list.
+   * Subchannel connectivity callbacks will only promote updated subchannel
+   * lists if they equal \a latest_pending_subchannel_list. In other words,
+   * racing callbacks that reference outdated subchannel lists won't perform any
+   * update. */
+  OrphanablePtr<RoundRobinSubchannelList> latest_pending_subchannel_list_;
+  /** have we started picking? */
+  bool started_picking_ = false;
+  /** are we shutting down? */
+  bool shutdown_ = false;
+  /** List of picks that are waiting on connectivity */
+  PickState* pending_picks_ = nullptr;
+  /** our connectivity state tracker */
+  grpc_connectivity_state_tracker state_tracker_;
+  /// Lock and data used to capture snapshots of this channel's child
+  /// channels and subchannels. This data is consumed by channelz.
+  gpr_mu child_refs_mu_;
+  channelz::ChildRefsList child_subchannels_;
+  channelz::ChildRefsList child_channels_;
+};
+
+RoundRobin::RoundRobin(const Args& args) : LoadBalancingPolicy(args) {
+  GPR_ASSERT(args.client_channel_factory != nullptr);
+  gpr_mu_init(&child_refs_mu_);
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
+                               "round_robin");
+  UpdateLocked(*args.args, args.lb_config);
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_INFO, "[RR %p] Created with %" PRIuPTR " subchannels", this,
+            subchannel_list_->num_subchannels());
+  }
+  grpc_subchannel_index_ref();
+}
+
+RoundRobin::~RoundRobin() {
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_INFO, "[RR %p] Destroying Round Robin policy", this);
+  }
+  gpr_mu_destroy(&child_refs_mu_);
+  GPR_ASSERT(subchannel_list_ == nullptr);
+  GPR_ASSERT(latest_pending_subchannel_list_ == nullptr);
+  GPR_ASSERT(pending_picks_ == nullptr);
+  grpc_connectivity_state_destroy(&state_tracker_);
+  grpc_subchannel_index_unref();
+}
+
+void RoundRobin::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
+  PickState* pick;
+  while ((pick = pending_picks_) != nullptr) {
+    pending_picks_ = pick->next;
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (new_policy->PickLocked(pick, &error)) {
+      // Synchronous return, schedule closure.
+      GRPC_CLOSURE_SCHED(pick->on_complete, error);
+    }
+  }
+}
+
+void RoundRobin::ShutdownLocked() {
+  AutoChildRefsUpdater guard(this);
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_INFO, "[RR %p] Shutting down", this);
+  }
+  shutdown_ = true;
+  PickState* pick;
+  while ((pick = pending_picks_) != nullptr) {
+    pending_picks_ = pick->next;
+    pick->connected_subchannel.reset();
+    GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_REF(error));
+  }
+  grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN,
+                              GRPC_ERROR_REF(error), "rr_shutdown");
+  subchannel_list_.reset();
+  latest_pending_subchannel_list_.reset();
+  TryReresolutionLocked(&grpc_lb_round_robin_trace, GRPC_ERROR_CANCELLED);
+  GRPC_ERROR_UNREF(error);
+}
+
+void RoundRobin::CancelPickLocked(PickState* pick, grpc_error* error) {
+  PickState* pp = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pp != nullptr) {
+    PickState* next = pp->next;
+    if (pp == pick) {
+      pick->connected_subchannel.reset();
+      GRPC_CLOSURE_SCHED(pick->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
+    }
+    pp = next;
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void RoundRobin::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                           uint32_t initial_metadata_flags_eq,
+                                           grpc_error* error) {
+  PickState* pick = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pick != nullptr) {
+    PickState* next = pick->next;
+    if ((*pick->initial_metadata_flags & initial_metadata_flags_mask) ==
+        initial_metadata_flags_eq) {
+      pick->connected_subchannel.reset();
+      GRPC_CLOSURE_SCHED(pick->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pick->next = pending_picks_;
+      pending_picks_ = pick;
+    }
+    pick = next;
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void RoundRobin::StartPickingLocked() {
+  started_picking_ = true;
+  subchannel_list_->StartWatchingLocked();
+}
+
+void RoundRobin::ExitIdleLocked() {
+  if (!started_picking_) {
+    StartPickingLocked();
+  }
+}
+
+void RoundRobin::ResetBackoffLocked() {
+  subchannel_list_->ResetBackoffLocked();
+  if (latest_pending_subchannel_list_ != nullptr) {
+    latest_pending_subchannel_list_->ResetBackoffLocked();
+  }
+}
+
+bool RoundRobin::DoPickLocked(PickState* pick) {
+  const size_t next_ready_index =
+      subchannel_list_->GetNextReadySubchannelIndexLocked();
+  if (next_ready_index < subchannel_list_->num_subchannels()) {
+    /* readily available, report right away */
+    RoundRobinSubchannelData* sd =
+        subchannel_list_->subchannel(next_ready_index);
+    GPR_ASSERT(sd->connected_subchannel() != nullptr);
+    pick->connected_subchannel = sd->connected_subchannel()->Ref();
+    if (grpc_lb_round_robin_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[RR %p] Picked target <-- Subchannel %p (connected %p) (sl %p, "
+              "index %" PRIuPTR ")",
+              this, sd->subchannel(), pick->connected_subchannel.get(),
+              sd->subchannel_list(), next_ready_index);
+    }
+    /* only advance the last picked pointer if the selection was used */
+    subchannel_list_->UpdateLastReadySubchannelIndexLocked(next_ready_index);
+    return true;
+  }
+  return false;
+}
+
+void RoundRobin::DrainPendingPicksLocked() {
+  PickState* pick;
+  while ((pick = pending_picks_)) {
+    pending_picks_ = pick->next;
+    GPR_ASSERT(DoPickLocked(pick));
+    GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE);
+  }
+}
+
+bool RoundRobin::PickLocked(PickState* pick, grpc_error** error) {
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_INFO, "[RR %p] Trying to pick (shutdown: %d)", this, shutdown_);
+  }
+  GPR_ASSERT(!shutdown_);
+  if (subchannel_list_ != nullptr) {
+    if (DoPickLocked(pick)) return true;
+  }
+  if (pick->on_complete == nullptr) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "No pick result available but synchronous result required.");
+    return true;
+  }
+  /* no pick currently available. Save for later in list of pending picks */
+  pick->next = pending_picks_;
+  pending_picks_ = pick;
+  if (!started_picking_) {
+    StartPickingLocked();
+  }
+  return false;
+}
+
+void RoundRobin::FillChildRefsForChannelz(
+    channelz::ChildRefsList* child_subchannels_to_fill,
+    channelz::ChildRefsList* ignored) {
+  MutexLock lock(&child_refs_mu_);
+  for (size_t i = 0; i < child_subchannels_.size(); ++i) {
+    // TODO(ncteisen): implement a de dup loop that is not O(n^2). Might
+    // have to implement lightweight set. For now, we don't care about
+    // performance when channelz requests are made.
+    bool found = false;
+    for (size_t j = 0; j < child_subchannels_to_fill->size(); ++j) {
+      if ((*child_subchannels_to_fill)[j] == child_subchannels_[i]) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      child_subchannels_to_fill->push_back(child_subchannels_[i]);
+    }
+  }
+}
+
+void RoundRobin::UpdateChildRefsLocked() {
+  channelz::ChildRefsList cs;
+  if (subchannel_list_ != nullptr) {
+    subchannel_list_->PopulateChildRefsList(&cs);
+  }
+  if (latest_pending_subchannel_list_ != nullptr) {
+    latest_pending_subchannel_list_->PopulateChildRefsList(&cs);
+  }
+  // atomically update the data that channelz will actually be looking at.
+  MutexLock lock(&child_refs_mu_);
+  child_subchannels_ = std::move(cs);
+}
+
+void RoundRobin::RoundRobinSubchannelList::StartWatchingLocked() {
+  if (num_subchannels() == 0) return;
+  // Check current state of each subchannel synchronously, since any
+  // subchannel already used by some other channel may have a non-IDLE
+  // state.
+  for (size_t i = 0; i < num_subchannels(); ++i) {
+    grpc_error* error = GRPC_ERROR_NONE;
+    grpc_connectivity_state state =
+        subchannel(i)->CheckConnectivityStateLocked(&error);
+    if (state != GRPC_CHANNEL_IDLE) {
+      subchannel(i)->UpdateConnectivityStateLocked(state, error);
+    }
+  }
+  // Now set the LB policy's state based on the subchannels' states.
+  UpdateRoundRobinStateFromSubchannelStateCountsLocked();
+  // Start connectivity watch for each subchannel.
+  for (size_t i = 0; i < num_subchannels(); i++) {
+    if (subchannel(i)->subchannel() != nullptr) {
+      subchannel(i)->StartConnectivityWatchLocked();
+    }
+  }
+}
+
+void RoundRobin::RoundRobinSubchannelList::UpdateStateCountersLocked(
+    grpc_connectivity_state old_state, grpc_connectivity_state new_state,
+    grpc_error* transient_failure_error) {
+  GPR_ASSERT(old_state != GRPC_CHANNEL_SHUTDOWN);
+  GPR_ASSERT(new_state != GRPC_CHANNEL_SHUTDOWN);
+  if (old_state == GRPC_CHANNEL_READY) {
+    GPR_ASSERT(num_ready_ > 0);
+    --num_ready_;
+  } else if (old_state == GRPC_CHANNEL_CONNECTING) {
+    GPR_ASSERT(num_connecting_ > 0);
+    --num_connecting_;
+  } else if (old_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+    GPR_ASSERT(num_transient_failure_ > 0);
+    --num_transient_failure_;
+  }
+  if (new_state == GRPC_CHANNEL_READY) {
+    ++num_ready_;
+  } else if (new_state == GRPC_CHANNEL_CONNECTING) {
+    ++num_connecting_;
+  } else if (new_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+    ++num_transient_failure_;
+  }
+  GRPC_ERROR_UNREF(last_transient_failure_error_);
+  last_transient_failure_error_ = transient_failure_error;
+}
+
+// Sets the RR policy's connectivity state based on the current
+// subchannel list.
+void RoundRobin::RoundRobinSubchannelList::
+    MaybeUpdateRoundRobinConnectivityStateLocked() {
+  RoundRobin* p = static_cast<RoundRobin*>(policy());
+  // Only set connectivity state if this is the current subchannel list.
+  if (p->subchannel_list_.get() != this) return;
+  /* In priority order. The first rule to match terminates the search (ie, if we
+   * are on rule n, all previous rules were unfulfilled).
+   *
+   * 1) RULE: ANY subchannel is READY => policy is READY.
+   *    CHECK: subchannel_list->num_ready > 0.
+   *
+   * 2) RULE: ANY subchannel is CONNECTING => policy is CONNECTING.
+   *    CHECK: sd->curr_connectivity_state == CONNECTING.
+   *
+   * 3) RULE: ALL subchannels are TRANSIENT_FAILURE => policy is
+   *                                                   TRANSIENT_FAILURE.
+   *    CHECK: subchannel_list->num_transient_failures ==
+   *           subchannel_list->num_subchannels.
+   */
+  if (num_ready_ > 0) {
+    /* 1) READY */
+    grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_READY,
+                                GRPC_ERROR_NONE, "rr_ready");
+  } else if (num_connecting_ > 0) {
+    /* 2) CONNECTING */
+    grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_CONNECTING,
+                                GRPC_ERROR_NONE, "rr_connecting");
+  } else if (num_transient_failure_ == num_subchannels()) {
+    /* 3) TRANSIENT_FAILURE */
+    grpc_connectivity_state_set(&p->state_tracker_,
+                                GRPC_CHANNEL_TRANSIENT_FAILURE,
+                                GRPC_ERROR_REF(last_transient_failure_error_),
+                                "rr_exhausted_subchannels");
+  }
+}
+
+void RoundRobin::RoundRobinSubchannelList::
+    UpdateRoundRobinStateFromSubchannelStateCountsLocked() {
+  RoundRobin* p = static_cast<RoundRobin*>(policy());
+  AutoChildRefsUpdater guard(p);
+  if (num_ready_ > 0) {
+    if (p->subchannel_list_.get() != this) {
+      // Promote this list to p->subchannel_list_.
+      // This list must be p->latest_pending_subchannel_list_, because
+      // any previous update would have been shut down already and
+      // therefore we would not be receiving a notification for them.
+      GPR_ASSERT(p->latest_pending_subchannel_list_.get() == this);
+      GPR_ASSERT(!shutting_down());
+      if (grpc_lb_round_robin_trace.enabled()) {
+        const size_t old_num_subchannels =
+            p->subchannel_list_ != nullptr
+                ? p->subchannel_list_->num_subchannels()
+                : 0;
+        gpr_log(GPR_INFO,
+                "[RR %p] phasing out subchannel list %p (size %" PRIuPTR
+                ") in favor of %p (size %" PRIuPTR ")",
+                p, p->subchannel_list_.get(), old_num_subchannels, this,
+                num_subchannels());
+      }
+      p->subchannel_list_ = std::move(p->latest_pending_subchannel_list_);
+    }
+    // Drain pending picks.
+    p->DrainPendingPicksLocked();
+  }
+  // Update the RR policy's connectivity state if needed.
+  MaybeUpdateRoundRobinConnectivityStateLocked();
+}
+
+void RoundRobin::RoundRobinSubchannelData::UpdateConnectivityStateLocked(
+    grpc_connectivity_state connectivity_state, grpc_error* error) {
+  RoundRobin* p = static_cast<RoundRobin*>(subchannel_list()->policy());
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(
+        GPR_INFO,
+        "[RR %p] connectivity changed for subchannel %p, subchannel_list %p "
+        "(index %" PRIuPTR " of %" PRIuPTR "): prev_state=%s new_state=%s",
+        p, subchannel(), subchannel_list(), Index(),
+        subchannel_list()->num_subchannels(),
+        grpc_connectivity_state_name(last_connectivity_state_),
+        grpc_connectivity_state_name(connectivity_state));
+  }
+  subchannel_list()->UpdateStateCountersLocked(last_connectivity_state_,
+                                               connectivity_state, error);
+  last_connectivity_state_ = connectivity_state;
+}
+
+void RoundRobin::RoundRobinSubchannelData::ProcessConnectivityChangeLocked(
+    grpc_connectivity_state connectivity_state, grpc_error* error) {
+  RoundRobin* p = static_cast<RoundRobin*>(subchannel_list()->policy());
+  GPR_ASSERT(subchannel() != nullptr);
+  // If the new state is TRANSIENT_FAILURE, re-resolve.
+  // Only do this if we've started watching, not at startup time.
+  // Otherwise, if the subchannel was already in state TRANSIENT_FAILURE
+  // when the subchannel list was created, we'd wind up in a constant
+  // loop of re-resolution.
+  if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+    if (grpc_lb_round_robin_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[RR %p] Subchannel %p has gone into TRANSIENT_FAILURE. "
+              "Requesting re-resolution",
+              p, subchannel());
+    }
+    p->TryReresolutionLocked(&grpc_lb_round_robin_trace, GRPC_ERROR_NONE);
+  }
+  // Update state counters.
+  UpdateConnectivityStateLocked(connectivity_state, error);
+  // Update overall state and renew notification.
+  subchannel_list()->UpdateRoundRobinStateFromSubchannelStateCountsLocked();
+  RenewConnectivityWatchLocked();
+}
+
+/** Returns the index into p->subchannel_list->subchannels of the next
+ * subchannel in READY state, or p->subchannel_list->num_subchannels if no
+ * subchannel is READY.
+ *
+ * Note that this function does *not* update p->last_ready_subchannel_index.
+ * The caller must do that if it returns a pick. */
+size_t
+RoundRobin::RoundRobinSubchannelList::GetNextReadySubchannelIndexLocked() {
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[RR %p] getting next ready subchannel (out of %" PRIuPTR
+            "), last_ready_index=%" PRIuPTR,
+            policy(), num_subchannels(), last_ready_index_);
+  }
+  for (size_t i = 0; i < num_subchannels(); ++i) {
+    const size_t index = (i + last_ready_index_ + 1) % num_subchannels();
+    if (grpc_lb_round_robin_trace.enabled()) {
+      gpr_log(
+          GPR_INFO,
+          "[RR %p] checking subchannel %p, subchannel_list %p, index %" PRIuPTR
+          ": state=%s",
+          policy(), subchannel(index)->subchannel(), this, index,
+          grpc_connectivity_state_name(
+              subchannel(index)->connectivity_state()));
+    }
+    if (subchannel(index)->connectivity_state() == GRPC_CHANNEL_READY) {
+      if (grpc_lb_round_robin_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "[RR %p] found next ready subchannel (%p) at index %" PRIuPTR
+                " of subchannel_list %p",
+                policy(), subchannel(index)->subchannel(), index, this);
+      }
+      return index;
+    }
+  }
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_INFO, "[RR %p] no subchannels in ready state", this);
+  }
+  return num_subchannels();
+}
+
+// Sets last_ready_index_ to last_ready_index.
+void RoundRobin::RoundRobinSubchannelList::UpdateLastReadySubchannelIndexLocked(
+    size_t last_ready_index) {
+  GPR_ASSERT(last_ready_index < num_subchannels());
+  last_ready_index_ = last_ready_index;
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[RR %p] setting last_ready_subchannel_index=%" PRIuPTR
+            " (SC %p, CSC %p)",
+            policy(), last_ready_index,
+            subchannel(last_ready_index)->subchannel(),
+            subchannel(last_ready_index)->connected_subchannel());
+  }
+}
+
+grpc_connectivity_state RoundRobin::CheckConnectivityLocked(
+    grpc_error** error) {
+  return grpc_connectivity_state_get(&state_tracker_, error);
+}
+
+void RoundRobin::NotifyOnStateChangeLocked(grpc_connectivity_state* current,
+                                           grpc_closure* notify) {
+  grpc_connectivity_state_notify_on_state_change(&state_tracker_, current,
+                                                 notify);
+}
+
+void RoundRobin::UpdateLocked(const grpc_channel_args& args,
+                              grpc_json* lb_config) {
+  AutoChildRefsUpdater guard(this);
+  const ServerAddressList* addresses = FindServerAddressListChannelArg(&args);
+  if (addresses == nullptr) {
+    gpr_log(GPR_ERROR, "[RR %p] update provided no addresses; ignoring", this);
+    // If we don't have a current subchannel list, go into TRANSIENT_FAILURE.
+    // Otherwise, keep using the current subchannel list (ignore this update).
+    if (subchannel_list_ == nullptr) {
+      grpc_connectivity_state_set(
+          &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"),
+          "rr_update_missing");
+    }
+    return;
+  }
+  if (grpc_lb_round_robin_trace.enabled()) {
+    gpr_log(GPR_INFO, "[RR %p] received update with %" PRIuPTR " addresses",
+            this, addresses->size());
+  }
+  // Replace latest_pending_subchannel_list_.
+  if (latest_pending_subchannel_list_ != nullptr) {
+    if (grpc_lb_round_robin_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[RR %p] Shutting down previous pending subchannel list %p", this,
+              latest_pending_subchannel_list_.get());
+    }
+  }
+  latest_pending_subchannel_list_ = MakeOrphanable<RoundRobinSubchannelList>(
+      this, &grpc_lb_round_robin_trace, *addresses, combiner(),
+      client_channel_factory(), args);
+  // If we haven't started picking yet or the new list is empty,
+  // immediately promote the new list to the current list.
+  if (!started_picking_ ||
+      latest_pending_subchannel_list_->num_subchannels() == 0) {
+    if (latest_pending_subchannel_list_->num_subchannels() == 0) {
+      grpc_connectivity_state_set(
+          &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"),
+          "rr_update_empty");
+    }
+    subchannel_list_ = std::move(latest_pending_subchannel_list_);
+  } else {
+    // If we've started picking, start watching the new list.
+    latest_pending_subchannel_list_->StartWatchingLocked();
+  }
+}
+
+//
+// factory
+//
+
+class RoundRobinFactory : public LoadBalancingPolicyFactory {
+ public:
+  OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const override {
+    return OrphanablePtr<LoadBalancingPolicy>(New<RoundRobin>(args));
+  }
+
+  const char* name() const override { return kRoundRobin; }
+};
+
+}  // namespace
+
+}  // namespace grpc_core
+
+void grpc_lb_policy_round_robin_init() {
+  grpc_core::LoadBalancingPolicyRegistry::Builder::
+      RegisterLoadBalancingPolicyFactory(
+          grpc_core::UniquePtr<grpc_core::LoadBalancingPolicyFactory>(
+              grpc_core::New<grpc_core::RoundRobinFactory>()));
+}
+
+void grpc_lb_policy_round_robin_shutdown() {}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
new file mode 100644
index 0000000..6f31a64
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
@@ -0,0 +1,593 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H
+
+#include <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/transport/connectivity_state.h"
+
+// Code for maintaining a list of subchannels within an LB policy.
+//
+// To use this, callers must create their own subclasses, like so:
+/*
+
+class MySubchannelList;  // Forward declaration.
+
+class MySubchannelData
+    : public SubchannelData<MySubchannelList, MySubchannelData> {
+ public:
+  void ProcessConnectivityChangeLocked(
+      grpc_connectivity_state connectivity_state, grpc_error* error) override {
+    // ...code to handle connectivity changes...
+  }
+};
+
+class MySubchannelList
+    : public SubchannelList<MySubchannelList, MySubchannelData> {
+};
+
+*/
+// All methods with a Locked() suffix must be called from within the
+// client_channel combiner.
+
+namespace grpc_core {
+
+// Forward declaration.
+template <typename SubchannelListType, typename SubchannelDataType>
+class SubchannelList;
+
+// Stores data for a particular subchannel in a subchannel list.
+// Callers must create a subclass that implements the
+// ProcessConnectivityChangeLocked() method.
+template <typename SubchannelListType, typename SubchannelDataType>
+class SubchannelData {
+ public:
+  // Returns a pointer to the subchannel list containing this object.
+  SubchannelListType* subchannel_list() const {
+    return static_cast<SubchannelListType*>(subchannel_list_);
+  }
+
+  // Returns the index into the subchannel list of this object.
+  size_t Index() const {
+    return static_cast<size_t>(static_cast<const SubchannelDataType*>(this) -
+                               subchannel_list_->subchannel(0));
+  }
+
+  // Returns a pointer to the subchannel.
+  grpc_subchannel* subchannel() const { return subchannel_; }
+
+  // Returns the connected subchannel.  Will be null if the subchannel
+  // is not connected.
+  ConnectedSubchannel* connected_subchannel() const {
+    return connected_subchannel_.get();
+  }
+
+  // Synchronously checks the subchannel's connectivity state.
+  // Must not be called while there is a connectivity notification
+  // pending (i.e., between calling StartConnectivityWatchLocked() or
+  // RenewConnectivityWatchLocked() and the resulting invocation of
+  // ProcessConnectivityChangeLocked()).
+  grpc_connectivity_state CheckConnectivityStateLocked(grpc_error** error) {
+    GPR_ASSERT(!connectivity_notification_pending_);
+    pending_connectivity_state_unsafe_ = grpc_subchannel_check_connectivity(
+        subchannel(), error, subchannel_list_->inhibit_health_checking());
+    UpdateConnectedSubchannelLocked();
+    return pending_connectivity_state_unsafe_;
+  }
+
+  // Resets the connection backoff.
+  // TODO(roth): This method should go away when we move the backoff
+  // code out of the subchannel and into the LB policies.
+  void ResetBackoffLocked();
+
+  // Starts watching the connectivity state of the subchannel.
+  // ProcessConnectivityChangeLocked() will be called when the
+  // connectivity state changes.
+  void StartConnectivityWatchLocked();
+
+  // Renews watching the connectivity state of the subchannel.
+  void RenewConnectivityWatchLocked();
+
+  // Stops watching the connectivity state of the subchannel.
+  void StopConnectivityWatchLocked();
+
+  // Cancels watching the connectivity state of the subchannel.
+  // Must be called only while there is a connectivity notification
+  // pending (i.e., between calling StartConnectivityWatchLocked() or
+  // RenewConnectivityWatchLocked() and the resulting invocation of
+  // ProcessConnectivityChangeLocked()).
+  // From within ProcessConnectivityChangeLocked(), use
+  // StopConnectivityWatchLocked() instead.
+  void CancelConnectivityWatchLocked(const char* reason);
+
+  // Cancels any pending connectivity watch and unrefs the subchannel.
+  void ShutdownLocked();
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  SubchannelData(
+      SubchannelList<SubchannelListType, SubchannelDataType>* subchannel_list,
+      const ServerAddress& address, grpc_subchannel* subchannel,
+      grpc_combiner* combiner);
+
+  virtual ~SubchannelData();
+
+  // After StartConnectivityWatchLocked() or RenewConnectivityWatchLocked()
+  // is called, this method will be invoked when the subchannel's connectivity
+  // state changes.
+  // Implementations must invoke either RenewConnectivityWatchLocked() or
+  // StopConnectivityWatchLocked() before returning.
+  virtual void ProcessConnectivityChangeLocked(
+      grpc_connectivity_state connectivity_state,
+      grpc_error* error) GRPC_ABSTRACT;
+
+  // Unrefs the subchannel.
+  void UnrefSubchannelLocked(const char* reason);
+
+ private:
+  // Updates connected_subchannel_ based on pending_connectivity_state_unsafe_.
+  // Returns true if the connectivity state should be reported.
+  bool UpdateConnectedSubchannelLocked();
+
+  static void OnConnectivityChangedLocked(void* arg, grpc_error* error);
+
+  // Backpointer to owning subchannel list.  Not owned.
+  SubchannelList<SubchannelListType, SubchannelDataType>* subchannel_list_;
+
+  // The subchannel and connected subchannel.
+  grpc_subchannel* subchannel_;
+  RefCountedPtr<ConnectedSubchannel> connected_subchannel_;
+
+  // Notification that connectivity has changed on subchannel.
+  grpc_closure connectivity_changed_closure_;
+  // Is a connectivity notification pending?
+  bool connectivity_notification_pending_ = false;
+  // Connectivity state to be updated by
+  // grpc_subchannel_notify_on_state_change(), not guarded by
+  // the combiner.
+  grpc_connectivity_state pending_connectivity_state_unsafe_;
+};
+
+// A list of subchannels.
+template <typename SubchannelListType, typename SubchannelDataType>
+class SubchannelList : public InternallyRefCounted<SubchannelListType> {
+ public:
+  typedef InlinedVector<SubchannelDataType, 10> SubchannelVector;
+
+  // The number of subchannels in the list.
+  size_t num_subchannels() const { return subchannels_.size(); }
+
+  // The data for the subchannel at a particular index.
+  SubchannelDataType* subchannel(size_t index) { return &subchannels_[index]; }
+
+  // Returns true if the subchannel list is shutting down.
+  bool shutting_down() const { return shutting_down_; }
+
+  // Populates refs_list with the uuids of this SubchannelLists's subchannels.
+  void PopulateChildRefsList(channelz::ChildRefsList* refs_list) {
+    for (size_t i = 0; i < subchannels_.size(); ++i) {
+      if (subchannels_[i].subchannel() != nullptr) {
+        grpc_core::channelz::SubchannelNode* subchannel_node =
+            grpc_subchannel_get_channelz_node(subchannels_[i].subchannel());
+        if (subchannel_node != nullptr) {
+          refs_list->push_back(subchannel_node->uuid());
+        }
+      }
+    }
+  }
+
+  // Accessors.
+  LoadBalancingPolicy* policy() const { return policy_; }
+  TraceFlag* tracer() const { return tracer_; }
+  bool inhibit_health_checking() const { return inhibit_health_checking_; }
+
+  // Resets connection backoff of all subchannels.
+  // TODO(roth): We will probably need to rethink this as part of moving
+  // the backoff code out of subchannels and into LB policies.
+  void ResetBackoffLocked();
+
+  // Note: Caller must ensure that this is invoked inside of the combiner.
+  void Orphan() override {
+    ShutdownLocked();
+    InternallyRefCounted<SubchannelListType>::Unref(DEBUG_LOCATION, "shutdown");
+  }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  SubchannelList(LoadBalancingPolicy* policy, TraceFlag* tracer,
+                 const ServerAddressList& addresses, grpc_combiner* combiner,
+                 grpc_client_channel_factory* client_channel_factory,
+                 const grpc_channel_args& args);
+
+  virtual ~SubchannelList();
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T, typename... Args>
+  friend T* New(Args&&... args);
+
+  // For accessing Ref() and Unref().
+  friend class SubchannelData<SubchannelListType, SubchannelDataType>;
+
+  void ShutdownLocked();
+
+  // Backpointer to owning policy.
+  LoadBalancingPolicy* policy_;
+
+  TraceFlag* tracer_;
+
+  bool inhibit_health_checking_;
+
+  grpc_combiner* combiner_;
+
+  // The list of subchannels.
+  SubchannelVector subchannels_;
+
+  // Is this list shutting down? This may be true due to the shutdown of the
+  // policy itself or because a newer update has arrived while this one hadn't
+  // finished processing.
+  bool shutting_down_ = false;
+};
+
+//
+// implementation -- no user-servicable parts below
+//
+
+//
+// SubchannelData
+//
+
+template <typename SubchannelListType, typename SubchannelDataType>
+SubchannelData<SubchannelListType, SubchannelDataType>::SubchannelData(
+    SubchannelList<SubchannelListType, SubchannelDataType>* subchannel_list,
+    const ServerAddress& address, grpc_subchannel* subchannel,
+    grpc_combiner* combiner)
+    : subchannel_list_(subchannel_list),
+      subchannel_(subchannel),
+      // We assume that the current state is IDLE.  If not, we'll get a
+      // callback telling us that.
+      pending_connectivity_state_unsafe_(GRPC_CHANNEL_IDLE) {
+  GRPC_CLOSURE_INIT(
+      &connectivity_changed_closure_,
+      (&SubchannelData<SubchannelListType,
+                       SubchannelDataType>::OnConnectivityChangedLocked),
+      this, grpc_combiner_scheduler(combiner));
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+SubchannelData<SubchannelListType, SubchannelDataType>::~SubchannelData() {
+  UnrefSubchannelLocked("subchannel_data_destroy");
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelData<SubchannelListType, SubchannelDataType>::
+    UnrefSubchannelLocked(const char* reason) {
+  if (subchannel_ != nullptr) {
+    if (subchannel_list_->tracer()->enabled()) {
+      gpr_log(GPR_INFO,
+              "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
+              " (subchannel %p): unreffing subchannel",
+              subchannel_list_->tracer()->name(), subchannel_list_->policy(),
+              subchannel_list_, Index(), subchannel_list_->num_subchannels(),
+              subchannel_);
+    }
+    GRPC_SUBCHANNEL_UNREF(subchannel_, reason);
+    subchannel_ = nullptr;
+    connected_subchannel_.reset();
+  }
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelData<SubchannelListType,
+                    SubchannelDataType>::ResetBackoffLocked() {
+  if (subchannel_ != nullptr) {
+    grpc_subchannel_reset_backoff(subchannel_);
+  }
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelData<SubchannelListType,
+                    SubchannelDataType>::StartConnectivityWatchLocked() {
+  if (subchannel_list_->tracer()->enabled()) {
+    gpr_log(GPR_INFO,
+            "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
+            " (subchannel %p): starting watch: requesting connectivity change "
+            "notification (from %s)",
+            subchannel_list_->tracer()->name(), subchannel_list_->policy(),
+            subchannel_list_, Index(), subchannel_list_->num_subchannels(),
+            subchannel_,
+            grpc_connectivity_state_name(pending_connectivity_state_unsafe_));
+  }
+  GPR_ASSERT(!connectivity_notification_pending_);
+  connectivity_notification_pending_ = true;
+  subchannel_list()->Ref(DEBUG_LOCATION, "connectivity_watch").release();
+  grpc_subchannel_notify_on_state_change(
+      subchannel_, subchannel_list_->policy()->interested_parties(),
+      &pending_connectivity_state_unsafe_, &connectivity_changed_closure_,
+      subchannel_list_->inhibit_health_checking());
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelData<SubchannelListType,
+                    SubchannelDataType>::RenewConnectivityWatchLocked() {
+  if (subchannel_list_->tracer()->enabled()) {
+    gpr_log(GPR_INFO,
+            "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
+            " (subchannel %p): renewing watch: requesting connectivity change "
+            "notification (from %s)",
+            subchannel_list_->tracer()->name(), subchannel_list_->policy(),
+            subchannel_list_, Index(), subchannel_list_->num_subchannels(),
+            subchannel_,
+            grpc_connectivity_state_name(pending_connectivity_state_unsafe_));
+  }
+  GPR_ASSERT(connectivity_notification_pending_);
+  grpc_subchannel_notify_on_state_change(
+      subchannel_, subchannel_list_->policy()->interested_parties(),
+      &pending_connectivity_state_unsafe_, &connectivity_changed_closure_,
+      subchannel_list_->inhibit_health_checking());
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelData<SubchannelListType,
+                    SubchannelDataType>::StopConnectivityWatchLocked() {
+  if (subchannel_list_->tracer()->enabled()) {
+    gpr_log(GPR_INFO,
+            "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
+            " (subchannel %p): stopping connectivity watch",
+            subchannel_list_->tracer()->name(), subchannel_list_->policy(),
+            subchannel_list_, Index(), subchannel_list_->num_subchannels(),
+            subchannel_);
+  }
+  GPR_ASSERT(connectivity_notification_pending_);
+  connectivity_notification_pending_ = false;
+  subchannel_list()->Unref(DEBUG_LOCATION, "connectivity_watch");
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelData<SubchannelListType, SubchannelDataType>::
+    CancelConnectivityWatchLocked(const char* reason) {
+  if (subchannel_list_->tracer()->enabled()) {
+    gpr_log(GPR_INFO,
+            "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
+            " (subchannel %p): canceling connectivity watch (%s)",
+            subchannel_list_->tracer()->name(), subchannel_list_->policy(),
+            subchannel_list_, Index(), subchannel_list_->num_subchannels(),
+            subchannel_, reason);
+  }
+  GPR_ASSERT(connectivity_notification_pending_);
+  grpc_subchannel_notify_on_state_change(
+      subchannel_, nullptr, nullptr, &connectivity_changed_closure_,
+      subchannel_list_->inhibit_health_checking());
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+bool SubchannelData<SubchannelListType,
+                    SubchannelDataType>::UpdateConnectedSubchannelLocked() {
+  // If the subchannel is READY, take a ref to the connected subchannel.
+  if (pending_connectivity_state_unsafe_ == GRPC_CHANNEL_READY) {
+    connected_subchannel_ =
+        grpc_subchannel_get_connected_subchannel(subchannel_);
+    // If the subchannel became disconnected between the time that READY
+    // was reported and the time we got here (e.g., between when a
+    // notification callback is scheduled and when it was actually run in
+    // the combiner), then the connected subchannel may have disappeared out
+    // from under us.  In that case, we don't actually want to consider the
+    // subchannel to be in state READY.  Instead, we use IDLE as the
+    // basis for any future connectivity watch; this is the one state that
+    // the subchannel will never transition back into, so this ensures
+    // that we will get a notification for the next state, even if that state
+    // is READY again (e.g., if the subchannel has transitioned back to
+    // READY before the next watch gets requested).
+    if (connected_subchannel_ == nullptr) {
+      if (subchannel_list_->tracer()->enabled()) {
+        gpr_log(GPR_INFO,
+                "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
+                " (subchannel %p): state is READY but connected subchannel is "
+                "null; moving to state IDLE",
+                subchannel_list_->tracer()->name(), subchannel_list_->policy(),
+                subchannel_list_, Index(), subchannel_list_->num_subchannels(),
+                subchannel_);
+      }
+      pending_connectivity_state_unsafe_ = GRPC_CHANNEL_IDLE;
+      return false;
+    }
+  } else {
+    // For any state other than READY, unref the connected subchannel.
+    connected_subchannel_.reset();
+  }
+  return true;
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelData<SubchannelListType, SubchannelDataType>::
+    OnConnectivityChangedLocked(void* arg, grpc_error* error) {
+  SubchannelData* sd = static_cast<SubchannelData*>(arg);
+  if (sd->subchannel_list_->tracer()->enabled()) {
+    gpr_log(
+        GPR_INFO,
+        "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
+        " (subchannel %p): connectivity changed: state=%s, error=%s, "
+        "shutting_down=%d",
+        sd->subchannel_list_->tracer()->name(), sd->subchannel_list_->policy(),
+        sd->subchannel_list_, sd->Index(),
+        sd->subchannel_list_->num_subchannels(), sd->subchannel_,
+        grpc_connectivity_state_name(sd->pending_connectivity_state_unsafe_),
+        grpc_error_string(error), sd->subchannel_list_->shutting_down());
+  }
+  // If shutting down, unref subchannel and stop watching.
+  if (sd->subchannel_list_->shutting_down() || error == GRPC_ERROR_CANCELLED) {
+    sd->UnrefSubchannelLocked("connectivity_shutdown");
+    sd->StopConnectivityWatchLocked();
+    return;
+  }
+  // Get or release ref to connected subchannel.
+  if (!sd->UpdateConnectedSubchannelLocked()) {
+    // We don't want to report this connectivity state, so renew the watch.
+    sd->RenewConnectivityWatchLocked();
+    return;
+  }
+  // Call the subclass's ProcessConnectivityChangeLocked() method.
+  sd->ProcessConnectivityChangeLocked(sd->pending_connectivity_state_unsafe_,
+                                      GRPC_ERROR_REF(error));
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelData<SubchannelListType, SubchannelDataType>::ShutdownLocked() {
+  // If there's a pending notification for this subchannel, cancel it;
+  // the callback is responsible for unreffing the subchannel.
+  // Otherwise, unref the subchannel directly.
+  if (connectivity_notification_pending_) {
+    CancelConnectivityWatchLocked("shutdown");
+  } else if (subchannel_ != nullptr) {
+    UnrefSubchannelLocked("shutdown");
+  }
+}
+
+//
+// SubchannelList
+//
+
+template <typename SubchannelListType, typename SubchannelDataType>
+SubchannelList<SubchannelListType, SubchannelDataType>::SubchannelList(
+    LoadBalancingPolicy* policy, TraceFlag* tracer,
+    const ServerAddressList& addresses, grpc_combiner* combiner,
+    grpc_client_channel_factory* client_channel_factory,
+    const grpc_channel_args& args)
+    : InternallyRefCounted<SubchannelListType>(tracer),
+      policy_(policy),
+      tracer_(tracer),
+      combiner_(GRPC_COMBINER_REF(combiner, "subchannel_list")) {
+  if (tracer_->enabled()) {
+    gpr_log(GPR_INFO,
+            "[%s %p] Creating subchannel list %p for %" PRIuPTR " subchannels",
+            tracer_->name(), policy, this, addresses.size());
+  }
+  subchannels_.reserve(addresses.size());
+  // We need to remove the LB addresses in order to be able to compare the
+  // subchannel keys of subchannels from a different batch of addresses.
+  // We also remove the inhibit-health-checking arg, since we are
+  // handling that here.
+  inhibit_health_checking_ = grpc_channel_arg_get_bool(
+      grpc_channel_args_find(&args, GRPC_ARG_INHIBIT_HEALTH_CHECKING), false);
+  static const char* keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS,
+                                         GRPC_ARG_SERVER_ADDRESS_LIST,
+                                         GRPC_ARG_INHIBIT_HEALTH_CHECKING};
+  // Create a subchannel for each address.
+  grpc_subchannel_args sc_args;
+  for (size_t i = 0; i < addresses.size(); i++) {
+    // If there were any balancer addresses, we would have chosen grpclb
+    // policy, which does not use a SubchannelList.
+    GPR_ASSERT(!addresses[i].IsBalancer());
+    memset(&sc_args, 0, sizeof(grpc_subchannel_args));
+    InlinedVector<grpc_arg, 4> args_to_add;
+    args_to_add.emplace_back(
+        grpc_create_subchannel_address_arg(&addresses[i].address()));
+    if (addresses[i].args() != nullptr) {
+      for (size_t j = 0; j < addresses[i].args()->num_args; ++j) {
+        args_to_add.emplace_back(addresses[i].args()->args[j]);
+      }
+    }
+    grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove(
+        &args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove),
+        args_to_add.data(), args_to_add.size());
+    gpr_free(args_to_add[0].value.string);
+    sc_args.args = new_args;
+    grpc_subchannel* subchannel = grpc_client_channel_factory_create_subchannel(
+        client_channel_factory, &sc_args);
+    grpc_channel_args_destroy(new_args);
+    if (subchannel == nullptr) {
+      // Subchannel could not be created.
+      if (tracer_->enabled()) {
+        char* address_uri = grpc_sockaddr_to_uri(&addresses[i].address());
+        gpr_log(GPR_INFO,
+                "[%s %p] could not create subchannel for address uri %s, "
+                "ignoring",
+                tracer_->name(), policy_, address_uri);
+        gpr_free(address_uri);
+      }
+      continue;
+    }
+    if (tracer_->enabled()) {
+      char* address_uri = grpc_sockaddr_to_uri(&addresses[i].address());
+      gpr_log(GPR_INFO,
+              "[%s %p] subchannel list %p index %" PRIuPTR
+              ": Created subchannel %p for address uri %s",
+              tracer_->name(), policy_, this, subchannels_.size(), subchannel,
+              address_uri);
+      gpr_free(address_uri);
+    }
+    subchannels_.emplace_back(this, addresses[i], subchannel, combiner);
+  }
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+SubchannelList<SubchannelListType, SubchannelDataType>::~SubchannelList() {
+  if (tracer_->enabled()) {
+    gpr_log(GPR_INFO, "[%s %p] Destroying subchannel_list %p", tracer_->name(),
+            policy_, this);
+  }
+  GRPC_COMBINER_UNREF(combiner_, "subchannel_list");
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelList<SubchannelListType, SubchannelDataType>::ShutdownLocked() {
+  if (tracer_->enabled()) {
+    gpr_log(GPR_INFO, "[%s %p] Shutting down subchannel_list %p",
+            tracer_->name(), policy_, this);
+  }
+  GPR_ASSERT(!shutting_down_);
+  shutting_down_ = true;
+  for (size_t i = 0; i < subchannels_.size(); i++) {
+    SubchannelDataType* sd = &subchannels_[i];
+    sd->ShutdownLocked();
+  }
+}
+
+template <typename SubchannelListType, typename SubchannelDataType>
+void SubchannelList<SubchannelListType,
+                    SubchannelDataType>::ResetBackoffLocked() {
+  for (size_t i = 0; i < subchannels_.size(); i++) {
+    SubchannelDataType* sd = &subchannels_[i];
+    sd->ResetBackoffLocked();
+  }
+}
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
new file mode 100644
index 0000000..8787f5b
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
@@ -0,0 +1,1676 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/// Implementation of the gRPC LB policy.
+///
+/// This policy takes as input a list of resolved addresses, which must
+/// include at least one balancer address.
+///
+/// An internal channel (\a lb_channel_) is created for the addresses
+/// from that are balancers.  This channel behaves just like a regular
+/// channel that uses pick_first to select from the list of balancer
+/// addresses.
+///
+/// The first time the xDS policy gets a request for a pick or to exit the idle
+/// state, \a StartPickingLocked() is called. This method is responsible for
+/// instantiating the internal *streaming* call to the LB server (whichever
+/// address pick_first chose). The call will be complete when either the
+/// balancer sends status or when we cancel the call (e.g., because we are
+/// shutting down). In needed, we retry the call. If we received at least one
+/// valid message from the server, a new call attempt will be made immediately;
+/// otherwise, we apply back-off delays between attempts.
+///
+/// We maintain an internal child policy (round_robin) instance for distributing
+/// requests across backends.  Whenever we receive a new serverlist from
+/// the balancer, we update the child policy with the new list of
+/// addresses.
+///
+/// Once a child policy instance is in place (and getting updated as
+/// described), calls for a pick, or a cancellation will be serviced right away
+/// by forwarding them to the child policy instance. Any time there's no child
+/// policy available (i.e., right after the creation of the xDS policy), pick
+/// requests are added to a list of pending picks to be flushed and serviced
+/// when the child policy instance becomes available.
+///
+/// \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the
+/// high level design and details.
+
+// With the addition of a libuv endpoint, sockaddr.h now includes uv.h when
+// using that endpoint. Because of various transitive includes in uv.h,
+// including windows.h on Windows, uv.h must be included before other system
+// headers. Therefore, sockaddr.h must always be included first.
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/socket_utils.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/byte_buffer_reader.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/client_channel_factory.h"
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h"
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h"
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h"
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h"
+#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/ext/filters/client_channel/subchannel_index.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_hash_table.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#define GRPC_XDS_INITIAL_CONNECT_BACKOFF_SECONDS 1
+#define GRPC_XDS_RECONNECT_BACKOFF_MULTIPLIER 1.6
+#define GRPC_XDS_RECONNECT_MAX_BACKOFF_SECONDS 120
+#define GRPC_XDS_RECONNECT_JITTER 0.2
+#define GRPC_XDS_DEFAULT_FALLBACK_TIMEOUT_MS 10000
+
+namespace grpc_core {
+
+TraceFlag grpc_lb_xds_trace(false, "xds");
+
+namespace {
+
+constexpr char kXds[] = "xds_experimental";
+
+class XdsLb : public LoadBalancingPolicy {
+ public:
+  explicit XdsLb(const Args& args);
+
+  const char* name() const override { return kXds; }
+
+  void UpdateLocked(const grpc_channel_args& args,
+                    grpc_json* lb_config) override;
+  bool PickLocked(PickState* pick, grpc_error** error) override;
+  void CancelPickLocked(PickState* pick, grpc_error* error) override;
+  void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                 uint32_t initial_metadata_flags_eq,
+                                 grpc_error* error) override;
+  void NotifyOnStateChangeLocked(grpc_connectivity_state* state,
+                                 grpc_closure* closure) override;
+  grpc_connectivity_state CheckConnectivityLocked(
+      grpc_error** connectivity_error) override;
+  void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override;
+  void ExitIdleLocked() override;
+  void ResetBackoffLocked() override;
+  void FillChildRefsForChannelz(
+      channelz::ChildRefsList* child_subchannels,
+      channelz::ChildRefsList* child_channels) override;
+
+ private:
+  /// Linked list of pending pick requests. It stores all information needed to
+  /// eventually call pick() on them. They mainly stay pending waiting for the
+  /// child policy to be created.
+  ///
+  /// Note that when a pick is sent to the child policy, we inject our own
+  /// on_complete callback, so that we can intercept the result before
+  /// invoking the original on_complete callback.  This allows us to set the
+  /// LB token metadata and add client_stats to the call context.
+  /// See \a pending_pick_complete() for details.
+  struct PendingPick {
+    // The xds lb instance that created the wrapping. This instance is not
+    // owned; reference counts are untouched. It's used only for logging
+    // purposes.
+    XdsLb* xdslb_policy;
+    // The original pick.
+    PickState* pick;
+    // Our on_complete closure and the original one.
+    grpc_closure on_complete;
+    grpc_closure* original_on_complete;
+    // Stats for client-side load reporting.
+    RefCountedPtr<XdsLbClientStats> client_stats;
+    // Next pending pick.
+    PendingPick* next = nullptr;
+  };
+
+  /// Contains a call to the LB server and all the data related to the call.
+  class BalancerCallState : public InternallyRefCounted<BalancerCallState> {
+   public:
+    explicit BalancerCallState(
+        RefCountedPtr<LoadBalancingPolicy> parent_xdslb_policy);
+
+    // It's the caller's responsibility to ensure that Orphan() is called from
+    // inside the combiner.
+    void Orphan() override;
+
+    void StartQuery();
+
+    XdsLbClientStats* client_stats() const { return client_stats_.get(); }
+
+    bool seen_initial_response() const { return seen_initial_response_; }
+
+   private:
+    // So Delete() can access our private dtor.
+    template <typename T>
+    friend void grpc_core::Delete(T*);
+
+    ~BalancerCallState();
+
+    XdsLb* xdslb_policy() const {
+      return static_cast<XdsLb*>(xdslb_policy_.get());
+    }
+
+    void ScheduleNextClientLoadReportLocked();
+    void SendClientLoadReportLocked();
+
+    static bool LoadReportCountersAreZero(xds_grpclb_request* request);
+
+    static void MaybeSendClientLoadReportLocked(void* arg, grpc_error* error);
+    static void OnInitialRequestSentLocked(void* arg, grpc_error* error);
+    static void OnBalancerMessageReceivedLocked(void* arg, grpc_error* error);
+    static void OnBalancerStatusReceivedLocked(void* arg, grpc_error* error);
+
+    // The owning LB policy.
+    RefCountedPtr<LoadBalancingPolicy> xdslb_policy_;
+
+    // The streaming call to the LB server. Always non-NULL.
+    grpc_call* lb_call_ = nullptr;
+
+    // recv_initial_metadata
+    grpc_metadata_array lb_initial_metadata_recv_;
+
+    // send_message
+    grpc_byte_buffer* send_message_payload_ = nullptr;
+    grpc_closure lb_on_initial_request_sent_;
+
+    // recv_message
+    grpc_byte_buffer* recv_message_payload_ = nullptr;
+    grpc_closure lb_on_balancer_message_received_;
+    bool seen_initial_response_ = false;
+
+    // recv_trailing_metadata
+    grpc_closure lb_on_balancer_status_received_;
+    grpc_metadata_array lb_trailing_metadata_recv_;
+    grpc_status_code lb_call_status_;
+    grpc_slice lb_call_status_details_;
+
+    // The stats for client-side load reporting associated with this LB call.
+    // Created after the first serverlist is received.
+    RefCountedPtr<XdsLbClientStats> client_stats_;
+    grpc_millis client_stats_report_interval_ = 0;
+    grpc_timer client_load_report_timer_;
+    bool client_load_report_timer_callback_pending_ = false;
+    bool last_client_load_report_counters_were_zero_ = false;
+    bool client_load_report_is_due_ = false;
+    // The closure used for either the load report timer or the callback for
+    // completion of sending the load report.
+    grpc_closure client_load_report_closure_;
+  };
+
+  ~XdsLb();
+
+  void ShutdownLocked() override;
+
+  // Helper function used in ctor and UpdateLocked().
+  void ProcessChannelArgsLocked(const grpc_channel_args& args);
+
+  // Methods for dealing with the balancer channel and call.
+  void StartPickingLocked();
+  void StartBalancerCallLocked();
+  static void OnFallbackTimerLocked(void* arg, grpc_error* error);
+  void StartBalancerCallRetryTimerLocked();
+  static void OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error);
+  static void OnBalancerChannelConnectivityChangedLocked(void* arg,
+                                                         grpc_error* error);
+
+  // Pending pick methods.
+  static void PendingPickCleanup(PendingPick* pp);
+  PendingPick* PendingPickCreate(PickState* pick);
+  void AddPendingPick(PendingPick* pp);
+  static void OnPendingPickComplete(void* arg, grpc_error* error);
+
+  // Methods for dealing with the child policy.
+  void CreateOrUpdateChildPolicyLocked();
+  grpc_channel_args* CreateChildPolicyArgsLocked();
+  void CreateChildPolicyLocked(const Args& args);
+  bool PickFromChildPolicyLocked(bool force_async, PendingPick* pp,
+                                 grpc_error** error);
+  void UpdateConnectivityStateFromChildPolicyLocked(
+      grpc_error* child_state_error);
+  static void OnChildPolicyConnectivityChangedLocked(void* arg,
+                                                     grpc_error* error);
+  static void OnChildPolicyRequestReresolutionLocked(void* arg,
+                                                     grpc_error* error);
+
+  // Who the client is trying to communicate with.
+  const char* server_name_ = nullptr;
+
+  // Current channel args from the resolver.
+  grpc_channel_args* args_ = nullptr;
+
+  // Internal state.
+  bool started_picking_ = false;
+  bool shutting_down_ = false;
+  grpc_connectivity_state_tracker state_tracker_;
+
+  // The channel for communicating with the LB server.
+  grpc_channel* lb_channel_ = nullptr;
+  // Mutex to protect the channel to the LB server. This is used when
+  // processing a channelz request.
+  gpr_mu lb_channel_mu_;
+  grpc_connectivity_state lb_channel_connectivity_;
+  grpc_closure lb_channel_on_connectivity_changed_;
+  // Are we already watching the LB channel's connectivity?
+  bool watching_lb_channel_ = false;
+  // Response generator to inject address updates into lb_channel_.
+  RefCountedPtr<FakeResolverResponseGenerator> response_generator_;
+
+  // The data associated with the current LB call. It holds a ref to this LB
+  // policy. It's initialized every time we query for backends. It's reset to
+  // NULL whenever the current LB call is no longer needed (e.g., the LB policy
+  // is shutting down, or the LB call has ended). A non-NULL lb_calld_ always
+  // contains a non-NULL lb_call_.
+  OrphanablePtr<BalancerCallState> lb_calld_;
+  // Timeout in milliseconds for the LB call. 0 means no deadline.
+  int lb_call_timeout_ms_ = 0;
+  // Balancer call retry state.
+  BackOff lb_call_backoff_;
+  bool retry_timer_callback_pending_ = false;
+  grpc_timer lb_call_retry_timer_;
+  grpc_closure lb_on_call_retry_;
+
+  // The deserialized response from the balancer. May be nullptr until one
+  // such response has arrived.
+  xds_grpclb_serverlist* serverlist_ = nullptr;
+
+  // Timeout in milliseconds for before using fallback backend addresses.
+  // 0 means not using fallback.
+  int lb_fallback_timeout_ms_ = 0;
+  // The backend addresses from the resolver.
+  UniquePtr<ServerAddressList> fallback_backend_addresses_;
+  // Fallback timer.
+  bool fallback_timer_callback_pending_ = false;
+  grpc_timer lb_fallback_timer_;
+  grpc_closure lb_on_fallback_;
+
+  // Pending picks that are waiting on the xDS policy's connectivity.
+  PendingPick* pending_picks_ = nullptr;
+
+  // The policy to use for the backends.
+  OrphanablePtr<LoadBalancingPolicy> child_policy_;
+  grpc_connectivity_state child_connectivity_state_;
+  grpc_closure on_child_connectivity_changed_;
+  grpc_closure on_child_request_reresolution_;
+};
+
+//
+// serverlist parsing code
+//
+
+// Returns the backend addresses extracted from the given addresses.
+UniquePtr<ServerAddressList> ExtractBackendAddresses(
+    const ServerAddressList& addresses) {
+  auto backend_addresses = MakeUnique<ServerAddressList>();
+  for (size_t i = 0; i < addresses.size(); ++i) {
+    if (!addresses[i].IsBalancer()) {
+      backend_addresses->emplace_back(addresses[i]);
+    }
+  }
+  return backend_addresses;
+}
+
+bool IsServerValid(const xds_grpclb_server* server, size_t idx, bool log) {
+  if (server->drop) return false;
+  const xds_grpclb_ip_address* ip = &server->ip_address;
+  if (GPR_UNLIKELY(server->port >> 16 != 0)) {
+    if (log) {
+      gpr_log(GPR_ERROR,
+              "Invalid port '%d' at index %lu of serverlist. Ignoring.",
+              server->port, (unsigned long)idx);
+    }
+    return false;
+  }
+  if (GPR_UNLIKELY(ip->size != 4 && ip->size != 16)) {
+    if (log) {
+      gpr_log(GPR_ERROR,
+              "Expected IP to be 4 or 16 bytes, got %d at index %lu of "
+              "serverlist. Ignoring",
+              ip->size, (unsigned long)idx);
+    }
+    return false;
+  }
+  return true;
+}
+
+void ParseServer(const xds_grpclb_server* server, grpc_resolved_address* addr) {
+  memset(addr, 0, sizeof(*addr));
+  if (server->drop) return;
+  const uint16_t netorder_port = grpc_htons((uint16_t)server->port);
+  /* the addresses are given in binary format (a in(6)_addr struct) in
+   * server->ip_address.bytes. */
+  const xds_grpclb_ip_address* ip = &server->ip_address;
+  if (ip->size == 4) {
+    addr->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in));
+    grpc_sockaddr_in* addr4 = reinterpret_cast<grpc_sockaddr_in*>(&addr->addr);
+    addr4->sin_family = GRPC_AF_INET;
+    memcpy(&addr4->sin_addr, ip->bytes, ip->size);
+    addr4->sin_port = netorder_port;
+  } else if (ip->size == 16) {
+    addr->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in6));
+    grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)&addr->addr;
+    addr6->sin6_family = GRPC_AF_INET6;
+    memcpy(&addr6->sin6_addr, ip->bytes, ip->size);
+    addr6->sin6_port = netorder_port;
+  }
+}
+
+// Returns addresses extracted from \a serverlist.
+UniquePtr<ServerAddressList> ProcessServerlist(
+    const xds_grpclb_serverlist* serverlist) {
+  auto addresses = MakeUnique<ServerAddressList>();
+  for (size_t i = 0; i < serverlist->num_servers; ++i) {
+    const xds_grpclb_server* server = serverlist->servers[i];
+    if (!IsServerValid(serverlist->servers[i], i, false)) continue;
+    grpc_resolved_address addr;
+    ParseServer(server, &addr);
+    addresses->emplace_back(addr, nullptr);
+  }
+  return addresses;
+}
+
+//
+// XdsLb::BalancerCallState
+//
+
+XdsLb::BalancerCallState::BalancerCallState(
+    RefCountedPtr<LoadBalancingPolicy> parent_xdslb_policy)
+    : InternallyRefCounted<BalancerCallState>(&grpc_lb_xds_trace),
+      xdslb_policy_(std::move(parent_xdslb_policy)) {
+  GPR_ASSERT(xdslb_policy_ != nullptr);
+  GPR_ASSERT(!xdslb_policy()->shutting_down_);
+  // Init the LB call. Note that the LB call will progress every time there's
+  // activity in xdslb_policy_->interested_parties(), which is comprised of
+  // the polling entities from client_channel.
+  GPR_ASSERT(xdslb_policy()->server_name_ != nullptr);
+  GPR_ASSERT(xdslb_policy()->server_name_[0] != '\0');
+  const grpc_millis deadline =
+      xdslb_policy()->lb_call_timeout_ms_ == 0
+          ? GRPC_MILLIS_INF_FUTURE
+          : ExecCtx::Get()->Now() + xdslb_policy()->lb_call_timeout_ms_;
+  lb_call_ = grpc_channel_create_pollset_set_call(
+      xdslb_policy()->lb_channel_, nullptr, GRPC_PROPAGATE_DEFAULTS,
+      xdslb_policy_->interested_parties(),
+      GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD,
+      nullptr, deadline, nullptr);
+  // Init the LB call request payload.
+  xds_grpclb_request* request =
+      xds_grpclb_request_create(xdslb_policy()->server_name_);
+  grpc_slice request_payload_slice = xds_grpclb_request_encode(request);
+  send_message_payload_ =
+      grpc_raw_byte_buffer_create(&request_payload_slice, 1);
+  grpc_slice_unref_internal(request_payload_slice);
+  xds_grpclb_request_destroy(request);
+  // Init other data associated with the LB call.
+  grpc_metadata_array_init(&lb_initial_metadata_recv_);
+  grpc_metadata_array_init(&lb_trailing_metadata_recv_);
+  GRPC_CLOSURE_INIT(&lb_on_initial_request_sent_, OnInitialRequestSentLocked,
+                    this, grpc_combiner_scheduler(xdslb_policy()->combiner()));
+  GRPC_CLOSURE_INIT(&lb_on_balancer_message_received_,
+                    OnBalancerMessageReceivedLocked, this,
+                    grpc_combiner_scheduler(xdslb_policy()->combiner()));
+  GRPC_CLOSURE_INIT(&lb_on_balancer_status_received_,
+                    OnBalancerStatusReceivedLocked, this,
+                    grpc_combiner_scheduler(xdslb_policy()->combiner()));
+}
+
+XdsLb::BalancerCallState::~BalancerCallState() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  grpc_call_unref(lb_call_);
+  grpc_metadata_array_destroy(&lb_initial_metadata_recv_);
+  grpc_metadata_array_destroy(&lb_trailing_metadata_recv_);
+  grpc_byte_buffer_destroy(send_message_payload_);
+  grpc_byte_buffer_destroy(recv_message_payload_);
+  grpc_slice_unref_internal(lb_call_status_details_);
+}
+
+void XdsLb::BalancerCallState::Orphan() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  // If we are here because xdslb_policy wants to cancel the call,
+  // lb_on_balancer_status_received_ will complete the cancellation and clean
+  // up. Otherwise, we are here because xdslb_policy has to orphan a failed
+  // call, then the following cancellation will be a no-op.
+  grpc_call_cancel(lb_call_, nullptr);
+  if (client_load_report_timer_callback_pending_) {
+    grpc_timer_cancel(&client_load_report_timer_);
+  }
+  // Note that the initial ref is hold by lb_on_balancer_status_received_
+  // instead of the caller of this function. So the corresponding unref happens
+  // in lb_on_balancer_status_received_ instead of here.
+}
+
+void XdsLb::BalancerCallState::StartQuery() {
+  GPR_ASSERT(lb_call_ != nullptr);
+  if (grpc_lb_xds_trace.enabled()) {
+    gpr_log(GPR_INFO, "[xdslb %p] Starting LB call (lb_calld: %p, lb_call: %p)",
+            xdslb_policy_.get(), this, lb_call_);
+  }
+  // Create the ops.
+  grpc_call_error call_error;
+  grpc_op ops[3];
+  memset(ops, 0, sizeof(ops));
+  // Op: send initial metadata.
+  grpc_op* op = ops;
+  op->op = GRPC_OP_SEND_INITIAL_METADATA;
+  op->data.send_initial_metadata.count = 0;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // Op: send request message.
+  GPR_ASSERT(send_message_payload_ != nullptr);
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = send_message_payload_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  auto self = Ref(DEBUG_LOCATION, "on_initial_request_sent");
+  self.release();
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_initial_request_sent_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+  // Op: recv initial metadata.
+  op = ops;
+  op->op = GRPC_OP_RECV_INITIAL_METADATA;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &lb_initial_metadata_recv_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // Op: recv response.
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &recv_message_payload_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  self = Ref(DEBUG_LOCATION, "on_message_received");
+  self.release();
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_message_received_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+  // Op: recv server status.
+  op = ops;
+  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
+  op->data.recv_status_on_client.trailing_metadata =
+      &lb_trailing_metadata_recv_;
+  op->data.recv_status_on_client.status = &lb_call_status_;
+  op->data.recv_status_on_client.status_details = &lb_call_status_details_;
+  op->flags = 0;
+  op->reserved = nullptr;
+  op++;
+  // This callback signals the end of the LB call, so it relies on the initial
+  // ref instead of a new ref. When it's invoked, it's the initial ref that is
+  // unreffed.
+  call_error = grpc_call_start_batch_and_execute(
+      lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_status_received_);
+  GPR_ASSERT(GRPC_CALL_OK == call_error);
+}
+
+void XdsLb::BalancerCallState::ScheduleNextClientLoadReportLocked() {
+  const grpc_millis next_client_load_report_time =
+      ExecCtx::Get()->Now() + client_stats_report_interval_;
+  GRPC_CLOSURE_INIT(&client_load_report_closure_,
+                    MaybeSendClientLoadReportLocked, this,
+                    grpc_combiner_scheduler(xdslb_policy()->combiner()));
+  grpc_timer_init(&client_load_report_timer_, next_client_load_report_time,
+                  &client_load_report_closure_);
+  client_load_report_timer_callback_pending_ = true;
+}
+
+void XdsLb::BalancerCallState::MaybeSendClientLoadReportLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  XdsLb* xdslb_policy = lb_calld->xdslb_policy();
+  lb_calld->client_load_report_timer_callback_pending_ = false;
+  if (error != GRPC_ERROR_NONE || lb_calld != xdslb_policy->lb_calld_.get()) {
+    lb_calld->Unref(DEBUG_LOCATION, "client_load_report");
+    return;
+  }
+  // If we've already sent the initial request, then we can go ahead and send
+  // the load report. Otherwise, we need to wait until the initial request has
+  // been sent to send this (see OnInitialRequestSentLocked()).
+  if (lb_calld->send_message_payload_ == nullptr) {
+    lb_calld->SendClientLoadReportLocked();
+  } else {
+    lb_calld->client_load_report_is_due_ = true;
+  }
+}
+
+bool XdsLb::BalancerCallState::LoadReportCountersAreZero(
+    xds_grpclb_request* request) {
+  XdsLbClientStats::DroppedCallCounts* drop_entries =
+      static_cast<XdsLbClientStats::DroppedCallCounts*>(
+          request->client_stats.calls_finished_with_drop.arg);
+  return request->client_stats.num_calls_started == 0 &&
+         request->client_stats.num_calls_finished == 0 &&
+         request->client_stats.num_calls_finished_with_client_failed_to_send ==
+             0 &&
+         request->client_stats.num_calls_finished_known_received == 0 &&
+         (drop_entries == nullptr || drop_entries->empty());
+}
+
+// TODO(vpowar): Use LRS to send the client Load Report.
+void XdsLb::BalancerCallState::SendClientLoadReportLocked() {
+  // Construct message payload.
+  GPR_ASSERT(send_message_payload_ == nullptr);
+  xds_grpclb_request* request =
+      xds_grpclb_load_report_request_create_locked(client_stats_.get());
+  // Skip client load report if the counters were all zero in the last
+  // report and they are still zero in this one.
+  if (LoadReportCountersAreZero(request)) {
+    if (last_client_load_report_counters_were_zero_) {
+      xds_grpclb_request_destroy(request);
+      ScheduleNextClientLoadReportLocked();
+      return;
+    }
+    last_client_load_report_counters_were_zero_ = true;
+  } else {
+    last_client_load_report_counters_were_zero_ = false;
+  }
+  // TODO(vpowar): Send the report on LRS stream.
+  xds_grpclb_request_destroy(request);
+}
+
+void XdsLb::BalancerCallState::OnInitialRequestSentLocked(void* arg,
+                                                          grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  grpc_byte_buffer_destroy(lb_calld->send_message_payload_);
+  lb_calld->send_message_payload_ = nullptr;
+  // If we attempted to send a client load report before the initial request was
+  // sent (and this lb_calld is still in use), send the load report now.
+  if (lb_calld->client_load_report_is_due_ &&
+      lb_calld == lb_calld->xdslb_policy()->lb_calld_.get()) {
+    lb_calld->SendClientLoadReportLocked();
+    lb_calld->client_load_report_is_due_ = false;
+  }
+  lb_calld->Unref(DEBUG_LOCATION, "on_initial_request_sent");
+}
+
+void XdsLb::BalancerCallState::OnBalancerMessageReceivedLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  XdsLb* xdslb_policy = lb_calld->xdslb_policy();
+  // Empty payload means the LB call was cancelled.
+  if (lb_calld != xdslb_policy->lb_calld_.get() ||
+      lb_calld->recv_message_payload_ == nullptr) {
+    lb_calld->Unref(DEBUG_LOCATION, "on_message_received");
+    return;
+  }
+  grpc_byte_buffer_reader bbr;
+  grpc_byte_buffer_reader_init(&bbr, lb_calld->recv_message_payload_);
+  grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
+  grpc_byte_buffer_reader_destroy(&bbr);
+  grpc_byte_buffer_destroy(lb_calld->recv_message_payload_);
+  lb_calld->recv_message_payload_ = nullptr;
+  xds_grpclb_initial_response* initial_response;
+  xds_grpclb_serverlist* serverlist;
+  if (!lb_calld->seen_initial_response_ &&
+      (initial_response = xds_grpclb_initial_response_parse(response_slice)) !=
+          nullptr) {
+    // Have NOT seen initial response, look for initial response.
+    if (initial_response->has_client_stats_report_interval) {
+      lb_calld->client_stats_report_interval_ = GPR_MAX(
+          GPR_MS_PER_SEC, xds_grpclb_duration_to_millis(
+                              &initial_response->client_stats_report_interval));
+      if (grpc_lb_xds_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "[xdslb %p] Received initial LB response message; "
+                "client load reporting interval = %" PRId64 " milliseconds",
+                xdslb_policy, lb_calld->client_stats_report_interval_);
+      }
+    } else if (grpc_lb_xds_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[xdslb %p] Received initial LB response message; client load "
+              "reporting NOT enabled",
+              xdslb_policy);
+    }
+    xds_grpclb_initial_response_destroy(initial_response);
+    lb_calld->seen_initial_response_ = true;
+  } else if ((serverlist = xds_grpclb_response_parse_serverlist(
+                  response_slice)) != nullptr) {
+    // Have seen initial response, look for serverlist.
+    GPR_ASSERT(lb_calld->lb_call_ != nullptr);
+    if (grpc_lb_xds_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[xdslb %p] Serverlist with %" PRIuPTR " servers received",
+              xdslb_policy, serverlist->num_servers);
+      for (size_t i = 0; i < serverlist->num_servers; ++i) {
+        grpc_resolved_address addr;
+        ParseServer(serverlist->servers[i], &addr);
+        char* ipport;
+        grpc_sockaddr_to_string(&ipport, &addr, false);
+        gpr_log(GPR_INFO, "[xdslb %p] Serverlist[%" PRIuPTR "]: %s",
+                xdslb_policy, i, ipport);
+        gpr_free(ipport);
+      }
+    }
+    /* update serverlist */
+    if (serverlist->num_servers > 0) {
+      // Start sending client load report only after we start using the
+      // serverlist returned from the current LB call.
+      if (lb_calld->client_stats_report_interval_ > 0 &&
+          lb_calld->client_stats_ == nullptr) {
+        lb_calld->client_stats_.reset(New<XdsLbClientStats>());
+        // TODO(roth): We currently track this ref manually.  Once the
+        // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+        // with the callback.
+        auto self = lb_calld->Ref(DEBUG_LOCATION, "client_load_report");
+        self.release();
+        lb_calld->ScheduleNextClientLoadReportLocked();
+      }
+      if (xds_grpclb_serverlist_equals(xdslb_policy->serverlist_, serverlist)) {
+        if (grpc_lb_xds_trace.enabled()) {
+          gpr_log(GPR_INFO,
+                  "[xdslb %p] Incoming server list identical to current, "
+                  "ignoring.",
+                  xdslb_policy);
+        }
+        xds_grpclb_destroy_serverlist(serverlist);
+      } else { /* new serverlist */
+        if (xdslb_policy->serverlist_ != nullptr) {
+          /* dispose of the old serverlist */
+          xds_grpclb_destroy_serverlist(xdslb_policy->serverlist_);
+        } else {
+          /* or dispose of the fallback */
+          xdslb_policy->fallback_backend_addresses_.reset();
+          if (xdslb_policy->fallback_timer_callback_pending_) {
+            grpc_timer_cancel(&xdslb_policy->lb_fallback_timer_);
+          }
+        }
+        // and update the copy in the XdsLb instance. This
+        // serverlist instance will be destroyed either upon the next
+        // update or when the XdsLb instance is destroyed.
+        xdslb_policy->serverlist_ = serverlist;
+        xdslb_policy->CreateOrUpdateChildPolicyLocked();
+      }
+    } else {
+      if (grpc_lb_xds_trace.enabled()) {
+        gpr_log(GPR_INFO, "[xdslb %p] Received empty server list, ignoring.",
+                xdslb_policy);
+      }
+      xds_grpclb_destroy_serverlist(serverlist);
+    }
+  } else {
+    // No valid initial response or serverlist found.
+    char* response_slice_str =
+        grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX);
+    gpr_log(GPR_ERROR,
+            "[xdslb %p] Invalid LB response received: '%s'. Ignoring.",
+            xdslb_policy, response_slice_str);
+    gpr_free(response_slice_str);
+  }
+  grpc_slice_unref_internal(response_slice);
+  if (!xdslb_policy->shutting_down_) {
+    // Keep listening for serverlist updates.
+    grpc_op op;
+    memset(&op, 0, sizeof(op));
+    op.op = GRPC_OP_RECV_MESSAGE;
+    op.data.recv_message.recv_message = &lb_calld->recv_message_payload_;
+    op.flags = 0;
+    op.reserved = nullptr;
+    // Reuse the "OnBalancerMessageReceivedLocked" ref taken in StartQuery().
+    const grpc_call_error call_error = grpc_call_start_batch_and_execute(
+        lb_calld->lb_call_, &op, 1,
+        &lb_calld->lb_on_balancer_message_received_);
+    GPR_ASSERT(GRPC_CALL_OK == call_error);
+  } else {
+    lb_calld->Unref(DEBUG_LOCATION, "on_message_received+xds_shutdown");
+  }
+}
+
+void XdsLb::BalancerCallState::OnBalancerStatusReceivedLocked(
+    void* arg, grpc_error* error) {
+  BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg);
+  XdsLb* xdslb_policy = lb_calld->xdslb_policy();
+  GPR_ASSERT(lb_calld->lb_call_ != nullptr);
+  if (grpc_lb_xds_trace.enabled()) {
+    char* status_details =
+        grpc_slice_to_c_string(lb_calld->lb_call_status_details_);
+    gpr_log(GPR_INFO,
+            "[xdslb %p] Status from LB server received. Status = %d, details "
+            "= '%s', (lb_calld: %p, lb_call: %p), error '%s'",
+            xdslb_policy, lb_calld->lb_call_status_, status_details, lb_calld,
+            lb_calld->lb_call_, grpc_error_string(error));
+    gpr_free(status_details);
+  }
+  xdslb_policy->TryReresolutionLocked(&grpc_lb_xds_trace, GRPC_ERROR_NONE);
+  // If this lb_calld is still in use, this call ended because of a failure so
+  // we want to retry connecting. Otherwise, we have deliberately ended this
+  // call and no further action is required.
+  if (lb_calld == xdslb_policy->lb_calld_.get()) {
+    xdslb_policy->lb_calld_.reset();
+    GPR_ASSERT(!xdslb_policy->shutting_down_);
+    if (lb_calld->seen_initial_response_) {
+      // If we lose connection to the LB server, reset the backoff and restart
+      // the LB call immediately.
+      xdslb_policy->lb_call_backoff_.Reset();
+      xdslb_policy->StartBalancerCallLocked();
+    } else {
+      // If this LB call fails establishing any connection to the LB server,
+      // retry later.
+      xdslb_policy->StartBalancerCallRetryTimerLocked();
+    }
+  }
+  lb_calld->Unref(DEBUG_LOCATION, "lb_call_ended");
+}
+
+//
+// helper code for creating balancer channel
+//
+
+UniquePtr<ServerAddressList> ExtractBalancerAddresses(
+    const ServerAddressList& addresses) {
+  auto balancer_addresses = MakeUnique<ServerAddressList>();
+  for (size_t i = 0; i < addresses.size(); ++i) {
+    if (addresses[i].IsBalancer()) {
+      balancer_addresses->emplace_back(addresses[i]);
+    }
+  }
+  return balancer_addresses;
+}
+
+/* Returns the channel args for the LB channel, used to create a bidirectional
+ * stream for the reception of load balancing updates.
+ *
+ * Inputs:
+ *   - \a addresses: corresponding to the balancers.
+ *   - \a response_generator: in order to propagate updates from the resolver
+ *   above the grpclb policy.
+ *   - \a args: other args inherited from the xds policy. */
+grpc_channel_args* BuildBalancerChannelArgs(
+    const ServerAddressList& addresses,
+    FakeResolverResponseGenerator* response_generator,
+    const grpc_channel_args* args) {
+  UniquePtr<ServerAddressList> balancer_addresses =
+      ExtractBalancerAddresses(addresses);
+  // Channel args to remove.
+  static const char* args_to_remove[] = {
+      // LB policy name, since we want to use the default (pick_first) in
+      // the LB channel.
+      GRPC_ARG_LB_POLICY_NAME,
+      // The channel arg for the server URI, since that will be different for
+      // the LB channel than for the parent channel.  The client channel
+      // factory will re-add this arg with the right value.
+      GRPC_ARG_SERVER_URI,
+      // The resolved addresses, which will be generated by the name resolver
+      // used in the LB channel.  Note that the LB channel will use the fake
+      // resolver, so this won't actually generate a query to DNS (or some
+      // other name service).  However, the addresses returned by the fake
+      // resolver will have is_balancer=false, whereas our own addresses have
+      // is_balancer=true.  We need the LB channel to return addresses with
+      // is_balancer=false so that it does not wind up recursively using the
+      // xds LB policy, as per the special case logic in client_channel.c.
+      GRPC_ARG_SERVER_ADDRESS_LIST,
+      // The fake resolver response generator, because we are replacing it
+      // with the one from the xds policy, used to propagate updates to
+      // the LB channel.
+      GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
+      // The LB channel should use the authority indicated by the target
+      // authority table (see \a grpc_lb_policy_xds_modify_lb_channel_args),
+      // as opposed to the authority from the parent channel.
+      GRPC_ARG_DEFAULT_AUTHORITY,
+      // Just as for \a GRPC_ARG_DEFAULT_AUTHORITY, the LB channel should be
+      // treated as a stand-alone channel and not inherit this argument from the
+      // args of the parent channel.
+      GRPC_SSL_TARGET_NAME_OVERRIDE_ARG,
+  };
+  // Channel args to add.
+  const grpc_arg args_to_add[] = {
+      // New server address list.
+      // Note that we pass these in both when creating the LB channel
+      // and via the fake resolver.  The latter is what actually gets used.
+      CreateServerAddressListChannelArg(balancer_addresses.get()),
+      // The fake resolver response generator, which we use to inject
+      // address updates into the LB channel.
+      grpc_core::FakeResolverResponseGenerator::MakeChannelArg(
+          response_generator),
+      // A channel arg indicating the target is a xds load balancer.
+      grpc_channel_arg_integer_create(
+          const_cast<char*>(GRPC_ARG_ADDRESS_IS_XDS_LOAD_BALANCER), 1),
+      // A channel arg indicating this is an internal channels, aka it is
+      // owned by components in Core, not by the user application.
+      grpc_channel_arg_integer_create(
+          const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), 1),
+  };
+  // Construct channel args.
+  grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove(
+      args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), args_to_add,
+      GPR_ARRAY_SIZE(args_to_add));
+  // Make any necessary modifications for security.
+  return grpc_lb_policy_xds_modify_lb_channel_args(new_args);
+}
+
+//
+// ctor and dtor
+//
+
+// TODO(vishalpowar): Use lb_config in args to configure LB policy.
+XdsLb::XdsLb(const LoadBalancingPolicy::Args& args)
+    : LoadBalancingPolicy(args),
+      response_generator_(MakeRefCounted<FakeResolverResponseGenerator>()),
+      lb_call_backoff_(
+          BackOff::Options()
+              .set_initial_backoff(GRPC_XDS_INITIAL_CONNECT_BACKOFF_SECONDS *
+                                   1000)
+              .set_multiplier(GRPC_XDS_RECONNECT_BACKOFF_MULTIPLIER)
+              .set_jitter(GRPC_XDS_RECONNECT_JITTER)
+              .set_max_backoff(GRPC_XDS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) {
+  // Initialization.
+  gpr_mu_init(&lb_channel_mu_);
+  grpc_subchannel_index_ref();
+  GRPC_CLOSURE_INIT(&lb_channel_on_connectivity_changed_,
+                    &XdsLb::OnBalancerChannelConnectivityChangedLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  GRPC_CLOSURE_INIT(&on_child_connectivity_changed_,
+                    &XdsLb::OnChildPolicyConnectivityChangedLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  GRPC_CLOSURE_INIT(&on_child_request_reresolution_,
+                    &XdsLb::OnChildPolicyRequestReresolutionLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, "xds");
+  // Record server name.
+  const grpc_arg* arg = grpc_channel_args_find(args.args, GRPC_ARG_SERVER_URI);
+  const char* server_uri = grpc_channel_arg_get_string(arg);
+  GPR_ASSERT(server_uri != nullptr);
+  grpc_uri* uri = grpc_uri_parse(server_uri, true);
+  GPR_ASSERT(uri->path[0] != '\0');
+  server_name_ = gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
+  if (grpc_lb_xds_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[xdslb %p] Will use '%s' as the server name for LB request.", this,
+            server_name_);
+  }
+  grpc_uri_destroy(uri);
+  // Record LB call timeout.
+  arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS);
+  lb_call_timeout_ms_ = grpc_channel_arg_get_integer(arg, {0, 0, INT_MAX});
+  // Record fallback timeout.
+  arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS);
+  lb_fallback_timeout_ms_ = grpc_channel_arg_get_integer(
+      arg, {GRPC_XDS_DEFAULT_FALLBACK_TIMEOUT_MS, 0, INT_MAX});
+  // Process channel args.
+  ProcessChannelArgsLocked(*args.args);
+}
+
+XdsLb::~XdsLb() {
+  GPR_ASSERT(pending_picks_ == nullptr);
+  gpr_mu_destroy(&lb_channel_mu_);
+  gpr_free((void*)server_name_);
+  grpc_channel_args_destroy(args_);
+  grpc_connectivity_state_destroy(&state_tracker_);
+  if (serverlist_ != nullptr) {
+    xds_grpclb_destroy_serverlist(serverlist_);
+  }
+  grpc_subchannel_index_unref();
+}
+
+void XdsLb::ShutdownLocked() {
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown");
+  shutting_down_ = true;
+  lb_calld_.reset();
+  if (retry_timer_callback_pending_) {
+    grpc_timer_cancel(&lb_call_retry_timer_);
+  }
+  if (fallback_timer_callback_pending_) {
+    grpc_timer_cancel(&lb_fallback_timer_);
+  }
+  child_policy_.reset();
+  TryReresolutionLocked(&grpc_lb_xds_trace, GRPC_ERROR_CANCELLED);
+  // We destroy the LB channel here instead of in our destructor because
+  // destroying the channel triggers a last callback to
+  // OnBalancerChannelConnectivityChangedLocked(), and we need to be
+  // alive when that callback is invoked.
+  if (lb_channel_ != nullptr) {
+    gpr_mu_lock(&lb_channel_mu_);
+    grpc_channel_destroy(lb_channel_);
+    lb_channel_ = nullptr;
+    gpr_mu_unlock(&lb_channel_mu_);
+  }
+  grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN,
+                              GRPC_ERROR_REF(error), "xds_shutdown");
+  // Clear pending picks.
+  PendingPick* pp;
+  while ((pp = pending_picks_) != nullptr) {
+    pending_picks_ = pp->next;
+    pp->pick->connected_subchannel.reset();
+    // Note: pp is deleted in this callback.
+    GRPC_CLOSURE_SCHED(&pp->on_complete, GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+//
+// public methods
+//
+
+void XdsLb::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) {
+  PendingPick* pp;
+  while ((pp = pending_picks_) != nullptr) {
+    pending_picks_ = pp->next;
+    pp->pick->on_complete = pp->original_on_complete;
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (new_policy->PickLocked(pp->pick, &error)) {
+      // Synchronous return; schedule closure.
+      GRPC_CLOSURE_SCHED(pp->pick->on_complete, error);
+    }
+    Delete(pp);
+  }
+}
+
+// Cancel a specific pending pick.
+//
+// A pick progresses as follows:
+// - If there's a child policy available, it'll be handed over to child policy
+//   (in CreateChildPolicyLocked()). From that point onwards, it'll be the
+//   child policy's responsibility. For cancellations, that implies the pick
+//   needs to be also cancelled by the child policy instance.
+// - Otherwise, without a child policy instance, picks stay pending at this
+//   policy's level (xds), inside the pending_picks_ list. To cancel these,
+//   we invoke the completion closure and set the pick's connected
+//   subchannel to nullptr right here.
+void XdsLb::CancelPickLocked(PickState* pick, grpc_error* error) {
+  PendingPick* pp = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pp != nullptr) {
+    PendingPick* next = pp->next;
+    if (pp->pick == pick) {
+      pick->connected_subchannel.reset();
+      // Note: pp is deleted in this callback.
+      GRPC_CLOSURE_SCHED(&pp->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
+    }
+    pp = next;
+  }
+  if (child_policy_ != nullptr) {
+    child_policy_->CancelPickLocked(pick, GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+// Cancel all pending picks.
+//
+// A pick progresses as follows:
+// - If there's a child policy available, it'll be handed over to child policy
+//   (in CreateChildPolicyLocked()). From that point onwards, it'll be the
+//   child policy's responsibility. For cancellations, that implies the pick
+//   needs to be also cancelled by the child policy instance.
+// - Otherwise, without a child policy instance, picks stay pending at this
+//   policy's level (xds), inside the pending_picks_ list. To cancel these,
+//   we invoke the completion closure and set the pick's connected
+//   subchannel to nullptr right here.
+void XdsLb::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask,
+                                      uint32_t initial_metadata_flags_eq,
+                                      grpc_error* error) {
+  PendingPick* pp = pending_picks_;
+  pending_picks_ = nullptr;
+  while (pp != nullptr) {
+    PendingPick* next = pp->next;
+    if ((*pp->pick->initial_metadata_flags & initial_metadata_flags_mask) ==
+        initial_metadata_flags_eq) {
+      // Note: pp is deleted in this callback.
+      GRPC_CLOSURE_SCHED(&pp->on_complete,
+                         GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Pick Cancelled", &error, 1));
+    } else {
+      pp->next = pending_picks_;
+      pending_picks_ = pp;
+    }
+    pp = next;
+  }
+  if (child_policy_ != nullptr) {
+    child_policy_->CancelMatchingPicksLocked(initial_metadata_flags_mask,
+                                             initial_metadata_flags_eq,
+                                             GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void XdsLb::ExitIdleLocked() {
+  if (!started_picking_) {
+    StartPickingLocked();
+  }
+}
+
+void XdsLb::ResetBackoffLocked() {
+  if (lb_channel_ != nullptr) {
+    grpc_channel_reset_connect_backoff(lb_channel_);
+  }
+  if (child_policy_ != nullptr) {
+    child_policy_->ResetBackoffLocked();
+  }
+}
+
+bool XdsLb::PickLocked(PickState* pick, grpc_error** error) {
+  PendingPick* pp = PendingPickCreate(pick);
+  bool pick_done = false;
+  if (child_policy_ != nullptr) {
+    if (grpc_lb_xds_trace.enabled()) {
+      gpr_log(GPR_INFO, "[xdslb %p] about to PICK from policy %p", this,
+              child_policy_.get());
+    }
+    pick_done = PickFromChildPolicyLocked(false /* force_async */, pp, error);
+  } else {  // child_policy_ == NULL
+    if (pick->on_complete == nullptr) {
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "No pick result available but synchronous result required.");
+      pick_done = true;
+    } else {
+      if (grpc_lb_xds_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "[xdslb %p] No child policy. Adding to xds's pending picks",
+                this);
+      }
+      AddPendingPick(pp);
+      if (!started_picking_) {
+        StartPickingLocked();
+      }
+      pick_done = false;
+    }
+  }
+  return pick_done;
+}
+
+void XdsLb::FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels,
+                                     channelz::ChildRefsList* child_channels) {
+  // delegate to the child_policy_ to fill the children subchannels.
+  child_policy_->FillChildRefsForChannelz(child_subchannels, child_channels);
+  MutexLock lock(&lb_channel_mu_);
+  if (lb_channel_ != nullptr) {
+    grpc_core::channelz::ChannelNode* channel_node =
+        grpc_channel_get_channelz_node(lb_channel_);
+    if (channel_node != nullptr) {
+      child_channels->push_back(channel_node->uuid());
+    }
+  }
+}
+
+grpc_connectivity_state XdsLb::CheckConnectivityLocked(
+    grpc_error** connectivity_error) {
+  return grpc_connectivity_state_get(&state_tracker_, connectivity_error);
+}
+
+void XdsLb::NotifyOnStateChangeLocked(grpc_connectivity_state* current,
+                                      grpc_closure* closure) {
+  grpc_connectivity_state_notify_on_state_change(&state_tracker_, current,
+                                                 closure);
+}
+
+void XdsLb::ProcessChannelArgsLocked(const grpc_channel_args& args) {
+  const ServerAddressList* addresses = FindServerAddressListChannelArg(&args);
+  if (addresses == nullptr) {
+    // Ignore this update.
+    gpr_log(GPR_ERROR,
+            "[xdslb %p] No valid LB addresses channel arg in update, ignoring.",
+            this);
+    return;
+  }
+  // Update fallback address list.
+  fallback_backend_addresses_ = ExtractBackendAddresses(*addresses);
+  // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args,
+  // since we use this to trigger the client_load_reporting filter.
+  static const char* args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME};
+  grpc_arg new_arg = grpc_channel_arg_string_create(
+      (char*)GRPC_ARG_LB_POLICY_NAME, (char*)"xds");
+  grpc_channel_args_destroy(args_);
+  args_ = grpc_channel_args_copy_and_add_and_remove(
+      &args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1);
+  // Construct args for balancer channel.
+  grpc_channel_args* lb_channel_args =
+      BuildBalancerChannelArgs(*addresses, response_generator_.get(), &args);
+  // Create balancer channel if needed.
+  if (lb_channel_ == nullptr) {
+    char* uri_str;
+    gpr_asprintf(&uri_str, "fake:///%s", server_name_);
+    gpr_mu_lock(&lb_channel_mu_);
+    lb_channel_ = grpc_client_channel_factory_create_channel(
+        client_channel_factory(), uri_str,
+        GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, lb_channel_args);
+    gpr_mu_unlock(&lb_channel_mu_);
+    GPR_ASSERT(lb_channel_ != nullptr);
+    gpr_free(uri_str);
+  }
+  // Propagate updates to the LB channel (pick_first) through the fake
+  // resolver.
+  response_generator_->SetResponse(lb_channel_args);
+  grpc_channel_args_destroy(lb_channel_args);
+}
+
+// TODO(vishalpowar): Use lb_config to configure LB policy.
+void XdsLb::UpdateLocked(const grpc_channel_args& args, grpc_json* lb_config) {
+  ProcessChannelArgsLocked(args);
+  // Update the existing child policy.
+  // Note: We have disabled fallback mode in the code, so this child policy must
+  // have been created from a serverlist.
+  // TODO(vpowar): Handle the fallback_address changes when we add support for
+  // fallback in xDS.
+  if (child_policy_ != nullptr) CreateOrUpdateChildPolicyLocked();
+  // Start watching the LB channel connectivity for connection, if not
+  // already doing so.
+  if (!watching_lb_channel_) {
+    lb_channel_connectivity_ = grpc_channel_check_connectivity_state(
+        lb_channel_, true /* try to connect */);
+    grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
+        grpc_channel_get_channel_stack(lb_channel_));
+    GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+    watching_lb_channel_ = true;
+    // TODO(roth): We currently track this ref manually.  Once the
+    // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+    // with the callback.
+    auto self = Ref(DEBUG_LOCATION, "watch_lb_channel_connectivity");
+    self.release();
+    grpc_client_channel_watch_connectivity_state(
+        client_channel_elem,
+        grpc_polling_entity_create_from_pollset_set(interested_parties()),
+        &lb_channel_connectivity_, &lb_channel_on_connectivity_changed_,
+        nullptr);
+  }
+}
+
+//
+// code for balancer channel and call
+//
+
+void XdsLb::StartPickingLocked() {
+  // Start a timer to fall back.
+  if (lb_fallback_timeout_ms_ > 0 && serverlist_ == nullptr &&
+      !fallback_timer_callback_pending_) {
+    grpc_millis deadline = ExecCtx::Get()->Now() + lb_fallback_timeout_ms_;
+    // TODO(roth): We currently track this ref manually.  Once the
+    // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+    // with the callback.
+    auto self = Ref(DEBUG_LOCATION, "on_fallback_timer");
+    self.release();
+    GRPC_CLOSURE_INIT(&lb_on_fallback_, &XdsLb::OnFallbackTimerLocked, this,
+                      grpc_combiner_scheduler(combiner()));
+    fallback_timer_callback_pending_ = true;
+    grpc_timer_init(&lb_fallback_timer_, deadline, &lb_on_fallback_);
+  }
+  started_picking_ = true;
+  StartBalancerCallLocked();
+}
+
+void XdsLb::StartBalancerCallLocked() {
+  GPR_ASSERT(lb_channel_ != nullptr);
+  if (shutting_down_) return;
+  // Init the LB call data.
+  GPR_ASSERT(lb_calld_ == nullptr);
+  lb_calld_ = MakeOrphanable<BalancerCallState>(Ref());
+  if (grpc_lb_xds_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[xdslb %p] Query for backends (lb_channel: %p, lb_calld: %p)",
+            this, lb_channel_, lb_calld_.get());
+  }
+  lb_calld_->StartQuery();
+}
+
+void XdsLb::OnFallbackTimerLocked(void* arg, grpc_error* error) {
+  XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
+  xdslb_policy->fallback_timer_callback_pending_ = false;
+  // If we receive a serverlist after the timer fires but before this callback
+  // actually runs, don't fall back.
+  if (xdslb_policy->serverlist_ == nullptr && !xdslb_policy->shutting_down_ &&
+      error == GRPC_ERROR_NONE) {
+    if (grpc_lb_xds_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "[xdslb %p] Fallback timer fired. Not using fallback backends",
+              xdslb_policy);
+    }
+  }
+  xdslb_policy->Unref(DEBUG_LOCATION, "on_fallback_timer");
+}
+
+void XdsLb::StartBalancerCallRetryTimerLocked() {
+  grpc_millis next_try = lb_call_backoff_.NextAttemptTime();
+  if (grpc_lb_xds_trace.enabled()) {
+    gpr_log(GPR_INFO, "[xdslb %p] Connection to LB server lost...", this);
+    grpc_millis timeout = next_try - ExecCtx::Get()->Now();
+    if (timeout > 0) {
+      gpr_log(GPR_INFO, "[xdslb %p] ... retry_timer_active in %" PRId64 "ms.",
+              this, timeout);
+    } else {
+      gpr_log(GPR_INFO, "[xdslb %p] ... retry_timer_active immediately.", this);
+    }
+  }
+  // TODO(roth): We currently track this ref manually.  Once the
+  // ClosureRef API is ready, we should pass the RefCountedPtr<> along
+  // with the callback.
+  auto self = Ref(DEBUG_LOCATION, "on_balancer_call_retry_timer");
+  self.release();
+  GRPC_CLOSURE_INIT(&lb_on_call_retry_, &XdsLb::OnBalancerCallRetryTimerLocked,
+                    this, grpc_combiner_scheduler(combiner()));
+  retry_timer_callback_pending_ = true;
+  grpc_timer_init(&lb_call_retry_timer_, next_try, &lb_on_call_retry_);
+}
+
+void XdsLb::OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error) {
+  XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
+  xdslb_policy->retry_timer_callback_pending_ = false;
+  if (!xdslb_policy->shutting_down_ && error == GRPC_ERROR_NONE &&
+      xdslb_policy->lb_calld_ == nullptr) {
+    if (grpc_lb_xds_trace.enabled()) {
+      gpr_log(GPR_INFO, "[xdslb %p] Restarting call to LB server",
+              xdslb_policy);
+    }
+    xdslb_policy->StartBalancerCallLocked();
+  }
+  xdslb_policy->Unref(DEBUG_LOCATION, "on_balancer_call_retry_timer");
+}
+
+// Invoked as part of the update process. It continues watching the LB channel
+// until it shuts down or becomes READY. It's invoked even if the LB channel
+// stayed READY throughout the update (for example if the update is identical).
+void XdsLb::OnBalancerChannelConnectivityChangedLocked(void* arg,
+                                                       grpc_error* error) {
+  XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
+  if (xdslb_policy->shutting_down_) goto done;
+  // Re-initialize the lb_call. This should also take care of updating the
+  // child policy. Note that the current child policy, if any, will
+  // stay in effect until an update from the new lb_call is received.
+  switch (xdslb_policy->lb_channel_connectivity_) {
+    case GRPC_CHANNEL_CONNECTING:
+    case GRPC_CHANNEL_TRANSIENT_FAILURE: {
+      // Keep watching the LB channel.
+      grpc_channel_element* client_channel_elem =
+          grpc_channel_stack_last_element(
+              grpc_channel_get_channel_stack(xdslb_policy->lb_channel_));
+      GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
+      grpc_client_channel_watch_connectivity_state(
+          client_channel_elem,
+          grpc_polling_entity_create_from_pollset_set(
+              xdslb_policy->interested_parties()),
+          &xdslb_policy->lb_channel_connectivity_,
+          &xdslb_policy->lb_channel_on_connectivity_changed_, nullptr);
+      break;
+    }
+      // The LB channel may be IDLE because it's shut down before the update.
+      // Restart the LB call to kick the LB channel into gear.
+    case GRPC_CHANNEL_IDLE:
+    case GRPC_CHANNEL_READY:
+      xdslb_policy->lb_calld_.reset();
+      if (xdslb_policy->started_picking_) {
+        if (xdslb_policy->retry_timer_callback_pending_) {
+          grpc_timer_cancel(&xdslb_policy->lb_call_retry_timer_);
+        }
+        xdslb_policy->lb_call_backoff_.Reset();
+        xdslb_policy->StartBalancerCallLocked();
+      }
+      // Fall through.
+    case GRPC_CHANNEL_SHUTDOWN:
+    done:
+      xdslb_policy->watching_lb_channel_ = false;
+      xdslb_policy->Unref(DEBUG_LOCATION,
+                          "watch_lb_channel_connectivity_cb_shutdown");
+  }
+}
+
+//
+// PendingPick
+//
+
+// Destroy function used when embedding client stats in call context.
+void DestroyClientStats(void* arg) {
+  static_cast<XdsLbClientStats*>(arg)->Unref();
+}
+
+void XdsLb::PendingPickCleanup(PendingPick* pp) {
+  // If connected_subchannel is nullptr, no pick has been made by the
+  // child policy (e.g., all addresses failed to connect).
+  if (pp->pick->connected_subchannel != nullptr) {
+    // Pass on client stats via context. Passes ownership of the reference.
+    if (pp->client_stats != nullptr) {
+      pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].value =
+          pp->client_stats.release();
+      pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].destroy =
+          DestroyClientStats;
+    }
+  } else {
+    pp->client_stats.reset();
+  }
+}
+
+/* The \a on_complete closure passed as part of the pick requires keeping a
+ * reference to its associated child policy instance. We wrap this closure in
+ * order to unref the child policy instance upon its invocation */
+void XdsLb::OnPendingPickComplete(void* arg, grpc_error* error) {
+  PendingPick* pp = static_cast<PendingPick*>(arg);
+  PendingPickCleanup(pp);
+  GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_REF(error));
+  Delete(pp);
+}
+
+XdsLb::PendingPick* XdsLb::PendingPickCreate(PickState* pick) {
+  PendingPick* pp = New<PendingPick>();
+  pp->xdslb_policy = this;
+  pp->pick = pick;
+  GRPC_CLOSURE_INIT(&pp->on_complete, &XdsLb::OnPendingPickComplete, pp,
+                    grpc_schedule_on_exec_ctx);
+  pp->original_on_complete = pick->on_complete;
+  pick->on_complete = &pp->on_complete;
+  return pp;
+}
+
+void XdsLb::AddPendingPick(PendingPick* pp) {
+  pp->next = pending_picks_;
+  pending_picks_ = pp;
+}
+
+//
+// code for interacting with the child policy
+//
+
+// Performs a pick over \a child_policy_. Given that a pick can return
+// immediately (ignoring its completion callback), we need to perform the
+// cleanups this callback would otherwise be responsible for.
+// If \a force_async is true, then we will manually schedule the
+// completion callback even if the pick is available immediately.
+bool XdsLb::PickFromChildPolicyLocked(bool force_async, PendingPick* pp,
+                                      grpc_error** error) {
+  // Set client_stats.
+  if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) {
+    pp->client_stats = lb_calld_->client_stats()->Ref();
+  }
+  // Pick via the child policy.
+  bool pick_done = child_policy_->PickLocked(pp->pick, error);
+  if (pick_done) {
+    PendingPickCleanup(pp);
+    if (force_async) {
+      GRPC_CLOSURE_SCHED(pp->original_on_complete, *error);
+      *error = GRPC_ERROR_NONE;
+      pick_done = false;
+    }
+    Delete(pp);
+  }
+  // else, the pending pick will be registered and taken care of by the
+  // pending pick list inside the child policy.  Eventually,
+  // OnPendingPickComplete() will be called, which will (among other
+  // things) add the LB token to the call's initial metadata.
+  return pick_done;
+}
+
+void XdsLb::CreateChildPolicyLocked(const Args& args) {
+  GPR_ASSERT(child_policy_ == nullptr);
+  child_policy_ = LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
+      "round_robin", args);
+  if (GPR_UNLIKELY(child_policy_ == nullptr)) {
+    gpr_log(GPR_ERROR, "[xdslb %p] Failure creating a child policy", this);
+    return;
+  }
+  // TODO(roth): We currently track this ref manually.  Once the new
+  // ClosureRef API is done, pass the RefCountedPtr<> along with the closure.
+  auto self = Ref(DEBUG_LOCATION, "on_child_reresolution_requested");
+  self.release();
+  child_policy_->SetReresolutionClosureLocked(&on_child_request_reresolution_);
+  grpc_error* child_state_error = nullptr;
+  child_connectivity_state_ =
+      child_policy_->CheckConnectivityLocked(&child_state_error);
+  // Connectivity state is a function of the child policy updated/created.
+  UpdateConnectivityStateFromChildPolicyLocked(child_state_error);
+  // Add the xDS's interested_parties pollset_set to that of the newly created
+  // child policy. This will make the child policy progress upon activity on
+  // xDS LB, which in turn is tied to the application's call.
+  grpc_pollset_set_add_pollset_set(child_policy_->interested_parties(),
+                                   interested_parties());
+  // Subscribe to changes to the connectivity of the new child policy.
+  // TODO(roth): We currently track this ref manually.  Once the new
+  // ClosureRef API is done, pass the RefCountedPtr<> along with the closure.
+  self = Ref(DEBUG_LOCATION, "on_child_connectivity_changed");
+  self.release();
+  child_policy_->NotifyOnStateChangeLocked(&child_connectivity_state_,
+                                           &on_child_connectivity_changed_);
+  child_policy_->ExitIdleLocked();
+  // Send pending picks to child policy.
+  PendingPick* pp;
+  while ((pp = pending_picks_)) {
+    pending_picks_ = pp->next;
+    if (grpc_lb_xds_trace.enabled()) {
+      gpr_log(
+          GPR_INFO,
+          "[xdslb %p] Pending pick about to (async) PICK from child policy %p",
+          this, child_policy_.get());
+    }
+    grpc_error* error = GRPC_ERROR_NONE;
+    PickFromChildPolicyLocked(true /* force_async */, pp, &error);
+  }
+}
+
+grpc_channel_args* XdsLb::CreateChildPolicyArgsLocked() {
+  bool is_backend_from_grpclb_load_balancer = false;
+  // This should never be invoked if we do not have serverlist_, as fallback
+  // mode is disabled for xDS plugin.
+  GPR_ASSERT(serverlist_ != nullptr);
+  GPR_ASSERT(serverlist_->num_servers > 0);
+  UniquePtr<ServerAddressList> addresses = ProcessServerlist(serverlist_);
+  GPR_ASSERT(addresses != nullptr);
+  is_backend_from_grpclb_load_balancer = true;
+  // Replace the server address list in the channel args that we pass down to
+  // the subchannel.
+  static const char* keys_to_remove[] = {GRPC_ARG_SERVER_ADDRESS_LIST};
+  const grpc_arg args_to_add[] = {
+      CreateServerAddressListChannelArg(addresses.get()),
+      // A channel arg indicating if the target is a backend inferred from a
+      // grpclb load balancer.
+      grpc_channel_arg_integer_create(
+          const_cast<char*>(GRPC_ARG_ADDRESS_IS_BACKEND_FROM_XDS_LOAD_BALANCER),
+          is_backend_from_grpclb_load_balancer),
+  };
+  grpc_channel_args* args = grpc_channel_args_copy_and_add_and_remove(
+      args_, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), args_to_add,
+      GPR_ARRAY_SIZE(args_to_add));
+  return args;
+}
+
+void XdsLb::CreateOrUpdateChildPolicyLocked() {
+  if (shutting_down_) return;
+  grpc_channel_args* args = CreateChildPolicyArgsLocked();
+  GPR_ASSERT(args != nullptr);
+  if (child_policy_ != nullptr) {
+    if (grpc_lb_xds_trace.enabled()) {
+      gpr_log(GPR_INFO, "[xdslb %p] Updating the child policy %p", this,
+              child_policy_.get());
+    }
+    // TODO(vishalpowar): Pass the correct LB config.
+    child_policy_->UpdateLocked(*args, nullptr);
+  } else {
+    LoadBalancingPolicy::Args lb_policy_args;
+    lb_policy_args.combiner = combiner();
+    lb_policy_args.client_channel_factory = client_channel_factory();
+    lb_policy_args.args = args;
+    CreateChildPolicyLocked(lb_policy_args);
+    if (grpc_lb_xds_trace.enabled()) {
+      gpr_log(GPR_INFO, "[xdslb %p] Created a new child policy %p", this,
+              child_policy_.get());
+    }
+  }
+  grpc_channel_args_destroy(args);
+}
+
+void XdsLb::OnChildPolicyRequestReresolutionLocked(void* arg,
+                                                   grpc_error* error) {
+  XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
+  if (xdslb_policy->shutting_down_ || error != GRPC_ERROR_NONE) {
+    xdslb_policy->Unref(DEBUG_LOCATION, "on_child_reresolution_requested");
+    return;
+  }
+  if (grpc_lb_xds_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[xdslb %p] Re-resolution requested from child policy "
+            "(%p).",
+            xdslb_policy, xdslb_policy->child_policy_.get());
+  }
+  // If we are talking to a balancer, we expect to get updated addresses form
+  // the balancer, so we can ignore the re-resolution request from the child
+  // policy.
+  // Otherwise, handle the re-resolution request using the xds policy's
+  // original re-resolution closure.
+  if (xdslb_policy->lb_calld_ == nullptr ||
+      !xdslb_policy->lb_calld_->seen_initial_response()) {
+    xdslb_policy->TryReresolutionLocked(&grpc_lb_xds_trace, GRPC_ERROR_NONE);
+  }
+  // Give back the wrapper closure to the child policy.
+  xdslb_policy->child_policy_->SetReresolutionClosureLocked(
+      &xdslb_policy->on_child_request_reresolution_);
+}
+
+void XdsLb::UpdateConnectivityStateFromChildPolicyLocked(
+    grpc_error* child_state_error) {
+  const grpc_connectivity_state curr_glb_state =
+      grpc_connectivity_state_check(&state_tracker_);
+  /* The new connectivity status is a function of the previous one and the new
+   * input coming from the status of the child policy.
+   *
+   *  current state (xds's)
+   *  |
+   *  v  || I  |  C  |  R  |  TF  |  SD  |  <- new state (child policy's)
+   *  ===++====+=====+=====+======+======+
+   *   I || I  |  C  |  R  | [I]  | [I]  |
+   *  ---++----+-----+-----+------+------+
+   *   C || I  |  C  |  R  | [C]  | [C]  |
+   *  ---++----+-----+-----+------+------+
+   *   R || I  |  C  |  R  | [R]  | [R]  |
+   *  ---++----+-----+-----+------+------+
+   *  TF || I  |  C  |  R  | [TF] | [TF] |
+   *  ---++----+-----+-----+------+------+
+   *  SD || NA |  NA |  NA |  NA  |  NA  | (*)
+   *  ---++----+-----+-----+------+------+
+   *
+   * A [STATE] indicates that the old child policy is kept. In those cases,
+   * STATE is the current state of xds, which is left untouched.
+   *
+   *  In summary, if the new state is TRANSIENT_FAILURE or SHUTDOWN, stick to
+   *  the previous child policy instance.
+   *
+   *  Note that the status is never updated to SHUTDOWN as a result of calling
+   *  this function. Only glb_shutdown() has the power to set that state.
+   *
+   *  (*) This function mustn't be called during shutting down. */
+  GPR_ASSERT(curr_glb_state != GRPC_CHANNEL_SHUTDOWN);
+  switch (child_connectivity_state_) {
+    case GRPC_CHANNEL_TRANSIENT_FAILURE:
+    case GRPC_CHANNEL_SHUTDOWN:
+      GPR_ASSERT(child_state_error != GRPC_ERROR_NONE);
+      break;
+    case GRPC_CHANNEL_IDLE:
+    case GRPC_CHANNEL_CONNECTING:
+    case GRPC_CHANNEL_READY:
+      GPR_ASSERT(child_state_error == GRPC_ERROR_NONE);
+  }
+  if (grpc_lb_xds_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "[xdslb %p] Setting xds's state to %s from child policy %p state.",
+            this, grpc_connectivity_state_name(child_connectivity_state_),
+            child_policy_.get());
+  }
+  grpc_connectivity_state_set(&state_tracker_, child_connectivity_state_,
+                              child_state_error,
+                              "update_lb_connectivity_status_locked");
+}
+
+void XdsLb::OnChildPolicyConnectivityChangedLocked(void* arg,
+                                                   grpc_error* error) {
+  XdsLb* xdslb_policy = static_cast<XdsLb*>(arg);
+  if (xdslb_policy->shutting_down_) {
+    xdslb_policy->Unref(DEBUG_LOCATION, "on_child_connectivity_changed");
+    return;
+  }
+  xdslb_policy->UpdateConnectivityStateFromChildPolicyLocked(
+      GRPC_ERROR_REF(error));
+  // Resubscribe. Reuse the "on_child_connectivity_changed" ref.
+  xdslb_policy->child_policy_->NotifyOnStateChangeLocked(
+      &xdslb_policy->child_connectivity_state_,
+      &xdslb_policy->on_child_connectivity_changed_);
+}
+
+//
+// factory
+//
+
+class XdsFactory : public LoadBalancingPolicyFactory {
+ public:
+  OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const override {
+    /* Count the number of gRPC-LB addresses. There must be at least one. */
+    const ServerAddressList* addresses =
+        FindServerAddressListChannelArg(args.args);
+    if (addresses == nullptr) return nullptr;
+    bool found_balancer_address = false;
+    for (size_t i = 0; i < addresses->size(); ++i) {
+      if ((*addresses)[i].IsBalancer()) {
+        found_balancer_address = true;
+        break;
+      }
+    }
+    if (!found_balancer_address) return nullptr;
+    return OrphanablePtr<LoadBalancingPolicy>(New<XdsLb>(args));
+  }
+
+  const char* name() const override { return kXds; }
+};
+
+}  // namespace
+
+}  // namespace grpc_core
+
+//
+// Plugin registration
+//
+
+void grpc_lb_policy_xds_init() {
+  grpc_core::LoadBalancingPolicyRegistry::Builder::
+      RegisterLoadBalancingPolicyFactory(
+          grpc_core::UniquePtr<grpc_core::LoadBalancingPolicyFactory>(
+              grpc_core::New<grpc_core::XdsFactory>()));
+}
+
+void grpc_lb_policy_xds_shutdown() {}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.h
new file mode 100644
index 0000000..8b20680
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_H
+
+#include <grpc/support/port_platform.h>
+
+/** Channel arg indicating if a target corresponding to the address is grpclb
+ * loadbalancer. The type of this arg is an integer and the value is treated as
+ * a bool. */
+#define GRPC_ARG_ADDRESS_IS_XDS_LOAD_BALANCER \
+  "grpc.address_is_xds_load_balancer"
+/** Channel arg indicating if a target corresponding to the address is a backend
+ * received from a balancer. The type of this arg is an integer and the value is
+ * treated as a bool. */
+#define GRPC_ARG_ADDRESS_IS_BACKEND_FROM_XDS_LOAD_BALANCER \
+  "grpc.address_is_backend_from_xds_load_balancer"
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc
new file mode 100644
index 0000000..0aa145a
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h"
+
+grpc_channel_args* grpc_lb_policy_xds_modify_lb_channel_args(
+    grpc_channel_args* args) {
+  return args;
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h
new file mode 100644
index 0000000..f713b7f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+/// Makes any necessary modifications to \a args for use in the xds
+/// balancer channel.
+///
+/// Takes ownership of \a args.
+///
+/// Caller takes ownership of the returned args.
+grpc_channel_args* grpc_lb_policy_xds_modify_lb_channel_args(
+    grpc_channel_args* args);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_secure.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_secure.cc
new file mode 100644
index 0000000..55c646e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_secure.cc
@@ -0,0 +1,104 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include <string.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+namespace grpc_core {
+namespace {
+
+int BalancerNameCmp(const grpc_core::UniquePtr<char>& a,
+                    const grpc_core::UniquePtr<char>& b) {
+  return strcmp(a.get(), b.get());
+}
+
+RefCountedPtr<TargetAuthorityTable> CreateTargetAuthorityTable(
+    const ServerAddressList& addresses) {
+  TargetAuthorityTable::Entry* target_authority_entries =
+      static_cast<TargetAuthorityTable::Entry*>(
+          gpr_zalloc(sizeof(*target_authority_entries) * addresses.size()));
+  for (size_t i = 0; i < addresses.size(); ++i) {
+    char* addr_str;
+    GPR_ASSERT(
+        grpc_sockaddr_to_string(&addr_str, &addresses[i].address(), true) > 0);
+    target_authority_entries[i].key = grpc_slice_from_copied_string(addr_str);
+    gpr_free(addr_str);
+    char* balancer_name = grpc_channel_arg_get_string(grpc_channel_args_find(
+        addresses[i].args(), GRPC_ARG_ADDRESS_BALANCER_NAME));
+    target_authority_entries[i].value.reset(gpr_strdup(balancer_name));
+  }
+  RefCountedPtr<TargetAuthorityTable> target_authority_table =
+      TargetAuthorityTable::Create(addresses.size(), target_authority_entries,
+                                   BalancerNameCmp);
+  gpr_free(target_authority_entries);
+  return target_authority_table;
+}
+
+}  // namespace
+}  // namespace grpc_core
+
+grpc_channel_args* grpc_lb_policy_xds_modify_lb_channel_args(
+    grpc_channel_args* args) {
+  const char* args_to_remove[1];
+  size_t num_args_to_remove = 0;
+  grpc_arg args_to_add[2];
+  size_t num_args_to_add = 0;
+  // Add arg for targets info table.
+  grpc_core::ServerAddressList* addresses =
+      grpc_core::FindServerAddressListChannelArg(args);
+  GPR_ASSERT(addresses != nullptr);
+  grpc_core::RefCountedPtr<grpc_core::TargetAuthorityTable>
+      target_authority_table =
+          grpc_core::CreateTargetAuthorityTable(*addresses);
+  args_to_add[num_args_to_add++] =
+      grpc_core::CreateTargetAuthorityTableChannelArg(
+          target_authority_table.get());
+  // Substitute the channel credentials with a version without call
+  // credentials: the load balancer is not necessarily trusted to handle
+  // bearer token credentials.
+  grpc_channel_credentials* channel_credentials =
+      grpc_channel_credentials_find_in_args(args);
+  grpc_core::RefCountedPtr<grpc_channel_credentials> creds_sans_call_creds;
+  if (channel_credentials != nullptr) {
+    creds_sans_call_creds =
+        channel_credentials->duplicate_without_call_credentials();
+    GPR_ASSERT(creds_sans_call_creds != nullptr);
+    args_to_remove[num_args_to_remove++] = GRPC_ARG_CHANNEL_CREDENTIALS;
+    args_to_add[num_args_to_add++] =
+        grpc_channel_credentials_to_arg(creds_sans_call_creds.get());
+  }
+  grpc_channel_args* result = grpc_channel_args_copy_and_add_and_remove(
+      args, args_to_remove, num_args_to_remove, args_to_add, num_args_to_add);
+  // Clean up.
+  grpc_channel_args_destroy(args);
+  return result;
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc
new file mode 100644
index 0000000..cdf5408
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h"
+
+#include <grpc/support/atm.h>
+#include <grpc/support/string_util.h>
+#include <string.h>
+
+namespace grpc_core {
+
+void XdsLbClientStats::AddCallStarted() {
+  gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1);
+}
+
+void XdsLbClientStats::AddCallFinished(bool finished_with_client_failed_to_send,
+                                       bool finished_known_received) {
+  gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1);
+  if (finished_with_client_failed_to_send) {
+    gpr_atm_full_fetch_add(&num_calls_finished_with_client_failed_to_send_,
+                           (gpr_atm)1);
+  }
+  if (finished_known_received) {
+    gpr_atm_full_fetch_add(&num_calls_finished_known_received_, (gpr_atm)1);
+  }
+}
+
+void XdsLbClientStats::AddCallDroppedLocked(char* token) {
+  // Increment num_calls_started and num_calls_finished.
+  gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1);
+  gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1);
+  // Record the drop.
+  if (drop_token_counts_ == nullptr) {
+    drop_token_counts_.reset(New<DroppedCallCounts>());
+  }
+  for (size_t i = 0; i < drop_token_counts_->size(); ++i) {
+    if (strcmp((*drop_token_counts_)[i].token.get(), token) == 0) {
+      ++(*drop_token_counts_)[i].count;
+      return;
+    }
+  }
+  // Not found, so add a new entry.
+  drop_token_counts_->emplace_back(UniquePtr<char>(gpr_strdup(token)), 1);
+}
+
+namespace {
+
+void AtomicGetAndResetCounter(int64_t* value, gpr_atm* counter) {
+  *value = static_cast<int64_t>(gpr_atm_full_xchg(counter, (gpr_atm)0));
+}
+
+}  // namespace
+
+void XdsLbClientStats::GetLocked(
+    int64_t* num_calls_started, int64_t* num_calls_finished,
+    int64_t* num_calls_finished_with_client_failed_to_send,
+    int64_t* num_calls_finished_known_received,
+    UniquePtr<DroppedCallCounts>* drop_token_counts) {
+  AtomicGetAndResetCounter(num_calls_started, &num_calls_started_);
+  AtomicGetAndResetCounter(num_calls_finished, &num_calls_finished_);
+  AtomicGetAndResetCounter(num_calls_finished_with_client_failed_to_send,
+                           &num_calls_finished_with_client_failed_to_send_);
+  AtomicGetAndResetCounter(num_calls_finished_known_received,
+                           &num_calls_finished_known_received_);
+  *drop_token_counts = std::move(drop_token_counts_);
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h
new file mode 100644
index 0000000..fa0b9f4
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CLIENT_STATS_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CLIENT_STATS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+
+namespace grpc_core {
+
+class XdsLbClientStats : public RefCounted<XdsLbClientStats> {
+ public:
+  struct DropTokenCount {
+    UniquePtr<char> token;
+    int64_t count;
+
+    DropTokenCount(UniquePtr<char> token, int64_t count)
+        : token(std::move(token)), count(count) {}
+  };
+
+  typedef InlinedVector<DropTokenCount, 10> DroppedCallCounts;
+
+  XdsLbClientStats() {}
+
+  void AddCallStarted();
+  void AddCallFinished(bool finished_with_client_failed_to_send,
+                       bool finished_known_received);
+
+  // This method is not thread-safe; caller must synchronize.
+  void AddCallDroppedLocked(char* token);
+
+  // This method is not thread-safe; caller must synchronize.
+  void GetLocked(int64_t* num_calls_started, int64_t* num_calls_finished,
+                 int64_t* num_calls_finished_with_client_failed_to_send,
+                 int64_t* num_calls_finished_known_received,
+                 UniquePtr<DroppedCallCounts>* drop_token_counts);
+
+ private:
+  // This field must only be accessed via *_locked() methods.
+  UniquePtr<DroppedCallCounts> drop_token_counts_;
+  // These fields may be accessed from multiple threads at a time.
+  gpr_atm num_calls_started_ = 0;
+  gpr_atm num_calls_finished_ = 0;
+  gpr_atm num_calls_finished_with_client_failed_to_send_ = 0;
+  gpr_atm num_calls_finished_known_received_ = 0;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CLIENT_STATS_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc
new file mode 100644
index 0000000..79b7bdb
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc
@@ -0,0 +1,307 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "pb_decode.h"
+#include "pb_encode.h"
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h"
+
+#include <grpc/support/alloc.h>
+
+/* invoked once for every Server in ServerList */
+static bool count_serverlist(pb_istream_t* stream, const pb_field_t* field,
+                             void** arg) {
+  xds_grpclb_serverlist* sl = static_cast<xds_grpclb_serverlist*>(*arg);
+  xds_grpclb_server server;
+  if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, &server))) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream));
+    return false;
+  }
+  ++sl->num_servers;
+  return true;
+}
+
+typedef struct decode_serverlist_arg {
+  /* The decoding callback is invoked once per server in serverlist. Remember
+   * which index of the serverlist are we currently decoding */
+  size_t decoding_idx;
+  /* The decoded serverlist */
+  xds_grpclb_serverlist* serverlist;
+} decode_serverlist_arg;
+
+/* invoked once for every Server in ServerList */
+static bool decode_serverlist(pb_istream_t* stream, const pb_field_t* field,
+                              void** arg) {
+  decode_serverlist_arg* dec_arg = static_cast<decode_serverlist_arg*>(*arg);
+  GPR_ASSERT(dec_arg->serverlist->num_servers >= dec_arg->decoding_idx);
+  xds_grpclb_server* server =
+      static_cast<xds_grpclb_server*>(gpr_zalloc(sizeof(xds_grpclb_server)));
+  if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, server))) {
+    gpr_free(server);
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream));
+    return false;
+  }
+  dec_arg->serverlist->servers[dec_arg->decoding_idx++] = server;
+  return true;
+}
+
+xds_grpclb_request* xds_grpclb_request_create(const char* lb_service_name) {
+  xds_grpclb_request* req =
+      static_cast<xds_grpclb_request*>(gpr_malloc(sizeof(xds_grpclb_request)));
+  req->has_client_stats = false;
+  req->has_initial_request = true;
+  req->initial_request.has_name = true;
+  strncpy(req->initial_request.name, lb_service_name,
+          XDS_SERVICE_NAME_MAX_LENGTH);
+  return req;
+}
+
+static void populate_timestamp(gpr_timespec timestamp,
+                               xds_grpclb_timestamp* timestamp_pb) {
+  timestamp_pb->has_seconds = true;
+  timestamp_pb->seconds = timestamp.tv_sec;
+  timestamp_pb->has_nanos = true;
+  timestamp_pb->nanos = timestamp.tv_nsec;
+}
+
+static bool encode_string(pb_ostream_t* stream, const pb_field_t* field,
+                          void* const* arg) {
+  char* str = static_cast<char*>(*arg);
+  if (!pb_encode_tag_for_field(stream, field)) return false;
+  return pb_encode_string(stream, reinterpret_cast<uint8_t*>(str), strlen(str));
+}
+
+static bool encode_drops(pb_ostream_t* stream, const pb_field_t* field,
+                         void* const* arg) {
+  grpc_core::XdsLbClientStats::DroppedCallCounts* drop_entries =
+      static_cast<grpc_core::XdsLbClientStats::DroppedCallCounts*>(*arg);
+  if (drop_entries == nullptr) return true;
+  for (size_t i = 0; i < drop_entries->size(); ++i) {
+    if (!pb_encode_tag_for_field(stream, field)) return false;
+    grpc_lb_v1_ClientStatsPerToken drop_message;
+    drop_message.load_balance_token.funcs.encode = encode_string;
+    drop_message.load_balance_token.arg = (*drop_entries)[i].token.get();
+    drop_message.has_num_calls = true;
+    drop_message.num_calls = (*drop_entries)[i].count;
+    if (!pb_encode_submessage(stream, grpc_lb_v1_ClientStatsPerToken_fields,
+                              &drop_message)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+xds_grpclb_request* xds_grpclb_load_report_request_create_locked(
+    grpc_core::XdsLbClientStats* client_stats) {
+  xds_grpclb_request* req =
+      static_cast<xds_grpclb_request*>(gpr_zalloc(sizeof(xds_grpclb_request)));
+  req->has_client_stats = true;
+  req->client_stats.has_timestamp = true;
+  populate_timestamp(gpr_now(GPR_CLOCK_REALTIME), &req->client_stats.timestamp);
+  req->client_stats.has_num_calls_started = true;
+  req->client_stats.has_num_calls_finished = true;
+  req->client_stats.has_num_calls_finished_with_client_failed_to_send = true;
+  req->client_stats.has_num_calls_finished_with_client_failed_to_send = true;
+  req->client_stats.has_num_calls_finished_known_received = true;
+  req->client_stats.calls_finished_with_drop.funcs.encode = encode_drops;
+  grpc_core::UniquePtr<grpc_core::XdsLbClientStats::DroppedCallCounts>
+      drop_counts;
+  client_stats->GetLocked(
+      &req->client_stats.num_calls_started,
+      &req->client_stats.num_calls_finished,
+      &req->client_stats.num_calls_finished_with_client_failed_to_send,
+      &req->client_stats.num_calls_finished_known_received, &drop_counts);
+  // Will be deleted in xds_grpclb_request_destroy().
+  req->client_stats.calls_finished_with_drop.arg = drop_counts.release();
+  return req;
+}
+
+grpc_slice xds_grpclb_request_encode(const xds_grpclb_request* request) {
+  size_t encoded_length;
+  pb_ostream_t sizestream;
+  pb_ostream_t outputstream;
+  grpc_slice slice;
+  memset(&sizestream, 0, sizeof(pb_ostream_t));
+  pb_encode(&sizestream, grpc_lb_v1_LoadBalanceRequest_fields, request);
+  encoded_length = sizestream.bytes_written;
+
+  slice = GRPC_SLICE_MALLOC(encoded_length);
+  outputstream =
+      pb_ostream_from_buffer(GRPC_SLICE_START_PTR(slice), encoded_length);
+  GPR_ASSERT(pb_encode(&outputstream, grpc_lb_v1_LoadBalanceRequest_fields,
+                       request) != 0);
+  return slice;
+}
+
+void xds_grpclb_request_destroy(xds_grpclb_request* request) {
+  if (request->has_client_stats) {
+    grpc_core::XdsLbClientStats::DroppedCallCounts* drop_entries =
+        static_cast<grpc_core::XdsLbClientStats::DroppedCallCounts*>(
+            request->client_stats.calls_finished_with_drop.arg);
+    grpc_core::Delete(drop_entries);
+  }
+  gpr_free(request);
+}
+
+typedef grpc_lb_v1_LoadBalanceResponse xds_grpclb_response;
+xds_grpclb_initial_response* xds_grpclb_initial_response_parse(
+    grpc_slice encoded_xds_grpclb_response) {
+  pb_istream_t stream =
+      pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_xds_grpclb_response),
+                             GRPC_SLICE_LENGTH(encoded_xds_grpclb_response));
+  xds_grpclb_response res;
+  memset(&res, 0, sizeof(xds_grpclb_response));
+  if (GPR_UNLIKELY(
+          !pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res))) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return nullptr;
+  }
+
+  if (!res.has_initial_response) return nullptr;
+
+  xds_grpclb_initial_response* initial_res =
+      static_cast<xds_grpclb_initial_response*>(
+          gpr_malloc(sizeof(xds_grpclb_initial_response)));
+  memcpy(initial_res, &res.initial_response,
+         sizeof(xds_grpclb_initial_response));
+
+  return initial_res;
+}
+
+xds_grpclb_serverlist* xds_grpclb_response_parse_serverlist(
+    grpc_slice encoded_xds_grpclb_response) {
+  pb_istream_t stream =
+      pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_xds_grpclb_response),
+                             GRPC_SLICE_LENGTH(encoded_xds_grpclb_response));
+  pb_istream_t stream_at_start = stream;
+  xds_grpclb_serverlist* sl = static_cast<xds_grpclb_serverlist*>(
+      gpr_zalloc(sizeof(xds_grpclb_serverlist)));
+  xds_grpclb_response res;
+  memset(&res, 0, sizeof(xds_grpclb_response));
+  // First pass: count number of servers.
+  res.server_list.servers.funcs.decode = count_serverlist;
+  res.server_list.servers.arg = sl;
+  bool status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res);
+  if (GPR_UNLIKELY(!status)) {
+    gpr_free(sl);
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return nullptr;
+  }
+  // Second pass: populate servers.
+  if (sl->num_servers > 0) {
+    sl->servers = static_cast<xds_grpclb_server**>(
+        gpr_zalloc(sizeof(xds_grpclb_server*) * sl->num_servers));
+    decode_serverlist_arg decode_arg;
+    memset(&decode_arg, 0, sizeof(decode_arg));
+    decode_arg.serverlist = sl;
+    res.server_list.servers.funcs.decode = decode_serverlist;
+    res.server_list.servers.arg = &decode_arg;
+    status = pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields,
+                       &res);
+    if (GPR_UNLIKELY(!status)) {
+      xds_grpclb_destroy_serverlist(sl);
+      gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+      return nullptr;
+    }
+  }
+  return sl;
+}
+
+void xds_grpclb_destroy_serverlist(xds_grpclb_serverlist* serverlist) {
+  if (serverlist == nullptr) {
+    return;
+  }
+  for (size_t i = 0; i < serverlist->num_servers; i++) {
+    gpr_free(serverlist->servers[i]);
+  }
+  gpr_free(serverlist->servers);
+  gpr_free(serverlist);
+}
+
+xds_grpclb_serverlist* xds_grpclb_serverlist_copy(
+    const xds_grpclb_serverlist* sl) {
+  xds_grpclb_serverlist* copy = static_cast<xds_grpclb_serverlist*>(
+      gpr_zalloc(sizeof(xds_grpclb_serverlist)));
+  copy->num_servers = sl->num_servers;
+  copy->servers = static_cast<xds_grpclb_server**>(
+      gpr_malloc(sizeof(xds_grpclb_server*) * sl->num_servers));
+  for (size_t i = 0; i < sl->num_servers; i++) {
+    copy->servers[i] =
+        static_cast<xds_grpclb_server*>(gpr_malloc(sizeof(xds_grpclb_server)));
+    memcpy(copy->servers[i], sl->servers[i], sizeof(xds_grpclb_server));
+  }
+  return copy;
+}
+
+bool xds_grpclb_serverlist_equals(const xds_grpclb_serverlist* lhs,
+                                  const xds_grpclb_serverlist* rhs) {
+  if (lhs == nullptr || rhs == nullptr) {
+    return false;
+  }
+  if (lhs->num_servers != rhs->num_servers) {
+    return false;
+  }
+  for (size_t i = 0; i < lhs->num_servers; i++) {
+    if (!xds_grpclb_server_equals(lhs->servers[i], rhs->servers[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool xds_grpclb_server_equals(const xds_grpclb_server* lhs,
+                              const xds_grpclb_server* rhs) {
+  return memcmp(lhs, rhs, sizeof(xds_grpclb_server)) == 0;
+}
+
+int xds_grpclb_duration_compare(const xds_grpclb_duration* lhs,
+                                const xds_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;
+}
+
+grpc_millis xds_grpclb_duration_to_millis(xds_grpclb_duration* duration_pb) {
+  return static_cast<grpc_millis>(
+      (duration_pb->has_seconds ? duration_pb->seconds : 0) * GPR_MS_PER_SEC +
+      (duration_pb->has_nanos ? duration_pb->nanos : 0) / GPR_NS_PER_MS);
+}
+
+void xds_grpclb_initial_response_destroy(
+    xds_grpclb_initial_response* response) {
+  gpr_free(response);
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h
new file mode 100644
index 0000000..6704995
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_LOAD_BALANCER_API_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_LOAD_BALANCER_API_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h"
+#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+#define XDS_SERVICE_NAME_MAX_LENGTH 128
+
+typedef grpc_lb_v1_Server_ip_address_t xds_grpclb_ip_address;
+typedef grpc_lb_v1_LoadBalanceRequest xds_grpclb_request;
+typedef grpc_lb_v1_InitialLoadBalanceResponse xds_grpclb_initial_response;
+typedef grpc_lb_v1_Server xds_grpclb_server;
+typedef google_protobuf_Duration xds_grpclb_duration;
+typedef google_protobuf_Timestamp xds_grpclb_timestamp;
+
+typedef struct {
+  xds_grpclb_server** servers;
+  size_t num_servers;
+} xds_grpclb_serverlist;
+
+/** Create a request for a gRPC LB service under \a lb_service_name */
+xds_grpclb_request* xds_grpclb_request_create(const char* lb_service_name);
+xds_grpclb_request* xds_grpclb_load_report_request_create_locked(
+    grpc_core::XdsLbClientStats* client_stats);
+
+/** Protocol Buffers v3-encode \a request */
+grpc_slice xds_grpclb_request_encode(const xds_grpclb_request* request);
+
+/** Destroy \a request */
+void xds_grpclb_request_destroy(xds_grpclb_request* request);
+
+/** Parse (ie, decode) the bytes in \a encoded_xds_grpclb_response as a \a
+ * xds_grpclb_initial_response */
+xds_grpclb_initial_response* xds_grpclb_initial_response_parse(
+    grpc_slice encoded_xds_grpclb_response);
+
+/** Parse the list of servers from an encoded \a xds_grpclb_response */
+xds_grpclb_serverlist* xds_grpclb_response_parse_serverlist(
+    grpc_slice encoded_xds_grpclb_response);
+
+/** Return a copy of \a sl. The caller is responsible for calling \a
+ * xds_grpclb_destroy_serverlist on the returned copy. */
+xds_grpclb_serverlist* xds_grpclb_serverlist_copy(
+    const xds_grpclb_serverlist* sl);
+
+bool xds_grpclb_serverlist_equals(const xds_grpclb_serverlist* lhs,
+                                  const xds_grpclb_serverlist* rhs);
+
+bool xds_grpclb_server_equals(const xds_grpclb_server* lhs,
+                              const xds_grpclb_server* rhs);
+
+/** Destroy \a serverlist */
+void xds_grpclb_destroy_serverlist(xds_grpclb_serverlist* serverlist);
+
+/** 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 xds_grpclb_duration_compare(const xds_grpclb_duration* lhs,
+                                const xds_grpclb_duration* rhs);
+
+grpc_millis xds_grpclb_duration_to_millis(xds_grpclb_duration* duration_pb);
+
+/** Destroy \a initial_response */
+void xds_grpclb_initial_response_destroy(xds_grpclb_initial_response* response);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_LOAD_BALANCER_API_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_factory.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_factory.h
new file mode 100644
index 0000000..a165eba
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_factory.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/orphanable.h"
+
+namespace grpc_core {
+
+class LoadBalancingPolicyFactory {
+ public:
+  /// Returns a new LB policy instance.
+  virtual OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const LoadBalancingPolicy::Args& args) const GRPC_ABSTRACT;
+
+  /// Returns the LB policy name that this factory provides.
+  /// Caller does NOT take ownership of result.
+  virtual const char* name() const GRPC_ABSTRACT;
+
+  virtual ~LoadBalancingPolicyFactory() {}
+
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.cc
new file mode 100644
index 0000000..ad459c9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.cc
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+
+#include <string.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+
+namespace grpc_core {
+
+namespace {
+
+class RegistryState {
+ public:
+  RegistryState() {}
+
+  void RegisterLoadBalancingPolicyFactory(
+      UniquePtr<LoadBalancingPolicyFactory> factory) {
+    for (size_t i = 0; i < factories_.size(); ++i) {
+      GPR_ASSERT(strcmp(factories_[i]->name(), factory->name()) != 0);
+    }
+    factories_.push_back(std::move(factory));
+  }
+
+  LoadBalancingPolicyFactory* GetLoadBalancingPolicyFactory(
+      const char* name) const {
+    for (size_t i = 0; i < factories_.size(); ++i) {
+      if (strcmp(name, factories_[i]->name()) == 0) {
+        return factories_[i].get();
+      }
+    }
+    return nullptr;
+  }
+
+ private:
+  InlinedVector<UniquePtr<LoadBalancingPolicyFactory>, 10> factories_;
+};
+
+RegistryState* g_state = nullptr;
+
+}  // namespace
+
+//
+// LoadBalancingPolicyRegistry::Builder
+//
+
+void LoadBalancingPolicyRegistry::Builder::InitRegistry() {
+  if (g_state == nullptr) g_state = New<RegistryState>();
+}
+
+void LoadBalancingPolicyRegistry::Builder::ShutdownRegistry() {
+  Delete(g_state);
+  g_state = nullptr;
+}
+
+void LoadBalancingPolicyRegistry::Builder::RegisterLoadBalancingPolicyFactory(
+    UniquePtr<LoadBalancingPolicyFactory> factory) {
+  InitRegistry();
+  g_state->RegisterLoadBalancingPolicyFactory(std::move(factory));
+}
+
+//
+// LoadBalancingPolicyRegistry
+//
+
+OrphanablePtr<LoadBalancingPolicy>
+LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
+    const char* name, const LoadBalancingPolicy::Args& args) {
+  GPR_ASSERT(g_state != nullptr);
+  // Find factory.
+  LoadBalancingPolicyFactory* factory =
+      g_state->GetLoadBalancingPolicyFactory(name);
+  if (factory == nullptr) return nullptr;  // Specified name not found.
+  // Create policy via factory.
+  return factory->CreateLoadBalancingPolicy(args);
+}
+
+bool LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(const char* name) {
+  GPR_ASSERT(g_state != nullptr);
+  return g_state->GetLoadBalancingPolicyFactory(name) != nullptr;
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.h
new file mode 100644
index 0000000..338f7c9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/orphanable.h"
+
+namespace grpc_core {
+
+class LoadBalancingPolicyRegistry {
+ public:
+  /// Methods used to create and populate the LoadBalancingPolicyRegistry.
+  /// NOT THREAD SAFE -- to be used only during global gRPC
+  /// initialization and shutdown.
+  class Builder {
+   public:
+    /// Global initialization and shutdown hooks.
+    static void InitRegistry();
+    static void ShutdownRegistry();
+
+    /// Registers an LB policy factory.  The factory will be used to create an
+    /// LB policy whose name matches that of the factory.
+    static void RegisterLoadBalancingPolicyFactory(
+        UniquePtr<LoadBalancingPolicyFactory> factory);
+  };
+
+  /// Creates an LB policy of the type specified by \a name.
+  static OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy(
+      const char* name, const LoadBalancingPolicy::Args& args);
+
+  /// Returns true if the LB policy factory specified by \a name exists in this
+  /// registry.
+  static bool LoadBalancingPolicyExists(const char* name);
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/parse_address.cc b/third_party/grpc/src/core/ext/filters/client_channel/parse_address.cc
new file mode 100644
index 0000000..707beb8
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/parse_address.cc
@@ -0,0 +1,215 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/socket_utils.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef GRPC_HAVE_UNIX_SOCKET
+#include <sys/un.h>
+#endif
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+
+#ifdef GRPC_HAVE_UNIX_SOCKET
+
+bool grpc_parse_unix(const grpc_uri* uri,
+                     grpc_resolved_address* resolved_addr) {
+  if (strcmp("unix", uri->scheme) != 0) {
+    gpr_log(GPR_ERROR, "Expected 'unix' scheme, got '%s'", uri->scheme);
+    return false;
+  }
+  struct sockaddr_un* un =
+      reinterpret_cast<struct sockaddr_un*>(resolved_addr->addr);
+  const size_t maxlen = sizeof(un->sun_path);
+  const size_t path_len = strnlen(uri->path, maxlen);
+  if (path_len == maxlen) return false;
+  un->sun_family = AF_UNIX;
+  strcpy(un->sun_path, uri->path);
+  resolved_addr->len = static_cast<socklen_t>(sizeof(*un));
+  return true;
+}
+
+#else /* GRPC_HAVE_UNIX_SOCKET */
+
+bool grpc_parse_unix(const grpc_uri* uri,
+                     grpc_resolved_address* resolved_addr) {
+  abort();
+}
+
+#endif /* GRPC_HAVE_UNIX_SOCKET */
+
+bool grpc_parse_ipv4_hostport(const char* hostport, grpc_resolved_address* addr,
+                              bool log_errors) {
+  bool success = false;
+  // Split host and port.
+  char* host;
+  char* port;
+  if (!gpr_split_host_port(hostport, &host, &port)) return false;
+  // Parse IP address.
+  memset(addr, 0, sizeof(*addr));
+  addr->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in));
+  grpc_sockaddr_in* in = reinterpret_cast<grpc_sockaddr_in*>(addr->addr);
+  in->sin_family = GRPC_AF_INET;
+  if (grpc_inet_pton(GRPC_AF_INET, host, &in->sin_addr) == 0) {
+    if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host);
+    goto done;
+  }
+  // Parse port.
+  if (port == nullptr) {
+    if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv4 scheme");
+    goto done;
+  }
+  int port_num;
+  if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) {
+    if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port);
+    goto done;
+  }
+  in->sin_port = grpc_htons(static_cast<uint16_t>(port_num));
+  success = true;
+done:
+  gpr_free(host);
+  gpr_free(port);
+  return success;
+}
+
+bool grpc_parse_ipv4(const grpc_uri* uri,
+                     grpc_resolved_address* resolved_addr) {
+  if (strcmp("ipv4", uri->scheme) != 0) {
+    gpr_log(GPR_ERROR, "Expected 'ipv4' scheme, got '%s'", uri->scheme);
+    return false;
+  }
+  const char* host_port = uri->path;
+  if (*host_port == '/') ++host_port;
+  return grpc_parse_ipv4_hostport(host_port, resolved_addr,
+                                  true /* log_errors */);
+}
+
+bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr,
+                              bool log_errors) {
+  bool success = false;
+  // Split host and port.
+  char* host;
+  char* port;
+  if (!gpr_split_host_port(hostport, &host, &port)) return false;
+  // Parse IP address.
+  memset(addr, 0, sizeof(*addr));
+  addr->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in6));
+  grpc_sockaddr_in6* in6 = reinterpret_cast<grpc_sockaddr_in6*>(addr->addr);
+  in6->sin6_family = GRPC_AF_INET6;
+  // Handle the RFC6874 syntax for IPv6 zone identifiers.
+  char* host_end = static_cast<char*>(gpr_memrchr(host, '%', strlen(host)));
+  if (host_end != nullptr) {
+    GPR_ASSERT(host_end >= host);
+    char host_without_scope[GRPC_INET6_ADDRSTRLEN + 1];
+    size_t host_without_scope_len = static_cast<size_t>(host_end - host);
+    uint32_t sin6_scope_id = 0;
+    if (host_without_scope_len > GRPC_INET6_ADDRSTRLEN) {
+      if (log_errors) {
+        gpr_log(
+            GPR_ERROR,
+            "invalid ipv6 address length %zu. Length cannot be greater than "
+            "GRPC_INET6_ADDRSTRLEN i.e %d)",
+            host_without_scope_len, GRPC_INET6_ADDRSTRLEN);
+      }
+      goto done;
+    }
+    strncpy(host_without_scope, host, host_without_scope_len);
+    host_without_scope[host_without_scope_len] = '\0';
+    if (grpc_inet_pton(GRPC_AF_INET6, host_without_scope, &in6->sin6_addr) ==
+        0) {
+      if (log_errors) {
+        gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope);
+      }
+      goto done;
+    }
+    if (gpr_parse_bytes_to_uint32(host_end + 1,
+                                  strlen(host) - host_without_scope_len - 1,
+                                  &sin6_scope_id) == 0) {
+      if (log_errors) {
+        gpr_log(GPR_ERROR, "invalid ipv6 scope id: '%s'", host_end + 1);
+      }
+      goto done;
+    }
+    // Handle "sin6_scope_id" being type "u_long". See grpc issue #10027.
+    in6->sin6_scope_id = sin6_scope_id;
+  } else {
+    if (grpc_inet_pton(GRPC_AF_INET6, host, &in6->sin6_addr) == 0) {
+      if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host);
+      goto done;
+    }
+  }
+  // Parse port.
+  if (port == nullptr) {
+    if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv6 scheme");
+    goto done;
+  }
+  int port_num;
+  if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) {
+    if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port);
+    goto done;
+  }
+  in6->sin6_port = grpc_htons(static_cast<uint16_t>(port_num));
+  success = true;
+done:
+  gpr_free(host);
+  gpr_free(port);
+  return success;
+}
+
+bool grpc_parse_ipv6(const grpc_uri* uri,
+                     grpc_resolved_address* resolved_addr) {
+  if (strcmp("ipv6", uri->scheme) != 0) {
+    gpr_log(GPR_ERROR, "Expected 'ipv6' scheme, got '%s'", uri->scheme);
+    return false;
+  }
+  const char* host_port = uri->path;
+  if (*host_port == '/') ++host_port;
+  return grpc_parse_ipv6_hostport(host_port, resolved_addr,
+                                  true /* log_errors */);
+}
+
+bool grpc_parse_uri(const grpc_uri* uri, grpc_resolved_address* resolved_addr) {
+  if (strcmp("unix", uri->scheme) == 0) {
+    return grpc_parse_unix(uri, resolved_addr);
+  } else if (strcmp("ipv4", uri->scheme) == 0) {
+    return grpc_parse_ipv4(uri, resolved_addr);
+  } else if (strcmp("ipv6", uri->scheme) == 0) {
+    return grpc_parse_ipv6(uri, resolved_addr);
+  }
+  gpr_log(GPR_ERROR, "Can't parse scheme '%s'", uri->scheme);
+  return false;
+}
+
+uint16_t grpc_strhtons(const char* port) {
+  if (strcmp(port, "http") == 0) {
+    return htons(80);
+  } else if (strcmp(port, "https") == 0) {
+    return htons(443);
+  }
+  return htons(static_cast<unsigned short>(atoi(port)));
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/parse_address.h b/third_party/grpc/src/core/ext/filters/client_channel/parse_address.h
new file mode 100644
index 0000000..5c050a2
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/parse_address.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+/** Populate \a resolved_addr from \a uri, whose path is expected to contain a
+ * unix socket path. Returns true upon success. */
+bool grpc_parse_unix(const grpc_uri* uri, grpc_resolved_address* resolved_addr);
+
+/** Populate \a resolved_addr from \a uri, whose path is expected to contain an
+ * IPv4 host:port pair. Returns true upon success. */
+bool grpc_parse_ipv4(const grpc_uri* uri, grpc_resolved_address* resolved_addr);
+
+/** Populate \a resolved_addr from \a uri, whose path is expected to contain an
+ * IPv6 host:port pair. Returns true upon success. */
+bool grpc_parse_ipv6(const grpc_uri* uri, grpc_resolved_address* resolved_addr);
+
+/** Populate \a resolved_addr from \a uri. Returns true upon success. */
+bool grpc_parse_uri(const grpc_uri* uri, grpc_resolved_address* resolved_addr);
+
+/** Parse bare IPv4 or IPv6 "IP:port" strings. */
+bool grpc_parse_ipv4_hostport(const char* hostport, grpc_resolved_address* addr,
+                              bool log_errors);
+bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr,
+                              bool log_errors);
+
+/* Converts named or numeric port to a uint16 suitable for use in a sockaddr. */
+uint16_t grpc_strhtons(const char* port);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.cc b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.cc
new file mode 100644
index 0000000..c4da067
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.cc
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/proxy_mapper.h"
+
+void grpc_proxy_mapper_init(const grpc_proxy_mapper_vtable* vtable,
+                            grpc_proxy_mapper* mapper) {
+  mapper->vtable = vtable;
+}
+
+bool grpc_proxy_mapper_map_name(grpc_proxy_mapper* mapper,
+                                const char* server_uri,
+                                const grpc_channel_args* args,
+                                char** name_to_resolve,
+                                grpc_channel_args** new_args) {
+  return mapper->vtable->map_name(mapper, server_uri, args, name_to_resolve,
+                                  new_args);
+}
+
+bool grpc_proxy_mapper_map_address(grpc_proxy_mapper* mapper,
+                                   const grpc_resolved_address* address,
+                                   const grpc_channel_args* args,
+                                   grpc_resolved_address** new_address,
+                                   grpc_channel_args** new_args) {
+  return mapper->vtable->map_address(mapper, address, args, new_address,
+                                     new_args);
+}
+
+void grpc_proxy_mapper_destroy(grpc_proxy_mapper* mapper) {
+  mapper->vtable->destroy(mapper);
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.h b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.h
new file mode 100644
index 0000000..634b0ed
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/iomgr/resolve_address.h"
+
+typedef struct grpc_proxy_mapper grpc_proxy_mapper;
+
+typedef struct {
+  /// Determines the proxy name to resolve for \a server_uri.
+  /// If no proxy is needed, returns false.
+  /// Otherwise, sets \a name_to_resolve, optionally sets \a new_args,
+  /// and returns true.
+  bool (*map_name)(grpc_proxy_mapper* mapper, const char* server_uri,
+                   const grpc_channel_args* args, char** name_to_resolve,
+                   grpc_channel_args** new_args);
+  /// Determines the proxy address to use to contact \a address.
+  /// If no proxy is needed, returns false.
+  /// Otherwise, sets \a new_address, optionally sets \a new_args, and
+  /// returns true.
+  bool (*map_address)(grpc_proxy_mapper* mapper,
+                      const grpc_resolved_address* address,
+                      const grpc_channel_args* args,
+                      grpc_resolved_address** new_address,
+                      grpc_channel_args** new_args);
+  /// Destroys \a mapper.
+  void (*destroy)(grpc_proxy_mapper* mapper);
+} grpc_proxy_mapper_vtable;
+
+struct grpc_proxy_mapper {
+  const grpc_proxy_mapper_vtable* vtable;
+};
+
+void grpc_proxy_mapper_init(const grpc_proxy_mapper_vtable* vtable,
+                            grpc_proxy_mapper* mapper);
+
+bool grpc_proxy_mapper_map_name(grpc_proxy_mapper* mapper,
+                                const char* server_uri,
+                                const grpc_channel_args* args,
+                                char** name_to_resolve,
+                                grpc_channel_args** new_args);
+
+bool grpc_proxy_mapper_map_address(grpc_proxy_mapper* mapper,
+                                   const grpc_resolved_address* address,
+                                   const grpc_channel_args* args,
+                                   grpc_resolved_address** new_address,
+                                   grpc_channel_args** new_args);
+
+void grpc_proxy_mapper_destroy(grpc_proxy_mapper* mapper);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.cc b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.cc
new file mode 100644
index 0000000..a02a5f5
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.cc
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+//
+// grpc_proxy_mapper_list
+//
+
+typedef struct {
+  grpc_proxy_mapper** list;
+  size_t num_mappers;
+} grpc_proxy_mapper_list;
+
+static void grpc_proxy_mapper_list_register(grpc_proxy_mapper_list* list,
+                                            bool at_start,
+                                            grpc_proxy_mapper* mapper) {
+  list->list = static_cast<grpc_proxy_mapper**>(gpr_realloc(
+      list->list, (list->num_mappers + 1) * sizeof(grpc_proxy_mapper*)));
+  if (at_start) {
+    memmove(list->list + 1, list->list,
+            sizeof(grpc_proxy_mapper*) * list->num_mappers);
+    list->list[0] = mapper;
+  } else {
+    list->list[list->num_mappers] = mapper;
+  }
+  ++list->num_mappers;
+}
+
+static bool grpc_proxy_mapper_list_map_name(grpc_proxy_mapper_list* list,
+                                            const char* server_uri,
+                                            const grpc_channel_args* args,
+                                            char** name_to_resolve,
+                                            grpc_channel_args** new_args) {
+  for (size_t i = 0; i < list->num_mappers; ++i) {
+    if (grpc_proxy_mapper_map_name(list->list[i], server_uri, args,
+                                   name_to_resolve, new_args)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool grpc_proxy_mapper_list_map_address(
+    grpc_proxy_mapper_list* list, const grpc_resolved_address* address,
+    const grpc_channel_args* args, grpc_resolved_address** new_address,
+    grpc_channel_args** new_args) {
+  for (size_t i = 0; i < list->num_mappers; ++i) {
+    if (grpc_proxy_mapper_map_address(list->list[i], address, args, new_address,
+                                      new_args)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static void grpc_proxy_mapper_list_destroy(grpc_proxy_mapper_list* list) {
+  for (size_t i = 0; i < list->num_mappers; ++i) {
+    grpc_proxy_mapper_destroy(list->list[i]);
+  }
+  gpr_free(list->list);
+  // Clean up in case we re-initialze later.
+  // TODO(ctiller): This should ideally live in
+  // grpc_proxy_mapper_registry_init().  However, if we did this there,
+  // then we would do it AFTER we start registering proxy mappers from
+  // third-party plugins, so they'd never show up (and would leak memory).
+  // We probably need some sort of dependency system for plugins to fix
+  // this.
+  memset(list, 0, sizeof(*list));
+}
+
+//
+// plugin
+//
+
+static grpc_proxy_mapper_list g_proxy_mapper_list;
+
+void grpc_proxy_mapper_registry_init() {}
+
+void grpc_proxy_mapper_registry_shutdown() {
+  grpc_proxy_mapper_list_destroy(&g_proxy_mapper_list);
+}
+
+void grpc_proxy_mapper_register(bool at_start, grpc_proxy_mapper* mapper) {
+  grpc_proxy_mapper_list_register(&g_proxy_mapper_list, at_start, mapper);
+}
+
+bool grpc_proxy_mappers_map_name(const char* server_uri,
+                                 const grpc_channel_args* args,
+                                 char** name_to_resolve,
+                                 grpc_channel_args** new_args) {
+  return grpc_proxy_mapper_list_map_name(&g_proxy_mapper_list, server_uri, args,
+                                         name_to_resolve, new_args);
+}
+bool grpc_proxy_mappers_map_address(const grpc_resolved_address* address,
+                                    const grpc_channel_args* args,
+                                    grpc_resolved_address** new_address,
+                                    grpc_channel_args** new_args) {
+  return grpc_proxy_mapper_list_map_address(&g_proxy_mapper_list, address, args,
+                                            new_address, new_args);
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.h b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.h
new file mode 100644
index 0000000..326b582
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/proxy_mapper.h"
+
+void grpc_proxy_mapper_registry_init();
+void grpc_proxy_mapper_registry_shutdown();
+
+/// Registers a new proxy mapper.  Takes ownership.
+/// If \a at_start is true, the new mapper will be at the beginning of
+/// the list.  Otherwise, it will be added to the end.
+void grpc_proxy_mapper_register(bool at_start, grpc_proxy_mapper* mapper);
+
+bool grpc_proxy_mappers_map_name(const char* server_uri,
+                                 const grpc_channel_args* args,
+                                 char** name_to_resolve,
+                                 grpc_channel_args** new_args);
+
+bool grpc_proxy_mappers_map_address(const grpc_resolved_address* address,
+                                    const grpc_channel_args* args,
+                                    grpc_resolved_address** new_address,
+                                    grpc_channel_args** new_args);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/request_routing.cc b/third_party/grpc/src/core/ext/filters/client_channel/request_routing.cc
new file mode 100644
index 0000000..f9a7e16
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/request_routing.cc
@@ -0,0 +1,936 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/request_routing.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.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/sync.h>
+
+#include "src/core/ext/filters/client_channel/backup_poller.h"
+#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/retry_throttle.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/ext/filters/deadline/deadline_filter.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/iomgr/combiner.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/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/metadata_batch.h"
+#include "src/core/lib/transport/service_config.h"
+#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/status_metadata.h"
+
+namespace grpc_core {
+
+//
+// RequestRouter::Request::ResolverResultWaiter
+//
+
+// Handles waiting for a resolver result.
+// Used only for the first call on an idle channel.
+class RequestRouter::Request::ResolverResultWaiter {
+ public:
+  explicit ResolverResultWaiter(Request* request)
+      : request_router_(request->request_router_),
+        request_(request),
+        tracer_enabled_(request_router_->tracer_->enabled()) {
+    if (tracer_enabled_) {
+      gpr_log(GPR_INFO,
+              "request_router=%p request=%p: deferring pick pending resolver "
+              "result",
+              request_router_, request);
+    }
+    // Add closure to be run when a resolver result is available.
+    GRPC_CLOSURE_INIT(&done_closure_, &DoneLocked, this,
+                      grpc_combiner_scheduler(request_router_->combiner_));
+    AddToWaitingList();
+    // Set cancellation closure, so that we abort if the call is cancelled.
+    GRPC_CLOSURE_INIT(&cancel_closure_, &CancelLocked, this,
+                      grpc_combiner_scheduler(request_router_->combiner_));
+    grpc_call_combiner_set_notify_on_cancel(request->call_combiner_,
+                                            &cancel_closure_);
+  }
+
+ private:
+  // Adds done_closure_ to
+  // request_router_->waiting_for_resolver_result_closures_.
+  void AddToWaitingList() {
+    grpc_closure_list_append(
+        &request_router_->waiting_for_resolver_result_closures_, &done_closure_,
+        GRPC_ERROR_NONE);
+  }
+
+  // Invoked when a resolver result is available.
+  static void DoneLocked(void* arg, grpc_error* error) {
+    ResolverResultWaiter* self = static_cast<ResolverResultWaiter*>(arg);
+    RequestRouter* request_router = self->request_router_;
+    // If CancelLocked() has already run, delete ourselves without doing
+    // anything.  Note that the call stack may have already been destroyed,
+    // so it's not safe to access anything in state_.
+    if (GPR_UNLIKELY(self->finished_)) {
+      if (self->tracer_enabled_) {
+        gpr_log(GPR_INFO,
+                "request_router=%p: call cancelled before resolver result",
+                request_router);
+      }
+      Delete(self);
+      return;
+    }
+    // Otherwise, process the resolver result.
+    Request* request = self->request_;
+    if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) {
+      if (self->tracer_enabled_) {
+        gpr_log(GPR_INFO,
+                "request_router=%p request=%p: resolver failed to return data",
+                request_router, request);
+      }
+      GRPC_CLOSURE_RUN(request->on_route_done_, GRPC_ERROR_REF(error));
+    } else if (GPR_UNLIKELY(request_router->resolver_ == nullptr)) {
+      // Shutting down.
+      if (self->tracer_enabled_) {
+        gpr_log(GPR_INFO, "request_router=%p request=%p: resolver disconnected",
+                request_router, request);
+      }
+      GRPC_CLOSURE_RUN(request->on_route_done_,
+                       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Disconnected"));
+    } else if (GPR_UNLIKELY(request_router->lb_policy_ == nullptr)) {
+      // Transient resolver failure.
+      // If call has wait_for_ready=true, try again; otherwise, fail.
+      if (*request->pick_.initial_metadata_flags &
+          GRPC_INITIAL_METADATA_WAIT_FOR_READY) {
+        if (self->tracer_enabled_) {
+          gpr_log(GPR_INFO,
+                  "request_router=%p request=%p: resolver returned but no LB "
+                  "policy; wait_for_ready=true; trying again",
+                  request_router, request);
+        }
+        // Re-add ourselves to the waiting list.
+        self->AddToWaitingList();
+        // Return early so that we don't set finished_ to true below.
+        return;
+      } else {
+        if (self->tracer_enabled_) {
+          gpr_log(GPR_INFO,
+                  "request_router=%p request=%p: resolver returned but no LB "
+                  "policy; wait_for_ready=false; failing",
+                  request_router, request);
+        }
+        GRPC_CLOSURE_RUN(
+            request->on_route_done_,
+            grpc_error_set_int(
+                GRPC_ERROR_CREATE_FROM_STATIC_STRING("Name resolution failure"),
+                GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
+      }
+    } else {
+      if (self->tracer_enabled_) {
+        gpr_log(GPR_INFO,
+                "request_router=%p request=%p: resolver returned, doing LB "
+                "pick",
+                request_router, request);
+      }
+      request->ProcessServiceConfigAndStartLbPickLocked();
+    }
+    self->finished_ = true;
+  }
+
+  // Invoked when the call is cancelled.
+  // Note: This runs under the client_channel combiner, but will NOT be
+  // holding the call combiner.
+  static void CancelLocked(void* arg, grpc_error* error) {
+    ResolverResultWaiter* self = static_cast<ResolverResultWaiter*>(arg);
+    RequestRouter* request_router = self->request_router_;
+    // If DoneLocked() has already run, delete ourselves without doing anything.
+    if (self->finished_) {
+      Delete(self);
+      return;
+    }
+    Request* request = self->request_;
+    // If we are being cancelled, immediately invoke on_route_done_
+    // to propagate the error back to the caller.
+    if (error != GRPC_ERROR_NONE) {
+      if (self->tracer_enabled_) {
+        gpr_log(GPR_INFO,
+                "request_router=%p request=%p: cancelling call waiting for "
+                "name resolution",
+                request_router, request);
+      }
+      // Note: Although we are not in the call combiner here, we are
+      // basically stealing the call combiner from the pending pick, so
+      // it's safe to run on_route_done_ here -- we are essentially
+      // calling it here instead of calling it in DoneLocked().
+      GRPC_CLOSURE_RUN(request->on_route_done_,
+                       GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                           "Pick cancelled", &error, 1));
+    }
+    self->finished_ = true;
+  }
+
+  RequestRouter* request_router_;
+  Request* request_;
+  const bool tracer_enabled_;
+  grpc_closure done_closure_;
+  grpc_closure cancel_closure_;
+  bool finished_ = false;
+};
+
+//
+// RequestRouter::Request::AsyncPickCanceller
+//
+
+// Handles the call combiner cancellation callback for an async LB pick.
+class RequestRouter::Request::AsyncPickCanceller {
+ public:
+  explicit AsyncPickCanceller(Request* request)
+      : request_router_(request->request_router_),
+        request_(request),
+        tracer_enabled_(request_router_->tracer_->enabled()) {
+    GRPC_CALL_STACK_REF(request->owning_call_, "pick_callback_cancel");
+    // Set cancellation closure, so that we abort if the call is cancelled.
+    GRPC_CLOSURE_INIT(&cancel_closure_, &CancelLocked, this,
+                      grpc_combiner_scheduler(request_router_->combiner_));
+    grpc_call_combiner_set_notify_on_cancel(request->call_combiner_,
+                                            &cancel_closure_);
+  }
+
+  void MarkFinishedLocked() {
+    finished_ = true;
+    GRPC_CALL_STACK_UNREF(request_->owning_call_, "pick_callback_cancel");
+  }
+
+ private:
+  // Invoked when the call is cancelled.
+  // Note: This runs under the client_channel combiner, but will NOT be
+  // holding the call combiner.
+  static void CancelLocked(void* arg, grpc_error* error) {
+    AsyncPickCanceller* self = static_cast<AsyncPickCanceller*>(arg);
+    Request* request = self->request_;
+    RequestRouter* request_router = self->request_router_;
+    if (!self->finished_) {
+      // Note: request_router->lb_policy_ may have changed since we started our
+      // pick, in which case we will be cancelling the pick on a policy other
+      // than the one we started it on.  However, this will just be a no-op.
+      if (error != GRPC_ERROR_NONE && request_router->lb_policy_ != nullptr) {
+        if (self->tracer_enabled_) {
+          gpr_log(GPR_INFO,
+                  "request_router=%p request=%p: cancelling pick from LB "
+                  "policy %p",
+                  request_router, request, request_router->lb_policy_.get());
+        }
+        request_router->lb_policy_->CancelPickLocked(&request->pick_,
+                                                     GRPC_ERROR_REF(error));
+      }
+      request->pick_canceller_ = nullptr;
+      GRPC_CALL_STACK_UNREF(request->owning_call_, "pick_callback_cancel");
+    }
+    Delete(self);
+  }
+
+  RequestRouter* request_router_;
+  Request* request_;
+  const bool tracer_enabled_;
+  grpc_closure cancel_closure_;
+  bool finished_ = false;
+};
+
+//
+// RequestRouter::Request
+//
+
+RequestRouter::Request::Request(grpc_call_stack* owning_call,
+                                grpc_call_combiner* call_combiner,
+                                grpc_polling_entity* pollent,
+                                grpc_metadata_batch* send_initial_metadata,
+                                uint32_t* send_initial_metadata_flags,
+                                ApplyServiceConfigCallback apply_service_config,
+                                void* apply_service_config_user_data,
+                                grpc_closure* on_route_done)
+    : owning_call_(owning_call),
+      call_combiner_(call_combiner),
+      pollent_(pollent),
+      apply_service_config_(apply_service_config),
+      apply_service_config_user_data_(apply_service_config_user_data),
+      on_route_done_(on_route_done) {
+  pick_.initial_metadata = send_initial_metadata;
+  pick_.initial_metadata_flags = send_initial_metadata_flags;
+}
+
+RequestRouter::Request::~Request() {
+  if (pick_.connected_subchannel != nullptr) {
+    pick_.connected_subchannel.reset();
+  }
+  for (size_t i = 0; i < GRPC_CONTEXT_COUNT; ++i) {
+    if (pick_.subchannel_call_context[i].destroy != nullptr) {
+      pick_.subchannel_call_context[i].destroy(
+          pick_.subchannel_call_context[i].value);
+    }
+  }
+}
+
+// Invoked once resolver results are available.
+void RequestRouter::Request::ProcessServiceConfigAndStartLbPickLocked() {
+  // Get service config data if needed.
+  if (!apply_service_config_(apply_service_config_user_data_)) return;
+  // Start LB pick.
+  StartLbPickLocked();
+}
+
+void RequestRouter::Request::MaybeAddCallToInterestedPartiesLocked() {
+  if (!pollent_added_to_interested_parties_) {
+    pollent_added_to_interested_parties_ = true;
+    grpc_polling_entity_add_to_pollset_set(
+        pollent_, request_router_->interested_parties_);
+  }
+}
+
+void RequestRouter::Request::MaybeRemoveCallFromInterestedPartiesLocked() {
+  if (pollent_added_to_interested_parties_) {
+    pollent_added_to_interested_parties_ = false;
+    grpc_polling_entity_del_from_pollset_set(
+        pollent_, request_router_->interested_parties_);
+  }
+}
+
+// Starts a pick on the LB policy.
+void RequestRouter::Request::StartLbPickLocked() {
+  if (request_router_->tracer_->enabled()) {
+    gpr_log(GPR_INFO,
+            "request_router=%p request=%p: starting pick on lb_policy=%p",
+            request_router_, this, request_router_->lb_policy_.get());
+  }
+  GRPC_CLOSURE_INIT(&on_pick_done_, &LbPickDoneLocked, this,
+                    grpc_combiner_scheduler(request_router_->combiner_));
+  pick_.on_complete = &on_pick_done_;
+  GRPC_CALL_STACK_REF(owning_call_, "pick_callback");
+  grpc_error* error = GRPC_ERROR_NONE;
+  const bool pick_done =
+      request_router_->lb_policy_->PickLocked(&pick_, &error);
+  if (pick_done) {
+    // Pick completed synchronously.
+    if (request_router_->tracer_->enabled()) {
+      gpr_log(GPR_INFO,
+              "request_router=%p request=%p: pick completed synchronously",
+              request_router_, this);
+    }
+    GRPC_CLOSURE_RUN(on_route_done_, error);
+    GRPC_CALL_STACK_UNREF(owning_call_, "pick_callback");
+  } else {
+    // Pick will be returned asynchronously.
+    // Add the request's polling entity to the request_router's
+    // interested_parties, so that the I/O of the LB policy can be done
+    // under it.  It will be removed in LbPickDoneLocked().
+    MaybeAddCallToInterestedPartiesLocked();
+    // Request notification on call cancellation.
+    // We allocate a separate object to track cancellation, since the
+    // cancellation closure might still be pending when we need to reuse
+    // the memory in which this Request object is stored for a subsequent
+    // retry attempt.
+    pick_canceller_ = New<AsyncPickCanceller>(this);
+  }
+}
+
+// Callback invoked by LoadBalancingPolicy::PickLocked() for async picks.
+// Unrefs the LB policy and invokes on_route_done_.
+void RequestRouter::Request::LbPickDoneLocked(void* arg, grpc_error* error) {
+  Request* self = static_cast<Request*>(arg);
+  RequestRouter* request_router = self->request_router_;
+  if (request_router->tracer_->enabled()) {
+    gpr_log(GPR_INFO,
+            "request_router=%p request=%p: pick completed asynchronously",
+            request_router, self);
+  }
+  self->MaybeRemoveCallFromInterestedPartiesLocked();
+  if (self->pick_canceller_ != nullptr) {
+    self->pick_canceller_->MarkFinishedLocked();
+  }
+  GRPC_CLOSURE_RUN(self->on_route_done_, GRPC_ERROR_REF(error));
+  GRPC_CALL_STACK_UNREF(self->owning_call_, "pick_callback");
+}
+
+//
+// RequestRouter::LbConnectivityWatcher
+//
+
+class RequestRouter::LbConnectivityWatcher {
+ public:
+  LbConnectivityWatcher(RequestRouter* request_router,
+                        grpc_connectivity_state state,
+                        LoadBalancingPolicy* lb_policy,
+                        grpc_channel_stack* owning_stack,
+                        grpc_combiner* combiner)
+      : request_router_(request_router),
+        state_(state),
+        lb_policy_(lb_policy),
+        owning_stack_(owning_stack) {
+    GRPC_CHANNEL_STACK_REF(owning_stack_, "LbConnectivityWatcher");
+    GRPC_CLOSURE_INIT(&on_changed_, &OnLbPolicyStateChangedLocked, this,
+                      grpc_combiner_scheduler(combiner));
+    lb_policy_->NotifyOnStateChangeLocked(&state_, &on_changed_);
+  }
+
+  ~LbConnectivityWatcher() {
+    GRPC_CHANNEL_STACK_UNREF(owning_stack_, "LbConnectivityWatcher");
+  }
+
+ private:
+  static void OnLbPolicyStateChangedLocked(void* arg, grpc_error* error) {
+    LbConnectivityWatcher* self = static_cast<LbConnectivityWatcher*>(arg);
+    // If the notification is not for the current policy, we're stale,
+    // so delete ourselves.
+    if (self->lb_policy_ != self->request_router_->lb_policy_.get()) {
+      Delete(self);
+      return;
+    }
+    // Otherwise, process notification.
+    if (self->request_router_->tracer_->enabled()) {
+      gpr_log(GPR_INFO, "request_router=%p: lb_policy=%p state changed to %s",
+              self->request_router_, self->lb_policy_,
+              grpc_connectivity_state_name(self->state_));
+    }
+    self->request_router_->SetConnectivityStateLocked(
+        self->state_, GRPC_ERROR_REF(error), "lb_changed");
+    // If shutting down, terminate watch.
+    if (self->state_ == GRPC_CHANNEL_SHUTDOWN) {
+      Delete(self);
+      return;
+    }
+    // Renew watch.
+    self->lb_policy_->NotifyOnStateChangeLocked(&self->state_,
+                                                &self->on_changed_);
+  }
+
+  RequestRouter* request_router_;
+  grpc_connectivity_state state_;
+  // LB policy address. No ref held, so not safe to dereference unless
+  // it happens to match request_router->lb_policy_.
+  LoadBalancingPolicy* lb_policy_;
+  grpc_channel_stack* owning_stack_;
+  grpc_closure on_changed_;
+};
+
+//
+// RequestRounter::ReresolutionRequestHandler
+//
+
+class RequestRouter::ReresolutionRequestHandler {
+ public:
+  ReresolutionRequestHandler(RequestRouter* request_router,
+                             LoadBalancingPolicy* lb_policy,
+                             grpc_channel_stack* owning_stack,
+                             grpc_combiner* combiner)
+      : request_router_(request_router),
+        lb_policy_(lb_policy),
+        owning_stack_(owning_stack) {
+    GRPC_CHANNEL_STACK_REF(owning_stack_, "ReresolutionRequestHandler");
+    GRPC_CLOSURE_INIT(&closure_, &OnRequestReresolutionLocked, this,
+                      grpc_combiner_scheduler(combiner));
+    lb_policy_->SetReresolutionClosureLocked(&closure_);
+  }
+
+ private:
+  static void OnRequestReresolutionLocked(void* arg, grpc_error* error) {
+    ReresolutionRequestHandler* self =
+        static_cast<ReresolutionRequestHandler*>(arg);
+    RequestRouter* request_router = self->request_router_;
+    // If this invocation is for a stale LB policy, treat it as an LB shutdown
+    // signal.
+    if (self->lb_policy_ != request_router->lb_policy_.get() ||
+        error != GRPC_ERROR_NONE || request_router->resolver_ == nullptr) {
+      GRPC_CHANNEL_STACK_UNREF(request_router->owning_stack_,
+                               "ReresolutionRequestHandler");
+      Delete(self);
+      return;
+    }
+    if (request_router->tracer_->enabled()) {
+      gpr_log(GPR_INFO, "request_router=%p: started name re-resolving",
+              request_router);
+    }
+    request_router->resolver_->RequestReresolutionLocked();
+    // Give back the closure to the LB policy.
+    self->lb_policy_->SetReresolutionClosureLocked(&self->closure_);
+  }
+
+  RequestRouter* request_router_;
+  // LB policy address. No ref held, so not safe to dereference unless
+  // it happens to match request_router->lb_policy_.
+  LoadBalancingPolicy* lb_policy_;
+  grpc_channel_stack* owning_stack_;
+  grpc_closure closure_;
+};
+
+//
+// RequestRouter
+//
+
+RequestRouter::RequestRouter(
+    grpc_channel_stack* owning_stack, grpc_combiner* combiner,
+    grpc_client_channel_factory* client_channel_factory,
+    grpc_pollset_set* interested_parties, TraceFlag* tracer,
+    ProcessResolverResultCallback process_resolver_result,
+    void* process_resolver_result_user_data, const char* target_uri,
+    const grpc_channel_args* args, grpc_error** error)
+    : owning_stack_(owning_stack),
+      combiner_(combiner),
+      client_channel_factory_(client_channel_factory),
+      interested_parties_(interested_parties),
+      tracer_(tracer),
+      process_resolver_result_(process_resolver_result),
+      process_resolver_result_user_data_(process_resolver_result_user_data) {
+  GRPC_CLOSURE_INIT(&on_resolver_result_changed_,
+                    &RequestRouter::OnResolverResultChangedLocked, this,
+                    grpc_combiner_scheduler(combiner));
+  grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE,
+                               "request_router");
+  grpc_channel_args* new_args = nullptr;
+  if (process_resolver_result == nullptr) {
+    grpc_arg arg = grpc_channel_arg_integer_create(
+        const_cast<char*>(GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION), 0);
+    new_args = grpc_channel_args_copy_and_add(args, &arg, 1);
+  }
+  resolver_ = ResolverRegistry::CreateResolver(
+      target_uri, (new_args == nullptr ? args : new_args), interested_parties_,
+      combiner_);
+  grpc_channel_args_destroy(new_args);
+  if (resolver_ == nullptr) {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("resolver creation failed");
+  }
+}
+
+RequestRouter::~RequestRouter() {
+  if (resolver_ != nullptr) {
+    // The only way we can get here is if we never started resolving,
+    // because we take a ref to the channel stack when we start
+    // resolving and do not release it until the resolver callback is
+    // invoked after the resolver shuts down.
+    resolver_.reset();
+  }
+  if (lb_policy_ != nullptr) {
+    grpc_pollset_set_del_pollset_set(lb_policy_->interested_parties(),
+                                     interested_parties_);
+    lb_policy_.reset();
+  }
+  if (client_channel_factory_ != nullptr) {
+    grpc_client_channel_factory_unref(client_channel_factory_);
+  }
+  grpc_connectivity_state_destroy(&state_tracker_);
+}
+
+namespace {
+
+const char* GetChannelConnectivityStateChangeString(
+    grpc_connectivity_state state) {
+  switch (state) {
+    case GRPC_CHANNEL_IDLE:
+      return "Channel state change to IDLE";
+    case GRPC_CHANNEL_CONNECTING:
+      return "Channel state change to CONNECTING";
+    case GRPC_CHANNEL_READY:
+      return "Channel state change to READY";
+    case GRPC_CHANNEL_TRANSIENT_FAILURE:
+      return "Channel state change to TRANSIENT_FAILURE";
+    case GRPC_CHANNEL_SHUTDOWN:
+      return "Channel state change to SHUTDOWN";
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
+
+}  // namespace
+
+void RequestRouter::SetConnectivityStateLocked(grpc_connectivity_state state,
+                                               grpc_error* error,
+                                               const char* reason) {
+  if (lb_policy_ != nullptr) {
+    if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+      // Cancel picks with wait_for_ready=false.
+      lb_policy_->CancelMatchingPicksLocked(
+          /* mask= */ GRPC_INITIAL_METADATA_WAIT_FOR_READY,
+          /* check= */ 0, GRPC_ERROR_REF(error));
+    } else if (state == GRPC_CHANNEL_SHUTDOWN) {
+      // Cancel all picks.
+      lb_policy_->CancelMatchingPicksLocked(/* mask= */ 0, /* check= */ 0,
+                                            GRPC_ERROR_REF(error));
+    }
+  }
+  if (tracer_->enabled()) {
+    gpr_log(GPR_INFO, "request_router=%p: setting connectivity state to %s",
+            this, grpc_connectivity_state_name(state));
+  }
+  if (channelz_node_ != nullptr) {
+    channelz_node_->AddTraceEvent(
+        channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string(
+            GetChannelConnectivityStateChangeString(state)));
+  }
+  grpc_connectivity_state_set(&state_tracker_, state, error, reason);
+}
+
+void RequestRouter::StartResolvingLocked() {
+  if (tracer_->enabled()) {
+    gpr_log(GPR_INFO, "request_router=%p: starting name resolution", this);
+  }
+  GPR_ASSERT(!started_resolving_);
+  started_resolving_ = true;
+  GRPC_CHANNEL_STACK_REF(owning_stack_, "resolver");
+  resolver_->NextLocked(&resolver_result_, &on_resolver_result_changed_);
+}
+
+// Invoked from the resolver NextLocked() callback when the resolver
+// is shutting down.
+void RequestRouter::OnResolverShutdownLocked(grpc_error* error) {
+  if (tracer_->enabled()) {
+    gpr_log(GPR_INFO, "request_router=%p: shutting down", this);
+  }
+  if (lb_policy_ != nullptr) {
+    if (tracer_->enabled()) {
+      gpr_log(GPR_INFO, "request_router=%p: shutting down lb_policy=%p", this,
+              lb_policy_.get());
+    }
+    grpc_pollset_set_del_pollset_set(lb_policy_->interested_parties(),
+                                     interested_parties_);
+    lb_policy_.reset();
+  }
+  if (resolver_ != nullptr) {
+    // This should never happen; it can only be triggered by a resolver
+    // implementation spotaneously deciding to report shutdown without
+    // being orphaned.  This code is included just to be defensive.
+    if (tracer_->enabled()) {
+      gpr_log(GPR_INFO,
+              "request_router=%p: spontaneous shutdown from resolver %p", this,
+              resolver_.get());
+    }
+    resolver_.reset();
+    SetConnectivityStateLocked(GRPC_CHANNEL_SHUTDOWN,
+                               GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                                   "Resolver spontaneous shutdown", &error, 1),
+                               "resolver_spontaneous_shutdown");
+  }
+  grpc_closure_list_fail_all(&waiting_for_resolver_result_closures_,
+                             GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                                 "Channel disconnected", &error, 1));
+  GRPC_CLOSURE_LIST_SCHED(&waiting_for_resolver_result_closures_);
+  GRPC_CHANNEL_STACK_UNREF(owning_stack_, "resolver");
+  grpc_channel_args_destroy(resolver_result_);
+  resolver_result_ = nullptr;
+  GRPC_ERROR_UNREF(error);
+}
+
+// Creates a new LB policy, replacing any previous one.
+// If the new policy is created successfully, sets *connectivity_state and
+// *connectivity_error to its initial connectivity state; otherwise,
+// leaves them unchanged.
+void RequestRouter::CreateNewLbPolicyLocked(
+    const char* lb_policy_name, grpc_json* lb_config,
+    grpc_connectivity_state* connectivity_state,
+    grpc_error** connectivity_error, TraceStringVector* trace_strings) {
+  LoadBalancingPolicy::Args lb_policy_args;
+  lb_policy_args.combiner = combiner_;
+  lb_policy_args.client_channel_factory = client_channel_factory_;
+  lb_policy_args.args = resolver_result_;
+  lb_policy_args.lb_config = lb_config;
+  OrphanablePtr<LoadBalancingPolicy> new_lb_policy =
+      LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(lb_policy_name,
+                                                             lb_policy_args);
+  if (GPR_UNLIKELY(new_lb_policy == nullptr)) {
+    gpr_log(GPR_ERROR, "could not create LB policy \"%s\"", lb_policy_name);
+    if (channelz_node_ != nullptr) {
+      char* str;
+      gpr_asprintf(&str, "Could not create LB policy \'%s\'", lb_policy_name);
+      trace_strings->push_back(str);
+    }
+  } else {
+    if (tracer_->enabled()) {
+      gpr_log(GPR_INFO, "request_router=%p: created new LB policy \"%s\" (%p)",
+              this, lb_policy_name, new_lb_policy.get());
+    }
+    if (channelz_node_ != nullptr) {
+      char* str;
+      gpr_asprintf(&str, "Created new LB policy \'%s\'", lb_policy_name);
+      trace_strings->push_back(str);
+    }
+    // Swap out the LB policy and update the fds in interested_parties_.
+    if (lb_policy_ != nullptr) {
+      if (tracer_->enabled()) {
+        gpr_log(GPR_INFO, "request_router=%p: shutting down lb_policy=%p", this,
+                lb_policy_.get());
+      }
+      grpc_pollset_set_del_pollset_set(lb_policy_->interested_parties(),
+                                       interested_parties_);
+      lb_policy_->HandOffPendingPicksLocked(new_lb_policy.get());
+    }
+    lb_policy_ = std::move(new_lb_policy);
+    grpc_pollset_set_add_pollset_set(lb_policy_->interested_parties(),
+                                     interested_parties_);
+    // Create re-resolution request handler for the new LB policy.  It
+    // will delete itself when no longer needed.
+    New<ReresolutionRequestHandler>(this, lb_policy_.get(), owning_stack_,
+                                    combiner_);
+    // Get the new LB policy's initial connectivity state and start a
+    // connectivity watch.
+    GRPC_ERROR_UNREF(*connectivity_error);
+    *connectivity_state =
+        lb_policy_->CheckConnectivityLocked(connectivity_error);
+    if (exit_idle_when_lb_policy_arrives_) {
+      lb_policy_->ExitIdleLocked();
+      exit_idle_when_lb_policy_arrives_ = false;
+    }
+    // Create new watcher.  It will delete itself when done.
+    New<LbConnectivityWatcher>(this, *connectivity_state, lb_policy_.get(),
+                               owning_stack_, combiner_);
+  }
+}
+
+void RequestRouter::MaybeAddTraceMessagesForAddressChangesLocked(
+    TraceStringVector* trace_strings) {
+  const ServerAddressList* addresses =
+      FindServerAddressListChannelArg(resolver_result_);
+  const bool resolution_contains_addresses =
+      addresses != nullptr && addresses->size() > 0;
+  if (!resolution_contains_addresses &&
+      previous_resolution_contained_addresses_) {
+    trace_strings->push_back(gpr_strdup("Address list became empty"));
+  } else if (resolution_contains_addresses &&
+             !previous_resolution_contained_addresses_) {
+    trace_strings->push_back(gpr_strdup("Address list became non-empty"));
+  }
+  previous_resolution_contained_addresses_ = resolution_contains_addresses;
+}
+
+void RequestRouter::ConcatenateAndAddChannelTraceLocked(
+    TraceStringVector* trace_strings) const {
+  if (!trace_strings->empty()) {
+    gpr_strvec v;
+    gpr_strvec_init(&v);
+    gpr_strvec_add(&v, gpr_strdup("Resolution event: "));
+    bool is_first = 1;
+    for (size_t i = 0; i < trace_strings->size(); ++i) {
+      if (!is_first) gpr_strvec_add(&v, gpr_strdup(", "));
+      is_first = false;
+      gpr_strvec_add(&v, (*trace_strings)[i]);
+    }
+    char* flat;
+    size_t flat_len = 0;
+    flat = gpr_strvec_flatten(&v, &flat_len);
+    channelz_node_->AddTraceEvent(
+        grpc_core::channelz::ChannelTrace::Severity::Info,
+        grpc_slice_new(flat, flat_len, gpr_free));
+    gpr_strvec_destroy(&v);
+  }
+}
+
+// Callback invoked when a resolver result is available.
+void RequestRouter::OnResolverResultChangedLocked(void* arg,
+                                                  grpc_error* error) {
+  RequestRouter* self = static_cast<RequestRouter*>(arg);
+  if (self->tracer_->enabled()) {
+    const char* disposition =
+        self->resolver_result_ != nullptr
+            ? ""
+            : (error == GRPC_ERROR_NONE ? " (transient error)"
+                                        : " (resolver shutdown)");
+    gpr_log(GPR_INFO,
+            "request_router=%p: got resolver result: resolver_result=%p "
+            "error=%s%s",
+            self, self->resolver_result_, grpc_error_string(error),
+            disposition);
+  }
+  // Handle shutdown.
+  if (error != GRPC_ERROR_NONE || self->resolver_ == nullptr) {
+    self->OnResolverShutdownLocked(GRPC_ERROR_REF(error));
+    return;
+  }
+  // Data used to set the channel's connectivity state.
+  bool set_connectivity_state = true;
+  // We only want to trace the address resolution in the follow cases:
+  // (a) Address resolution resulted in service config change.
+  // (b) Address resolution that causes number of backends to go from
+  //     zero to non-zero.
+  // (c) Address resolution that causes number of backends to go from
+  //     non-zero to zero.
+  // (d) Address resolution that causes a new LB policy to be created.
+  //
+  // we track a list of strings to eventually be concatenated and traced.
+  TraceStringVector trace_strings;
+  grpc_connectivity_state connectivity_state = GRPC_CHANNEL_TRANSIENT_FAILURE;
+  grpc_error* connectivity_error =
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("No load balancing policy");
+  // resolver_result_ will be null in the case of a transient
+  // resolution error.  In that case, we don't have any new result to
+  // process, which means that we keep using the previous result (if any).
+  if (self->resolver_result_ == nullptr) {
+    if (self->tracer_->enabled()) {
+      gpr_log(GPR_INFO, "request_router=%p: resolver transient failure", self);
+    }
+    // Don't override connectivity state if we already have an LB policy.
+    if (self->lb_policy_ != nullptr) set_connectivity_state = false;
+  } else {
+    // Parse the resolver result.
+    const char* lb_policy_name = nullptr;
+    grpc_json* lb_policy_config = nullptr;
+    const bool service_config_changed = self->process_resolver_result_(
+        self->process_resolver_result_user_data_, *self->resolver_result_,
+        &lb_policy_name, &lb_policy_config);
+    GPR_ASSERT(lb_policy_name != nullptr);
+    // Check to see if we're already using the right LB policy.
+    const bool lb_policy_name_changed =
+        self->lb_policy_ == nullptr ||
+        strcmp(self->lb_policy_->name(), lb_policy_name) != 0;
+    if (self->lb_policy_ != nullptr && !lb_policy_name_changed) {
+      // Continue using the same LB policy.  Update with new addresses.
+      if (self->tracer_->enabled()) {
+        gpr_log(GPR_INFO,
+                "request_router=%p: updating existing LB policy \"%s\" (%p)",
+                self, lb_policy_name, self->lb_policy_.get());
+      }
+      self->lb_policy_->UpdateLocked(*self->resolver_result_, lb_policy_config);
+      // No need to set the channel's connectivity state; the existing
+      // watch on the LB policy will take care of that.
+      set_connectivity_state = false;
+    } else {
+      // Instantiate new LB policy.
+      self->CreateNewLbPolicyLocked(lb_policy_name, lb_policy_config,
+                                    &connectivity_state, &connectivity_error,
+                                    &trace_strings);
+    }
+    // Add channel trace event.
+    if (self->channelz_node_ != nullptr) {
+      if (service_config_changed) {
+        // TODO(ncteisen): might be worth somehow including a snippet of the
+        // config in the trace, at the risk of bloating the trace logs.
+        trace_strings.push_back(gpr_strdup("Service config changed"));
+      }
+      self->MaybeAddTraceMessagesForAddressChangesLocked(&trace_strings);
+      self->ConcatenateAndAddChannelTraceLocked(&trace_strings);
+    }
+    // Clean up.
+    grpc_channel_args_destroy(self->resolver_result_);
+    self->resolver_result_ = nullptr;
+  }
+  // Set the channel's connectivity state if needed.
+  if (set_connectivity_state) {
+    self->SetConnectivityStateLocked(connectivity_state, connectivity_error,
+                                     "resolver_result");
+  } else {
+    GRPC_ERROR_UNREF(connectivity_error);
+  }
+  // Invoke closures that were waiting for results and renew the watch.
+  GRPC_CLOSURE_LIST_SCHED(&self->waiting_for_resolver_result_closures_);
+  self->resolver_->NextLocked(&self->resolver_result_,
+                              &self->on_resolver_result_changed_);
+}
+
+void RequestRouter::RouteCallLocked(Request* request) {
+  GPR_ASSERT(request->pick_.connected_subchannel == nullptr);
+  request->request_router_ = this;
+  if (lb_policy_ != nullptr) {
+    // We already have resolver results, so process the service config
+    // and start an LB pick.
+    request->ProcessServiceConfigAndStartLbPickLocked();
+  } else if (resolver_ == nullptr) {
+    GRPC_CLOSURE_RUN(request->on_route_done_,
+                     GRPC_ERROR_CREATE_FROM_STATIC_STRING("Disconnected"));
+  } else {
+    // We do not yet have an LB policy, so wait for a resolver result.
+    if (!started_resolving_) {
+      StartResolvingLocked();
+    }
+    // Create a new waiter, which will delete itself when done.
+    New<Request::ResolverResultWaiter>(request);
+    // Add the request's polling entity to the request_router's
+    // interested_parties, so that the I/O of the resolver can be done
+    // under it.  It will be removed in LbPickDoneLocked().
+    request->MaybeAddCallToInterestedPartiesLocked();
+  }
+}
+
+void RequestRouter::ShutdownLocked(grpc_error* error) {
+  if (resolver_ != nullptr) {
+    SetConnectivityStateLocked(GRPC_CHANNEL_SHUTDOWN, GRPC_ERROR_REF(error),
+                               "disconnect");
+    resolver_.reset();
+    if (!started_resolving_) {
+      grpc_closure_list_fail_all(&waiting_for_resolver_result_closures_,
+                                 GRPC_ERROR_REF(error));
+      GRPC_CLOSURE_LIST_SCHED(&waiting_for_resolver_result_closures_);
+    }
+    if (lb_policy_ != nullptr) {
+      grpc_pollset_set_del_pollset_set(lb_policy_->interested_parties(),
+                                       interested_parties_);
+      lb_policy_.reset();
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_connectivity_state RequestRouter::GetConnectivityState() {
+  return grpc_connectivity_state_check(&state_tracker_);
+}
+
+void RequestRouter::NotifyOnConnectivityStateChange(
+    grpc_connectivity_state* state, grpc_closure* closure) {
+  grpc_connectivity_state_notify_on_state_change(&state_tracker_, state,
+                                                 closure);
+}
+
+void RequestRouter::ExitIdleLocked() {
+  if (lb_policy_ != nullptr) {
+    lb_policy_->ExitIdleLocked();
+  } else {
+    exit_idle_when_lb_policy_arrives_ = true;
+    if (!started_resolving_ && resolver_ != nullptr) {
+      StartResolvingLocked();
+    }
+  }
+}
+
+void RequestRouter::ResetConnectionBackoffLocked() {
+  if (resolver_ != nullptr) {
+    resolver_->ResetBackoffLocked();
+    resolver_->RequestReresolutionLocked();
+  }
+  if (lb_policy_ != nullptr) {
+    lb_policy_->ResetBackoffLocked();
+  }
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/request_routing.h b/third_party/grpc/src/core/ext/filters/client_channel/request_routing.h
new file mode 100644
index 0000000..0c67122
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/request_routing.h
@@ -0,0 +1,177 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_REQUEST_ROUTING_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_REQUEST_ROUTING_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
+#include "src/core/ext/filters/client_channel/client_channel_factory.h"
+#include "src/core/ext/filters/client_channel/lb_policy.h"
+#include "src/core/ext/filters/client_channel/resolver.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/iomgr/call_combiner.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/metadata_batch.h"
+
+namespace grpc_core {
+
+class RequestRouter {
+ public:
+  class Request {
+   public:
+    // Synchronous callback that applies the service config to a call.
+    // Returns false if the call should be failed.
+    typedef bool (*ApplyServiceConfigCallback)(void* user_data);
+
+    Request(grpc_call_stack* owning_call, grpc_call_combiner* call_combiner,
+            grpc_polling_entity* pollent,
+            grpc_metadata_batch* send_initial_metadata,
+            uint32_t* send_initial_metadata_flags,
+            ApplyServiceConfigCallback apply_service_config,
+            void* apply_service_config_user_data, grpc_closure* on_route_done);
+
+    ~Request();
+
+    // TODO(roth): It seems a bit ugly to expose this member in a
+    // non-const way.  Find a better API to avoid this.
+    LoadBalancingPolicy::PickState* pick() { return &pick_; }
+
+   private:
+    friend class RequestRouter;
+
+    class ResolverResultWaiter;
+    class AsyncPickCanceller;
+
+    void ProcessServiceConfigAndStartLbPickLocked();
+    void StartLbPickLocked();
+    static void LbPickDoneLocked(void* arg, grpc_error* error);
+
+    void MaybeAddCallToInterestedPartiesLocked();
+    void MaybeRemoveCallFromInterestedPartiesLocked();
+
+    // Populated by caller.
+    grpc_call_stack* owning_call_;
+    grpc_call_combiner* call_combiner_;
+    grpc_polling_entity* pollent_;
+    ApplyServiceConfigCallback apply_service_config_;
+    void* apply_service_config_user_data_;
+    grpc_closure* on_route_done_;
+    LoadBalancingPolicy::PickState pick_;
+
+    // Internal state.
+    RequestRouter* request_router_ = nullptr;
+    bool pollent_added_to_interested_parties_ = false;
+    grpc_closure on_pick_done_;
+    AsyncPickCanceller* pick_canceller_ = nullptr;
+  };
+
+  // Synchronous callback that takes the service config JSON string and
+  // LB policy name.
+  // Returns true if the service config has changed since the last result.
+  typedef bool (*ProcessResolverResultCallback)(void* user_data,
+                                                const grpc_channel_args& args,
+                                                const char** lb_policy_name,
+                                                grpc_json** lb_policy_config);
+
+  RequestRouter(grpc_channel_stack* owning_stack, grpc_combiner* combiner,
+                grpc_client_channel_factory* client_channel_factory,
+                grpc_pollset_set* interested_parties, TraceFlag* tracer,
+                ProcessResolverResultCallback process_resolver_result,
+                void* process_resolver_result_user_data, const char* target_uri,
+                const grpc_channel_args* args, grpc_error** error);
+
+  ~RequestRouter();
+
+  void set_channelz_node(channelz::ClientChannelNode* channelz_node) {
+    channelz_node_ = channelz_node;
+  }
+
+  void RouteCallLocked(Request* request);
+
+  // TODO(roth): Add methods to cancel picks.
+
+  void ShutdownLocked(grpc_error* error);
+
+  void ExitIdleLocked();
+  void ResetConnectionBackoffLocked();
+
+  grpc_connectivity_state GetConnectivityState();
+  void NotifyOnConnectivityStateChange(grpc_connectivity_state* state,
+                                       grpc_closure* closure);
+
+  LoadBalancingPolicy* lb_policy() const { return lb_policy_.get(); }
+
+ private:
+  using TraceStringVector = grpc_core::InlinedVector<char*, 3>;
+
+  class ReresolutionRequestHandler;
+  class LbConnectivityWatcher;
+
+  void StartResolvingLocked();
+  void OnResolverShutdownLocked(grpc_error* error);
+  void CreateNewLbPolicyLocked(const char* lb_policy_name, grpc_json* lb_config,
+                               grpc_connectivity_state* connectivity_state,
+                               grpc_error** connectivity_error,
+                               TraceStringVector* trace_strings);
+  void MaybeAddTraceMessagesForAddressChangesLocked(
+      TraceStringVector* trace_strings);
+  void ConcatenateAndAddChannelTraceLocked(
+      TraceStringVector* trace_strings) const;
+  static void OnResolverResultChangedLocked(void* arg, grpc_error* error);
+
+  void SetConnectivityStateLocked(grpc_connectivity_state state,
+                                  grpc_error* error, const char* reason);
+
+  // Passed in from caller at construction time.
+  grpc_channel_stack* owning_stack_;
+  grpc_combiner* combiner_;
+  grpc_client_channel_factory* client_channel_factory_;
+  grpc_pollset_set* interested_parties_;
+  TraceFlag* tracer_;
+
+  channelz::ClientChannelNode* channelz_node_ = nullptr;
+
+  // Resolver and associated state.
+  OrphanablePtr<Resolver> resolver_;
+  ProcessResolverResultCallback process_resolver_result_;
+  void* process_resolver_result_user_data_;
+  bool started_resolving_ = false;
+  grpc_channel_args* resolver_result_ = nullptr;
+  bool previous_resolution_contained_addresses_ = false;
+  grpc_closure_list waiting_for_resolver_result_closures_;
+  grpc_closure on_resolver_result_changed_;
+
+  // LB policy and associated state.
+  OrphanablePtr<LoadBalancingPolicy> lb_policy_;
+  bool exit_idle_when_lb_policy_arrives_ = false;
+
+  grpc_connectivity_state_tracker state_tracker_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_REQUEST_ROUTING_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver.cc
new file mode 100644
index 0000000..601b08b
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver.cc
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/resolver.h"
+#include "src/core/lib/iomgr/combiner.h"
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount(false,
+                                                           "resolver_refcount");
+
+namespace grpc_core {
+
+Resolver::Resolver(grpc_combiner* combiner)
+    : InternallyRefCounted(&grpc_trace_resolver_refcount),
+      combiner_(GRPC_COMBINER_REF(combiner, "resolver")) {}
+
+Resolver::~Resolver() { GRPC_COMBINER_UNREF(combiner_, "resolver"); }
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver.h
new file mode 100644
index 0000000..9da849a
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver.h
@@ -0,0 +1,133 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/iomgr.h"
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount;
+
+namespace grpc_core {
+
+/// Interface for name resolution.
+///
+/// This interface is designed to support both push-based and pull-based
+/// mechanisms.  A push-based mechanism is one where the resolver will
+/// subscribe to updates for a given name, and the name service will
+/// proactively send new data to the resolver whenever the data associated
+/// with the name changes.  A pull-based mechanism is one where the resolver
+/// needs to query the name service again to get updated information (e.g.,
+/// DNS).
+///
+/// Note: All methods with a "Locked" suffix must be called from the
+/// combiner passed to the constructor.
+class Resolver : public InternallyRefCounted<Resolver> {
+ public:
+  // Not copyable nor movable.
+  Resolver(const Resolver&) = delete;
+  Resolver& operator=(const Resolver&) = delete;
+
+  /// Requests a callback when a new result becomes available.
+  /// When the new result is available, sets \a *result to the new result
+  /// and schedules \a on_complete for execution.
+  /// Upon transient failure, sets \a *result to nullptr and schedules
+  /// \a on_complete with no error.
+  /// If resolution is fatally broken, sets \a *result to nullptr and
+  /// schedules \a on_complete with an error.
+  /// TODO(roth): When we have time, improve the way this API represents
+  /// transient failure vs. shutdown.
+  ///
+  /// Note that the client channel will almost always have a request
+  /// to \a NextLocked() pending.  When it gets the callback, it will
+  /// process the new result and then immediately make another call to
+  /// \a NextLocked().  This allows push-based resolvers to provide new
+  /// data as soon as it becomes available.
+  virtual void NextLocked(grpc_channel_args** result,
+                          grpc_closure* on_complete) GRPC_ABSTRACT;
+
+  /// Asks the resolver to obtain an updated resolver result, if
+  /// applicable.
+  ///
+  /// This is useful for pull-based implementations to decide when to
+  /// re-resolve.  However, the implementation is not required to
+  /// re-resolve immediately upon receiving this call; it may instead
+  /// elect to delay based on some configured minimum time between
+  /// queries, to avoid hammering the name service with queries.
+  ///
+  /// For push-based implementations, this may be a no-op.
+  ///
+  /// If this causes new data to become available, then the currently
+  /// pending call to \a NextLocked() will return the new result.
+  virtual void RequestReresolutionLocked() {}
+
+  /// Resets the re-resolution backoff, if any.
+  /// This needs to be implemented only by pull-based implementations;
+  /// for push-based implementations, it will be a no-op.
+  /// TODO(roth): Pull the backoff code out of resolver and into
+  /// client_channel, so that it can be shared across resolver
+  /// implementations.  At that point, this method can go away.
+  virtual void ResetBackoffLocked() {}
+
+  void Orphan() override {
+    // Invoke ShutdownAndUnrefLocked() inside of the combiner.
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_CREATE(&Resolver::ShutdownAndUnrefLocked, this,
+                            grpc_combiner_scheduler(combiner_)),
+        GRPC_ERROR_NONE);
+  }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+  /// Does NOT take ownership of the reference to \a combiner.
+  // TODO(roth): Once we have a C++-like interface for combiners, this
+  // API should change to take a RefCountedPtr<>, so that we always take
+  // ownership of a new ref.
+  explicit Resolver(grpc_combiner* combiner);
+
+  virtual ~Resolver();
+
+  /// Shuts down the resolver.  If there is a pending call to
+  /// NextLocked(), the callback will be scheduled with an error.
+  virtual void ShutdownLocked() GRPC_ABSTRACT;
+
+  grpc_combiner* combiner() const { return combiner_; }
+
+ private:
+  static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored) {
+    Resolver* resolver = static_cast<Resolver*>(arg);
+    resolver->ShutdownLocked();
+    resolver->Unref();
+  }
+
+  grpc_combiner* combiner_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/README.md b/third_party/grpc/src/core/ext/filters/client_channel/resolver/README.md
new file mode 100644
index 0000000..b0e234e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/README.md
@@ -0,0 +1,4 @@
+# Resolver
+
+Implementations of various name resolution schemes.
+See the [naming spec](/doc/naming.md).
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
new file mode 100644
index 0000000..abacd0c
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc
@@ -0,0 +1,516 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#if GRPC_ARES == 1 && !defined(GRPC_UV)
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include <address_sorting/address_sorting.h>
+
+#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/gethostname.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/transport/service_config.h"
+
+#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
+#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
+#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
+#define GRPC_DNS_RECONNECT_JITTER 0.2
+
+namespace grpc_core {
+
+namespace {
+
+const char kDefaultPort[] = "https";
+
+class AresDnsResolver : public Resolver {
+ public:
+  explicit AresDnsResolver(const ResolverArgs& args);
+
+  void NextLocked(grpc_channel_args** result,
+                  grpc_closure* on_complete) override;
+
+  void RequestReresolutionLocked() override;
+
+  void ResetBackoffLocked() override;
+
+  void ShutdownLocked() override;
+
+ private:
+  virtual ~AresDnsResolver();
+
+  void MaybeStartResolvingLocked();
+  void StartResolvingLocked();
+  void MaybeFinishNextLocked();
+
+  static void OnNextResolutionLocked(void* arg, grpc_error* error);
+  static void OnResolvedLocked(void* arg, grpc_error* error);
+
+  /// DNS server to use (if not system default)
+  char* dns_server_;
+  /// name to resolve (usually the same as target_name)
+  char* name_to_resolve_;
+  /// channel args
+  grpc_channel_args* channel_args_;
+  /// whether to request the service config
+  bool request_service_config_;
+  /// pollset_set to drive the name resolution process
+  grpc_pollset_set* interested_parties_;
+  /// closures used by the combiner
+  grpc_closure on_next_resolution_;
+  grpc_closure on_resolved_;
+  /// are we currently resolving?
+  bool resolving_ = false;
+  /// the pending resolving request
+  grpc_ares_request* pending_request_ = nullptr;
+  /// which version of the result have we published?
+  int published_version_ = 0;
+  /// which version of the result is current?
+  int resolved_version_ = 0;
+  /// pending next completion, or NULL
+  grpc_closure* next_completion_ = nullptr;
+  /// target result address for next completion
+  grpc_channel_args** target_result_ = nullptr;
+  /// current (fully resolved) result
+  grpc_channel_args* resolved_result_ = nullptr;
+  /// next resolution timer
+  bool have_next_resolution_timer_ = false;
+  grpc_timer next_resolution_timer_;
+  /// min interval between DNS requests
+  grpc_millis min_time_between_resolutions_;
+  /// timestamp of last DNS request
+  grpc_millis last_resolution_timestamp_ = -1;
+  /// retry backoff state
+  BackOff backoff_;
+  /// currently resolving addresses
+  UniquePtr<ServerAddressList> addresses_;
+  /// currently resolving service config
+  char* service_config_json_ = nullptr;
+  // has shutdown been initiated
+  bool shutdown_initiated_ = false;
+  // timeout in milliseconds for active DNS queries
+  int query_timeout_ms_;
+};
+
+AresDnsResolver::AresDnsResolver(const ResolverArgs& args)
+    : Resolver(args.combiner),
+      backoff_(
+          BackOff::Options()
+              .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS *
+                                   1000)
+              .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
+              .set_jitter(GRPC_DNS_RECONNECT_JITTER)
+              .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) {
+  // Get name to resolve from URI path.
+  const char* path = args.uri->path;
+  if (path[0] == '/') ++path;
+  name_to_resolve_ = gpr_strdup(path);
+  // Get DNS server from URI authority.
+  dns_server_ = nullptr;
+  if (0 != strcmp(args.uri->authority, "")) {
+    dns_server_ = gpr_strdup(args.uri->authority);
+  }
+  channel_args_ = grpc_channel_args_copy(args.args);
+  const grpc_arg* arg = grpc_channel_args_find(
+      channel_args_, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION);
+  grpc_integer_options integer_options = {false, false, true};
+  request_service_config_ = !grpc_channel_arg_get_integer(arg, integer_options);
+  arg = grpc_channel_args_find(channel_args_,
+                               GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS);
+  min_time_between_resolutions_ =
+      grpc_channel_arg_get_integer(arg, {1000, 0, INT_MAX});
+  interested_parties_ = grpc_pollset_set_create();
+  if (args.pollset_set != nullptr) {
+    grpc_pollset_set_add_pollset_set(interested_parties_, args.pollset_set);
+  }
+  GRPC_CLOSURE_INIT(&on_next_resolution_, OnNextResolutionLocked, this,
+                    grpc_combiner_scheduler(combiner()));
+  GRPC_CLOSURE_INIT(&on_resolved_, OnResolvedLocked, this,
+                    grpc_combiner_scheduler(combiner()));
+  const grpc_arg* query_timeout_ms_arg =
+      grpc_channel_args_find(channel_args_, GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS);
+  query_timeout_ms_ = grpc_channel_arg_get_integer(
+      query_timeout_ms_arg,
+      {GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS, 0, INT_MAX});
+}
+
+AresDnsResolver::~AresDnsResolver() {
+  GRPC_CARES_TRACE_LOG("resolver:%p destroying AresDnsResolver", this);
+  if (resolved_result_ != nullptr) {
+    grpc_channel_args_destroy(resolved_result_);
+  }
+  grpc_pollset_set_destroy(interested_parties_);
+  gpr_free(dns_server_);
+  gpr_free(name_to_resolve_);
+  grpc_channel_args_destroy(channel_args_);
+}
+
+void AresDnsResolver::NextLocked(grpc_channel_args** target_result,
+                                 grpc_closure* on_complete) {
+  GRPC_CARES_TRACE_LOG("resolver:%p AresDnsResolver::NextLocked() is called.",
+                       this);
+  GPR_ASSERT(next_completion_ == nullptr);
+  next_completion_ = on_complete;
+  target_result_ = target_result;
+  if (resolved_version_ == 0 && !resolving_) {
+    MaybeStartResolvingLocked();
+  } else {
+    MaybeFinishNextLocked();
+  }
+}
+
+void AresDnsResolver::RequestReresolutionLocked() {
+  if (!resolving_) {
+    MaybeStartResolvingLocked();
+  }
+}
+
+void AresDnsResolver::ResetBackoffLocked() {
+  if (have_next_resolution_timer_) {
+    grpc_timer_cancel(&next_resolution_timer_);
+  }
+  backoff_.Reset();
+}
+
+void AresDnsResolver::ShutdownLocked() {
+  shutdown_initiated_ = true;
+  if (have_next_resolution_timer_) {
+    grpc_timer_cancel(&next_resolution_timer_);
+  }
+  if (pending_request_ != nullptr) {
+    grpc_cancel_ares_request_locked(pending_request_);
+  }
+  if (next_completion_ != nullptr) {
+    *target_result_ = nullptr;
+    GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                             "Resolver Shutdown"));
+    next_completion_ = nullptr;
+  }
+}
+
+void AresDnsResolver::OnNextResolutionLocked(void* arg, grpc_error* error) {
+  AresDnsResolver* r = static_cast<AresDnsResolver*>(arg);
+  GRPC_CARES_TRACE_LOG(
+      "resolver:%p re-resolution timer fired. error: %s. shutdown_initiated_: "
+      "%d",
+      r, grpc_error_string(error), r->shutdown_initiated_);
+  r->have_next_resolution_timer_ = false;
+  if (error == GRPC_ERROR_NONE && !r->shutdown_initiated_) {
+    if (!r->resolving_) {
+      GRPC_CARES_TRACE_LOG(
+          "resolver:%p start resolving due to re-resolution timer", r);
+      r->StartResolvingLocked();
+    }
+  }
+  r->Unref(DEBUG_LOCATION, "next_resolution_timer");
+}
+
+bool ValueInJsonArray(grpc_json* array, const char* value) {
+  for (grpc_json* entry = array->child; entry != nullptr; entry = entry->next) {
+    if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) {
+      return true;
+    }
+  }
+  return false;
+}
+
+char* ChooseServiceConfig(char* service_config_choice_json) {
+  grpc_json* choices_json = grpc_json_parse_string(service_config_choice_json);
+  if (choices_json == nullptr || choices_json->type != GRPC_JSON_ARRAY) {
+    gpr_log(GPR_ERROR, "cannot parse service config JSON string");
+    return nullptr;
+  }
+  char* service_config = nullptr;
+  for (grpc_json* choice = choices_json->child; choice != nullptr;
+       choice = choice->next) {
+    if (choice->type != GRPC_JSON_OBJECT) {
+      gpr_log(GPR_ERROR, "cannot parse service config JSON string");
+      break;
+    }
+    grpc_json* service_config_json = nullptr;
+    for (grpc_json* field = choice->child; field != nullptr;
+         field = field->next) {
+      // Check client language, if specified.
+      if (strcmp(field->key, "clientLanguage") == 0) {
+        if (field->type != GRPC_JSON_ARRAY || !ValueInJsonArray(field, "c++")) {
+          service_config_json = nullptr;
+          break;
+        }
+      }
+      // Check client hostname, if specified.
+      if (strcmp(field->key, "clientHostname") == 0) {
+        char* hostname = grpc_gethostname();
+        if (hostname == nullptr || field->type != GRPC_JSON_ARRAY ||
+            !ValueInJsonArray(field, hostname)) {
+          service_config_json = nullptr;
+          break;
+        }
+      }
+      // Check percentage, if specified.
+      if (strcmp(field->key, "percentage") == 0) {
+        if (field->type != GRPC_JSON_NUMBER) {
+          service_config_json = nullptr;
+          break;
+        }
+        int random_pct = rand() % 100;
+        int percentage;
+        if (sscanf(field->value, "%d", &percentage) != 1 ||
+            random_pct > percentage || percentage == 0) {
+          service_config_json = nullptr;
+          break;
+        }
+      }
+      // Save service config.
+      if (strcmp(field->key, "serviceConfig") == 0) {
+        if (field->type == GRPC_JSON_OBJECT) {
+          service_config_json = field;
+        }
+      }
+    }
+    if (service_config_json != nullptr) {
+      service_config = grpc_json_dump_to_string(service_config_json, 0);
+      break;
+    }
+  }
+  grpc_json_destroy(choices_json);
+  return service_config;
+}
+
+void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) {
+  AresDnsResolver* r = static_cast<AresDnsResolver*>(arg);
+  grpc_channel_args* result = nullptr;
+  GPR_ASSERT(r->resolving_);
+  r->resolving_ = false;
+  gpr_free(r->pending_request_);
+  r->pending_request_ = nullptr;
+  if (r->addresses_ != nullptr) {
+    static const char* args_to_remove[1];
+    size_t num_args_to_remove = 0;
+    grpc_arg args_to_add[2];
+    size_t num_args_to_add = 0;
+    args_to_add[num_args_to_add++] =
+        CreateServerAddressListChannelArg(r->addresses_.get());
+    char* service_config_string = nullptr;
+    if (r->service_config_json_ != nullptr) {
+      service_config_string = ChooseServiceConfig(r->service_config_json_);
+      gpr_free(r->service_config_json_);
+      if (service_config_string != nullptr) {
+        GRPC_CARES_TRACE_LOG("resolver:%p selected service config choice: %s",
+                             r, service_config_string);
+        args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG;
+        args_to_add[num_args_to_add++] = grpc_channel_arg_string_create(
+            (char*)GRPC_ARG_SERVICE_CONFIG, service_config_string);
+      }
+    }
+    result = grpc_channel_args_copy_and_add_and_remove(
+        r->channel_args_, args_to_remove, num_args_to_remove, args_to_add,
+        num_args_to_add);
+    gpr_free(service_config_string);
+    r->addresses_.reset();
+    // Reset backoff state so that we start from the beginning when the
+    // next request gets triggered.
+    r->backoff_.Reset();
+  } else if (!r->shutdown_initiated_) {
+    const char* msg = grpc_error_string(error);
+    GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed: %s", r, msg);
+    grpc_millis next_try = r->backoff_.NextAttemptTime();
+    grpc_millis timeout = next_try - ExecCtx::Get()->Now();
+    GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed (will retry): %s",
+                         r, grpc_error_string(error));
+    GPR_ASSERT(!r->have_next_resolution_timer_);
+    r->have_next_resolution_timer_ = true;
+    // TODO(roth): We currently deal with this ref manually.  Once the
+    // new closure API is done, find a way to track this ref with the timer
+    // callback as part of the type system.
+    RefCountedPtr<Resolver> self = r->Ref(DEBUG_LOCATION, "retry-timer");
+    self.release();
+    if (timeout > 0) {
+      GRPC_CARES_TRACE_LOG("resolver:%p retrying in %" PRId64 " milliseconds",
+                           r, timeout);
+    } else {
+      GRPC_CARES_TRACE_LOG("resolver:%p retrying immediately", r);
+    }
+    grpc_timer_init(&r->next_resolution_timer_, next_try,
+                    &r->on_next_resolution_);
+  }
+  if (r->resolved_result_ != nullptr) {
+    grpc_channel_args_destroy(r->resolved_result_);
+  }
+  r->resolved_result_ = result;
+  ++r->resolved_version_;
+  r->MaybeFinishNextLocked();
+  r->Unref(DEBUG_LOCATION, "dns-resolving");
+}
+
+void AresDnsResolver::MaybeStartResolvingLocked() {
+  // If there is an existing timer, the time it fires is the earliest time we
+  // can start the next resolution.
+  if (have_next_resolution_timer_) return;
+  if (last_resolution_timestamp_ >= 0) {
+    const grpc_millis earliest_next_resolution =
+        last_resolution_timestamp_ + min_time_between_resolutions_;
+    const grpc_millis ms_until_next_resolution =
+        earliest_next_resolution - grpc_core::ExecCtx::Get()->Now();
+    if (ms_until_next_resolution > 0) {
+      const grpc_millis last_resolution_ago =
+          grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_;
+      GRPC_CARES_TRACE_LOG(
+          "resolver:%p In cooldown from last resolution (from %" PRId64
+          " ms ago). Will resolve again in %" PRId64 " ms",
+          this, last_resolution_ago, ms_until_next_resolution);
+      have_next_resolution_timer_ = true;
+      // TODO(roth): We currently deal with this ref manually.  Once the
+      // new closure API is done, find a way to track this ref with the timer
+      // callback as part of the type system.
+      RefCountedPtr<Resolver> self =
+          Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown");
+      self.release();
+      grpc_timer_init(&next_resolution_timer_, ms_until_next_resolution,
+                      &on_next_resolution_);
+      return;
+    }
+  }
+  StartResolvingLocked();
+}
+
+void AresDnsResolver::StartResolvingLocked() {
+  // TODO(roth): We currently deal with this ref manually.  Once the
+  // new closure API is done, find a way to track this ref with the timer
+  // callback as part of the type system.
+  RefCountedPtr<Resolver> self = Ref(DEBUG_LOCATION, "dns-resolving");
+  self.release();
+  GPR_ASSERT(!resolving_);
+  resolving_ = true;
+  service_config_json_ = nullptr;
+  pending_request_ = grpc_dns_lookup_ares_locked(
+      dns_server_, name_to_resolve_, kDefaultPort, interested_parties_,
+      &on_resolved_, &addresses_, true /* check_grpclb */,
+      request_service_config_ ? &service_config_json_ : nullptr,
+      query_timeout_ms_, combiner());
+  last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now();
+  GRPC_CARES_TRACE_LOG("resolver:%p Started resolving. pending_request_:%p",
+                       this, pending_request_);
+}
+
+void AresDnsResolver::MaybeFinishNextLocked() {
+  if (next_completion_ != nullptr && resolved_version_ != published_version_) {
+    *target_result_ = resolved_result_ == nullptr
+                          ? nullptr
+                          : grpc_channel_args_copy(resolved_result_);
+    GRPC_CARES_TRACE_LOG("resolver:%p AresDnsResolver::MaybeFinishNextLocked()",
+                         this);
+    GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE);
+    next_completion_ = nullptr;
+    published_version_ = resolved_version_;
+  }
+}
+
+//
+// Factory
+//
+
+class AresDnsResolverFactory : public ResolverFactory {
+ public:
+  OrphanablePtr<Resolver> CreateResolver(
+      const ResolverArgs& args) const override {
+    return OrphanablePtr<Resolver>(New<AresDnsResolver>(args));
+  }
+
+  const char* scheme() const override { return "dns"; }
+};
+
+}  // namespace
+
+}  // namespace grpc_core
+
+extern grpc_address_resolver_vtable* grpc_resolve_address_impl;
+static grpc_address_resolver_vtable* default_resolver;
+
+static grpc_error* blocking_resolve_address_ares(
+    const char* name, const char* default_port,
+    grpc_resolved_addresses** addresses) {
+  return default_resolver->blocking_resolve_address(name, default_port,
+                                                    addresses);
+}
+
+static grpc_address_resolver_vtable ares_resolver = {
+    grpc_resolve_address_ares, blocking_resolve_address_ares};
+
+static bool should_use_ares(const char* resolver_env) {
+  return resolver_env != nullptr && gpr_stricmp(resolver_env, "ares") == 0;
+}
+
+void grpc_resolver_dns_ares_init() {
+  char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
+  /* TODO(zyc): Turn on c-ares based resolver by default after the address
+     sorter and the CNAME support are added. */
+  if (should_use_ares(resolver_env)) {
+    gpr_log(GPR_DEBUG, "Using ares dns resolver");
+    address_sorting_init();
+    grpc_error* error = grpc_ares_init();
+    if (error != GRPC_ERROR_NONE) {
+      GRPC_LOG_IF_ERROR("ares_library_init() failed", error);
+      return;
+    }
+    if (default_resolver == nullptr) {
+      default_resolver = grpc_resolve_address_impl;
+    }
+    grpc_set_resolver_impl(&ares_resolver);
+    grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
+        grpc_core::UniquePtr<grpc_core::ResolverFactory>(
+            grpc_core::New<grpc_core::AresDnsResolverFactory>()));
+  }
+  gpr_free(resolver_env);
+}
+
+void grpc_resolver_dns_ares_shutdown() {
+  char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
+  if (should_use_ares(resolver_env)) {
+    address_sorting_shutdown();
+    grpc_ares_cleanup();
+  }
+  gpr_free(resolver_env);
+}
+
+#else /* GRPC_ARES == 1 && !defined(GRPC_UV) */
+
+void grpc_resolver_dns_ares_init(void) {}
+
+void grpc_resolver_dns_ares_shutdown(void) {}
+
+#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc
new file mode 100644
index 0000000..d99c2e3
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc
@@ -0,0 +1,368 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && !defined(GRPC_UV)
+
+#include <ares.h>
+#include <string.h>
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/timer.h"
+
+typedef struct fd_node {
+  /** the owner of this fd node */
+  grpc_ares_ev_driver* ev_driver;
+  /** a closure wrapping on_readable_locked, which should be
+     invoked when the grpc_fd in this node becomes readable. */
+  grpc_closure read_closure;
+  /** a closure wrapping on_writable_locked, which should be
+     invoked when the grpc_fd in this node becomes writable. */
+  grpc_closure write_closure;
+  /** next fd node in the list */
+  struct fd_node* next;
+
+  /** wrapped fd that's polled by grpc's poller for the current platform */
+  grpc_core::GrpcPolledFd* grpc_polled_fd;
+  /** if the readable closure has been registered */
+  bool readable_registered;
+  /** if the writable closure has been registered */
+  bool writable_registered;
+  /** if the fd has been shutdown yet from grpc iomgr perspective */
+  bool already_shutdown;
+} fd_node;
+
+struct grpc_ares_ev_driver {
+  /** the ares_channel owned by this event driver */
+  ares_channel channel;
+  /** pollset set for driving the IO events of the channel */
+  grpc_pollset_set* pollset_set;
+  /** refcount of the event driver */
+  gpr_refcount refs;
+
+  /** combiner to synchronize c-ares and I/O callbacks on */
+  grpc_combiner* combiner;
+  /** a list of grpc_fd that this event driver is currently using. */
+  fd_node* fds;
+  /** is this event driver currently working? */
+  bool working;
+  /** is this event driver being shut down */
+  bool shutting_down;
+  /** request object that's using this ev driver */
+  grpc_ares_request* request;
+  /** Owned by the ev_driver. Creates new GrpcPolledFd's */
+  grpc_core::UniquePtr<grpc_core::GrpcPolledFdFactory> polled_fd_factory;
+  /** query timeout in milliseconds */
+  int query_timeout_ms;
+  /** alarm to cancel active queries */
+  grpc_timer query_timeout;
+  /** cancels queries on a timeout */
+  grpc_closure on_timeout_locked;
+};
+
+static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver);
+
+static grpc_ares_ev_driver* grpc_ares_ev_driver_ref(
+    grpc_ares_ev_driver* ev_driver) {
+  GRPC_CARES_TRACE_LOG("request:%p Ref ev_driver %p", ev_driver->request,
+                       ev_driver);
+  gpr_ref(&ev_driver->refs);
+  return ev_driver;
+}
+
+static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver* ev_driver) {
+  GRPC_CARES_TRACE_LOG("request:%p Unref ev_driver %p", ev_driver->request,
+                       ev_driver);
+  if (gpr_unref(&ev_driver->refs)) {
+    GRPC_CARES_TRACE_LOG("request:%p destroy ev_driver %p", ev_driver->request,
+                         ev_driver);
+    GPR_ASSERT(ev_driver->fds == nullptr);
+    GRPC_COMBINER_UNREF(ev_driver->combiner, "free ares event driver");
+    ares_destroy(ev_driver->channel);
+    grpc_ares_complete_request_locked(ev_driver->request);
+    grpc_core::Delete(ev_driver);
+  }
+}
+
+static void fd_node_destroy_locked(fd_node* fdn) {
+  GRPC_CARES_TRACE_LOG("request:%p delete fd: %s", fdn->ev_driver->request,
+                       fdn->grpc_polled_fd->GetName());
+  GPR_ASSERT(!fdn->readable_registered);
+  GPR_ASSERT(!fdn->writable_registered);
+  GPR_ASSERT(fdn->already_shutdown);
+  grpc_core::Delete(fdn->grpc_polled_fd);
+  gpr_free(fdn);
+}
+
+static void fd_node_shutdown_locked(fd_node* fdn, const char* reason) {
+  if (!fdn->already_shutdown) {
+    fdn->already_shutdown = true;
+    fdn->grpc_polled_fd->ShutdownLocked(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING(reason));
+  }
+}
+
+static void on_timeout_locked(void* arg, grpc_error* error);
+
+grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver,
+                                              grpc_pollset_set* pollset_set,
+                                              int query_timeout_ms,
+                                              grpc_combiner* combiner,
+                                              grpc_ares_request* request) {
+  *ev_driver = grpc_core::New<grpc_ares_ev_driver>();
+  ares_options opts;
+  memset(&opts, 0, sizeof(opts));
+  opts.flags |= ARES_FLAG_STAYOPEN;
+  int status = ares_init_options(&(*ev_driver)->channel, &opts, ARES_OPT_FLAGS);
+  GRPC_CARES_TRACE_LOG("request:%p grpc_ares_ev_driver_create_locked", request);
+  if (status != ARES_SUCCESS) {
+    char* err_msg;
+    gpr_asprintf(&err_msg, "Failed to init ares channel. C-ares error: %s",
+                 ares_strerror(status));
+    grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg);
+    gpr_free(err_msg);
+    gpr_free(*ev_driver);
+    return err;
+  }
+  (*ev_driver)->combiner = GRPC_COMBINER_REF(combiner, "ares event driver");
+  gpr_ref_init(&(*ev_driver)->refs, 1);
+  (*ev_driver)->pollset_set = pollset_set;
+  (*ev_driver)->fds = nullptr;
+  (*ev_driver)->working = false;
+  (*ev_driver)->shutting_down = false;
+  (*ev_driver)->request = request;
+  (*ev_driver)->polled_fd_factory =
+      grpc_core::NewGrpcPolledFdFactory((*ev_driver)->combiner);
+  (*ev_driver)
+      ->polled_fd_factory->ConfigureAresChannelLocked((*ev_driver)->channel);
+  GRPC_CLOSURE_INIT(&(*ev_driver)->on_timeout_locked, on_timeout_locked,
+                    *ev_driver, grpc_combiner_scheduler(combiner));
+  (*ev_driver)->query_timeout_ms = query_timeout_ms;
+  return GRPC_ERROR_NONE;
+}
+
+void grpc_ares_ev_driver_on_queries_complete_locked(
+    grpc_ares_ev_driver* ev_driver) {
+  // We mark the event driver as being shut down. If the event driver
+  // is working, grpc_ares_notify_on_event_locked will shut down the
+  // fds; if it's not working, there are no fds to shut down.
+  ev_driver->shutting_down = true;
+  grpc_timer_cancel(&ev_driver->query_timeout);
+  grpc_ares_ev_driver_unref(ev_driver);
+}
+
+void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver) {
+  ev_driver->shutting_down = true;
+  fd_node* fn = ev_driver->fds;
+  while (fn != nullptr) {
+    fd_node_shutdown_locked(fn, "grpc_ares_ev_driver_shutdown");
+    fn = fn->next;
+  }
+}
+
+// Search fd in the fd_node list head. This is an O(n) search, the max possible
+// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests.
+static fd_node* pop_fd_node_locked(fd_node** head, ares_socket_t as) {
+  fd_node dummy_head;
+  dummy_head.next = *head;
+  fd_node* node = &dummy_head;
+  while (node->next != nullptr) {
+    if (node->next->grpc_polled_fd->GetWrappedAresSocketLocked() == as) {
+      fd_node* ret = node->next;
+      node->next = node->next->next;
+      *head = dummy_head.next;
+      return ret;
+    }
+    node = node->next;
+  }
+  return nullptr;
+}
+
+static void on_timeout_locked(void* arg, grpc_error* error) {
+  grpc_ares_ev_driver* driver = static_cast<grpc_ares_ev_driver*>(arg);
+  GRPC_CARES_TRACE_LOG(
+      "request:%p ev_driver=%p on_timeout_locked. driver->shutting_down=%d. "
+      "err=%s",
+      driver->request, driver, driver->shutting_down, grpc_error_string(error));
+  if (!driver->shutting_down && error == GRPC_ERROR_NONE) {
+    grpc_ares_ev_driver_shutdown_locked(driver);
+  }
+  grpc_ares_ev_driver_unref(driver);
+}
+
+static void on_readable_locked(void* arg, grpc_error* error) {
+  fd_node* fdn = static_cast<fd_node*>(arg);
+  grpc_ares_ev_driver* ev_driver = fdn->ev_driver;
+  const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked();
+  fdn->readable_registered = false;
+  GRPC_CARES_TRACE_LOG("request:%p readable on %s", fdn->ev_driver->request,
+                       fdn->grpc_polled_fd->GetName());
+  if (error == GRPC_ERROR_NONE) {
+    do {
+      ares_process_fd(ev_driver->channel, as, ARES_SOCKET_BAD);
+    } while (fdn->grpc_polled_fd->IsFdStillReadableLocked());
+  } else {
+    // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or
+    // timed out. The pending lookups made on this ev_driver will be cancelled
+    // by the following ares_cancel() and the on_done callbacks will be invoked
+    // with a status of ARES_ECANCELLED. The remaining file descriptors in this
+    // ev_driver will be cleaned up in the follwing
+    // grpc_ares_notify_on_event_locked().
+    ares_cancel(ev_driver->channel);
+  }
+  grpc_ares_notify_on_event_locked(ev_driver);
+  grpc_ares_ev_driver_unref(ev_driver);
+}
+
+static void on_writable_locked(void* arg, grpc_error* error) {
+  fd_node* fdn = static_cast<fd_node*>(arg);
+  grpc_ares_ev_driver* ev_driver = fdn->ev_driver;
+  const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked();
+  fdn->writable_registered = false;
+  GRPC_CARES_TRACE_LOG("request:%p writable on %s", ev_driver->request,
+                       fdn->grpc_polled_fd->GetName());
+  if (error == GRPC_ERROR_NONE) {
+    ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, as);
+  } else {
+    // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or
+    // timed out. The pending lookups made on this ev_driver will be cancelled
+    // by the following ares_cancel() and the on_done callbacks will be invoked
+    // with a status of ARES_ECANCELLED. The remaining file descriptors in this
+    // ev_driver will be cleaned up in the follwing
+    // grpc_ares_notify_on_event_locked().
+    ares_cancel(ev_driver->channel);
+  }
+  grpc_ares_notify_on_event_locked(ev_driver);
+  grpc_ares_ev_driver_unref(ev_driver);
+}
+
+ares_channel* grpc_ares_ev_driver_get_channel_locked(
+    grpc_ares_ev_driver* ev_driver) {
+  return &ev_driver->channel;
+}
+
+// Get the file descriptors used by the ev_driver's ares channel, register
+// driver_closure with these filedescriptors.
+static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver) {
+  fd_node* new_list = nullptr;
+  if (!ev_driver->shutting_down) {
+    ares_socket_t socks[ARES_GETSOCK_MAXNUM];
+    int socks_bitmask =
+        ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM);
+    for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
+      if (ARES_GETSOCK_READABLE(socks_bitmask, i) ||
+          ARES_GETSOCK_WRITABLE(socks_bitmask, i)) {
+        fd_node* fdn = pop_fd_node_locked(&ev_driver->fds, socks[i]);
+        // Create a new fd_node if sock[i] is not in the fd_node list.
+        if (fdn == nullptr) {
+          fdn = static_cast<fd_node*>(gpr_malloc(sizeof(fd_node)));
+          fdn->grpc_polled_fd =
+              ev_driver->polled_fd_factory->NewGrpcPolledFdLocked(
+                  socks[i], ev_driver->pollset_set, ev_driver->combiner);
+          GRPC_CARES_TRACE_LOG("request:%p new fd: %s", ev_driver->request,
+                               fdn->grpc_polled_fd->GetName());
+          fdn->ev_driver = ev_driver;
+          fdn->readable_registered = false;
+          fdn->writable_registered = false;
+          fdn->already_shutdown = false;
+          GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_locked, fdn,
+                            grpc_combiner_scheduler(ev_driver->combiner));
+          GRPC_CLOSURE_INIT(&fdn->write_closure, on_writable_locked, fdn,
+                            grpc_combiner_scheduler(ev_driver->combiner));
+        }
+        fdn->next = new_list;
+        new_list = fdn;
+        // Register read_closure if the socket is readable and read_closure has
+        // not been registered with this socket.
+        if (ARES_GETSOCK_READABLE(socks_bitmask, i) &&
+            !fdn->readable_registered) {
+          grpc_ares_ev_driver_ref(ev_driver);
+          GRPC_CARES_TRACE_LOG("request:%p notify read on: %s",
+                               ev_driver->request,
+                               fdn->grpc_polled_fd->GetName());
+          fdn->grpc_polled_fd->RegisterForOnReadableLocked(&fdn->read_closure);
+          fdn->readable_registered = true;
+        }
+        // Register write_closure if the socket is writable and write_closure
+        // has not been registered with this socket.
+        if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) &&
+            !fdn->writable_registered) {
+          GRPC_CARES_TRACE_LOG("request:%p notify write on: %s",
+                               ev_driver->request,
+                               fdn->grpc_polled_fd->GetName());
+          grpc_ares_ev_driver_ref(ev_driver);
+          fdn->grpc_polled_fd->RegisterForOnWriteableLocked(
+              &fdn->write_closure);
+          fdn->writable_registered = true;
+        }
+      }
+    }
+  }
+  // Any remaining fds in ev_driver->fds were not returned by ares_getsock() and
+  // are therefore no longer in use, so they can be shut down and removed from
+  // the list.
+  while (ev_driver->fds != nullptr) {
+    fd_node* cur = ev_driver->fds;
+    ev_driver->fds = ev_driver->fds->next;
+    fd_node_shutdown_locked(cur, "c-ares fd shutdown");
+    if (!cur->readable_registered && !cur->writable_registered) {
+      fd_node_destroy_locked(cur);
+    } else {
+      cur->next = new_list;
+      new_list = cur;
+    }
+  }
+  ev_driver->fds = new_list;
+  // If the ev driver has no working fd, all the tasks are done.
+  if (new_list == nullptr) {
+    ev_driver->working = false;
+    GRPC_CARES_TRACE_LOG("request:%p ev driver stop working",
+                         ev_driver->request);
+  }
+}
+
+void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver) {
+  if (!ev_driver->working) {
+    ev_driver->working = true;
+    grpc_ares_notify_on_event_locked(ev_driver);
+    grpc_millis timeout =
+        ev_driver->query_timeout_ms == 0
+            ? GRPC_MILLIS_INF_FUTURE
+            : ev_driver->query_timeout_ms + grpc_core::ExecCtx::Get()->Now();
+    GRPC_CARES_TRACE_LOG(
+        "request:%p ev_driver=%p grpc_ares_ev_driver_start_locked. timeout in "
+        "%" PRId64 " ms",
+        ev_driver->request, ev_driver, timeout);
+    grpc_ares_ev_driver_ref(ev_driver);
+    grpc_timer_init(&ev_driver->query_timeout, timeout,
+                    &ev_driver->on_timeout_locked);
+  }
+}
+
+#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h
new file mode 100644
index 0000000..b8cefd9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <ares.h>
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+typedef struct grpc_ares_ev_driver grpc_ares_ev_driver;
+
+/* Start \a ev_driver. It will keep working until all IO on its ares_channel is
+   done, or grpc_ares_ev_driver_destroy() is called. It may notify the callbacks
+   bound to its ares_channel when necessary. */
+void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver);
+
+/* Returns the ares_channel owned by \a ev_driver. To bind a c-ares query to
+   \a ev_driver, use the ares_channel owned by \a ev_driver as the arg of the
+   query. */
+ares_channel* grpc_ares_ev_driver_get_channel_locked(
+    grpc_ares_ev_driver* ev_driver);
+
+/* Creates a new grpc_ares_ev_driver. Returns GRPC_ERROR_NONE if \a ev_driver is
+   created successfully. */
+grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver,
+                                              grpc_pollset_set* pollset_set,
+                                              int query_timeout_ms,
+                                              grpc_combiner* combiner,
+                                              grpc_ares_request* request);
+
+/* Called back when all DNS lookups have completed. */
+void grpc_ares_ev_driver_on_queries_complete_locked(
+    grpc_ares_ev_driver* ev_driver);
+
+/* Shutdown all the grpc_fds used by \a ev_driver */
+void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver);
+
+namespace grpc_core {
+
+/* A wrapped fd that integrates with the grpc iomgr of the current platform.
+ * A GrpcPolledFd knows how to create grpc platform-specific iomgr endpoints
+ * from "ares_socket_t" sockets, and then sign up for readability/writeability
+ * with that poller, and do shutdown and destruction. */
+class GrpcPolledFd {
+ public:
+  virtual ~GrpcPolledFd() {}
+  /* Called when c-ares library is interested and there's no pending callback */
+  virtual void RegisterForOnReadableLocked(grpc_closure* read_closure)
+      GRPC_ABSTRACT;
+  /* Called when c-ares library is interested and there's no pending callback */
+  virtual void RegisterForOnWriteableLocked(grpc_closure* write_closure)
+      GRPC_ABSTRACT;
+  /* Indicates if there is data left even after just being read from */
+  virtual bool IsFdStillReadableLocked() GRPC_ABSTRACT;
+  /* Called once and only once. Must cause cancellation of any pending
+   * read/write callbacks. */
+  virtual void ShutdownLocked(grpc_error* error) GRPC_ABSTRACT;
+  /* Get the underlying ares_socket_t that this was created from */
+  virtual ares_socket_t GetWrappedAresSocketLocked() GRPC_ABSTRACT;
+  /* A unique name, for logging */
+  virtual const char* GetName() GRPC_ABSTRACT;
+
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+/* A GrpcPolledFdFactory is 1-to-1 with and owned by the
+ * ares event driver. It knows how to create GrpcPolledFd's
+ * for the current platform, and the ares driver uses it for all of
+ * its fd's. */
+class GrpcPolledFdFactory {
+ public:
+  virtual ~GrpcPolledFdFactory() {}
+  /* Creates a new wrapped fd for the current platform */
+  virtual GrpcPolledFd* NewGrpcPolledFdLocked(
+      ares_socket_t as, grpc_pollset_set* driver_pollset_set,
+      grpc_combiner* combiner) GRPC_ABSTRACT;
+  /* Optionally configures the ares channel after creation */
+  virtual void ConfigureAresChannelLocked(ares_channel channel) GRPC_ABSTRACT;
+
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+UniquePtr<GrpcPolledFdFactory> NewGrpcPolledFdFactory(grpc_combiner* combiner);
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
new file mode 100644
index 0000000..aa58e1a
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
@@ -0,0 +1,106 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER)
+
+#include <ares.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+namespace grpc_core {
+
+class GrpcPolledFdPosix : public GrpcPolledFd {
+ public:
+  GrpcPolledFdPosix(ares_socket_t as, grpc_pollset_set* driver_pollset_set)
+      : as_(as) {
+    gpr_asprintf(&name_, "c-ares fd: %d", (int)as);
+    fd_ = grpc_fd_create((int)as, name_, false);
+    driver_pollset_set_ = driver_pollset_set;
+    grpc_pollset_set_add_fd(driver_pollset_set_, fd_);
+  }
+
+  ~GrpcPolledFdPosix() {
+    gpr_free(name_);
+    grpc_pollset_set_del_fd(driver_pollset_set_, fd_);
+    /* c-ares library will close the fd inside grpc_fd. This fd may be picked up
+       immediately by another thread, and should not be closed by the following
+       grpc_fd_orphan. */
+    int dummy_release_fd;
+    grpc_fd_orphan(fd_, nullptr, &dummy_release_fd, "c-ares query finished");
+  }
+
+  void RegisterForOnReadableLocked(grpc_closure* read_closure) override {
+    grpc_fd_notify_on_read(fd_, read_closure);
+  }
+
+  void RegisterForOnWriteableLocked(grpc_closure* write_closure) override {
+    grpc_fd_notify_on_write(fd_, write_closure);
+  }
+
+  bool IsFdStillReadableLocked() override {
+    size_t bytes_available = 0;
+    return ioctl(grpc_fd_wrapped_fd(fd_), FIONREAD, &bytes_available) == 0 &&
+           bytes_available > 0;
+  }
+
+  void ShutdownLocked(grpc_error* error) override {
+    grpc_fd_shutdown(fd_, error);
+  }
+
+  ares_socket_t GetWrappedAresSocketLocked() override { return as_; }
+
+  const char* GetName() override { return name_; }
+
+  char* name_;
+  ares_socket_t as_;
+  grpc_fd* fd_;
+  grpc_pollset_set* driver_pollset_set_;
+};
+
+class GrpcPolledFdFactoryPosix : public GrpcPolledFdFactory {
+ public:
+  GrpcPolledFd* NewGrpcPolledFdLocked(ares_socket_t as,
+                                      grpc_pollset_set* driver_pollset_set,
+                                      grpc_combiner* combiner) override {
+    return New<GrpcPolledFdPosix>(as, driver_pollset_set);
+  }
+
+  void ConfigureAresChannelLocked(ares_channel channel) override {}
+};
+
+UniquePtr<GrpcPolledFdFactory> NewGrpcPolledFdFactory(grpc_combiner* combiner) {
+  return UniquePtr<GrpcPolledFdFactory>(New<GrpcPolledFdFactoryPosix>());
+}
+
+}  // namespace grpc_core
+
+#endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
new file mode 100644
index 0000000..02121aa
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
@@ -0,0 +1,537 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && defined(GPR_WINDOWS)
+
+#include <ares.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_windows.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include <string.h>
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+#include "src/core/lib/iomgr/tcp_windows.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+
+/* TODO(apolcyn): remove this hack after fixing upstream.
+ * Our grpc/c-ares code on Windows uses the ares_set_socket_functions API,
+ * which uses "struct iovec" type, which on Windows is defined inside of
+ * a c-ares header that is not public.
+ * See https://github.com/c-ares/c-ares/issues/206. */
+struct iovec {
+  void* iov_base;
+  size_t iov_len;
+};
+
+namespace grpc_core {
+
+/* c-ares creates its own sockets and is meant to read them when readable and
+ * write them when writeable. To fit this socket usage model into the grpc
+ * windows poller (which gives notifications when attempted reads and writes are
+ * actually fulfilled rather than possible), this GrpcPolledFdWindows class
+ * takes advantage of the ares_set_socket_functions API and acts as a virtual
+ * socket. It holds its own read and write buffers which are written to and read
+ * from c-ares and are used with the grpc windows poller, and it, e.g.,
+ * manufactures virtual socket error codes when it e.g. needs to tell the c-ares
+ * library to wait for an async read. */
+class GrpcPolledFdWindows : public GrpcPolledFd {
+ public:
+  enum WriteState {
+    WRITE_IDLE,
+    WRITE_REQUESTED,
+    WRITE_PENDING,
+    WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY,
+  };
+
+  GrpcPolledFdWindows(ares_socket_t as, grpc_combiner* combiner)
+      : read_buf_(grpc_empty_slice()),
+        write_buf_(grpc_empty_slice()),
+        write_state_(WRITE_IDLE),
+        gotten_into_driver_list_(false) {
+    gpr_asprintf(&name_, "c-ares socket: %" PRIdPTR, as);
+    winsocket_ = grpc_winsocket_create(as, name_);
+    combiner_ = GRPC_COMBINER_REF(combiner, name_);
+    GRPC_CLOSURE_INIT(&outer_read_closure_,
+                      &GrpcPolledFdWindows::OnIocpReadable, this,
+                      grpc_combiner_scheduler(combiner_));
+    GRPC_CLOSURE_INIT(&outer_write_closure_,
+                      &GrpcPolledFdWindows::OnIocpWriteable, this,
+                      grpc_combiner_scheduler(combiner_));
+  }
+
+  ~GrpcPolledFdWindows() {
+    GRPC_COMBINER_UNREF(combiner_, name_);
+    grpc_slice_unref_internal(read_buf_);
+    grpc_slice_unref_internal(write_buf_);
+    GPR_ASSERT(read_closure_ == nullptr);
+    GPR_ASSERT(write_closure_ == nullptr);
+    grpc_winsocket_destroy(winsocket_);
+    gpr_free(name_);
+  }
+
+  void ScheduleAndNullReadClosure(grpc_error* error) {
+    GRPC_CLOSURE_SCHED(read_closure_, error);
+    read_closure_ = nullptr;
+  }
+
+  void ScheduleAndNullWriteClosure(grpc_error* error) {
+    GRPC_CLOSURE_SCHED(write_closure_, error);
+    write_closure_ = nullptr;
+  }
+
+  void RegisterForOnReadableLocked(grpc_closure* read_closure) override {
+    GPR_ASSERT(read_closure_ == nullptr);
+    read_closure_ = read_closure;
+    GPR_ASSERT(GRPC_SLICE_LENGTH(read_buf_) == 0);
+    grpc_slice_unref_internal(read_buf_);
+    read_buf_ = GRPC_SLICE_MALLOC(4192);
+    WSABUF buffer;
+    buffer.buf = (char*)GRPC_SLICE_START_PTR(read_buf_);
+    buffer.len = GRPC_SLICE_LENGTH(read_buf_);
+    memset(&winsocket_->read_info.overlapped, 0, sizeof(OVERLAPPED));
+    recv_from_source_addr_len_ = sizeof(recv_from_source_addr_);
+    DWORD flags = 0;
+    if (WSARecvFrom(grpc_winsocket_wrapped_socket(winsocket_), &buffer, 1,
+                    nullptr, &flags, (sockaddr*)recv_from_source_addr_,
+                    &recv_from_source_addr_len_,
+                    &winsocket_->read_info.overlapped, nullptr)) {
+      char* msg = gpr_format_message(WSAGetLastError());
+      grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      GRPC_CARES_TRACE_LOG(
+          "RegisterForOnReadableLocked: WSARecvFrom error:|%s|. fd:|%s|", msg,
+          GetName());
+      gpr_free(msg);
+      if (WSAGetLastError() != WSA_IO_PENDING) {
+        ScheduleAndNullReadClosure(error);
+        return;
+      }
+    }
+    grpc_socket_notify_on_read(winsocket_, &outer_read_closure_);
+  }
+
+  void RegisterForOnWriteableLocked(grpc_closure* write_closure) override {
+    GRPC_CARES_TRACE_LOG(
+        "RegisterForOnWriteableLocked. fd:|%s|. Current write state: %d",
+        GetName(), write_state_);
+    GPR_ASSERT(write_closure_ == nullptr);
+    write_closure_ = write_closure;
+    switch (write_state_) {
+      case WRITE_IDLE:
+        ScheduleAndNullWriteClosure(GRPC_ERROR_NONE);
+        break;
+      case WRITE_REQUESTED:
+        write_state_ = WRITE_PENDING;
+        SendWriteBuf(nullptr, &winsocket_->write_info.overlapped);
+        grpc_socket_notify_on_write(winsocket_, &outer_write_closure_);
+        break;
+      case WRITE_PENDING:
+      case WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY:
+        abort();
+    }
+  }
+
+  bool IsFdStillReadableLocked() override {
+    return GRPC_SLICE_LENGTH(read_buf_) > 0;
+  }
+
+  void ShutdownLocked(grpc_error* error) override {
+    grpc_winsocket_shutdown(winsocket_);
+  }
+
+  ares_socket_t GetWrappedAresSocketLocked() override {
+    return grpc_winsocket_wrapped_socket(winsocket_);
+  }
+
+  const char* GetName() override { return name_; }
+
+  ares_ssize_t RecvFrom(void* data, ares_socket_t data_len, int flags,
+                        struct sockaddr* from, ares_socklen_t* from_len) {
+    GRPC_CARES_TRACE_LOG(
+        "RecvFrom called on fd:|%s|. Current read buf length:|%d|", GetName(),
+        GRPC_SLICE_LENGTH(read_buf_));
+    if (GRPC_SLICE_LENGTH(read_buf_) == 0) {
+      WSASetLastError(WSAEWOULDBLOCK);
+      return -1;
+    }
+    ares_ssize_t bytes_read = 0;
+    for (size_t i = 0; i < GRPC_SLICE_LENGTH(read_buf_) && i < data_len; i++) {
+      ((char*)data)[i] = GRPC_SLICE_START_PTR(read_buf_)[i];
+      bytes_read++;
+    }
+    read_buf_ = grpc_slice_sub_no_ref(read_buf_, bytes_read,
+                                      GRPC_SLICE_LENGTH(read_buf_));
+    /* c-ares overloads this recv_from virtual socket function to receive
+     * data on both UDP and TCP sockets, and from is nullptr for TCP. */
+    if (from != nullptr) {
+      GPR_ASSERT(*from_len <= recv_from_source_addr_len_);
+      memcpy(from, &recv_from_source_addr_, recv_from_source_addr_len_);
+      *from_len = recv_from_source_addr_len_;
+    }
+    return bytes_read;
+  }
+
+  grpc_slice FlattenIovec(const struct iovec* iov, int iov_count) {
+    int total = 0;
+    for (int i = 0; i < iov_count; i++) {
+      total += iov[i].iov_len;
+    }
+    grpc_slice out = GRPC_SLICE_MALLOC(total);
+    size_t cur = 0;
+    for (int i = 0; i < iov_count; i++) {
+      for (int k = 0; k < iov[i].iov_len; k++) {
+        GRPC_SLICE_START_PTR(out)[cur++] = ((char*)iov[i].iov_base)[k];
+      }
+    }
+    return out;
+  }
+
+  int SendWriteBuf(LPDWORD bytes_sent_ptr, LPWSAOVERLAPPED overlapped) {
+    WSABUF buf;
+    buf.len = GRPC_SLICE_LENGTH(write_buf_);
+    buf.buf = (char*)GRPC_SLICE_START_PTR(write_buf_);
+    DWORD flags = 0;
+    int out = WSASend(grpc_winsocket_wrapped_socket(winsocket_), &buf, 1,
+                      bytes_sent_ptr, flags, overlapped, nullptr);
+    GRPC_CARES_TRACE_LOG(
+        "WSASend: name:%s. buf len:%d. bytes sent: %d. overlapped %p. return "
+        "val: %d",
+        GetName(), buf.len, *bytes_sent_ptr, overlapped, out);
+    return out;
+  }
+
+  ares_ssize_t TrySendWriteBufSyncNonBlocking() {
+    GPR_ASSERT(write_state_ == WRITE_IDLE);
+    ares_ssize_t total_sent;
+    DWORD bytes_sent = 0;
+    if (SendWriteBuf(&bytes_sent, nullptr) != 0) {
+      char* msg = gpr_format_message(WSAGetLastError());
+      GRPC_CARES_TRACE_LOG(
+          "TrySendWriteBufSyncNonBlocking: SendWriteBuf error:|%s|. fd:|%s|",
+          msg, GetName());
+      gpr_free(msg);
+      if (WSAGetLastError() == WSA_IO_PENDING) {
+        WSASetLastError(WSAEWOULDBLOCK);
+        write_state_ = WRITE_REQUESTED;
+      }
+    }
+    write_buf_ = grpc_slice_sub_no_ref(write_buf_, bytes_sent,
+                                       GRPC_SLICE_LENGTH(write_buf_));
+    return bytes_sent;
+  }
+
+  ares_ssize_t SendV(const struct iovec* iov, int iov_count) {
+    GRPC_CARES_TRACE_LOG("SendV called on fd:|%s|. Current write state: %d",
+                         GetName(), write_state_);
+    switch (write_state_) {
+      case WRITE_IDLE:
+        GPR_ASSERT(GRPC_SLICE_LENGTH(write_buf_) == 0);
+        grpc_slice_unref_internal(write_buf_);
+        write_buf_ = FlattenIovec(iov, iov_count);
+        return TrySendWriteBufSyncNonBlocking();
+      case WRITE_REQUESTED:
+      case WRITE_PENDING:
+        WSASetLastError(WSAEWOULDBLOCK);
+        return -1;
+      case WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY:
+        grpc_slice currently_attempted = FlattenIovec(iov, iov_count);
+        GPR_ASSERT(GRPC_SLICE_LENGTH(currently_attempted) >=
+                   GRPC_SLICE_LENGTH(write_buf_));
+        ares_ssize_t total_sent = 0;
+        for (size_t i = 0; i < GRPC_SLICE_LENGTH(write_buf_); i++) {
+          GPR_ASSERT(GRPC_SLICE_START_PTR(currently_attempted)[i] ==
+                     GRPC_SLICE_START_PTR(write_buf_)[i]);
+          total_sent++;
+        }
+        grpc_slice_unref_internal(write_buf_);
+        write_buf_ =
+            grpc_slice_sub_no_ref(currently_attempted, total_sent,
+                                  GRPC_SLICE_LENGTH(currently_attempted));
+        write_state_ = WRITE_IDLE;
+        total_sent += TrySendWriteBufSyncNonBlocking();
+        return total_sent;
+    }
+    abort();
+  }
+
+  int Connect(const struct sockaddr* target, ares_socklen_t target_len) {
+    SOCKET s = grpc_winsocket_wrapped_socket(winsocket_);
+    GRPC_CARES_TRACE_LOG("Connect: fd:|%s|", GetName());
+    int out =
+        WSAConnect(s, target, target_len, nullptr, nullptr, nullptr, nullptr);
+    if (out != 0) {
+      char* msg = gpr_format_message(WSAGetLastError());
+      GRPC_CARES_TRACE_LOG("Connect error code:|%d|, msg:|%s|. fd:|%s|",
+                           WSAGetLastError(), msg, GetName());
+      gpr_free(msg);
+      // c-ares expects a posix-style connect API
+      out = -1;
+    }
+    return out;
+  }
+
+  static void OnIocpReadable(void* arg, grpc_error* error) {
+    GrpcPolledFdWindows* polled_fd = static_cast<GrpcPolledFdWindows*>(arg);
+    polled_fd->OnIocpReadableInner(error);
+  }
+
+  void OnIocpReadableInner(grpc_error* error) {
+    if (error == GRPC_ERROR_NONE) {
+      if (winsocket_->read_info.wsa_error != 0) {
+        /* WSAEMSGSIZE would be due to receiving more data
+         * than our read buffer's fixed capacity. Assume that
+         * the connection is TCP and read the leftovers
+         * in subsequent c-ares reads. */
+        if (winsocket_->read_info.wsa_error != WSAEMSGSIZE) {
+          GRPC_ERROR_UNREF(error);
+          char* msg = gpr_format_message(winsocket_->read_info.wsa_error);
+          GRPC_CARES_TRACE_LOG(
+              "OnIocpReadableInner. winsocket error:|%s|. fd:|%s|", msg,
+              GetName());
+          error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+          gpr_free(msg);
+        }
+      }
+    }
+    if (error == GRPC_ERROR_NONE) {
+      read_buf_ = grpc_slice_sub_no_ref(read_buf_, 0,
+                                        winsocket_->read_info.bytes_transfered);
+    } else {
+      grpc_slice_unref_internal(read_buf_);
+      read_buf_ = grpc_empty_slice();
+    }
+    GRPC_CARES_TRACE_LOG(
+        "OnIocpReadable finishing. read buf length now:|%d|. :fd:|%s|",
+        GRPC_SLICE_LENGTH(read_buf_), GetName());
+    ScheduleAndNullReadClosure(error);
+  }
+
+  static void OnIocpWriteable(void* arg, grpc_error* error) {
+    GrpcPolledFdWindows* polled_fd = static_cast<GrpcPolledFdWindows*>(arg);
+    polled_fd->OnIocpWriteableInner(error);
+  }
+
+  void OnIocpWriteableInner(grpc_error* error) {
+    GRPC_CARES_TRACE_LOG("OnIocpWriteableInner. fd:|%s|", GetName());
+    if (error == GRPC_ERROR_NONE) {
+      if (winsocket_->write_info.wsa_error != 0) {
+        char* msg = gpr_format_message(winsocket_->write_info.wsa_error);
+        GRPC_CARES_TRACE_LOG(
+            "OnIocpWriteableInner. winsocket error:|%s|. fd:|%s|", msg,
+            GetName());
+        GRPC_ERROR_UNREF(error);
+        error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+        gpr_free(msg);
+      }
+    }
+    GPR_ASSERT(write_state_ == WRITE_PENDING);
+    if (error == GRPC_ERROR_NONE) {
+      write_state_ = WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY;
+      write_buf_ = grpc_slice_sub_no_ref(
+          write_buf_, 0, winsocket_->write_info.bytes_transfered);
+    } else {
+      grpc_slice_unref_internal(write_buf_);
+      write_buf_ = grpc_empty_slice();
+    }
+    ScheduleAndNullWriteClosure(error);
+  }
+
+  bool gotten_into_driver_list() const { return gotten_into_driver_list_; }
+  void set_gotten_into_driver_list() { gotten_into_driver_list_ = true; }
+
+  grpc_combiner* combiner_;
+  char recv_from_source_addr_[200];
+  ares_socklen_t recv_from_source_addr_len_;
+  grpc_slice read_buf_;
+  grpc_slice write_buf_;
+  grpc_closure* read_closure_ = nullptr;
+  grpc_closure* write_closure_ = nullptr;
+  grpc_closure outer_read_closure_;
+  grpc_closure outer_write_closure_;
+  grpc_winsocket* winsocket_;
+  WriteState write_state_;
+  char* name_ = nullptr;
+  bool gotten_into_driver_list_;
+};
+
+struct SockToPolledFdEntry {
+  SockToPolledFdEntry(SOCKET s, GrpcPolledFdWindows* fd)
+      : socket(s), polled_fd(fd) {}
+  SOCKET socket;
+  GrpcPolledFdWindows* polled_fd;
+  SockToPolledFdEntry* next = nullptr;
+};
+
+/* A SockToPolledFdMap can make ares_socket_t types (SOCKET's on windows)
+ * to GrpcPolledFdWindow's, and is used to find the appropriate
+ * GrpcPolledFdWindows to handle a virtual socket call when c-ares makes that
+ * socket call on the ares_socket_t type. Instances are owned by and one-to-one
+ * with a GrpcPolledFdWindows factory and event driver */
+class SockToPolledFdMap {
+ public:
+  SockToPolledFdMap(grpc_combiner* combiner) {
+    combiner_ = GRPC_COMBINER_REF(combiner, "sock to polled fd map");
+  }
+
+  ~SockToPolledFdMap() {
+    GPR_ASSERT(head_ == nullptr);
+    GRPC_COMBINER_UNREF(combiner_, "sock to polled fd map");
+  }
+
+  void AddNewSocket(SOCKET s, GrpcPolledFdWindows* polled_fd) {
+    SockToPolledFdEntry* new_node = New<SockToPolledFdEntry>(s, polled_fd);
+    new_node->next = head_;
+    head_ = new_node;
+  }
+
+  GrpcPolledFdWindows* LookupPolledFd(SOCKET s) {
+    for (SockToPolledFdEntry* node = head_; node != nullptr;
+         node = node->next) {
+      if (node->socket == s) {
+        GPR_ASSERT(node->polled_fd != nullptr);
+        return node->polled_fd;
+      }
+    }
+    abort();
+  }
+
+  void RemoveEntry(SOCKET s) {
+    GPR_ASSERT(head_ != nullptr);
+    SockToPolledFdEntry** prev = &head_;
+    for (SockToPolledFdEntry* node = head_; node != nullptr;
+         node = node->next) {
+      if (node->socket == s) {
+        *prev = node->next;
+        Delete(node);
+        return;
+      }
+      prev = &node->next;
+    }
+    abort();
+  }
+
+  /* These virtual socket functions are called from within the c-ares
+   * library. These methods generally dispatch those socket calls to the
+   * appropriate methods. The virtual "socket" and "close" methods are
+   * special and instead create/add and remove/destroy GrpcPolledFdWindows
+   * objects.
+   */
+  static ares_socket_t Socket(int af, int type, int protocol, void* user_data) {
+    SockToPolledFdMap* map = static_cast<SockToPolledFdMap*>(user_data);
+    SOCKET s = WSASocket(af, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED);
+    if (s == INVALID_SOCKET) {
+      return s;
+    }
+    grpc_tcp_set_non_block(s);
+    GrpcPolledFdWindows* polled_fd =
+        New<GrpcPolledFdWindows>(s, map->combiner_);
+    map->AddNewSocket(s, polled_fd);
+    return s;
+  }
+
+  static int Connect(ares_socket_t as, const struct sockaddr* target,
+                     ares_socklen_t target_len, void* user_data) {
+    SockToPolledFdMap* map = static_cast<SockToPolledFdMap*>(user_data);
+    GrpcPolledFdWindows* polled_fd = map->LookupPolledFd(as);
+    return polled_fd->Connect(target, target_len);
+  }
+
+  static ares_ssize_t SendV(ares_socket_t as, const struct iovec* iov,
+                            int iovec_count, void* user_data) {
+    SockToPolledFdMap* map = static_cast<SockToPolledFdMap*>(user_data);
+    GrpcPolledFdWindows* polled_fd = map->LookupPolledFd(as);
+    return polled_fd->SendV(iov, iovec_count);
+  }
+
+  static ares_ssize_t RecvFrom(ares_socket_t as, void* data, size_t data_len,
+                               int flags, struct sockaddr* from,
+                               ares_socklen_t* from_len, void* user_data) {
+    SockToPolledFdMap* map = static_cast<SockToPolledFdMap*>(user_data);
+    GrpcPolledFdWindows* polled_fd = map->LookupPolledFd(as);
+    return polled_fd->RecvFrom(data, data_len, flags, from, from_len);
+  }
+
+  static int CloseSocket(SOCKET s, void* user_data) {
+    SockToPolledFdMap* map = static_cast<SockToPolledFdMap*>(user_data);
+    GrpcPolledFdWindows* polled_fd = map->LookupPolledFd(s);
+    map->RemoveEntry(s);
+    // If a gRPC polled fd has not made it in to the driver's list yet, then
+    // the driver has not and will never see this socket.
+    if (!polled_fd->gotten_into_driver_list()) {
+      polled_fd->ShutdownLocked(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "Shut down c-ares fd before without it ever having made it into the "
+          "driver's list"));
+      return 0;
+    }
+    return 0;
+  }
+
+ private:
+  SockToPolledFdEntry* head_ = nullptr;
+  grpc_combiner* combiner_;
+};
+
+const struct ares_socket_functions custom_ares_sock_funcs = {
+    &SockToPolledFdMap::Socket /* socket */,
+    &SockToPolledFdMap::CloseSocket /* close */,
+    &SockToPolledFdMap::Connect /* connect */,
+    &SockToPolledFdMap::RecvFrom /* recvfrom */,
+    &SockToPolledFdMap::SendV /* sendv */,
+};
+
+class GrpcPolledFdFactoryWindows : public GrpcPolledFdFactory {
+ public:
+  GrpcPolledFdFactoryWindows(grpc_combiner* combiner)
+      : sock_to_polled_fd_map_(combiner) {}
+
+  GrpcPolledFd* NewGrpcPolledFdLocked(ares_socket_t as,
+                                      grpc_pollset_set* driver_pollset_set,
+                                      grpc_combiner* combiner) override {
+    GrpcPolledFdWindows* polled_fd = sock_to_polled_fd_map_.LookupPolledFd(as);
+    // Set a flag so that the virtual socket "close" method knows it
+    // doesn't need to call ShutdownLocked, since now the driver will.
+    polled_fd->set_gotten_into_driver_list();
+    return polled_fd;
+  }
+
+  void ConfigureAresChannelLocked(ares_channel channel) override {
+    ares_set_socket_functions(channel, &custom_ares_sock_funcs,
+                              &sock_to_polled_fd_map_);
+  }
+
+ private:
+  SockToPolledFdMap sock_to_polled_fd_map_;
+};
+
+UniquePtr<GrpcPolledFdFactory> NewGrpcPolledFdFactory(grpc_combiner* combiner) {
+  return UniquePtr<GrpcPolledFdFactory>(
+      New<GrpcPolledFdFactoryWindows>(combiner));
+}
+
+}  // namespace grpc_core
+
+#endif /* GRPC_ARES == 1 && defined(GPR_WINDOWS) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
new file mode 100644
index 0000000..1a7e5d0
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc
@@ -0,0 +1,702 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#if GRPC_ARES == 1 && !defined(GRPC_UV)
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <ares.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include <address_sorting/address_sorting.h>
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/nameser.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+using grpc_core::ServerAddress;
+using grpc_core::ServerAddressList;
+
+static gpr_once g_basic_init = GPR_ONCE_INIT;
+static gpr_mu g_init_mu;
+
+grpc_core::TraceFlag grpc_trace_cares_address_sorting(false,
+                                                      "cares_address_sorting");
+
+grpc_core::TraceFlag grpc_trace_cares_resolver(false, "cares_resolver");
+
+struct grpc_ares_request {
+  /** indicates the DNS server to use, if specified */
+  struct ares_addr_port_node dns_server_addr;
+  /** following members are set in grpc_resolve_address_ares_impl */
+  /** closure to call when the request completes */
+  grpc_closure* on_done;
+  /** the pointer to receive the resolved addresses */
+  grpc_core::UniquePtr<grpc_core::ServerAddressList>* addresses_out;
+  /** the pointer to receive the service config in JSON */
+  char** service_config_json_out;
+  /** the evernt driver used by this request */
+  grpc_ares_ev_driver* ev_driver;
+  /** number of ongoing queries */
+  size_t pending_queries;
+
+  /** is there at least one successful query, set in on_done_cb */
+  bool success;
+  /** the errors explaining the request failure, set in on_done_cb */
+  grpc_error* error;
+};
+
+typedef struct grpc_ares_hostbyname_request {
+  /** following members are set in create_hostbyname_request_locked
+   */
+  /** the top-level request instance */
+  grpc_ares_request* parent_request;
+  /** host to resolve, parsed from the name to resolve */
+  char* host;
+  /** port to fill in sockaddr_in, parsed from the name to resolve */
+  uint16_t port;
+  /** is it a grpclb address */
+  bool is_balancer;
+} grpc_ares_hostbyname_request;
+
+static void do_basic_init(void) { gpr_mu_init(&g_init_mu); }
+
+static void log_address_sorting_list(const ServerAddressList& addresses,
+                                     const char* input_output_str) {
+  for (size_t i = 0; i < addresses.size(); i++) {
+    char* addr_str;
+    if (grpc_sockaddr_to_string(&addr_str, &addresses[i].address(), true)) {
+      gpr_log(GPR_INFO, "c-ares address sorting: %s[%" PRIuPTR "]=%s",
+              input_output_str, i, addr_str);
+      gpr_free(addr_str);
+    } else {
+      gpr_log(GPR_INFO,
+              "c-ares address sorting: %s[%" PRIuPTR "]=<unprintable>",
+              input_output_str, i);
+    }
+  }
+}
+
+void grpc_cares_wrapper_address_sorting_sort(ServerAddressList* addresses) {
+  if (grpc_trace_cares_address_sorting.enabled()) {
+    log_address_sorting_list(*addresses, "input");
+  }
+  address_sorting_sortable* sortables = (address_sorting_sortable*)gpr_zalloc(
+      sizeof(address_sorting_sortable) * addresses->size());
+  for (size_t i = 0; i < addresses->size(); ++i) {
+    sortables[i].user_data = &(*addresses)[i];
+    memcpy(&sortables[i].dest_addr.addr, &(*addresses)[i].address().addr,
+           (*addresses)[i].address().len);
+    sortables[i].dest_addr.len = (*addresses)[i].address().len;
+  }
+  address_sorting_rfc_6724_sort(sortables, addresses->size());
+  ServerAddressList sorted;
+  sorted.reserve(addresses->size());
+  for (size_t i = 0; i < addresses->size(); ++i) {
+    sorted.emplace_back(*static_cast<ServerAddress*>(sortables[i].user_data));
+  }
+  gpr_free(sortables);
+  *addresses = std::move(sorted);
+  if (grpc_trace_cares_address_sorting.enabled()) {
+    log_address_sorting_list(*addresses, "output");
+  }
+}
+
+static void grpc_ares_request_ref_locked(grpc_ares_request* r) {
+  r->pending_queries++;
+}
+
+static void grpc_ares_request_unref_locked(grpc_ares_request* r) {
+  r->pending_queries--;
+  if (r->pending_queries == 0u) {
+    grpc_ares_ev_driver_on_queries_complete_locked(r->ev_driver);
+  }
+}
+
+void grpc_ares_complete_request_locked(grpc_ares_request* r) {
+  /* Invoke on_done callback and destroy the
+     request */
+  r->ev_driver = nullptr;
+  ServerAddressList* addresses = r->addresses_out->get();
+  if (addresses != nullptr) {
+    grpc_cares_wrapper_address_sorting_sort(addresses);
+  }
+  GRPC_CLOSURE_SCHED(r->on_done, r->error);
+}
+
+static grpc_ares_hostbyname_request* create_hostbyname_request_locked(
+    grpc_ares_request* parent_request, char* host, uint16_t port,
+    bool is_balancer) {
+  grpc_ares_hostbyname_request* hr = static_cast<grpc_ares_hostbyname_request*>(
+      gpr_zalloc(sizeof(grpc_ares_hostbyname_request)));
+  hr->parent_request = parent_request;
+  hr->host = gpr_strdup(host);
+  hr->port = port;
+  hr->is_balancer = is_balancer;
+  grpc_ares_request_ref_locked(parent_request);
+  return hr;
+}
+
+static void destroy_hostbyname_request_locked(
+    grpc_ares_hostbyname_request* hr) {
+  grpc_ares_request_unref_locked(hr->parent_request);
+  gpr_free(hr->host);
+  gpr_free(hr);
+}
+
+static void on_hostbyname_done_locked(void* arg, int status, int timeouts,
+                                      struct hostent* hostent) {
+  grpc_ares_hostbyname_request* hr =
+      static_cast<grpc_ares_hostbyname_request*>(arg);
+  grpc_ares_request* r = hr->parent_request;
+  if (status == ARES_SUCCESS) {
+    GRPC_ERROR_UNREF(r->error);
+    r->error = GRPC_ERROR_NONE;
+    r->success = true;
+    if (*r->addresses_out == nullptr) {
+      *r->addresses_out = grpc_core::MakeUnique<ServerAddressList>();
+    }
+    ServerAddressList& addresses = **r->addresses_out;
+    for (size_t i = 0; hostent->h_addr_list[i] != nullptr; ++i) {
+      grpc_core::InlinedVector<grpc_arg, 2> args_to_add;
+      if (hr->is_balancer) {
+        args_to_add.emplace_back(grpc_channel_arg_integer_create(
+            const_cast<char*>(GRPC_ARG_ADDRESS_IS_BALANCER), 1));
+        args_to_add.emplace_back(grpc_channel_arg_string_create(
+            const_cast<char*>(GRPC_ARG_ADDRESS_BALANCER_NAME), hr->host));
+      }
+      grpc_channel_args* args = grpc_channel_args_copy_and_add(
+          nullptr, args_to_add.data(), args_to_add.size());
+      switch (hostent->h_addrtype) {
+        case AF_INET6: {
+          size_t addr_len = sizeof(struct sockaddr_in6);
+          struct sockaddr_in6 addr;
+          memset(&addr, 0, addr_len);
+          memcpy(&addr.sin6_addr, hostent->h_addr_list[i],
+                 sizeof(struct in6_addr));
+          addr.sin6_family = static_cast<unsigned char>(hostent->h_addrtype);
+          addr.sin6_port = hr->port;
+          addresses.emplace_back(&addr, addr_len, args);
+          char output[INET6_ADDRSTRLEN];
+          ares_inet_ntop(AF_INET6, &addr.sin6_addr, output, INET6_ADDRSTRLEN);
+          GRPC_CARES_TRACE_LOG(
+              "request:%p c-ares resolver gets a AF_INET6 result: \n"
+              "  addr: %s\n  port: %d\n  sin6_scope_id: %d\n",
+              r, output, ntohs(hr->port), addr.sin6_scope_id);
+          break;
+        }
+        case AF_INET: {
+          size_t addr_len = sizeof(struct sockaddr_in);
+          struct sockaddr_in addr;
+          memset(&addr, 0, addr_len);
+          memcpy(&addr.sin_addr, hostent->h_addr_list[i],
+                 sizeof(struct in_addr));
+          addr.sin_family = static_cast<unsigned char>(hostent->h_addrtype);
+          addr.sin_port = hr->port;
+          addresses.emplace_back(&addr, addr_len, args);
+          char output[INET_ADDRSTRLEN];
+          ares_inet_ntop(AF_INET, &addr.sin_addr, output, INET_ADDRSTRLEN);
+          GRPC_CARES_TRACE_LOG(
+              "request:%p c-ares resolver gets a AF_INET result: \n"
+              "  addr: %s\n  port: %d\n",
+              r, output, ntohs(hr->port));
+          break;
+        }
+      }
+    }
+  } else if (!r->success) {
+    char* error_msg;
+    gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s",
+                 ares_strerror(status));
+    grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+    gpr_free(error_msg);
+    if (r->error == GRPC_ERROR_NONE) {
+      r->error = error;
+    } else {
+      r->error = grpc_error_add_child(error, r->error);
+    }
+  }
+  destroy_hostbyname_request_locked(hr);
+}
+
+static void on_srv_query_done_locked(void* arg, int status, int timeouts,
+                                     unsigned char* abuf, int alen) {
+  grpc_ares_request* r = static_cast<grpc_ares_request*>(arg);
+  GRPC_CARES_TRACE_LOG("request:%p on_query_srv_done_locked", r);
+  if (status == ARES_SUCCESS) {
+    GRPC_CARES_TRACE_LOG("request:%p on_query_srv_done_locked ARES_SUCCESS", r);
+    struct ares_srv_reply* reply;
+    const int parse_status = ares_parse_srv_reply(abuf, alen, &reply);
+    if (parse_status == ARES_SUCCESS) {
+      ares_channel* channel =
+          grpc_ares_ev_driver_get_channel_locked(r->ev_driver);
+      for (struct ares_srv_reply* srv_it = reply; srv_it != nullptr;
+           srv_it = srv_it->next) {
+        if (grpc_ares_query_ipv6()) {
+          grpc_ares_hostbyname_request* hr = create_hostbyname_request_locked(
+              r, srv_it->host, htons(srv_it->port), true /* is_balancer */);
+          ares_gethostbyname(*channel, hr->host, AF_INET6,
+                             on_hostbyname_done_locked, hr);
+        }
+        grpc_ares_hostbyname_request* hr = create_hostbyname_request_locked(
+            r, srv_it->host, htons(srv_it->port), true /* is_balancer */);
+        ares_gethostbyname(*channel, hr->host, AF_INET,
+                           on_hostbyname_done_locked, hr);
+        grpc_ares_ev_driver_start_locked(r->ev_driver);
+      }
+    }
+    if (reply != nullptr) {
+      ares_free_data(reply);
+    }
+  } else if (!r->success) {
+    char* error_msg;
+    gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s",
+                 ares_strerror(status));
+    grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+    gpr_free(error_msg);
+    if (r->error == GRPC_ERROR_NONE) {
+      r->error = error;
+    } else {
+      r->error = grpc_error_add_child(error, r->error);
+    }
+  }
+  grpc_ares_request_unref_locked(r);
+}
+
+static const char g_service_config_attribute_prefix[] = "grpc_config=";
+
+static void on_txt_done_locked(void* arg, int status, int timeouts,
+                               unsigned char* buf, int len) {
+  char* error_msg;
+  grpc_ares_request* r = static_cast<grpc_ares_request*>(arg);
+  GRPC_CARES_TRACE_LOG("request:%p on_txt_done_locked", r);
+  const size_t prefix_len = sizeof(g_service_config_attribute_prefix) - 1;
+  struct ares_txt_ext* result = nullptr;
+  struct ares_txt_ext* reply = nullptr;
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (status != ARES_SUCCESS) goto fail;
+  status = ares_parse_txt_reply_ext(buf, len, &reply);
+  if (status != ARES_SUCCESS) goto fail;
+  // Find service config in TXT record.
+  for (result = reply; result != nullptr; result = result->next) {
+    if (result->record_start &&
+        memcmp(result->txt, g_service_config_attribute_prefix, prefix_len) ==
+            0) {
+      break;
+    }
+  }
+  // Found a service config record.
+  if (result != nullptr) {
+    size_t service_config_len = result->length - prefix_len;
+    *r->service_config_json_out =
+        static_cast<char*>(gpr_malloc(service_config_len + 1));
+    memcpy(*r->service_config_json_out, result->txt + prefix_len,
+           service_config_len);
+    for (result = result->next; result != nullptr && !result->record_start;
+         result = result->next) {
+      *r->service_config_json_out = static_cast<char*>(
+          gpr_realloc(*r->service_config_json_out,
+                      service_config_len + result->length + 1));
+      memcpy(*r->service_config_json_out + service_config_len, result->txt,
+             result->length);
+      service_config_len += result->length;
+    }
+    (*r->service_config_json_out)[service_config_len] = '\0';
+    GRPC_CARES_TRACE_LOG("request:%p found service config: %s", r,
+                         *r->service_config_json_out);
+  }
+  // Clean up.
+  ares_free_data(reply);
+  goto done;
+fail:
+  gpr_asprintf(&error_msg, "C-ares TXT lookup status is not ARES_SUCCESS: %s",
+               ares_strerror(status));
+  error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+  gpr_free(error_msg);
+  if (r->error == GRPC_ERROR_NONE) {
+    r->error = error;
+  } else {
+    r->error = grpc_error_add_child(error, r->error);
+  }
+done:
+  grpc_ares_request_unref_locked(r);
+}
+
+void grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked(
+    grpc_ares_request* r, const char* dns_server, const char* name,
+    const char* default_port, grpc_pollset_set* interested_parties,
+    bool check_grpclb, int query_timeout_ms, grpc_combiner* combiner) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  grpc_ares_hostbyname_request* hr = nullptr;
+  ares_channel* channel = nullptr;
+  /* parse name, splitting it into host and port parts */
+  char* host;
+  char* port;
+  gpr_split_host_port(name, &host, &port);
+  if (host == nullptr) {
+    error = grpc_error_set_str(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"),
+        GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name));
+    goto error_cleanup;
+  } else if (port == nullptr) {
+    if (default_port == nullptr) {
+      error = grpc_error_set_str(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"),
+          GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name));
+      goto error_cleanup;
+    }
+    port = gpr_strdup(default_port);
+  }
+  error = grpc_ares_ev_driver_create_locked(&r->ev_driver, interested_parties,
+                                            query_timeout_ms, combiner, r);
+  if (error != GRPC_ERROR_NONE) goto error_cleanup;
+  channel = grpc_ares_ev_driver_get_channel_locked(r->ev_driver);
+  // If dns_server is specified, use it.
+  if (dns_server != nullptr) {
+    GRPC_CARES_TRACE_LOG("request:%p Using DNS server %s", r, dns_server);
+    grpc_resolved_address addr;
+    if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) {
+      r->dns_server_addr.family = AF_INET;
+      struct sockaddr_in* in = reinterpret_cast<struct sockaddr_in*>(addr.addr);
+      memcpy(&r->dns_server_addr.addr.addr4, &in->sin_addr,
+             sizeof(struct in_addr));
+      r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr);
+      r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr);
+    } else if (grpc_parse_ipv6_hostport(dns_server, &addr,
+                                        false /* log_errors */)) {
+      r->dns_server_addr.family = AF_INET6;
+      struct sockaddr_in6* in6 =
+          reinterpret_cast<struct sockaddr_in6*>(addr.addr);
+      memcpy(&r->dns_server_addr.addr.addr6, &in6->sin6_addr,
+             sizeof(struct in6_addr));
+      r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr);
+      r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr);
+    } else {
+      error = grpc_error_set_str(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("cannot parse authority"),
+          GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name));
+      goto error_cleanup;
+    }
+    int status = ares_set_servers_ports(*channel, &r->dns_server_addr);
+    if (status != ARES_SUCCESS) {
+      char* error_msg;
+      gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s",
+                   ares_strerror(status));
+      error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+      gpr_free(error_msg);
+      goto error_cleanup;
+    }
+  }
+  r->pending_queries = 1;
+  if (grpc_ares_query_ipv6()) {
+    hr = create_hostbyname_request_locked(r, host, grpc_strhtons(port),
+                                          false /* is_balancer */);
+    ares_gethostbyname(*channel, hr->host, AF_INET6, on_hostbyname_done_locked,
+                       hr);
+  }
+  hr = create_hostbyname_request_locked(r, host, grpc_strhtons(port),
+                                        false /* is_balancer */);
+  ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_locked,
+                     hr);
+  if (check_grpclb) {
+    /* Query the SRV record */
+    grpc_ares_request_ref_locked(r);
+    char* service_name;
+    gpr_asprintf(&service_name, "_grpclb._tcp.%s", host);
+    ares_query(*channel, service_name, ns_c_in, ns_t_srv,
+               on_srv_query_done_locked, r);
+    gpr_free(service_name);
+  }
+  if (r->service_config_json_out != nullptr) {
+    grpc_ares_request_ref_locked(r);
+    char* config_name;
+    gpr_asprintf(&config_name, "_grpc_config.%s", host);
+    ares_search(*channel, config_name, ns_c_in, ns_t_txt, on_txt_done_locked,
+                r);
+    gpr_free(config_name);
+  }
+  grpc_ares_ev_driver_start_locked(r->ev_driver);
+  grpc_ares_request_unref_locked(r);
+  gpr_free(host);
+  gpr_free(port);
+  return;
+
+error_cleanup:
+  GRPC_CLOSURE_SCHED(r->on_done, error);
+  gpr_free(host);
+  gpr_free(port);
+}
+
+static bool inner_resolve_as_ip_literal_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs, char** host,
+    char** port, char** hostport) {
+  gpr_split_host_port(name, host, port);
+  if (*host == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Failed to parse %s to host:port while attempting to resolve as ip "
+            "literal.",
+            name);
+    return false;
+  }
+  if (*port == nullptr) {
+    if (default_port == nullptr) {
+      gpr_log(GPR_ERROR,
+              "No port or default port for %s while attempting to resolve as "
+              "ip literal.",
+              name);
+      return false;
+    }
+    *port = gpr_strdup(default_port);
+  }
+  grpc_resolved_address addr;
+  GPR_ASSERT(gpr_join_host_port(hostport, *host, atoi(*port)));
+  if (grpc_parse_ipv4_hostport(*hostport, &addr, false /* log errors */) ||
+      grpc_parse_ipv6_hostport(*hostport, &addr, false /* log errors */)) {
+    GPR_ASSERT(*addrs == nullptr);
+    *addrs = grpc_core::MakeUnique<ServerAddressList>();
+    (*addrs)->emplace_back(addr.addr, addr.len, nullptr /* args */);
+    return true;
+  }
+  return false;
+}
+
+static bool resolve_as_ip_literal_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs) {
+  char* host = nullptr;
+  char* port = nullptr;
+  char* hostport = nullptr;
+  bool out = inner_resolve_as_ip_literal_locked(name, default_port, addrs,
+                                                &host, &port, &hostport);
+  gpr_free(host);
+  gpr_free(port);
+  gpr_free(hostport);
+  return out;
+}
+
+static bool target_matches_localhost_inner(const char* name, char** host,
+                                           char** port) {
+  if (!gpr_split_host_port(name, host, port)) {
+    gpr_log(GPR_ERROR, "Unable to split host and port for name: %s", name);
+    return false;
+  }
+  if (gpr_stricmp(*host, "localhost") == 0) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+static bool target_matches_localhost(const char* name) {
+  char* host = nullptr;
+  char* port = nullptr;
+  bool out = target_matches_localhost_inner(name, &host, &port);
+  gpr_free(host);
+  gpr_free(port);
+  return out;
+}
+
+static grpc_ares_request* grpc_dns_lookup_ares_locked_impl(
+    const char* dns_server, const char* name, const char* default_port,
+    grpc_pollset_set* interested_parties, grpc_closure* on_done,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs,
+    bool check_grpclb, char** service_config_json, int query_timeout_ms,
+    grpc_combiner* combiner) {
+  grpc_ares_request* r =
+      static_cast<grpc_ares_request*>(gpr_zalloc(sizeof(grpc_ares_request)));
+  r->ev_driver = nullptr;
+  r->on_done = on_done;
+  r->addresses_out = addrs;
+  r->service_config_json_out = service_config_json;
+  r->success = false;
+  r->error = GRPC_ERROR_NONE;
+  r->pending_queries = 0;
+  GRPC_CARES_TRACE_LOG(
+      "request:%p c-ares grpc_dns_lookup_ares_locked_impl name=%s, "
+      "default_port=%s",
+      r, name, default_port);
+  // Early out if the target is an ipv4 or ipv6 literal.
+  if (resolve_as_ip_literal_locked(name, default_port, addrs)) {
+    GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE);
+    return r;
+  }
+  // Early out if the target is localhost and we're on Windows.
+  if (grpc_ares_maybe_resolve_localhost_manually_locked(name, default_port,
+                                                        addrs)) {
+    GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE);
+    return r;
+  }
+  // Don't query for SRV and TXT records if the target is "localhost", so
+  // as to cut down on lookups over the network, especially in tests:
+  // https://github.com/grpc/proposal/pull/79
+  if (target_matches_localhost(name)) {
+    check_grpclb = false;
+    r->service_config_json_out = nullptr;
+  }
+  // Look up name using c-ares lib.
+  grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked(
+      r, dns_server, name, default_port, interested_parties, check_grpclb,
+      query_timeout_ms, combiner);
+  return r;
+}
+
+grpc_ares_request* (*grpc_dns_lookup_ares_locked)(
+    const char* dns_server, const char* name, const char* default_port,
+    grpc_pollset_set* interested_parties, grpc_closure* on_done,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs,
+    bool check_grpclb, char** service_config_json, int query_timeout_ms,
+    grpc_combiner* combiner) = grpc_dns_lookup_ares_locked_impl;
+
+static void grpc_cancel_ares_request_locked_impl(grpc_ares_request* r) {
+  GPR_ASSERT(r != nullptr);
+  if (r->ev_driver != nullptr) {
+    grpc_ares_ev_driver_shutdown_locked(r->ev_driver);
+  }
+}
+
+void (*grpc_cancel_ares_request_locked)(grpc_ares_request* r) =
+    grpc_cancel_ares_request_locked_impl;
+
+grpc_error* grpc_ares_init(void) {
+  gpr_once_init(&g_basic_init, do_basic_init);
+  gpr_mu_lock(&g_init_mu);
+  int status = ares_library_init(ARES_LIB_INIT_ALL);
+  gpr_mu_unlock(&g_init_mu);
+
+  if (status != ARES_SUCCESS) {
+    char* error_msg;
+    gpr_asprintf(&error_msg, "ares_library_init failed: %s",
+                 ares_strerror(status));
+    grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
+    gpr_free(error_msg);
+    return error;
+  }
+  return GRPC_ERROR_NONE;
+}
+
+void grpc_ares_cleanup(void) {
+  gpr_mu_lock(&g_init_mu);
+  ares_library_cleanup();
+  gpr_mu_unlock(&g_init_mu);
+}
+
+/*
+ * grpc_resolve_address_ares related structs and functions
+ */
+
+typedef struct grpc_resolve_address_ares_request {
+  /* combiner that queries and related callbacks run under */
+  grpc_combiner* combiner;
+  /** the pointer to receive the resolved addresses */
+  grpc_resolved_addresses** addrs_out;
+  /** currently resolving addresses */
+  grpc_core::UniquePtr<ServerAddressList> addresses;
+  /** closure to call when the resolve_address_ares request completes */
+  grpc_closure* on_resolve_address_done;
+  /** a closure wrapping on_resolve_address_done, which should be invoked when
+     the grpc_dns_lookup_ares_locked operation is done. */
+  grpc_closure on_dns_lookup_done_locked;
+  /* target name */
+  const char* name;
+  /* default port to use if none is specified */
+  const char* default_port;
+  /* pollset_set to be driven by */
+  grpc_pollset_set* interested_parties;
+  /* underlying ares_request that the query is performed on */
+  grpc_ares_request* ares_request = nullptr;
+} grpc_resolve_address_ares_request;
+
+static void on_dns_lookup_done_locked(void* arg, grpc_error* error) {
+  grpc_resolve_address_ares_request* r =
+      static_cast<grpc_resolve_address_ares_request*>(arg);
+  gpr_free(r->ares_request);
+  grpc_resolved_addresses** resolved_addresses = r->addrs_out;
+  if (r->addresses == nullptr || r->addresses->empty()) {
+    *resolved_addresses = nullptr;
+  } else {
+    *resolved_addresses = static_cast<grpc_resolved_addresses*>(
+        gpr_zalloc(sizeof(grpc_resolved_addresses)));
+    (*resolved_addresses)->naddrs = r->addresses->size();
+    (*resolved_addresses)->addrs =
+        static_cast<grpc_resolved_address*>(gpr_zalloc(
+            sizeof(grpc_resolved_address) * (*resolved_addresses)->naddrs));
+    for (size_t i = 0; i < (*resolved_addresses)->naddrs; ++i) {
+      GPR_ASSERT(!(*r->addresses)[i].IsBalancer());
+      memcpy(&(*resolved_addresses)->addrs[i], &(*r->addresses)[i].address(),
+             sizeof(grpc_resolved_address));
+    }
+  }
+  GRPC_CLOSURE_SCHED(r->on_resolve_address_done, GRPC_ERROR_REF(error));
+  GRPC_COMBINER_UNREF(r->combiner, "on_dns_lookup_done_cb");
+  grpc_core::Delete(r);
+}
+
+static void grpc_resolve_address_invoke_dns_lookup_ares_locked(
+    void* arg, grpc_error* unused_error) {
+  grpc_resolve_address_ares_request* r =
+      static_cast<grpc_resolve_address_ares_request*>(arg);
+  r->ares_request = grpc_dns_lookup_ares_locked(
+      nullptr /* dns_server */, r->name, r->default_port, r->interested_parties,
+      &r->on_dns_lookup_done_locked, &r->addresses, false /* check_grpclb */,
+      nullptr /* service_config_json */, GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS,
+      r->combiner);
+}
+
+static void grpc_resolve_address_ares_impl(const char* name,
+                                           const char* default_port,
+                                           grpc_pollset_set* interested_parties,
+                                           grpc_closure* on_done,
+                                           grpc_resolved_addresses** addrs) {
+  grpc_resolve_address_ares_request* r =
+      grpc_core::New<grpc_resolve_address_ares_request>();
+  r->combiner = grpc_combiner_create();
+  r->addrs_out = addrs;
+  r->on_resolve_address_done = on_done;
+  GRPC_CLOSURE_INIT(&r->on_dns_lookup_done_locked, on_dns_lookup_done_locked, r,
+                    grpc_combiner_scheduler(r->combiner));
+  r->name = name;
+  r->default_port = default_port;
+  r->interested_parties = interested_parties;
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_CREATE(grpc_resolve_address_invoke_dns_lookup_ares_locked, r,
+                          grpc_combiner_scheduler(r->combiner)),
+      GRPC_ERROR_NONE);
+}
+
+void (*grpc_resolve_address_ares)(
+    const char* name, const char* default_port,
+    grpc_pollset_set* interested_parties, grpc_closure* on_done,
+    grpc_resolved_addresses** addrs) = grpc_resolve_address_ares_impl;
+
+#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h
new file mode 100644
index 0000000..2808250
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+
+#define GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS 10000
+
+extern grpc_core::TraceFlag grpc_trace_cares_address_sorting;
+
+extern grpc_core::TraceFlag grpc_trace_cares_resolver;
+
+#define GRPC_CARES_TRACE_LOG(format, ...)                         \
+  if (grpc_trace_cares_resolver.enabled()) {                      \
+    gpr_log(GPR_DEBUG, "(c-ares resolver) " format, __VA_ARGS__); \
+  }
+
+typedef struct grpc_ares_request grpc_ares_request;
+
+/* Asynchronously resolve \a name. Use \a default_port if a port isn't
+   designated in \a name, otherwise use the port in \a name. grpc_ares_init()
+   must be called at least once before this function. \a on_done may be
+   called directly in this function without being scheduled with \a exec_ctx,
+   so it must not try to acquire locks that are being held by the caller. */
+extern void (*grpc_resolve_address_ares)(const char* name,
+                                         const char* default_port,
+                                         grpc_pollset_set* interested_parties,
+                                         grpc_closure* on_done,
+                                         grpc_resolved_addresses** addresses);
+
+/* Asynchronously resolve \a name. It will try to resolve grpclb SRV records in
+  addition to the normal address records. For normal address records, it uses
+  \a default_port if a port isn't designated in \a name, otherwise it uses the
+  port in \a name. grpc_ares_init() must be called at least once before this
+  function. \a on_done may be called directly in this function without being
+  scheduled with \a exec_ctx, so it must not try to acquire locks that are
+  being held by the caller. The returned grpc_ares_request object is owned
+  by the caller and it is safe to free after on_done is called back. */
+extern grpc_ares_request* (*grpc_dns_lookup_ares_locked)(
+    const char* dns_server, const char* name, const char* default_port,
+    grpc_pollset_set* interested_parties, grpc_closure* on_done,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addresses,
+    bool check_grpclb, char** service_config_json, int query_timeout_ms,
+    grpc_combiner* combiner);
+
+/* Cancel the pending grpc_ares_request \a request */
+extern void (*grpc_cancel_ares_request_locked)(grpc_ares_request* request);
+
+/* Initialize gRPC ares wrapper. Must be called at least once before
+   grpc_resolve_address_ares(). */
+grpc_error* grpc_ares_init(void);
+
+/* Uninitialized gRPC ares wrapper. If there was more than one previous call to
+   grpc_ares_init(), this function uninitializes the gRPC ares wrapper only if
+   it has been called the same number of times as grpc_ares_init(). */
+void grpc_ares_cleanup(void);
+
+/** Schedules the desired callback for request completion
+ * and destroys the grpc_ares_request */
+void grpc_ares_complete_request_locked(grpc_ares_request* request);
+
+/* Indicates whether or not AAAA queries should be attempted. */
+/* E.g., return false if ipv6 is known to not be available. */
+bool grpc_ares_query_ipv6();
+
+/* Maybe (depending on the current platform) checks if "name" matches
+ * "localhost" and if so fills in addrs with the correct sockaddr structures.
+ * Returns a bool indicating whether or not such an action was performed.
+ * See https://github.com/grpc/grpc/issues/15158. */
+bool grpc_ares_maybe_resolve_localhost_manually_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs);
+
+/* Sorts destinations in lb_addrs according to RFC 6724. */
+void grpc_cares_wrapper_address_sorting_sort(
+    grpc_core::ServerAddressList* addresses);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
new file mode 100644
index 0000000..1f4701c
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2016-2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#if GRPC_ARES != 1 || defined(GRPC_UV)
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+
+struct grpc_ares_request {
+  char val;
+};
+
+static grpc_ares_request* grpc_dns_lookup_ares_locked_impl(
+    const char* dns_server, const char* name, const char* default_port,
+    grpc_pollset_set* interested_parties, grpc_closure* on_done,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs,
+    bool check_grpclb, char** service_config_json, int query_timeout_ms,
+    grpc_combiner* combiner) {
+  return NULL;
+}
+
+grpc_ares_request* (*grpc_dns_lookup_ares_locked)(
+    const char* dns_server, const char* name, const char* default_port,
+    grpc_pollset_set* interested_parties, grpc_closure* on_done,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs,
+    bool check_grpclb, char** service_config_json, int query_timeout_ms,
+    grpc_combiner* combiner) = grpc_dns_lookup_ares_locked_impl;
+
+static void grpc_cancel_ares_request_locked_impl(grpc_ares_request* r) {}
+
+void (*grpc_cancel_ares_request_locked)(grpc_ares_request* r) =
+    grpc_cancel_ares_request_locked_impl;
+
+grpc_error* grpc_ares_init(void) { return GRPC_ERROR_NONE; }
+
+void grpc_ares_cleanup(void) {}
+
+static void grpc_resolve_address_ares_impl(const char* name,
+                                           const char* default_port,
+                                           grpc_pollset_set* interested_parties,
+                                           grpc_closure* on_done,
+                                           grpc_resolved_addresses** addrs) {}
+
+void (*grpc_resolve_address_ares)(
+    const char* name, const char* default_port,
+    grpc_pollset_set* interested_parties, grpc_closure* on_done,
+    grpc_resolved_addresses** addrs) = grpc_resolve_address_ares_impl;
+
+#endif /* GRPC_ARES != 1 || defined(GRPC_UV) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
new file mode 100644
index 0000000..028d844
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER)
+
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); }
+
+bool grpc_ares_maybe_resolve_localhost_manually_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs) {
+  return false;
+}
+
+#endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
new file mode 100644
index 0000000..202452f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#if GRPC_ARES == 1 && defined(GPR_WINDOWS)
+
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+
+bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); }
+
+static bool inner_maybe_resolve_localhost_manually_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs, char** host,
+    char** port) {
+  gpr_split_host_port(name, host, port);
+  if (*host == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Failed to parse %s into host:port during Windows localhost "
+            "resolution check.",
+            name);
+    return false;
+  }
+  if (*port == nullptr) {
+    if (default_port == nullptr) {
+      gpr_log(GPR_ERROR,
+              "No port or default port for %s during Windows localhost "
+              "resolution check.",
+              name);
+      return false;
+    }
+    *port = gpr_strdup(default_port);
+  }
+  if (gpr_stricmp(*host, "localhost") == 0) {
+    GPR_ASSERT(*addrs == nullptr);
+    *addrs = grpc_core::MakeUnique<grpc_core::ServerAddressList>();
+    uint16_t numeric_port = grpc_strhtons(*port);
+    // Append the ipv6 loopback address.
+    struct sockaddr_in6 ipv6_loopback_addr;
+    memset(&ipv6_loopback_addr, 0, sizeof(ipv6_loopback_addr));
+    ((char*)&ipv6_loopback_addr.sin6_addr)[15] = 1;
+    ipv6_loopback_addr.sin6_family = AF_INET6;
+    ipv6_loopback_addr.sin6_port = numeric_port;
+    (*addrs)->emplace_back(&ipv6_loopback_addr, sizeof(ipv6_loopback_addr),
+                           nullptr /* args */);
+    // Append the ipv4 loopback address.
+    struct sockaddr_in ipv4_loopback_addr;
+    memset(&ipv4_loopback_addr, 0, sizeof(ipv4_loopback_addr));
+    ((char*)&ipv4_loopback_addr.sin_addr)[0] = 0x7f;
+    ((char*)&ipv4_loopback_addr.sin_addr)[3] = 0x01;
+    ipv4_loopback_addr.sin_family = AF_INET;
+    ipv4_loopback_addr.sin_port = numeric_port;
+    (*addrs)->emplace_back(&ipv4_loopback_addr, sizeof(ipv4_loopback_addr),
+                           nullptr /* args */);
+    // Let the address sorter figure out which one should be tried first.
+    grpc_cares_wrapper_address_sorting_sort(addrs->get());
+    return true;
+  }
+  return false;
+}
+
+bool grpc_ares_maybe_resolve_localhost_manually_locked(
+    const char* name, const char* default_port,
+    grpc_core::UniquePtr<grpc_core::ServerAddressList>* addrs) {
+  char* host = nullptr;
+  char* port = nullptr;
+  bool out = inner_maybe_resolve_localhost_manually_locked(name, default_port,
+                                                           addrs, &host, &port);
+  gpr_free(host);
+  gpr_free(port);
+  return out;
+}
+
+#endif /* GRPC_ARES == 1 && defined(GPR_WINDOWS) */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/README.md b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/README.md
new file mode 100644
index 0000000..695de47
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/README.md
@@ -0,0 +1,2 @@
+dns: scheme name resolution, using getaddrbyname
+(or other OS specific implementation)
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
new file mode 100644
index 0000000..c365f1a
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc
@@ -0,0 +1,343 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <inttypes.h>
+#include <climits>
+#include <cstring>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/timer.h"
+
+#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
+#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
+#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
+#define GRPC_DNS_RECONNECT_JITTER 0.2
+
+namespace grpc_core {
+
+namespace {
+
+const char kDefaultPort[] = "https";
+
+class NativeDnsResolver : public Resolver {
+ public:
+  explicit NativeDnsResolver(const ResolverArgs& args);
+
+  void NextLocked(grpc_channel_args** result,
+                  grpc_closure* on_complete) override;
+
+  void RequestReresolutionLocked() override;
+
+  void ResetBackoffLocked() override;
+
+  void ShutdownLocked() override;
+
+ private:
+  virtual ~NativeDnsResolver();
+
+  void MaybeStartResolvingLocked();
+  void StartResolvingLocked();
+  void MaybeFinishNextLocked();
+
+  static void OnNextResolutionLocked(void* arg, grpc_error* error);
+  static void OnResolvedLocked(void* arg, grpc_error* error);
+
+  /// name to resolve
+  char* name_to_resolve_ = nullptr;
+  /// channel args
+  grpc_channel_args* channel_args_ = nullptr;
+  /// pollset_set to drive the name resolution process
+  grpc_pollset_set* interested_parties_ = nullptr;
+  /// are we currently resolving?
+  bool resolving_ = false;
+  grpc_closure on_resolved_;
+  /// which version of the result have we published?
+  int published_version_ = 0;
+  /// which version of the result is current?
+  int resolved_version_ = 0;
+  /// pending next completion, or nullptr
+  grpc_closure* next_completion_ = nullptr;
+  /// target result address for next completion
+  grpc_channel_args** target_result_ = nullptr;
+  /// current (fully resolved) result
+  grpc_channel_args* resolved_result_ = nullptr;
+  /// next resolution timer
+  bool have_next_resolution_timer_ = false;
+  grpc_timer next_resolution_timer_;
+  grpc_closure on_next_resolution_;
+  /// min time between DNS requests
+  grpc_millis min_time_between_resolutions_;
+  /// timestamp of last DNS request
+  grpc_millis last_resolution_timestamp_ = -1;
+  /// retry backoff state
+  BackOff backoff_;
+  /// currently resolving addresses
+  grpc_resolved_addresses* addresses_ = nullptr;
+};
+
+NativeDnsResolver::NativeDnsResolver(const ResolverArgs& args)
+    : Resolver(args.combiner),
+      backoff_(
+          BackOff::Options()
+              .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS *
+                                   1000)
+              .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
+              .set_jitter(GRPC_DNS_RECONNECT_JITTER)
+              .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) {
+  char* path = args.uri->path;
+  if (path[0] == '/') ++path;
+  name_to_resolve_ = gpr_strdup(path);
+  channel_args_ = grpc_channel_args_copy(args.args);
+  const grpc_arg* arg = grpc_channel_args_find(
+      args.args, GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS);
+  min_time_between_resolutions_ =
+      grpc_channel_arg_get_integer(arg, {1000, 0, INT_MAX});
+  interested_parties_ = grpc_pollset_set_create();
+  if (args.pollset_set != nullptr) {
+    grpc_pollset_set_add_pollset_set(interested_parties_, args.pollset_set);
+  }
+  GRPC_CLOSURE_INIT(&on_next_resolution_,
+                    NativeDnsResolver::OnNextResolutionLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+  GRPC_CLOSURE_INIT(&on_resolved_, NativeDnsResolver::OnResolvedLocked, this,
+                    grpc_combiner_scheduler(args.combiner));
+}
+
+NativeDnsResolver::~NativeDnsResolver() {
+  if (resolved_result_ != nullptr) {
+    grpc_channel_args_destroy(resolved_result_);
+  }
+  grpc_pollset_set_destroy(interested_parties_);
+  gpr_free(name_to_resolve_);
+  grpc_channel_args_destroy(channel_args_);
+}
+
+void NativeDnsResolver::NextLocked(grpc_channel_args** result,
+                                   grpc_closure* on_complete) {
+  GPR_ASSERT(next_completion_ == nullptr);
+  next_completion_ = on_complete;
+  target_result_ = result;
+  if (resolved_version_ == 0 && !resolving_) {
+    MaybeStartResolvingLocked();
+  } else {
+    MaybeFinishNextLocked();
+  }
+}
+
+void NativeDnsResolver::RequestReresolutionLocked() {
+  if (!resolving_) {
+    MaybeStartResolvingLocked();
+  }
+}
+
+void NativeDnsResolver::ResetBackoffLocked() {
+  if (have_next_resolution_timer_) {
+    grpc_timer_cancel(&next_resolution_timer_);
+  }
+  backoff_.Reset();
+}
+
+void NativeDnsResolver::ShutdownLocked() {
+  if (have_next_resolution_timer_) {
+    grpc_timer_cancel(&next_resolution_timer_);
+  }
+  if (next_completion_ != nullptr) {
+    *target_result_ = nullptr;
+    GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                             "Resolver Shutdown"));
+    next_completion_ = nullptr;
+  }
+}
+
+void NativeDnsResolver::OnNextResolutionLocked(void* arg, grpc_error* error) {
+  NativeDnsResolver* r = static_cast<NativeDnsResolver*>(arg);
+  r->have_next_resolution_timer_ = false;
+  if (error == GRPC_ERROR_NONE && !r->resolving_) {
+    r->StartResolvingLocked();
+  }
+  r->Unref(DEBUG_LOCATION, "retry-timer");
+}
+
+void NativeDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) {
+  NativeDnsResolver* r = static_cast<NativeDnsResolver*>(arg);
+  grpc_channel_args* result = nullptr;
+  GPR_ASSERT(r->resolving_);
+  r->resolving_ = false;
+  GRPC_ERROR_REF(error);
+  error =
+      grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS,
+                         grpc_slice_from_copied_string(r->name_to_resolve_));
+  if (r->addresses_ != nullptr) {
+    ServerAddressList addresses;
+    for (size_t i = 0; i < r->addresses_->naddrs; ++i) {
+      addresses.emplace_back(&r->addresses_->addrs[i].addr,
+                             r->addresses_->addrs[i].len, nullptr /* args */);
+    }
+    grpc_arg new_arg = CreateServerAddressListChannelArg(&addresses);
+    result = grpc_channel_args_copy_and_add(r->channel_args_, &new_arg, 1);
+    grpc_resolved_addresses_destroy(r->addresses_);
+    // Reset backoff state so that we start from the beginning when the
+    // next request gets triggered.
+    r->backoff_.Reset();
+  } else {
+    grpc_millis next_try = r->backoff_.NextAttemptTime();
+    grpc_millis timeout = next_try - ExecCtx::Get()->Now();
+    gpr_log(GPR_INFO, "dns resolution failed (will retry): %s",
+            grpc_error_string(error));
+    GPR_ASSERT(!r->have_next_resolution_timer_);
+    r->have_next_resolution_timer_ = true;
+    // TODO(roth): We currently deal with this ref manually.  Once the
+    // new closure API is done, find a way to track this ref with the timer
+    // callback as part of the type system.
+    RefCountedPtr<Resolver> self =
+        r->Ref(DEBUG_LOCATION, "next_resolution_timer");
+    self.release();
+    if (timeout > 0) {
+      gpr_log(GPR_DEBUG, "retrying in %" PRId64 " milliseconds", timeout);
+    } else {
+      gpr_log(GPR_DEBUG, "retrying immediately");
+    }
+    grpc_timer_init(&r->next_resolution_timer_, next_try,
+                    &r->on_next_resolution_);
+  }
+  if (r->resolved_result_ != nullptr) {
+    grpc_channel_args_destroy(r->resolved_result_);
+  }
+  r->resolved_result_ = result;
+  ++r->resolved_version_;
+  r->MaybeFinishNextLocked();
+  GRPC_ERROR_UNREF(error);
+  r->Unref(DEBUG_LOCATION, "dns-resolving");
+}
+
+void NativeDnsResolver::MaybeStartResolvingLocked() {
+  // If there is an existing timer, the time it fires is the earliest time we
+  // can start the next resolution.
+  if (have_next_resolution_timer_) return;
+  if (last_resolution_timestamp_ >= 0) {
+    const grpc_millis earliest_next_resolution =
+        last_resolution_timestamp_ + min_time_between_resolutions_;
+    const grpc_millis ms_until_next_resolution =
+        earliest_next_resolution - grpc_core::ExecCtx::Get()->Now();
+    if (ms_until_next_resolution > 0) {
+      const grpc_millis last_resolution_ago =
+          grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_;
+      gpr_log(GPR_DEBUG,
+              "In cooldown from last resolution (from %" PRId64
+              " ms ago). Will resolve again in %" PRId64 " ms",
+              last_resolution_ago, ms_until_next_resolution);
+      have_next_resolution_timer_ = true;
+      // TODO(roth): We currently deal with this ref manually.  Once the
+      // new closure API is done, find a way to track this ref with the timer
+      // callback as part of the type system.
+      RefCountedPtr<Resolver> self =
+          Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown");
+      self.release();
+      grpc_timer_init(&next_resolution_timer_, ms_until_next_resolution,
+                      &on_next_resolution_);
+      return;
+    }
+  }
+  StartResolvingLocked();
+}
+
+void NativeDnsResolver::StartResolvingLocked() {
+  gpr_log(GPR_DEBUG, "Start resolving.");
+  // TODO(roth): We currently deal with this ref manually.  Once the
+  // new closure API is done, find a way to track this ref with the timer
+  // callback as part of the type system.
+  RefCountedPtr<Resolver> self = Ref(DEBUG_LOCATION, "dns-resolving");
+  self.release();
+  GPR_ASSERT(!resolving_);
+  resolving_ = true;
+  addresses_ = nullptr;
+  grpc_resolve_address(name_to_resolve_, kDefaultPort, interested_parties_,
+                       &on_resolved_, &addresses_);
+  last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now();
+}
+
+void NativeDnsResolver::MaybeFinishNextLocked() {
+  if (next_completion_ != nullptr && resolved_version_ != published_version_) {
+    *target_result_ = resolved_result_ == nullptr
+                          ? nullptr
+                          : grpc_channel_args_copy(resolved_result_);
+    GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE);
+    next_completion_ = nullptr;
+    published_version_ = resolved_version_;
+  }
+}
+
+//
+// Factory
+//
+
+class NativeDnsResolverFactory : public ResolverFactory {
+ public:
+  OrphanablePtr<Resolver> CreateResolver(
+      const ResolverArgs& args) const override {
+    if (GPR_UNLIKELY(0 != strcmp(args.uri->authority, ""))) {
+      gpr_log(GPR_ERROR, "authority based dns uri's not supported");
+      return OrphanablePtr<Resolver>(nullptr);
+    }
+    return OrphanablePtr<Resolver>(New<NativeDnsResolver>(args));
+  }
+
+  const char* scheme() const override { return "dns"; }
+};
+
+}  // namespace
+
+}  // namespace grpc_core
+
+void grpc_resolver_dns_native_init() {
+  char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER");
+  if (resolver_env != nullptr && gpr_stricmp(resolver_env, "native") == 0) {
+    gpr_log(GPR_DEBUG, "Using native dns resolver");
+    grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
+        grpc_core::UniquePtr<grpc_core::ResolverFactory>(
+            grpc_core::New<grpc_core::NativeDnsResolverFactory>()));
+  } else {
+    grpc_core::ResolverRegistry::Builder::InitRegistry();
+    grpc_core::ResolverFactory* existing_factory =
+        grpc_core::ResolverRegistry::LookupResolverFactory("dns");
+    if (existing_factory == nullptr) {
+      gpr_log(GPR_DEBUG, "Using native dns resolver");
+      grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
+          grpc_core::UniquePtr<grpc_core::ResolverFactory>(
+              grpc_core::New<grpc_core::NativeDnsResolverFactory>()));
+    }
+  }
+  gpr_free(resolver_env);
+}
+
+void grpc_resolver_dns_native_shutdown() {}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
new file mode 100644
index 0000000..25833949
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
@@ -0,0 +1,297 @@
+//
+// Copyright 2016 gRPC authors.
+//
+// Licensed 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.
+//
+
+// 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 <grpc/support/port_platform.h>
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
+
+namespace grpc_core {
+
+// This cannot be in an anonymous namespace, because it is a friend of
+// FakeResolverResponseGenerator.
+class FakeResolver : public Resolver {
+ public:
+  explicit FakeResolver(const ResolverArgs& args);
+
+  void NextLocked(grpc_channel_args** result,
+                  grpc_closure* on_complete) override;
+
+  void RequestReresolutionLocked() override;
+
+ private:
+  friend class FakeResolverResponseGenerator;
+
+  virtual ~FakeResolver();
+
+  void MaybeFinishNextLocked();
+
+  void ShutdownLocked() override;
+
+  // passed-in parameters
+  grpc_channel_args* channel_args_ = nullptr;
+  // If not NULL, the next set of resolution results to be returned to
+  // NextLocked()'s closure.
+  grpc_channel_args* next_results_ = nullptr;
+  // Results to use for the pretended re-resolution in
+  // RequestReresolutionLocked().
+  grpc_channel_args* reresolution_results_ = nullptr;
+  // pending next completion, or NULL
+  grpc_closure* next_completion_ = nullptr;
+  // target result address for next completion
+  grpc_channel_args** target_result_ = nullptr;
+  // if true, return failure
+  bool return_failure_ = false;
+};
+
+FakeResolver::FakeResolver(const ResolverArgs& args) : Resolver(args.combiner) {
+  channel_args_ = grpc_channel_args_copy(args.args);
+  FakeResolverResponseGenerator* response_generator =
+      FakeResolverResponseGenerator::GetFromArgs(args.args);
+  if (response_generator != nullptr) response_generator->resolver_ = this;
+}
+
+FakeResolver::~FakeResolver() {
+  grpc_channel_args_destroy(next_results_);
+  grpc_channel_args_destroy(reresolution_results_);
+  grpc_channel_args_destroy(channel_args_);
+}
+
+void FakeResolver::NextLocked(grpc_channel_args** target_result,
+                              grpc_closure* on_complete) {
+  GPR_ASSERT(next_completion_ == nullptr);
+  next_completion_ = on_complete;
+  target_result_ = target_result;
+  MaybeFinishNextLocked();
+}
+
+void FakeResolver::RequestReresolutionLocked() {
+  if (reresolution_results_ != nullptr || return_failure_) {
+    grpc_channel_args_destroy(next_results_);
+    next_results_ = grpc_channel_args_copy(reresolution_results_);
+    MaybeFinishNextLocked();
+  }
+}
+
+void FakeResolver::MaybeFinishNextLocked() {
+  if (next_completion_ != nullptr &&
+      (next_results_ != nullptr || return_failure_)) {
+    *target_result_ =
+        return_failure_ ? nullptr
+                        : grpc_channel_args_union(next_results_, channel_args_);
+    grpc_channel_args_destroy(next_results_);
+    next_results_ = nullptr;
+    GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE);
+    next_completion_ = nullptr;
+    return_failure_ = false;
+  }
+}
+
+void FakeResolver::ShutdownLocked() {
+  if (next_completion_ != nullptr) {
+    *target_result_ = nullptr;
+    GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                             "Resolver Shutdown"));
+    next_completion_ = nullptr;
+  }
+}
+
+//
+// FakeResolverResponseGenerator
+//
+
+struct SetResponseClosureArg {
+  grpc_closure set_response_closure;
+  FakeResolverResponseGenerator* generator;
+  grpc_channel_args* response;
+  bool immediate = true;
+};
+
+void FakeResolverResponseGenerator::SetResponseLocked(void* arg,
+                                                      grpc_error* error) {
+  SetResponseClosureArg* closure_arg = static_cast<SetResponseClosureArg*>(arg);
+  FakeResolver* resolver = closure_arg->generator->resolver_;
+  grpc_channel_args_destroy(resolver->next_results_);
+  resolver->next_results_ = closure_arg->response;
+  resolver->MaybeFinishNextLocked();
+  Delete(closure_arg);
+}
+
+void FakeResolverResponseGenerator::SetResponse(grpc_channel_args* response) {
+  GPR_ASSERT(response != nullptr);
+  GPR_ASSERT(resolver_ != nullptr);
+  SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>();
+  closure_arg->generator = this;
+  closure_arg->response = grpc_channel_args_copy(response);
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetResponseLocked,
+                        closure_arg,
+                        grpc_combiner_scheduler(resolver_->combiner())),
+      GRPC_ERROR_NONE);
+}
+
+void FakeResolverResponseGenerator::SetReresolutionResponseLocked(
+    void* arg, grpc_error* error) {
+  SetResponseClosureArg* closure_arg = static_cast<SetResponseClosureArg*>(arg);
+  FakeResolver* resolver = closure_arg->generator->resolver_;
+  grpc_channel_args_destroy(resolver->reresolution_results_);
+  resolver->reresolution_results_ = closure_arg->response;
+  Delete(closure_arg);
+}
+
+void FakeResolverResponseGenerator::SetReresolutionResponse(
+    grpc_channel_args* response) {
+  GPR_ASSERT(resolver_ != nullptr);
+  SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>();
+  closure_arg->generator = this;
+  closure_arg->response =
+      response != nullptr ? grpc_channel_args_copy(response) : nullptr;
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&closure_arg->set_response_closure,
+                        SetReresolutionResponseLocked, closure_arg,
+                        grpc_combiner_scheduler(resolver_->combiner())),
+      GRPC_ERROR_NONE);
+}
+
+void FakeResolverResponseGenerator::SetFailureLocked(void* arg,
+                                                     grpc_error* error) {
+  SetResponseClosureArg* closure_arg = static_cast<SetResponseClosureArg*>(arg);
+  FakeResolver* resolver = closure_arg->generator->resolver_;
+  resolver->return_failure_ = true;
+  if (closure_arg->immediate) resolver->MaybeFinishNextLocked();
+  Delete(closure_arg);
+}
+
+void FakeResolverResponseGenerator::SetFailure() {
+  GPR_ASSERT(resolver_ != nullptr);
+  SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>();
+  closure_arg->generator = this;
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked,
+                        closure_arg,
+                        grpc_combiner_scheduler(resolver_->combiner())),
+      GRPC_ERROR_NONE);
+}
+
+void FakeResolverResponseGenerator::SetFailureOnReresolution() {
+  GPR_ASSERT(resolver_ != nullptr);
+  SetResponseClosureArg* closure_arg = New<SetResponseClosureArg>();
+  closure_arg->generator = this;
+  closure_arg->immediate = false;
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked,
+                        closure_arg,
+                        grpc_combiner_scheduler(resolver_->combiner())),
+      GRPC_ERROR_NONE);
+}
+
+namespace {
+
+static void* response_generator_arg_copy(void* p) {
+  FakeResolverResponseGenerator* generator =
+      static_cast<FakeResolverResponseGenerator*>(p);
+  // TODO(roth): We currently deal with this ref manually.  Once the
+  // new channel args code is converted to C++, find a way to track this ref
+  // in a cleaner way.
+  RefCountedPtr<FakeResolverResponseGenerator> copy = generator->Ref();
+  copy.release();
+  return p;
+}
+
+static void response_generator_arg_destroy(void* p) {
+  FakeResolverResponseGenerator* generator =
+      static_cast<FakeResolverResponseGenerator*>(p);
+  generator->Unref();
+}
+
+static int response_generator_cmp(void* a, void* b) { return GPR_ICMP(a, b); }
+
+static const grpc_arg_pointer_vtable response_generator_arg_vtable = {
+    response_generator_arg_copy, response_generator_arg_destroy,
+    response_generator_cmp};
+
+}  // namespace
+
+grpc_arg FakeResolverResponseGenerator::MakeChannelArg(
+    FakeResolverResponseGenerator* generator) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_POINTER;
+  arg.key = (char*)GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR;
+  arg.value.pointer.p = generator;
+  arg.value.pointer.vtable = &response_generator_arg_vtable;
+  return arg;
+}
+
+FakeResolverResponseGenerator* FakeResolverResponseGenerator::GetFromArgs(
+    const grpc_channel_args* args) {
+  const grpc_arg* arg =
+      grpc_channel_args_find(args, GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR);
+  if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return nullptr;
+  return static_cast<FakeResolverResponseGenerator*>(arg->value.pointer.p);
+}
+
+//
+// Factory
+//
+
+namespace {
+
+class FakeResolverFactory : public ResolverFactory {
+ public:
+  OrphanablePtr<Resolver> CreateResolver(
+      const ResolverArgs& args) const override {
+    return OrphanablePtr<Resolver>(New<FakeResolver>(args));
+  }
+
+  const char* scheme() const override { return "fake"; }
+};
+
+}  // namespace
+
+}  // namespace grpc_core
+
+void grpc_resolver_fake_init() {
+  grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
+      grpc_core::UniquePtr<grpc_core::ResolverFactory>(
+          grpc_core::New<grpc_core::FakeResolverFactory>()));
+}
+
+void grpc_resolver_fake_shutdown() {}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
new file mode 100644
index 0000000..d86111c
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h
@@ -0,0 +1,87 @@
+//
+// Copyright 2016 gRPC authors.
+//
+// Licensed 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.
+//
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/iomgr/error.h"
+
+#define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \
+  "grpc.fake_resolver.response_generator"
+
+namespace grpc_core {
+
+class FakeResolver;
+
+/// A mechanism for generating responses for the fake resolver.
+/// An instance of this class is passed to the fake resolver via a channel
+/// argument (see \a MakeChannelArg()) and used to inject and trigger custom
+/// resolutions.
+// TODO(roth): I would ideally like this to be InternallyRefCounted
+// instead of RefCounted, but external refs are currently needed to
+// encode this in channel args.  Once channel_args are converted to C++,
+// see if we can find a way to fix this.
+class FakeResolverResponseGenerator
+    : public RefCounted<FakeResolverResponseGenerator> {
+ public:
+  FakeResolverResponseGenerator() {}
+
+  // Instructs the fake resolver associated with the response generator
+  // instance to trigger a new resolution with the specified response.
+  void SetResponse(grpc_channel_args* next_response);
+
+  // Sets the re-resolution response, which is returned by the fake resolver
+  // when re-resolution is requested (via \a RequestReresolutionLocked()).
+  // The new re-resolution response replaces any previous re-resolution
+  // response that may have been set by a previous call.
+  // If the re-resolution response is set to NULL, then the fake
+  // resolver will not return anything when \a RequestReresolutionLocked()
+  // is called.
+  void SetReresolutionResponse(grpc_channel_args* response);
+
+  // Tells the resolver to return a transient failure (signalled by
+  // returning a null result with no error).
+  void SetFailure();
+
+  // Same as SetFailure(), but instead of returning the error
+  // immediately, waits for the next call to RequestReresolutionLocked().
+  void SetFailureOnReresolution();
+
+  // Returns a channel arg containing \a generator.
+  static grpc_arg MakeChannelArg(FakeResolverResponseGenerator* generator);
+
+  // Returns the response generator in \a args, or null if not found.
+  static FakeResolverResponseGenerator* GetFromArgs(
+      const grpc_channel_args* args);
+
+ private:
+  friend class FakeResolver;
+
+  static void SetResponseLocked(void* arg, grpc_error* error);
+  static void SetReresolutionResponseLocked(void* arg, grpc_error* error);
+  static void SetFailureLocked(void* arg, grpc_error* error);
+
+  FakeResolver* resolver_ = nullptr;  // Do not own.
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/README.md b/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/README.md
new file mode 100644
index 0000000..e307ba8
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/README.md
@@ -0,0 +1 @@
+Support for resolving ipv4:, ipv6:, unix: schemes
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
new file mode 100644
index 0000000..1654747
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
@@ -0,0 +1,207 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+namespace grpc_core {
+
+namespace {
+
+class SockaddrResolver : public Resolver {
+ public:
+  /// Takes ownership of \a addresses.
+  SockaddrResolver(const ResolverArgs& args,
+                   UniquePtr<ServerAddressList> addresses);
+
+  void NextLocked(grpc_channel_args** result,
+                  grpc_closure* on_complete) override;
+
+  void ShutdownLocked() override;
+
+ private:
+  virtual ~SockaddrResolver();
+
+  void MaybeFinishNextLocked();
+
+  /// the addresses that we've "resolved"
+  UniquePtr<ServerAddressList> addresses_;
+  /// channel args
+  grpc_channel_args* channel_args_ = nullptr;
+  /// have we published?
+  bool published_ = false;
+  /// pending next completion, or NULL
+  grpc_closure* next_completion_ = nullptr;
+  /// target result address for next completion
+  grpc_channel_args** target_result_ = nullptr;
+};
+
+SockaddrResolver::SockaddrResolver(const ResolverArgs& args,
+                                   UniquePtr<ServerAddressList> addresses)
+    : Resolver(args.combiner),
+      addresses_(std::move(addresses)),
+      channel_args_(grpc_channel_args_copy(args.args)) {}
+
+SockaddrResolver::~SockaddrResolver() {
+  grpc_channel_args_destroy(channel_args_);
+}
+
+void SockaddrResolver::NextLocked(grpc_channel_args** target_result,
+                                  grpc_closure* on_complete) {
+  GPR_ASSERT(!next_completion_);
+  next_completion_ = on_complete;
+  target_result_ = target_result;
+  MaybeFinishNextLocked();
+}
+
+void SockaddrResolver::ShutdownLocked() {
+  if (next_completion_ != nullptr) {
+    *target_result_ = nullptr;
+    GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                             "Resolver Shutdown"));
+    next_completion_ = nullptr;
+  }
+}
+
+void SockaddrResolver::MaybeFinishNextLocked() {
+  if (next_completion_ != nullptr && !published_) {
+    published_ = true;
+    grpc_arg arg = CreateServerAddressListChannelArg(addresses_.get());
+    *target_result_ = grpc_channel_args_copy_and_add(channel_args_, &arg, 1);
+    GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE);
+    next_completion_ = nullptr;
+  }
+}
+
+//
+// Factory
+//
+
+void DoNothing(void* ignored) {}
+
+OrphanablePtr<Resolver> CreateSockaddrResolver(
+    const ResolverArgs& args,
+    bool parse(const grpc_uri* uri, grpc_resolved_address* dst)) {
+  if (0 != strcmp(args.uri->authority, "")) {
+    gpr_log(GPR_ERROR, "authority-based URIs not supported by the %s scheme",
+            args.uri->scheme);
+    return OrphanablePtr<Resolver>(nullptr);
+  }
+  // Construct addresses.
+  grpc_slice path_slice =
+      grpc_slice_new(args.uri->path, strlen(args.uri->path), DoNothing);
+  grpc_slice_buffer path_parts;
+  grpc_slice_buffer_init(&path_parts);
+  grpc_slice_split(path_slice, ",", &path_parts);
+  auto addresses = MakeUnique<ServerAddressList>();
+  bool errors_found = false;
+  for (size_t i = 0; i < path_parts.count; i++) {
+    grpc_uri ith_uri = *args.uri;
+    UniquePtr<char> part_str(grpc_slice_to_c_string(path_parts.slices[i]));
+    ith_uri.path = part_str.get();
+    grpc_resolved_address addr;
+    if (!parse(&ith_uri, &addr)) {
+      errors_found = true; /* GPR_TRUE */
+      break;
+    }
+    addresses->emplace_back(addr, nullptr /* args */);
+  }
+  grpc_slice_buffer_destroy_internal(&path_parts);
+  grpc_slice_unref_internal(path_slice);
+  if (errors_found) {
+    return OrphanablePtr<Resolver>(nullptr);
+  }
+  // Instantiate resolver.
+  return OrphanablePtr<Resolver>(
+      New<SockaddrResolver>(args, std::move(addresses)));
+}
+
+class IPv4ResolverFactory : public ResolverFactory {
+ public:
+  OrphanablePtr<Resolver> CreateResolver(
+      const ResolverArgs& args) const override {
+    return CreateSockaddrResolver(args, grpc_parse_ipv4);
+  }
+
+  const char* scheme() const override { return "ipv4"; }
+};
+
+class IPv6ResolverFactory : public ResolverFactory {
+ public:
+  OrphanablePtr<Resolver> CreateResolver(
+      const ResolverArgs& args) const override {
+    return CreateSockaddrResolver(args, grpc_parse_ipv6);
+  }
+
+  const char* scheme() const override { return "ipv6"; }
+};
+
+#ifdef GRPC_HAVE_UNIX_SOCKET
+class UnixResolverFactory : public ResolverFactory {
+ public:
+  OrphanablePtr<Resolver> CreateResolver(
+      const ResolverArgs& args) const override {
+    return CreateSockaddrResolver(args, grpc_parse_unix);
+  }
+
+  UniquePtr<char> GetDefaultAuthority(grpc_uri* uri) const override {
+    return UniquePtr<char>(gpr_strdup("localhost"));
+  }
+
+  const char* scheme() const override { return "unix"; }
+};
+#endif  // GRPC_HAVE_UNIX_SOCKET
+
+}  // namespace
+
+}  // namespace grpc_core
+
+void grpc_resolver_sockaddr_init() {
+  grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
+      grpc_core::UniquePtr<grpc_core::ResolverFactory>(
+          grpc_core::New<grpc_core::IPv4ResolverFactory>()));
+  grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
+      grpc_core::UniquePtr<grpc_core::ResolverFactory>(
+          grpc_core::New<grpc_core::IPv6ResolverFactory>()));
+#ifdef GRPC_HAVE_UNIX_SOCKET
+  grpc_core::ResolverRegistry::Builder::RegisterResolverFactory(
+      grpc_core::UniquePtr<grpc_core::ResolverFactory>(
+          grpc_core::New<grpc_core::UnixResolverFactory>()));
+#endif
+}
+
+void grpc_resolver_sockaddr_shutdown() {}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_factory.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver_factory.h
new file mode 100644
index 0000000..d891ef6
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_factory.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/resolver.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+namespace grpc_core {
+
+struct ResolverArgs {
+  /// The parsed URI to resolve.
+  grpc_uri* uri = nullptr;
+  /// Channel args to be included in resolver results.
+  const grpc_channel_args* args = nullptr;
+  /// Used to drive I/O in the name resolution process.
+  grpc_pollset_set* pollset_set = nullptr;
+  /// The combiner under which all resolver calls will be run.
+  grpc_combiner* combiner = nullptr;
+};
+
+class ResolverFactory {
+ public:
+  /// Returns a new resolver instance.
+  virtual OrphanablePtr<Resolver> CreateResolver(const ResolverArgs& args) const
+      GRPC_ABSTRACT;
+
+  /// Returns a string representing the default authority to use for this
+  /// scheme.
+  virtual UniquePtr<char> GetDefaultAuthority(grpc_uri* uri) const {
+    const char* path = uri->path;
+    if (path[0] == '/') ++path;
+    return UniquePtr<char>(gpr_strdup(path));
+  }
+
+  /// Returns the URI scheme that this factory implements.
+  /// Caller does NOT take ownership of result.
+  virtual const char* scheme() const GRPC_ABSTRACT;
+
+  virtual ~ResolverFactory() {}
+
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.cc
new file mode 100644
index 0000000..91c0267
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.cc
@@ -0,0 +1,178 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+namespace grpc_core {
+
+namespace {
+
+class RegistryState {
+ public:
+  RegistryState() : default_prefix_(gpr_strdup("dns:///")) {}
+
+  void SetDefaultPrefix(const char* default_resolver_prefix) {
+    GPR_ASSERT(default_resolver_prefix != nullptr);
+    GPR_ASSERT(*default_resolver_prefix != '\0');
+    default_prefix_.reset(gpr_strdup(default_resolver_prefix));
+  }
+
+  void RegisterResolverFactory(UniquePtr<ResolverFactory> factory) {
+    for (size_t i = 0; i < factories_.size(); ++i) {
+      GPR_ASSERT(strcmp(factories_[i]->scheme(), factory->scheme()) != 0);
+    }
+    factories_.push_back(std::move(factory));
+  }
+
+  ResolverFactory* LookupResolverFactory(const char* scheme) const {
+    for (size_t i = 0; i < factories_.size(); ++i) {
+      if (strcmp(scheme, factories_[i]->scheme()) == 0) {
+        return factories_[i].get();
+      }
+    }
+    return nullptr;
+  }
+
+  // Returns the factory for the scheme of \a target.  If \a target does
+  // not parse as a URI, prepends \a default_prefix_ and tries again.
+  // If URI parsing is successful (in either attempt), sets \a uri to
+  // point to the parsed URI.
+  // If \a default_prefix_ needs to be prepended, sets \a canonical_target
+  // to the canonical target string.
+  ResolverFactory* FindResolverFactory(const char* target, grpc_uri** uri,
+                                       char** canonical_target) const {
+    GPR_ASSERT(uri != nullptr);
+    *uri = grpc_uri_parse(target, 1);
+    ResolverFactory* factory =
+        *uri == nullptr ? nullptr : LookupResolverFactory((*uri)->scheme);
+    if (factory == nullptr) {
+      grpc_uri_destroy(*uri);
+      gpr_asprintf(canonical_target, "%s%s", default_prefix_.get(), target);
+      *uri = grpc_uri_parse(*canonical_target, 1);
+      factory =
+          *uri == nullptr ? nullptr : LookupResolverFactory((*uri)->scheme);
+      if (factory == nullptr) {
+        grpc_uri_destroy(grpc_uri_parse(target, 0));
+        grpc_uri_destroy(grpc_uri_parse(*canonical_target, 0));
+        gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target,
+                *canonical_target);
+      }
+    }
+    return factory;
+  }
+
+ private:
+  // We currently support 10 factories without doing additional
+  // allocation.  This number could be raised if there is a case where
+  // more factories are needed and the additional allocations are
+  // hurting performance (which is unlikely, since these allocations
+  // only occur at gRPC initialization time).
+  InlinedVector<UniquePtr<ResolverFactory>, 10> factories_;
+  UniquePtr<char> default_prefix_;
+};
+
+static RegistryState* g_state = nullptr;
+
+}  // namespace
+
+//
+// ResolverRegistry::Builder
+//
+
+void ResolverRegistry::Builder::InitRegistry() {
+  if (g_state == nullptr) g_state = New<RegistryState>();
+}
+
+void ResolverRegistry::Builder::ShutdownRegistry() {
+  Delete(g_state);
+  g_state = nullptr;
+}
+
+void ResolverRegistry::Builder::SetDefaultPrefix(
+    const char* default_resolver_prefix) {
+  InitRegistry();
+  g_state->SetDefaultPrefix(default_resolver_prefix);
+}
+
+void ResolverRegistry::Builder::RegisterResolverFactory(
+    UniquePtr<ResolverFactory> factory) {
+  InitRegistry();
+  g_state->RegisterResolverFactory(std::move(factory));
+}
+
+//
+// ResolverRegistry
+//
+
+ResolverFactory* ResolverRegistry::LookupResolverFactory(const char* scheme) {
+  GPR_ASSERT(g_state != nullptr);
+  return g_state->LookupResolverFactory(scheme);
+}
+
+OrphanablePtr<Resolver> ResolverRegistry::CreateResolver(
+    const char* target, const grpc_channel_args* args,
+    grpc_pollset_set* pollset_set, grpc_combiner* combiner) {
+  GPR_ASSERT(g_state != nullptr);
+  grpc_uri* uri = nullptr;
+  char* canonical_target = nullptr;
+  ResolverFactory* factory =
+      g_state->FindResolverFactory(target, &uri, &canonical_target);
+  ResolverArgs resolver_args;
+  resolver_args.uri = uri;
+  resolver_args.args = args;
+  resolver_args.pollset_set = pollset_set;
+  resolver_args.combiner = combiner;
+  OrphanablePtr<Resolver> resolver =
+      factory == nullptr ? nullptr : factory->CreateResolver(resolver_args);
+  grpc_uri_destroy(uri);
+  gpr_free(canonical_target);
+  return resolver;
+}
+
+UniquePtr<char> ResolverRegistry::GetDefaultAuthority(const char* target) {
+  GPR_ASSERT(g_state != nullptr);
+  grpc_uri* uri = nullptr;
+  char* canonical_target = nullptr;
+  ResolverFactory* factory =
+      g_state->FindResolverFactory(target, &uri, &canonical_target);
+  UniquePtr<char> authority =
+      factory == nullptr ? nullptr : factory->GetDefaultAuthority(uri);
+  grpc_uri_destroy(uri);
+  gpr_free(canonical_target);
+  return authority;
+}
+
+UniquePtr<char> ResolverRegistry::AddDefaultPrefixIfNeeded(const char* target) {
+  GPR_ASSERT(g_state != nullptr);
+  grpc_uri* uri = nullptr;
+  char* canonical_target = nullptr;
+  g_state->FindResolverFactory(target, &uri, &canonical_target);
+  grpc_uri_destroy(uri);
+  return UniquePtr<char>(canonical_target == nullptr ? gpr_strdup(target)
+                                                     : canonical_target);
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.h
new file mode 100644
index 0000000..d6ec681
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/resolver_factory.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+namespace grpc_core {
+
+class ResolverRegistry {
+ public:
+  /// Methods used to create and populate the ResolverRegistry.
+  /// NOT THREAD SAFE -- to be used only during global gRPC
+  /// initialization and shutdown.
+  class Builder {
+   public:
+    /// Global initialization and shutdown hooks.
+    static void InitRegistry();
+    static void ShutdownRegistry();
+
+    /// Sets the default URI prefix to \a default_prefix.
+    /// Calls InitRegistry() if it has not already been called.
+    static void SetDefaultPrefix(const char* default_prefix);
+
+    /// Registers a resolver factory.  The factory will be used to create a
+    /// resolver for any URI whose scheme matches that of the factory.
+    /// Calls InitRegistry() if it has not already been called.
+    static void RegisterResolverFactory(UniquePtr<ResolverFactory> factory);
+  };
+
+  /// Creates a resolver given \a target.
+  /// First tries to parse \a target as a URI. If this succeeds, tries
+  /// to locate a registered resolver factory based on the URI scheme.
+  /// If parsing fails or there is no factory for the URI's scheme,
+  /// prepends default_prefix to target and tries again.
+  /// If a resolver factory is found, uses it to instantiate a resolver and
+  /// returns it; otherwise, returns nullptr.
+  /// \a args, \a pollset_set, and \a combiner are passed to the factory's
+  /// \a CreateResolver() method.
+  /// \a args are the channel args to be included in resolver results.
+  /// \a pollset_set is used to drive I/O in the name resolution process.
+  /// \a combiner is the combiner under which all resolver calls will be run.
+  static OrphanablePtr<Resolver> CreateResolver(const char* target,
+                                                const grpc_channel_args* args,
+                                                grpc_pollset_set* pollset_set,
+                                                grpc_combiner* combiner);
+
+  /// Returns the default authority to pass from a client for \a target.
+  static UniquePtr<char> GetDefaultAuthority(const char* target);
+
+  /// Returns \a target with the default prefix prepended, if needed.
+  static UniquePtr<char> AddDefaultPrefixIfNeeded(const char* target);
+
+  /// Returns the resolver factory for \a scheme.
+  /// Caller does NOT own the return value.
+  static ResolverFactory* LookupResolverFactory(const char* scheme);
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.cc
new file mode 100644
index 0000000..9a0122e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.cc
@@ -0,0 +1,377 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
+#include "src/core/ext/filters/client_channel/server_address.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+// As per the retry design, we do not allow more than 5 retry attempts.
+#define MAX_MAX_RETRY_ATTEMPTS 5
+
+namespace grpc_core {
+namespace internal {
+
+ProcessedResolverResult::ProcessedResolverResult(
+    const grpc_channel_args& resolver_result, bool parse_retry) {
+  ProcessServiceConfig(resolver_result, parse_retry);
+  // If no LB config was found above, just find the LB policy name then.
+  if (lb_policy_name_ == nullptr) ProcessLbPolicyName(resolver_result);
+}
+
+void ProcessedResolverResult::ProcessServiceConfig(
+    const grpc_channel_args& resolver_result, bool parse_retry) {
+  const grpc_arg* channel_arg =
+      grpc_channel_args_find(&resolver_result, GRPC_ARG_SERVICE_CONFIG);
+  const char* service_config_json = grpc_channel_arg_get_string(channel_arg);
+  if (service_config_json != nullptr) {
+    service_config_json_.reset(gpr_strdup(service_config_json));
+    service_config_ = grpc_core::ServiceConfig::Create(service_config_json);
+    if (service_config_ != nullptr) {
+      if (parse_retry) {
+        channel_arg =
+            grpc_channel_args_find(&resolver_result, GRPC_ARG_SERVER_URI);
+        const char* server_uri = grpc_channel_arg_get_string(channel_arg);
+        GPR_ASSERT(server_uri != nullptr);
+        grpc_uri* uri = grpc_uri_parse(server_uri, true);
+        GPR_ASSERT(uri->path[0] != '\0');
+        server_name_ = uri->path[0] == '/' ? uri->path + 1 : uri->path;
+        service_config_->ParseGlobalParams(ParseServiceConfig, this);
+        grpc_uri_destroy(uri);
+      } else {
+        service_config_->ParseGlobalParams(ParseServiceConfig, this);
+      }
+      method_params_table_ = service_config_->CreateMethodConfigTable(
+          ClientChannelMethodParams::CreateFromJson);
+    }
+  }
+}
+
+void ProcessedResolverResult::ProcessLbPolicyName(
+    const grpc_channel_args& resolver_result) {
+  // Prefer the LB policy name found in the service config. Note that this is
+  // checking the deprecated loadBalancingPolicy field, rather than the new
+  // loadBalancingConfig field.
+  if (service_config_ != nullptr) {
+    lb_policy_name_.reset(
+        gpr_strdup(service_config_->GetLoadBalancingPolicyName()));
+    // Convert to lower-case.
+    if (lb_policy_name_ != nullptr) {
+      char* lb_policy_name = lb_policy_name_.get();
+      for (size_t i = 0; i < strlen(lb_policy_name); ++i) {
+        lb_policy_name[i] = tolower(lb_policy_name[i]);
+      }
+    }
+  }
+  // Otherwise, find the LB policy name set by the client API.
+  if (lb_policy_name_ == nullptr) {
+    const grpc_arg* channel_arg =
+        grpc_channel_args_find(&resolver_result, GRPC_ARG_LB_POLICY_NAME);
+    lb_policy_name_.reset(gpr_strdup(grpc_channel_arg_get_string(channel_arg)));
+  }
+  // Special case: If at least one balancer address is present, we use
+  // the grpclb policy, regardless of what the resolver has returned.
+  const ServerAddressList* addresses =
+      FindServerAddressListChannelArg(&resolver_result);
+  if (addresses != nullptr) {
+    bool found_balancer_address = false;
+    for (size_t i = 0; i < addresses->size(); ++i) {
+      const ServerAddress& address = (*addresses)[i];
+      if (address.IsBalancer()) {
+        found_balancer_address = true;
+        break;
+      }
+    }
+    if (found_balancer_address) {
+      if (lb_policy_name_ != nullptr &&
+          strcmp(lb_policy_name_.get(), "grpclb") != 0) {
+        gpr_log(GPR_INFO,
+                "resolver requested LB policy %s but provided at least one "
+                "balancer address -- forcing use of grpclb LB policy",
+                lb_policy_name_.get());
+      }
+      lb_policy_name_.reset(gpr_strdup("grpclb"));
+    }
+  }
+  // Use pick_first if nothing was specified and we didn't select grpclb
+  // above.
+  if (lb_policy_name_ == nullptr) {
+    lb_policy_name_.reset(gpr_strdup("pick_first"));
+  }
+}
+
+void ProcessedResolverResult::ParseServiceConfig(
+    const grpc_json* field, ProcessedResolverResult* parsing_state) {
+  parsing_state->ParseLbConfigFromServiceConfig(field);
+  if (parsing_state->server_name_ != nullptr) {
+    parsing_state->ParseRetryThrottleParamsFromServiceConfig(field);
+  }
+}
+
+void ProcessedResolverResult::ParseLbConfigFromServiceConfig(
+    const grpc_json* field) {
+  if (lb_policy_config_ != nullptr) return;  // Already found.
+  // Find the LB config global parameter.
+  if (field->key == nullptr || strcmp(field->key, "loadBalancingConfig") != 0 ||
+      field->type != GRPC_JSON_ARRAY) {
+    return;  // Not valid lb config array.
+  }
+  // Find the first LB policy that this client supports.
+  for (grpc_json* lb_config = field->child; lb_config != nullptr;
+       lb_config = lb_config->next) {
+    if (lb_config->type != GRPC_JSON_OBJECT) return;
+    // Find the policy object.
+    grpc_json* policy = nullptr;
+    for (grpc_json* field = lb_config->child; field != nullptr;
+         field = field->next) {
+      if (field->key == nullptr || strcmp(field->key, "policy") != 0 ||
+          field->type != GRPC_JSON_OBJECT) {
+        return;
+      }
+      if (policy != nullptr) return;  // Duplicate.
+      policy = field;
+    }
+    // Find the specific policy content since the policy object is of type
+    // "oneof".
+    grpc_json* policy_content = nullptr;
+    for (grpc_json* field = policy->child; field != nullptr;
+         field = field->next) {
+      if (field->key == nullptr || field->type != GRPC_JSON_OBJECT) return;
+      if (policy_content != nullptr) return;  // Violate "oneof" type.
+      policy_content = field;
+    }
+    // If we support this policy, then select it.
+    if (grpc_core::LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
+            policy_content->key)) {
+      lb_policy_name_.reset(gpr_strdup(policy_content->key));
+      lb_policy_config_ = policy_content->child;
+      return;
+    }
+  }
+}
+
+void ProcessedResolverResult::ParseRetryThrottleParamsFromServiceConfig(
+    const grpc_json* field) {
+  if (strcmp(field->key, "retryThrottling") == 0) {
+    if (retry_throttle_data_ != nullptr) return;  // Duplicate.
+    if (field->type != GRPC_JSON_OBJECT) return;
+    int max_milli_tokens = 0;
+    int milli_token_ratio = 0;
+    for (grpc_json* sub_field = field->child; sub_field != nullptr;
+         sub_field = sub_field->next) {
+      if (sub_field->key == nullptr) return;
+      if (strcmp(sub_field->key, "maxTokens") == 0) {
+        if (max_milli_tokens != 0) return;  // Duplicate.
+        if (sub_field->type != GRPC_JSON_NUMBER) return;
+        max_milli_tokens = gpr_parse_nonnegative_int(sub_field->value);
+        if (max_milli_tokens == -1) return;
+        max_milli_tokens *= 1000;
+      } else if (strcmp(sub_field->key, "tokenRatio") == 0) {
+        if (milli_token_ratio != 0) return;  // Duplicate.
+        if (sub_field->type != GRPC_JSON_NUMBER) return;
+        // We support up to 3 decimal digits.
+        size_t whole_len = strlen(sub_field->value);
+        uint32_t multiplier = 1;
+        uint32_t decimal_value = 0;
+        const char* decimal_point = strchr(sub_field->value, '.');
+        if (decimal_point != nullptr) {
+          whole_len = static_cast<size_t>(decimal_point - sub_field->value);
+          multiplier = 1000;
+          size_t decimal_len = strlen(decimal_point + 1);
+          if (decimal_len > 3) decimal_len = 3;
+          if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
+                                         &decimal_value)) {
+            return;
+          }
+          uint32_t decimal_multiplier = 1;
+          for (size_t i = 0; i < (3 - decimal_len); ++i) {
+            decimal_multiplier *= 10;
+          }
+          decimal_value *= decimal_multiplier;
+        }
+        uint32_t whole_value;
+        if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len,
+                                       &whole_value)) {
+          return;
+        }
+        milli_token_ratio =
+            static_cast<int>((whole_value * multiplier) + decimal_value);
+        if (milli_token_ratio <= 0) return;
+      }
+    }
+    retry_throttle_data_ =
+        grpc_core::internal::ServerRetryThrottleMap::GetDataForServer(
+            server_name_, max_milli_tokens, milli_token_ratio);
+  }
+}
+
+namespace {
+
+bool ParseWaitForReady(
+    grpc_json* field, ClientChannelMethodParams::WaitForReady* wait_for_ready) {
+  if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) {
+    return false;
+  }
+  *wait_for_ready = field->type == GRPC_JSON_TRUE
+                        ? ClientChannelMethodParams::WAIT_FOR_READY_TRUE
+                        : ClientChannelMethodParams::WAIT_FOR_READY_FALSE;
+  return true;
+}
+
+// Parses a JSON field of the form generated for a google.proto.Duration
+// proto message, as per:
+//   https://developers.google.com/protocol-buffers/docs/proto3#json
+bool ParseDuration(grpc_json* field, grpc_millis* duration) {
+  if (field->type != GRPC_JSON_STRING) return false;
+  size_t len = strlen(field->value);
+  if (field->value[len - 1] != 's') return false;
+  UniquePtr<char> buf(gpr_strdup(field->value));
+  *(buf.get() + len - 1) = '\0';  // Remove trailing 's'.
+  char* decimal_point = strchr(buf.get(), '.');
+  int nanos = 0;
+  if (decimal_point != nullptr) {
+    *decimal_point = '\0';
+    nanos = gpr_parse_nonnegative_int(decimal_point + 1);
+    if (nanos == -1) {
+      return false;
+    }
+    int num_digits = static_cast<int>(strlen(decimal_point + 1));
+    if (num_digits > 9) {  // We don't accept greater precision than nanos.
+      return false;
+    }
+    for (int i = 0; i < (9 - num_digits); ++i) {
+      nanos *= 10;
+    }
+  }
+  int seconds =
+      decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get());
+  if (seconds == -1) return false;
+  *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS;
+  return true;
+}
+
+UniquePtr<ClientChannelMethodParams::RetryPolicy> ParseRetryPolicy(
+    grpc_json* field) {
+  auto retry_policy = MakeUnique<ClientChannelMethodParams::RetryPolicy>();
+  if (field->type != GRPC_JSON_OBJECT) return nullptr;
+  for (grpc_json* sub_field = field->child; sub_field != nullptr;
+       sub_field = sub_field->next) {
+    if (sub_field->key == nullptr) return nullptr;
+    if (strcmp(sub_field->key, "maxAttempts") == 0) {
+      if (retry_policy->max_attempts != 0) return nullptr;  // Duplicate.
+      if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
+      retry_policy->max_attempts = gpr_parse_nonnegative_int(sub_field->value);
+      if (retry_policy->max_attempts <= 1) return nullptr;
+      if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
+        gpr_log(GPR_ERROR,
+                "service config: clamped retryPolicy.maxAttempts at %d",
+                MAX_MAX_RETRY_ATTEMPTS);
+        retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS;
+      }
+    } else if (strcmp(sub_field->key, "initialBackoff") == 0) {
+      if (retry_policy->initial_backoff > 0) return nullptr;  // Duplicate.
+      if (!ParseDuration(sub_field, &retry_policy->initial_backoff)) {
+        return nullptr;
+      }
+      if (retry_policy->initial_backoff == 0) return nullptr;
+    } else if (strcmp(sub_field->key, "maxBackoff") == 0) {
+      if (retry_policy->max_backoff > 0) return nullptr;  // Duplicate.
+      if (!ParseDuration(sub_field, &retry_policy->max_backoff)) {
+        return nullptr;
+      }
+      if (retry_policy->max_backoff == 0) return nullptr;
+    } else if (strcmp(sub_field->key, "backoffMultiplier") == 0) {
+      if (retry_policy->backoff_multiplier != 0) return nullptr;  // Duplicate.
+      if (sub_field->type != GRPC_JSON_NUMBER) return nullptr;
+      if (sscanf(sub_field->value, "%f", &retry_policy->backoff_multiplier) !=
+          1) {
+        return nullptr;
+      }
+      if (retry_policy->backoff_multiplier <= 0) return nullptr;
+    } else if (strcmp(sub_field->key, "retryableStatusCodes") == 0) {
+      if (!retry_policy->retryable_status_codes.Empty()) {
+        return nullptr;  // Duplicate.
+      }
+      if (sub_field->type != GRPC_JSON_ARRAY) return nullptr;
+      for (grpc_json* element = sub_field->child; element != nullptr;
+           element = element->next) {
+        if (element->type != GRPC_JSON_STRING) return nullptr;
+        grpc_status_code status;
+        if (!grpc_status_code_from_string(element->value, &status)) {
+          return nullptr;
+        }
+        retry_policy->retryable_status_codes.Add(status);
+      }
+      if (retry_policy->retryable_status_codes.Empty()) return nullptr;
+    }
+  }
+  // Make sure required fields are set.
+  if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 ||
+      retry_policy->max_backoff == 0 || retry_policy->backoff_multiplier == 0 ||
+      retry_policy->retryable_status_codes.Empty()) {
+    return nullptr;
+  }
+  return retry_policy;
+}
+
+}  // namespace
+
+RefCountedPtr<ClientChannelMethodParams>
+ClientChannelMethodParams::CreateFromJson(const grpc_json* json) {
+  RefCountedPtr<ClientChannelMethodParams> method_params =
+      MakeRefCounted<ClientChannelMethodParams>();
+  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
+    if (field->key == nullptr) continue;
+    if (strcmp(field->key, "waitForReady") == 0) {
+      if (method_params->wait_for_ready_ != WAIT_FOR_READY_UNSET) {
+        return nullptr;  // Duplicate.
+      }
+      if (!ParseWaitForReady(field, &method_params->wait_for_ready_)) {
+        return nullptr;
+      }
+    } else if (strcmp(field->key, "timeout") == 0) {
+      if (method_params->timeout_ > 0) return nullptr;  // Duplicate.
+      if (!ParseDuration(field, &method_params->timeout_)) return nullptr;
+    } else if (strcmp(field->key, "retryPolicy") == 0) {
+      if (method_params->retry_policy_ != nullptr) {
+        return nullptr;  // Duplicate.
+      }
+      method_params->retry_policy_ = ParseRetryPolicy(field);
+      if (method_params->retry_policy_ == nullptr) return nullptr;
+    }
+  }
+  return method_params;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.h
new file mode 100644
index 0000000..98a9d26
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.h
@@ -0,0 +1,142 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/retry_throttle.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/exec_ctx.h"  // for grpc_millis
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/slice/slice_hash_table.h"
+#include "src/core/lib/transport/service_config.h"
+
+namespace grpc_core {
+namespace internal {
+
+class ClientChannelMethodParams;
+
+// A table mapping from a method name to its method parameters.
+typedef SliceHashTable<RefCountedPtr<ClientChannelMethodParams>>
+    ClientChannelMethodParamsTable;
+
+// A container of processed fields from the resolver result. Simplifies the
+// usage of resolver result.
+class ProcessedResolverResult {
+ public:
+  // Processes the resolver result and populates the relative members
+  // for later consumption. Tries to parse retry parameters only if parse_retry
+  // is true.
+  ProcessedResolverResult(const grpc_channel_args& resolver_result,
+                          bool parse_retry);
+
+  // Getters. Any managed object's ownership is transferred.
+  UniquePtr<char> service_config_json() {
+    return std::move(service_config_json_);
+  }
+  RefCountedPtr<ServerRetryThrottleData> retry_throttle_data() {
+    return std::move(retry_throttle_data_);
+  }
+  RefCountedPtr<ClientChannelMethodParamsTable> method_params_table() {
+    return std::move(method_params_table_);
+  }
+  UniquePtr<char> lb_policy_name() { return std::move(lb_policy_name_); }
+  grpc_json* lb_policy_config() { return lb_policy_config_; }
+
+ private:
+  // Finds the service config; extracts LB config and (maybe) retry throttle
+  // params from it.
+  void ProcessServiceConfig(const grpc_channel_args& resolver_result,
+                            bool parse_retry);
+
+  // Finds the LB policy name (when no LB config was found).
+  void ProcessLbPolicyName(const grpc_channel_args& resolver_result);
+
+  // Parses the service config. Intended to be used by
+  // ServiceConfig::ParseGlobalParams.
+  static void ParseServiceConfig(const grpc_json* field,
+                                 ProcessedResolverResult* parsing_state);
+  // Parses the LB config from service config.
+  void ParseLbConfigFromServiceConfig(const grpc_json* field);
+  // Parses the retry throttle parameters from service config.
+  void ParseRetryThrottleParamsFromServiceConfig(const grpc_json* field);
+
+  // Service config.
+  UniquePtr<char> service_config_json_;
+  UniquePtr<grpc_core::ServiceConfig> service_config_;
+  // LB policy.
+  grpc_json* lb_policy_config_ = nullptr;
+  UniquePtr<char> lb_policy_name_;
+  // Retry throttle data.
+  char* server_name_ = nullptr;
+  RefCountedPtr<ServerRetryThrottleData> retry_throttle_data_;
+  // Method params table.
+  RefCountedPtr<ClientChannelMethodParamsTable> method_params_table_;
+};
+
+// The parameters of a method.
+class ClientChannelMethodParams : public RefCounted<ClientChannelMethodParams> {
+ public:
+  enum WaitForReady {
+    WAIT_FOR_READY_UNSET = 0,
+    WAIT_FOR_READY_FALSE,
+    WAIT_FOR_READY_TRUE
+  };
+
+  struct RetryPolicy {
+    int max_attempts = 0;
+    grpc_millis initial_backoff = 0;
+    grpc_millis max_backoff = 0;
+    float backoff_multiplier = 0;
+    StatusCodeSet retryable_status_codes;
+  };
+
+  /// Creates a method_parameters object from \a json.
+  /// Intended for use with ServiceConfig::CreateMethodConfigTable().
+  static RefCountedPtr<ClientChannelMethodParams> CreateFromJson(
+      const grpc_json* json);
+
+  grpc_millis timeout() const { return timeout_; }
+  WaitForReady wait_for_ready() const { return wait_for_ready_; }
+  const RetryPolicy* retry_policy() const { return retry_policy_.get(); }
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T, typename... Args>
+  friend T* grpc_core::New(Args&&... args);
+
+  // So Delete() can call our private dtor.
+  template <typename T>
+  friend void grpc_core::Delete(T*);
+
+  ClientChannelMethodParams() {}
+  virtual ~ClientChannelMethodParams() {}
+
+  grpc_millis timeout_ = 0;
+  WaitForReady wait_for_ready_ = WAIT_FOR_READY_UNSET;
+  UniquePtr<RetryPolicy> retry_policy_;
+};
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.cc b/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.cc
new file mode 100644
index 0000000..bdeb7e4
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.cc
@@ -0,0 +1,191 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/retry_throttle.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/avl/avl.h"
+
+namespace grpc_core {
+namespace internal {
+
+//
+// ServerRetryThrottleData
+//
+
+ServerRetryThrottleData::ServerRetryThrottleData(
+    intptr_t max_milli_tokens, intptr_t milli_token_ratio,
+    ServerRetryThrottleData* old_throttle_data)
+    : max_milli_tokens_(max_milli_tokens),
+      milli_token_ratio_(milli_token_ratio) {
+  intptr_t initial_milli_tokens = max_milli_tokens;
+  // If there was a pre-existing entry for this server name, initialize
+  // the token count by scaling proportionately to the old data.  This
+  // ensures that if we're already throttling retries on the old scale,
+  // we will start out doing the same thing on the new one.
+  if (old_throttle_data != nullptr) {
+    double token_fraction =
+        static_cast<intptr_t>(
+            gpr_atm_acq_load(&old_throttle_data->milli_tokens_)) /
+        static_cast<double>(old_throttle_data->max_milli_tokens_);
+    initial_milli_tokens =
+        static_cast<intptr_t>(token_fraction * max_milli_tokens);
+  }
+  gpr_atm_rel_store(&milli_tokens_, static_cast<gpr_atm>(initial_milli_tokens));
+  // If there was a pre-existing entry, mark it as stale and give it a
+  // pointer to the new entry, which is its replacement.
+  if (old_throttle_data != nullptr) {
+    Ref().release();  // Ref held by pre-existing entry.
+    gpr_atm_rel_store(&old_throttle_data->replacement_,
+                      reinterpret_cast<gpr_atm>(this));
+  }
+}
+
+ServerRetryThrottleData::~ServerRetryThrottleData() {
+  ServerRetryThrottleData* replacement =
+      reinterpret_cast<ServerRetryThrottleData*>(
+          gpr_atm_acq_load(&replacement_));
+  if (replacement != nullptr) {
+    replacement->Unref();
+  }
+}
+
+void ServerRetryThrottleData::GetReplacementThrottleDataIfNeeded(
+    ServerRetryThrottleData** throttle_data) {
+  while (true) {
+    ServerRetryThrottleData* new_throttle_data =
+        reinterpret_cast<ServerRetryThrottleData*>(
+            gpr_atm_acq_load(&(*throttle_data)->replacement_));
+    if (new_throttle_data == nullptr) return;
+    *throttle_data = new_throttle_data;
+  }
+}
+
+bool ServerRetryThrottleData::RecordFailure() {
+  // First, check if we are stale and need to be replaced.
+  ServerRetryThrottleData* throttle_data = this;
+  GetReplacementThrottleDataIfNeeded(&throttle_data);
+  // We decrement milli_tokens by 1000 (1 token) for each failure.
+  const intptr_t new_value =
+      static_cast<intptr_t>(gpr_atm_no_barrier_clamped_add(
+          &throttle_data->milli_tokens_, static_cast<gpr_atm>(-1000),
+          static_cast<gpr_atm>(0),
+          static_cast<gpr_atm>(throttle_data->max_milli_tokens_)));
+  // Retries are allowed as long as the new value is above the threshold
+  // (max_milli_tokens / 2).
+  return new_value > throttle_data->max_milli_tokens_ / 2;
+}
+
+void ServerRetryThrottleData::RecordSuccess() {
+  // First, check if we are stale and need to be replaced.
+  ServerRetryThrottleData* throttle_data = this;
+  GetReplacementThrottleDataIfNeeded(&throttle_data);
+  // We increment milli_tokens by milli_token_ratio for each success.
+  gpr_atm_no_barrier_clamped_add(
+      &throttle_data->milli_tokens_,
+      static_cast<gpr_atm>(throttle_data->milli_token_ratio_),
+      static_cast<gpr_atm>(0),
+      static_cast<gpr_atm>(throttle_data->max_milli_tokens_));
+}
+
+//
+// avl vtable for string -> server_retry_throttle_data map
+//
+
+namespace {
+
+void* copy_server_name(void* key, void* unused) {
+  return gpr_strdup(static_cast<const char*>(key));
+}
+
+long compare_server_name(void* key1, void* key2, void* unused) {
+  return strcmp(static_cast<const char*>(key1), static_cast<const char*>(key2));
+}
+
+void destroy_server_retry_throttle_data(void* value, void* unused) {
+  ServerRetryThrottleData* throttle_data =
+      static_cast<ServerRetryThrottleData*>(value);
+  throttle_data->Unref();
+}
+
+void* copy_server_retry_throttle_data(void* value, void* unused) {
+  ServerRetryThrottleData* throttle_data =
+      static_cast<ServerRetryThrottleData*>(value);
+  return throttle_data->Ref().release();
+}
+
+void destroy_server_name(void* key, void* unused) { gpr_free(key); }
+
+const grpc_avl_vtable avl_vtable = {
+    destroy_server_name, copy_server_name, compare_server_name,
+    destroy_server_retry_throttle_data, copy_server_retry_throttle_data};
+
+}  // namespace
+
+//
+// ServerRetryThrottleMap
+//
+
+static gpr_mu g_mu;
+static grpc_avl g_avl;
+
+void ServerRetryThrottleMap::Init() {
+  gpr_mu_init(&g_mu);
+  g_avl = grpc_avl_create(&avl_vtable);
+}
+
+void ServerRetryThrottleMap::Shutdown() {
+  gpr_mu_destroy(&g_mu);
+  grpc_avl_unref(g_avl, nullptr);
+}
+
+RefCountedPtr<ServerRetryThrottleData> ServerRetryThrottleMap::GetDataForServer(
+    const char* server_name, intptr_t max_milli_tokens,
+    intptr_t milli_token_ratio) {
+  RefCountedPtr<ServerRetryThrottleData> result;
+  gpr_mu_lock(&g_mu);
+  ServerRetryThrottleData* throttle_data =
+      static_cast<ServerRetryThrottleData*>(
+          grpc_avl_get(g_avl, const_cast<char*>(server_name), nullptr));
+  if (throttle_data == nullptr ||
+      throttle_data->max_milli_tokens() != max_milli_tokens ||
+      throttle_data->milli_token_ratio() != milli_token_ratio) {
+    // Entry not found, or found with old parameters.  Create a new one.
+    result = MakeRefCounted<ServerRetryThrottleData>(
+        max_milli_tokens, milli_token_ratio, throttle_data);
+    g_avl = grpc_avl_add(g_avl, gpr_strdup(server_name),
+                         result->Ref().release(), nullptr);
+  } else {
+    // Entry found.  Return a new ref to it.
+    result = throttle_data->Ref();
+  }
+  gpr_mu_unlock(&g_mu);
+  return result;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.h b/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.h
new file mode 100644
index 0000000..fddafcd
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/ref_counted.h"
+
+namespace grpc_core {
+namespace internal {
+
+/// Tracks retry throttling data for an individual server name.
+class ServerRetryThrottleData : public RefCounted<ServerRetryThrottleData> {
+ public:
+  ServerRetryThrottleData(intptr_t max_milli_tokens, intptr_t milli_token_ratio,
+                          ServerRetryThrottleData* old_throttle_data);
+
+  /// Records a failure.  Returns true if it's okay to send a retry.
+  bool RecordFailure();
+
+  /// Records a success.
+  void RecordSuccess();
+
+  intptr_t max_milli_tokens() const { return max_milli_tokens_; }
+  intptr_t milli_token_ratio() const { return milli_token_ratio_; }
+
+ private:
+  // So Delete() can call our private dtor.
+  template <typename T>
+  friend void grpc_core::Delete(T*);
+
+  ~ServerRetryThrottleData();
+
+  void GetReplacementThrottleDataIfNeeded(
+      ServerRetryThrottleData** throttle_data);
+
+  const intptr_t max_milli_tokens_;
+  const intptr_t milli_token_ratio_;
+  gpr_atm milli_tokens_;
+  // A pointer to the replacement for this ServerRetryThrottleData entry.
+  // If non-nullptr, then this entry is stale and must not be used.
+  // We hold a reference to the replacement.
+  gpr_atm replacement_ = 0;
+};
+
+/// Global map of server name to retry throttle data.
+class ServerRetryThrottleMap {
+ public:
+  /// Initializes global map of failure data for each server name.
+  static void Init();
+  /// Shuts down global map of failure data for each server name.
+  static void Shutdown();
+
+  /// Returns the failure data for \a server_name, creating a new entry if
+  /// needed.
+  static RefCountedPtr<ServerRetryThrottleData> GetDataForServer(
+      const char* server_name, intptr_t max_milli_tokens,
+      intptr_t milli_token_ratio);
+};
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/server_address.cc b/third_party/grpc/src/core/ext/filters/client_channel/server_address.cc
new file mode 100644
index 0000000..ec33cbb
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/server_address.cc
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/server_address.h"
+
+#include <string.h>
+
+namespace grpc_core {
+
+//
+// ServerAddress
+//
+
+ServerAddress::ServerAddress(const grpc_resolved_address& address,
+                             grpc_channel_args* args)
+    : address_(address), args_(args) {}
+
+ServerAddress::ServerAddress(const void* address, size_t address_len,
+                             grpc_channel_args* args)
+    : args_(args) {
+  memcpy(address_.addr, address, address_len);
+  address_.len = static_cast<socklen_t>(address_len);
+}
+
+int ServerAddress::Cmp(const ServerAddress& other) const {
+  if (address_.len > other.address_.len) return 1;
+  if (address_.len < other.address_.len) return -1;
+  int retval = memcmp(address_.addr, other.address_.addr, address_.len);
+  if (retval != 0) return retval;
+  return grpc_channel_args_compare(args_, other.args_);
+}
+
+bool ServerAddress::IsBalancer() const {
+  return grpc_channel_arg_get_bool(
+      grpc_channel_args_find(args_, GRPC_ARG_ADDRESS_IS_BALANCER), false);
+}
+
+//
+// ServerAddressList
+//
+
+namespace {
+
+void* ServerAddressListCopy(void* addresses) {
+  ServerAddressList* a = static_cast<ServerAddressList*>(addresses);
+  return New<ServerAddressList>(*a);
+}
+
+void ServerAddressListDestroy(void* addresses) {
+  ServerAddressList* a = static_cast<ServerAddressList*>(addresses);
+  Delete(a);
+}
+
+int ServerAddressListCompare(void* addresses1, void* addresses2) {
+  ServerAddressList* a1 = static_cast<ServerAddressList*>(addresses1);
+  ServerAddressList* a2 = static_cast<ServerAddressList*>(addresses2);
+  if (a1->size() > a2->size()) return 1;
+  if (a1->size() < a2->size()) return -1;
+  for (size_t i = 0; i < a1->size(); ++i) {
+    int retval = (*a1)[i].Cmp((*a2)[i]);
+    if (retval != 0) return retval;
+  }
+  return 0;
+}
+
+const grpc_arg_pointer_vtable server_addresses_arg_vtable = {
+    ServerAddressListCopy, ServerAddressListDestroy, ServerAddressListCompare};
+
+}  // namespace
+
+grpc_arg CreateServerAddressListChannelArg(const ServerAddressList* addresses) {
+  return grpc_channel_arg_pointer_create(
+      const_cast<char*>(GRPC_ARG_SERVER_ADDRESS_LIST),
+      const_cast<ServerAddressList*>(addresses), &server_addresses_arg_vtable);
+}
+
+ServerAddressList* FindServerAddressListChannelArg(
+    const grpc_channel_args* channel_args) {
+  const grpc_arg* lb_addresses_arg =
+      grpc_channel_args_find(channel_args, GRPC_ARG_SERVER_ADDRESS_LIST);
+  if (lb_addresses_arg == nullptr || lb_addresses_arg->type != GRPC_ARG_POINTER)
+    return nullptr;
+  return static_cast<ServerAddressList*>(lb_addresses_arg->value.pointer.p);
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/server_address.h b/third_party/grpc/src/core/ext/filters/client_channel/server_address.h
new file mode 100644
index 0000000..3a1bf1d
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/server_address.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SERVER_ADDRESS_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SERVER_ADDRESS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+// Channel arg key for ServerAddressList.
+#define GRPC_ARG_SERVER_ADDRESS_LIST "grpc.server_address_list"
+
+// Channel arg key for a bool indicating whether an address is a grpclb
+// load balancer (as opposed to a backend).
+#define GRPC_ARG_ADDRESS_IS_BALANCER "grpc.address_is_balancer"
+
+// Channel arg key for a string indicating an address's balancer name.
+#define GRPC_ARG_ADDRESS_BALANCER_NAME "grpc.address_balancer_name"
+
+namespace grpc_core {
+
+//
+// ServerAddress
+//
+
+// A server address is a grpc_resolved_address with an associated set of
+// channel args.  Any args present here will be merged into the channel
+// args when a subchannel is created for this address.
+class ServerAddress {
+ public:
+  // Takes ownership of args.
+  ServerAddress(const grpc_resolved_address& address, grpc_channel_args* args);
+  ServerAddress(const void* address, size_t address_len,
+                grpc_channel_args* args);
+
+  ~ServerAddress() { grpc_channel_args_destroy(args_); }
+
+  // Copyable.
+  ServerAddress(const ServerAddress& other)
+      : address_(other.address_), args_(grpc_channel_args_copy(other.args_)) {}
+  ServerAddress& operator=(const ServerAddress& other) {
+    address_ = other.address_;
+    grpc_channel_args_destroy(args_);
+    args_ = grpc_channel_args_copy(other.args_);
+    return *this;
+  }
+
+  // Movable.
+  ServerAddress(ServerAddress&& other)
+      : address_(other.address_), args_(other.args_) {
+    other.args_ = nullptr;
+  }
+  ServerAddress& operator=(ServerAddress&& other) {
+    address_ = other.address_;
+    args_ = other.args_;
+    other.args_ = nullptr;
+    return *this;
+  }
+
+  bool operator==(const ServerAddress& other) const { return Cmp(other) == 0; }
+
+  int Cmp(const ServerAddress& other) const;
+
+  const grpc_resolved_address& address() const { return address_; }
+  const grpc_channel_args* args() const { return args_; }
+
+  bool IsBalancer() const;
+
+ private:
+  grpc_resolved_address address_;
+  grpc_channel_args* args_;
+};
+
+//
+// ServerAddressList
+//
+
+typedef InlinedVector<ServerAddress, 1> ServerAddressList;
+
+// Returns a channel arg containing \a addresses.
+grpc_arg CreateServerAddressListChannelArg(const ServerAddressList* addresses);
+
+// Returns the ServerListAddress instance in channel_args or NULL.
+ServerAddressList* FindServerAddressListChannelArg(
+    const grpc_channel_args* channel_args);
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SERVER_ADDRESS_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/subchannel.cc b/third_party/grpc/src/core/ext/filters/client_channel/subchannel.cc
new file mode 100644
index 0000000..9077aa9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/subchannel.cc
@@ -0,0 +1,1156 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/subchannel.h"
+
+#include <inttypes.h>
+#include <limits.h>
+
+#include <algorithm>
+#include <cstring>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/health/health_check_client.h"
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
+#include "src/core/ext/filters/client_channel/subchannel_index.h"
+#include "src/core/lib/backoff/backoff.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/alloc.h"
+#include "src/core/lib/gprpp/debug_location.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/transport/service_config.h"
+#include "src/core/lib/transport/status_metadata.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+#define INTERNAL_REF_BITS 16
+#define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1))
+
+#define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1
+#define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6
+#define GRPC_SUBCHANNEL_RECONNECT_MIN_TIMEOUT_SECONDS 20
+#define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120
+#define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2
+
+namespace {
+struct state_watcher {
+  grpc_closure closure;
+  grpc_subchannel* subchannel;
+  grpc_connectivity_state connectivity_state;
+  grpc_connectivity_state last_connectivity_state;
+  grpc_core::OrphanablePtr<grpc_core::HealthCheckClient> health_check_client;
+  grpc_closure health_check_closure;
+  grpc_connectivity_state health_state;
+};
+}  // namespace
+
+typedef struct external_state_watcher {
+  grpc_subchannel* subchannel;
+  grpc_pollset_set* pollset_set;
+  grpc_closure* notify;
+  grpc_closure closure;
+  struct external_state_watcher* next;
+  struct external_state_watcher* prev;
+} external_state_watcher;
+
+namespace grpc_core {
+
+class ConnectedSubchannelStateWatcher;
+
+}  // namespace grpc_core
+
+struct grpc_subchannel {
+  grpc_connector* connector;
+
+  /** refcount
+      - lower INTERNAL_REF_BITS bits are for internal references:
+        these do not keep the subchannel open.
+      - upper remaining bits are for public references: these do
+        keep the subchannel open */
+  gpr_atm ref_pair;
+
+  /** non-transport related channel filters */
+  const grpc_channel_filter** filters;
+  size_t num_filters;
+  /** channel arguments */
+  grpc_channel_args* args;
+
+  grpc_subchannel_key* key;
+
+  /** set during connection */
+  grpc_connect_out_args connecting_result;
+
+  /** callback for connection finishing */
+  grpc_closure on_connected;
+
+  /** callback for our alarm */
+  grpc_closure on_alarm;
+
+  /** pollset_set tracking who's interested in a connection
+      being setup */
+  grpc_pollset_set* pollset_set;
+
+  grpc_core::UniquePtr<char> health_check_service_name;
+
+  /** mutex protecting remaining elements */
+  gpr_mu mu;
+
+  /** active connection, or null */
+  grpc_core::RefCountedPtr<grpc_core::ConnectedSubchannel> connected_subchannel;
+  grpc_core::OrphanablePtr<grpc_core::ConnectedSubchannelStateWatcher>
+      connected_subchannel_watcher;
+
+  /** have we seen a disconnection? */
+  bool disconnected;
+  /** are we connecting */
+  bool connecting;
+
+  /** connectivity state tracking */
+  grpc_connectivity_state_tracker state_tracker;
+  grpc_connectivity_state_tracker state_and_health_tracker;
+
+  external_state_watcher root_external_state_watcher;
+
+  /** backoff state */
+  grpc_core::ManualConstructor<grpc_core::BackOff> backoff;
+  grpc_millis next_attempt_deadline;
+  grpc_millis min_connect_timeout_ms;
+
+  /** do we have an active alarm? */
+  bool have_alarm;
+  /** have we started the backoff loop */
+  bool backoff_begun;
+  // reset_backoff() was called while alarm was pending
+  bool retry_immediately;
+  /** our alarm */
+  grpc_timer alarm;
+
+  grpc_core::RefCountedPtr<grpc_core::channelz::SubchannelNode>
+      channelz_subchannel;
+};
+
+struct grpc_subchannel_call {
+  grpc_subchannel_call(grpc_core::ConnectedSubchannel* connection,
+                       const grpc_core::ConnectedSubchannel::CallArgs& args)
+      : connection(connection), deadline(args.deadline) {}
+
+  grpc_core::ConnectedSubchannel* connection;
+  grpc_closure* schedule_closure_after_destroy = nullptr;
+  // state needed to support channelz interception of recv trailing metadata.
+  grpc_closure recv_trailing_metadata_ready;
+  grpc_closure* original_recv_trailing_metadata;
+  grpc_metadata_batch* recv_trailing_metadata = nullptr;
+  grpc_millis deadline;
+};
+
+static void maybe_start_connecting_locked(grpc_subchannel* c);
+
+static const char* subchannel_connectivity_state_change_string(
+    grpc_connectivity_state state) {
+  switch (state) {
+    case GRPC_CHANNEL_IDLE:
+      return "Subchannel state change to IDLE";
+    case GRPC_CHANNEL_CONNECTING:
+      return "Subchannel state change to CONNECTING";
+    case GRPC_CHANNEL_READY:
+      return "Subchannel state change to READY";
+    case GRPC_CHANNEL_TRANSIENT_FAILURE:
+      return "Subchannel state change to TRANSIENT_FAILURE";
+    case GRPC_CHANNEL_SHUTDOWN:
+      return "Subchannel state change to SHUTDOWN";
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
+
+static void set_subchannel_connectivity_state_locked(
+    grpc_subchannel* c, grpc_connectivity_state state, grpc_error* error,
+    const char* reason) {
+  if (c->channelz_subchannel != nullptr) {
+    c->channelz_subchannel->AddTraceEvent(
+        grpc_core::channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string(
+            subchannel_connectivity_state_change_string(state)));
+  }
+  grpc_connectivity_state_set(&c->state_tracker, state, error, reason);
+}
+
+namespace grpc_core {
+
+class ConnectedSubchannelStateWatcher
+    : public InternallyRefCounted<ConnectedSubchannelStateWatcher> {
+ public:
+  // Must be instantiated while holding c->mu.
+  explicit ConnectedSubchannelStateWatcher(grpc_subchannel* c)
+      : subchannel_(c) {
+    // Steal subchannel ref for connecting.
+    GRPC_SUBCHANNEL_WEAK_REF(subchannel_, "state_watcher");
+    GRPC_SUBCHANNEL_WEAK_UNREF(subchannel_, "connecting");
+    // Start watching for connectivity state changes.
+    // Callback uses initial ref to this.
+    GRPC_CLOSURE_INIT(&on_connectivity_changed_, OnConnectivityChanged, this,
+                      grpc_schedule_on_exec_ctx);
+    c->connected_subchannel->NotifyOnStateChange(c->pollset_set,
+                                                 &pending_connectivity_state_,
+                                                 &on_connectivity_changed_);
+    // Start health check if needed.
+    grpc_connectivity_state health_state = GRPC_CHANNEL_READY;
+    if (c->health_check_service_name != nullptr) {
+      health_check_client_ = grpc_core::MakeOrphanable<HealthCheckClient>(
+          c->health_check_service_name.get(), c->connected_subchannel,
+          c->pollset_set, c->channelz_subchannel);
+      GRPC_CLOSURE_INIT(&on_health_changed_, OnHealthChanged, this,
+                        grpc_schedule_on_exec_ctx);
+      Ref().release();  // Ref for health callback tracked manually.
+      health_check_client_->NotifyOnHealthChange(&health_state_,
+                                                 &on_health_changed_);
+      health_state = GRPC_CHANNEL_CONNECTING;
+    }
+    // Report initial state.
+    set_subchannel_connectivity_state_locked(
+        c, GRPC_CHANNEL_READY, GRPC_ERROR_NONE, "subchannel_connected");
+    grpc_connectivity_state_set(&c->state_and_health_tracker, health_state,
+                                GRPC_ERROR_NONE, "subchannel_connected");
+  }
+
+  ~ConnectedSubchannelStateWatcher() {
+    GRPC_SUBCHANNEL_WEAK_UNREF(subchannel_, "state_watcher");
+  }
+
+  void Orphan() override { health_check_client_.reset(); }
+
+ private:
+  static void OnConnectivityChanged(void* arg, grpc_error* error) {
+    auto* self = static_cast<ConnectedSubchannelStateWatcher*>(arg);
+    grpc_subchannel* c = self->subchannel_;
+    {
+      MutexLock lock(&c->mu);
+      switch (self->pending_connectivity_state_) {
+        case GRPC_CHANNEL_TRANSIENT_FAILURE:
+        case GRPC_CHANNEL_SHUTDOWN: {
+          if (!c->disconnected && c->connected_subchannel != nullptr) {
+            if (grpc_trace_stream_refcount.enabled()) {
+              gpr_log(GPR_INFO,
+                      "Connected subchannel %p of subchannel %p has gone into "
+                      "%s. Attempting to reconnect.",
+                      c->connected_subchannel.get(), c,
+                      grpc_connectivity_state_name(
+                          self->pending_connectivity_state_));
+            }
+            c->connected_subchannel.reset();
+            c->connected_subchannel_watcher.reset();
+            self->last_connectivity_state_ = GRPC_CHANNEL_TRANSIENT_FAILURE;
+            set_subchannel_connectivity_state_locked(
+                c, GRPC_CHANNEL_TRANSIENT_FAILURE, GRPC_ERROR_REF(error),
+                "reflect_child");
+            grpc_connectivity_state_set(&c->state_and_health_tracker,
+                                        GRPC_CHANNEL_TRANSIENT_FAILURE,
+                                        GRPC_ERROR_REF(error), "reflect_child");
+            c->backoff_begun = false;
+            c->backoff->Reset();
+            maybe_start_connecting_locked(c);
+          } else {
+            self->last_connectivity_state_ = GRPC_CHANNEL_SHUTDOWN;
+          }
+          self->health_check_client_.reset();
+          break;
+        }
+        default: {
+          // In principle, this should never happen.  We should not get
+          // a callback for READY, because that was the state we started
+          // this watch from.  And a connected subchannel should never go
+          // from READY to CONNECTING or IDLE.
+          self->last_connectivity_state_ = self->pending_connectivity_state_;
+          set_subchannel_connectivity_state_locked(
+              c, self->pending_connectivity_state_, GRPC_ERROR_REF(error),
+              "reflect_child");
+          if (self->pending_connectivity_state_ != GRPC_CHANNEL_READY) {
+            grpc_connectivity_state_set(&c->state_and_health_tracker,
+                                        self->pending_connectivity_state_,
+                                        GRPC_ERROR_REF(error), "reflect_child");
+          }
+          c->connected_subchannel->NotifyOnStateChange(
+              nullptr, &self->pending_connectivity_state_,
+              &self->on_connectivity_changed_);
+          self = nullptr;  // So we don't unref below.
+        }
+      }
+    }
+    // Don't unref until we've released the lock, because this might
+    // cause the subchannel (which contains the lock) to be destroyed.
+    if (self != nullptr) self->Unref();
+  }
+
+  static void OnHealthChanged(void* arg, grpc_error* error) {
+    auto* self = static_cast<ConnectedSubchannelStateWatcher*>(arg);
+    if (self->health_state_ == GRPC_CHANNEL_SHUTDOWN) {
+      self->Unref();
+      return;
+    }
+    grpc_subchannel* c = self->subchannel_;
+    MutexLock lock(&c->mu);
+    if (self->last_connectivity_state_ == GRPC_CHANNEL_READY) {
+      grpc_connectivity_state_set(&c->state_and_health_tracker,
+                                  self->health_state_, GRPC_ERROR_REF(error),
+                                  "health_changed");
+    }
+    self->health_check_client_->NotifyOnHealthChange(&self->health_state_,
+                                                     &self->on_health_changed_);
+  }
+
+  grpc_subchannel* subchannel_;
+  grpc_closure on_connectivity_changed_;
+  grpc_connectivity_state pending_connectivity_state_ = GRPC_CHANNEL_READY;
+  grpc_connectivity_state last_connectivity_state_ = GRPC_CHANNEL_READY;
+  grpc_core::OrphanablePtr<grpc_core::HealthCheckClient> health_check_client_;
+  grpc_closure on_health_changed_;
+  grpc_connectivity_state health_state_ = GRPC_CHANNEL_CONNECTING;
+};
+
+}  // namespace grpc_core
+
+#define SUBCHANNEL_CALL_TO_CALL_STACK(call)                          \
+  (grpc_call_stack*)((char*)(call) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \
+                                         sizeof(grpc_subchannel_call)))
+#define CALLSTACK_TO_SUBCHANNEL_CALL(callstack)           \
+  (grpc_subchannel_call*)(((char*)(call_stack)) -         \
+                          GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \
+                              sizeof(grpc_subchannel_call)))
+
+static void on_subchannel_connected(void* subchannel, grpc_error* error);
+
+#ifndef NDEBUG
+#define REF_REASON reason
+#define REF_MUTATE_EXTRA_ARGS \
+  GRPC_SUBCHANNEL_REF_EXTRA_ARGS, const char* purpose
+#define REF_MUTATE_PURPOSE(x) , file, line, reason, x
+#else
+#define REF_REASON ""
+#define REF_MUTATE_EXTRA_ARGS
+#define REF_MUTATE_PURPOSE(x)
+#endif
+
+/*
+ * connection implementation
+ */
+
+static void connection_destroy(void* arg, grpc_error* error) {
+  grpc_channel_stack* stk = static_cast<grpc_channel_stack*>(arg);
+  grpc_channel_stack_destroy(stk);
+  gpr_free(stk);
+}
+
+/*
+ * grpc_subchannel implementation
+ */
+
+static void subchannel_destroy(void* arg, grpc_error* error) {
+  grpc_subchannel* c = static_cast<grpc_subchannel*>(arg);
+  if (c->channelz_subchannel != nullptr) {
+    c->channelz_subchannel->AddTraceEvent(
+        grpc_core::channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string("Subchannel destroyed"));
+    c->channelz_subchannel->MarkSubchannelDestroyed();
+    c->channelz_subchannel.reset();
+  }
+  gpr_free((void*)c->filters);
+  c->health_check_service_name.reset();
+  grpc_channel_args_destroy(c->args);
+  grpc_connectivity_state_destroy(&c->state_tracker);
+  grpc_connectivity_state_destroy(&c->state_and_health_tracker);
+  grpc_connector_unref(c->connector);
+  grpc_pollset_set_destroy(c->pollset_set);
+  grpc_subchannel_key_destroy(c->key);
+  gpr_mu_destroy(&c->mu);
+  gpr_free(c);
+}
+
+static gpr_atm ref_mutate(grpc_subchannel* c, gpr_atm delta,
+                          int barrier REF_MUTATE_EXTRA_ARGS) {
+  gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta)
+                            : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta);
+#ifndef NDEBUG
+  if (grpc_trace_stream_refcount.enabled()) {
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "SUBCHANNEL: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c,
+            purpose, old_val, old_val + delta, reason);
+  }
+#endif
+  return old_val;
+}
+
+grpc_subchannel* grpc_subchannel_ref(
+    grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
+  gpr_atm old_refs;
+  old_refs = ref_mutate(c, (1 << INTERNAL_REF_BITS),
+                        0 REF_MUTATE_PURPOSE("STRONG_REF"));
+  GPR_ASSERT((old_refs & STRONG_REF_MASK) != 0);
+  return c;
+}
+
+grpc_subchannel* grpc_subchannel_weak_ref(
+    grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
+  gpr_atm old_refs;
+  old_refs = ref_mutate(c, 1, 0 REF_MUTATE_PURPOSE("WEAK_REF"));
+  GPR_ASSERT(old_refs != 0);
+  return c;
+}
+
+grpc_subchannel* grpc_subchannel_ref_from_weak_ref(
+    grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
+  if (!c) return nullptr;
+  for (;;) {
+    gpr_atm old_refs = gpr_atm_acq_load(&c->ref_pair);
+    if (old_refs >= (1 << INTERNAL_REF_BITS)) {
+      gpr_atm new_refs = old_refs + (1 << INTERNAL_REF_BITS);
+      if (gpr_atm_rel_cas(&c->ref_pair, old_refs, new_refs)) {
+        return c;
+      }
+    } else {
+      return nullptr;
+    }
+  }
+}
+
+static void disconnect(grpc_subchannel* c) {
+  grpc_subchannel_index_unregister(c->key, c);
+  gpr_mu_lock(&c->mu);
+  GPR_ASSERT(!c->disconnected);
+  c->disconnected = true;
+  grpc_connector_shutdown(c->connector, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                            "Subchannel disconnected"));
+  c->connected_subchannel.reset();
+  c->connected_subchannel_watcher.reset();
+  gpr_mu_unlock(&c->mu);
+}
+
+void grpc_subchannel_unref(grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
+  gpr_atm old_refs;
+  // add a weak ref and subtract a strong ref (atomically)
+  old_refs = ref_mutate(
+      c, static_cast<gpr_atm>(1) - static_cast<gpr_atm>(1 << INTERNAL_REF_BITS),
+      1 REF_MUTATE_PURPOSE("STRONG_UNREF"));
+  if ((old_refs & STRONG_REF_MASK) == (1 << INTERNAL_REF_BITS)) {
+    disconnect(c);
+  }
+  GRPC_SUBCHANNEL_WEAK_UNREF(c, "strong-unref");
+}
+
+void grpc_subchannel_weak_unref(
+    grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
+  gpr_atm old_refs;
+  old_refs = ref_mutate(c, -static_cast<gpr_atm>(1),
+                        1 REF_MUTATE_PURPOSE("WEAK_UNREF"));
+  if (old_refs == 1) {
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_CREATE(subchannel_destroy, c, grpc_schedule_on_exec_ctx),
+        GRPC_ERROR_NONE);
+  }
+}
+
+static void parse_args_for_backoff_values(
+    const grpc_channel_args* args, grpc_core::BackOff::Options* backoff_options,
+    grpc_millis* min_connect_timeout_ms) {
+  grpc_millis initial_backoff_ms =
+      GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000;
+  *min_connect_timeout_ms =
+      GRPC_SUBCHANNEL_RECONNECT_MIN_TIMEOUT_SECONDS * 1000;
+  grpc_millis max_backoff_ms =
+      GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000;
+  bool fixed_reconnect_backoff = false;
+  if (args != nullptr) {
+    for (size_t i = 0; i < args->num_args; i++) {
+      if (0 == strcmp(args->args[i].key,
+                      "grpc.testing.fixed_reconnect_backoff_ms")) {
+        fixed_reconnect_backoff = true;
+        initial_backoff_ms = *min_connect_timeout_ms = max_backoff_ms =
+            grpc_channel_arg_get_integer(
+                &args->args[i],
+                {static_cast<int>(initial_backoff_ms), 100, INT_MAX});
+      } else if (0 ==
+                 strcmp(args->args[i].key, GRPC_ARG_MIN_RECONNECT_BACKOFF_MS)) {
+        fixed_reconnect_backoff = false;
+        *min_connect_timeout_ms = grpc_channel_arg_get_integer(
+            &args->args[i],
+            {static_cast<int>(*min_connect_timeout_ms), 100, INT_MAX});
+      } else if (0 ==
+                 strcmp(args->args[i].key, GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) {
+        fixed_reconnect_backoff = false;
+        max_backoff_ms = grpc_channel_arg_get_integer(
+            &args->args[i], {static_cast<int>(max_backoff_ms), 100, INT_MAX});
+      } else if (0 == strcmp(args->args[i].key,
+                             GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS)) {
+        fixed_reconnect_backoff = false;
+        initial_backoff_ms = grpc_channel_arg_get_integer(
+            &args->args[i],
+            {static_cast<int>(initial_backoff_ms), 100, INT_MAX});
+      }
+    }
+  }
+  backoff_options->set_initial_backoff(initial_backoff_ms)
+      .set_multiplier(fixed_reconnect_backoff
+                          ? 1.0
+                          : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER)
+      .set_jitter(fixed_reconnect_backoff ? 0.0
+                                          : GRPC_SUBCHANNEL_RECONNECT_JITTER)
+      .set_max_backoff(max_backoff_ms);
+}
+
+namespace grpc_core {
+namespace {
+
+struct HealthCheckParams {
+  UniquePtr<char> service_name;
+
+  static void Parse(const grpc_json* field, HealthCheckParams* params) {
+    if (strcmp(field->key, "healthCheckConfig") == 0) {
+      if (field->type != GRPC_JSON_OBJECT) return;
+      for (grpc_json* sub_field = field->child; sub_field != nullptr;
+           sub_field = sub_field->next) {
+        if (sub_field->key == nullptr) return;
+        if (strcmp(sub_field->key, "serviceName") == 0) {
+          if (params->service_name != nullptr) return;  // Duplicate.
+          if (sub_field->type != GRPC_JSON_STRING) return;
+          params->service_name.reset(gpr_strdup(sub_field->value));
+        }
+      }
+    }
+  }
+};
+
+}  // namespace
+}  // namespace grpc_core
+
+grpc_subchannel* grpc_subchannel_create(grpc_connector* connector,
+                                        const grpc_subchannel_args* args) {
+  grpc_subchannel_key* key = grpc_subchannel_key_create(args);
+  grpc_subchannel* c = grpc_subchannel_index_find(key);
+  if (c) {
+    grpc_subchannel_key_destroy(key);
+    return c;
+  }
+
+  GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED();
+  c = static_cast<grpc_subchannel*>(gpr_zalloc(sizeof(*c)));
+  c->key = key;
+  gpr_atm_no_barrier_store(&c->ref_pair, 1 << INTERNAL_REF_BITS);
+  c->connector = connector;
+  grpc_connector_ref(c->connector);
+  c->num_filters = args->filter_count;
+  if (c->num_filters > 0) {
+    c->filters = static_cast<const grpc_channel_filter**>(
+        gpr_malloc(sizeof(grpc_channel_filter*) * c->num_filters));
+    memcpy((void*)c->filters, args->filters,
+           sizeof(grpc_channel_filter*) * c->num_filters);
+  } else {
+    c->filters = nullptr;
+  }
+  c->pollset_set = grpc_pollset_set_create();
+  grpc_resolved_address* addr =
+      static_cast<grpc_resolved_address*>(gpr_malloc(sizeof(*addr)));
+  grpc_get_subchannel_address_arg(args->args, addr);
+  grpc_resolved_address* new_address = nullptr;
+  grpc_channel_args* new_args = nullptr;
+  if (grpc_proxy_mappers_map_address(addr, args->args, &new_address,
+                                     &new_args)) {
+    GPR_ASSERT(new_address != nullptr);
+    gpr_free(addr);
+    addr = new_address;
+  }
+  static const char* keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS};
+  grpc_arg new_arg = grpc_create_subchannel_address_arg(addr);
+  gpr_free(addr);
+  c->args = grpc_channel_args_copy_and_add_and_remove(
+      new_args != nullptr ? new_args : args->args, keys_to_remove,
+      GPR_ARRAY_SIZE(keys_to_remove), &new_arg, 1);
+  gpr_free(new_arg.value.string);
+  if (new_args != nullptr) grpc_channel_args_destroy(new_args);
+  c->root_external_state_watcher.next = c->root_external_state_watcher.prev =
+      &c->root_external_state_watcher;
+  GRPC_CLOSURE_INIT(&c->on_connected, on_subchannel_connected, c,
+                    grpc_schedule_on_exec_ctx);
+  grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE,
+                               "subchannel");
+  grpc_connectivity_state_init(&c->state_and_health_tracker, GRPC_CHANNEL_IDLE,
+                               "subchannel");
+  grpc_core::BackOff::Options backoff_options;
+  parse_args_for_backoff_values(args->args, &backoff_options,
+                                &c->min_connect_timeout_ms);
+  c->backoff.Init(backoff_options);
+  gpr_mu_init(&c->mu);
+
+  // Check whether we should enable health checking.
+  const char* service_config_json = grpc_channel_arg_get_string(
+      grpc_channel_args_find(c->args, GRPC_ARG_SERVICE_CONFIG));
+  if (service_config_json != nullptr) {
+    grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config =
+        grpc_core::ServiceConfig::Create(service_config_json);
+    if (service_config != nullptr) {
+      grpc_core::HealthCheckParams params;
+      service_config->ParseGlobalParams(grpc_core::HealthCheckParams::Parse,
+                                        &params);
+      c->health_check_service_name = std::move(params.service_name);
+    }
+  }
+
+  const grpc_arg* arg =
+      grpc_channel_args_find(c->args, GRPC_ARG_ENABLE_CHANNELZ);
+  bool channelz_enabled =
+      grpc_channel_arg_get_bool(arg, GRPC_ENABLE_CHANNELZ_DEFAULT);
+  arg = grpc_channel_args_find(
+      c->args, GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE);
+  const grpc_integer_options options = {
+      GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX};
+  size_t channel_tracer_max_memory =
+      (size_t)grpc_channel_arg_get_integer(arg, options);
+  if (channelz_enabled) {
+    c->channelz_subchannel =
+        grpc_core::MakeRefCounted<grpc_core::channelz::SubchannelNode>(
+            c, channel_tracer_max_memory);
+    c->channelz_subchannel->AddTraceEvent(
+        grpc_core::channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string("Subchannel created"));
+  }
+
+  return grpc_subchannel_index_register(key, c);
+}
+
+grpc_core::channelz::SubchannelNode* grpc_subchannel_get_channelz_node(
+    grpc_subchannel* subchannel) {
+  return subchannel->channelz_subchannel.get();
+}
+
+intptr_t grpc_subchannel_get_child_socket_uuid(grpc_subchannel* subchannel) {
+  if (subchannel->connected_subchannel != nullptr) {
+    return subchannel->connected_subchannel->socket_uuid();
+  } else {
+    return 0;
+  }
+}
+
+static void continue_connect_locked(grpc_subchannel* c) {
+  grpc_connect_in_args args;
+  args.interested_parties = c->pollset_set;
+  const grpc_millis min_deadline =
+      c->min_connect_timeout_ms + grpc_core::ExecCtx::Get()->Now();
+  c->next_attempt_deadline = c->backoff->NextAttemptTime();
+  args.deadline = std::max(c->next_attempt_deadline, min_deadline);
+  args.channel_args = c->args;
+  set_subchannel_connectivity_state_locked(c, GRPC_CHANNEL_CONNECTING,
+                                           GRPC_ERROR_NONE, "connecting");
+  grpc_connectivity_state_set(&c->state_and_health_tracker,
+                              GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE,
+                              "connecting");
+  grpc_connector_connect(c->connector, &args, &c->connecting_result,
+                         &c->on_connected);
+}
+
+grpc_connectivity_state grpc_subchannel_check_connectivity(
+    grpc_subchannel* c, grpc_error** error, bool inhibit_health_checks) {
+  gpr_mu_lock(&c->mu);
+  grpc_connectivity_state_tracker* tracker =
+      inhibit_health_checks ? &c->state_tracker : &c->state_and_health_tracker;
+  grpc_connectivity_state state = grpc_connectivity_state_get(tracker, error);
+  gpr_mu_unlock(&c->mu);
+  return state;
+}
+
+static void on_external_state_watcher_done(void* arg, grpc_error* error) {
+  external_state_watcher* w = static_cast<external_state_watcher*>(arg);
+  grpc_closure* follow_up = w->notify;
+  if (w->pollset_set != nullptr) {
+    grpc_pollset_set_del_pollset_set(w->subchannel->pollset_set,
+                                     w->pollset_set);
+  }
+  gpr_mu_lock(&w->subchannel->mu);
+  w->next->prev = w->prev;
+  w->prev->next = w->next;
+  gpr_mu_unlock(&w->subchannel->mu);
+  GRPC_SUBCHANNEL_WEAK_UNREF(w->subchannel, "external_state_watcher");
+  gpr_free(w);
+  GRPC_CLOSURE_SCHED(follow_up, GRPC_ERROR_REF(error));
+}
+
+static void on_alarm(void* arg, grpc_error* error) {
+  grpc_subchannel* c = static_cast<grpc_subchannel*>(arg);
+  gpr_mu_lock(&c->mu);
+  c->have_alarm = false;
+  if (c->disconnected) {
+    error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Disconnected",
+                                                             &error, 1);
+  } else if (c->retry_immediately) {
+    c->retry_immediately = false;
+    error = GRPC_ERROR_NONE;
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  if (error == GRPC_ERROR_NONE) {
+    gpr_log(GPR_INFO, "Failed to connect to channel, retrying");
+    continue_connect_locked(c);
+    gpr_mu_unlock(&c->mu);
+  } else {
+    gpr_mu_unlock(&c->mu);
+    GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting");
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+static void maybe_start_connecting_locked(grpc_subchannel* c) {
+  if (c->disconnected) {
+    /* Don't try to connect if we're already disconnected */
+    return;
+  }
+  if (c->connecting) {
+    /* Already connecting: don't restart */
+    return;
+  }
+  if (c->connected_subchannel != nullptr) {
+    /* Already connected: don't restart */
+    return;
+  }
+  if (!grpc_connectivity_state_has_watchers(&c->state_tracker) &&
+      !grpc_connectivity_state_has_watchers(&c->state_and_health_tracker)) {
+    /* Nobody is interested in connecting: so don't just yet */
+    return;
+  }
+  c->connecting = true;
+  GRPC_SUBCHANNEL_WEAK_REF(c, "connecting");
+  if (!c->backoff_begun) {
+    c->backoff_begun = true;
+    continue_connect_locked(c);
+  } else {
+    GPR_ASSERT(!c->have_alarm);
+    c->have_alarm = true;
+    const grpc_millis time_til_next =
+        c->next_attempt_deadline - grpc_core::ExecCtx::Get()->Now();
+    if (time_til_next <= 0) {
+      gpr_log(GPR_INFO, "Subchannel %p: Retry immediately", c);
+    } else {
+      gpr_log(GPR_INFO, "Subchannel %p: Retry in %" PRId64 " milliseconds", c,
+              time_til_next);
+    }
+    GRPC_CLOSURE_INIT(&c->on_alarm, on_alarm, c, grpc_schedule_on_exec_ctx);
+    grpc_timer_init(&c->alarm, c->next_attempt_deadline, &c->on_alarm);
+  }
+}
+
+void grpc_subchannel_notify_on_state_change(
+    grpc_subchannel* c, grpc_pollset_set* interested_parties,
+    grpc_connectivity_state* state, grpc_closure* notify,
+    bool inhibit_health_checks) {
+  grpc_connectivity_state_tracker* tracker =
+      inhibit_health_checks ? &c->state_tracker : &c->state_and_health_tracker;
+  external_state_watcher* w;
+  if (state == nullptr) {
+    gpr_mu_lock(&c->mu);
+    for (w = c->root_external_state_watcher.next;
+         w != &c->root_external_state_watcher; w = w->next) {
+      if (w->notify == notify) {
+        grpc_connectivity_state_notify_on_state_change(tracker, nullptr,
+                                                       &w->closure);
+      }
+    }
+    gpr_mu_unlock(&c->mu);
+  } else {
+    w = static_cast<external_state_watcher*>(gpr_malloc(sizeof(*w)));
+    w->subchannel = c;
+    w->pollset_set = interested_parties;
+    w->notify = notify;
+    GRPC_CLOSURE_INIT(&w->closure, on_external_state_watcher_done, w,
+                      grpc_schedule_on_exec_ctx);
+    if (interested_parties != nullptr) {
+      grpc_pollset_set_add_pollset_set(c->pollset_set, interested_parties);
+    }
+    GRPC_SUBCHANNEL_WEAK_REF(c, "external_state_watcher");
+    gpr_mu_lock(&c->mu);
+    w->next = &c->root_external_state_watcher;
+    w->prev = w->next->prev;
+    w->next->prev = w->prev->next = w;
+    grpc_connectivity_state_notify_on_state_change(tracker, state, &w->closure);
+    maybe_start_connecting_locked(c);
+    gpr_mu_unlock(&c->mu);
+  }
+}
+
+static bool publish_transport_locked(grpc_subchannel* c) {
+  /* construct channel stack */
+  grpc_channel_stack_builder* builder = grpc_channel_stack_builder_create();
+  grpc_channel_stack_builder_set_channel_arguments(
+      builder, c->connecting_result.channel_args);
+  grpc_channel_stack_builder_set_transport(builder,
+                                           c->connecting_result.transport);
+
+  if (!grpc_channel_init_create_stack(builder, GRPC_CLIENT_SUBCHANNEL)) {
+    grpc_channel_stack_builder_destroy(builder);
+    return false;
+  }
+  grpc_channel_stack* stk;
+  grpc_error* error = grpc_channel_stack_builder_finish(
+      builder, 0, 1, connection_destroy, nullptr,
+      reinterpret_cast<void**>(&stk));
+  if (error != GRPC_ERROR_NONE) {
+    grpc_transport_destroy(c->connecting_result.transport);
+    gpr_log(GPR_ERROR, "error initializing subchannel stack: %s",
+            grpc_error_string(error));
+    GRPC_ERROR_UNREF(error);
+    return false;
+  }
+  intptr_t socket_uuid = c->connecting_result.socket_uuid;
+  memset(&c->connecting_result, 0, sizeof(c->connecting_result));
+
+  if (c->disconnected) {
+    grpc_channel_stack_destroy(stk);
+    gpr_free(stk);
+    return false;
+  }
+
+  /* publish */
+  c->connected_subchannel.reset(grpc_core::New<grpc_core::ConnectedSubchannel>(
+      stk, c->args, c->channelz_subchannel, socket_uuid));
+  gpr_log(GPR_INFO, "New connected subchannel at %p for subchannel %p",
+          c->connected_subchannel.get(), c);
+
+  // Instantiate state watcher.  Will clean itself up.
+  c->connected_subchannel_watcher =
+      grpc_core::MakeOrphanable<grpc_core::ConnectedSubchannelStateWatcher>(c);
+
+  return true;
+}
+
+static void on_subchannel_connected(void* arg, grpc_error* error) {
+  grpc_subchannel* c = static_cast<grpc_subchannel*>(arg);
+  grpc_channel_args* delete_channel_args = c->connecting_result.channel_args;
+
+  GRPC_SUBCHANNEL_WEAK_REF(c, "on_subchannel_connected");
+  gpr_mu_lock(&c->mu);
+  c->connecting = false;
+  if (c->connecting_result.transport != nullptr &&
+      publish_transport_locked(c)) {
+    /* do nothing, transport was published */
+  } else if (c->disconnected) {
+    GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting");
+  } else {
+    set_subchannel_connectivity_state_locked(
+        c, GRPC_CHANNEL_TRANSIENT_FAILURE,
+        grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                               "Connect Failed", &error, 1),
+                           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE),
+        "connect_failed");
+    grpc_connectivity_state_set(
+        &c->state_and_health_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+        grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                               "Connect Failed", &error, 1),
+                           GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE),
+        "connect_failed");
+
+    const char* errmsg = grpc_error_string(error);
+    gpr_log(GPR_INFO, "Connect failed: %s", errmsg);
+
+    maybe_start_connecting_locked(c);
+    GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting");
+  }
+  gpr_mu_unlock(&c->mu);
+  GRPC_SUBCHANNEL_WEAK_UNREF(c, "connected");
+  grpc_channel_args_destroy(delete_channel_args);
+}
+
+void grpc_subchannel_reset_backoff(grpc_subchannel* subchannel) {
+  gpr_mu_lock(&subchannel->mu);
+  subchannel->backoff->Reset();
+  if (subchannel->have_alarm) {
+    subchannel->retry_immediately = true;
+    grpc_timer_cancel(&subchannel->alarm);
+  } else {
+    subchannel->backoff_begun = false;
+    maybe_start_connecting_locked(subchannel);
+  }
+  gpr_mu_unlock(&subchannel->mu);
+}
+
+/*
+ * grpc_subchannel_call implementation
+ */
+
+static void subchannel_call_destroy(void* call, grpc_error* error) {
+  GPR_TIMER_SCOPE("grpc_subchannel_call_unref.destroy", 0);
+  grpc_subchannel_call* c = static_cast<grpc_subchannel_call*>(call);
+  grpc_core::ConnectedSubchannel* connection = c->connection;
+  grpc_call_stack_destroy(SUBCHANNEL_CALL_TO_CALL_STACK(c), nullptr,
+                          c->schedule_closure_after_destroy);
+  connection->Unref(DEBUG_LOCATION, "subchannel_call");
+  c->~grpc_subchannel_call();
+}
+
+void grpc_subchannel_call_set_cleanup_closure(grpc_subchannel_call* call,
+                                              grpc_closure* closure) {
+  GPR_ASSERT(call->schedule_closure_after_destroy == nullptr);
+  GPR_ASSERT(closure != nullptr);
+  call->schedule_closure_after_destroy = closure;
+}
+
+grpc_subchannel_call* grpc_subchannel_call_ref(
+    grpc_subchannel_call* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
+  GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON);
+  return c;
+}
+
+void grpc_subchannel_call_unref(
+    grpc_subchannel_call* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) {
+  GRPC_CALL_STACK_UNREF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON);
+}
+
+// Sets *status based on md_batch and error.
+static void get_call_status(grpc_subchannel_call* call,
+                            grpc_metadata_batch* md_batch, grpc_error* error,
+                            grpc_status_code* status) {
+  if (error != GRPC_ERROR_NONE) {
+    grpc_error_get_status(error, call->deadline, status, nullptr, nullptr,
+                          nullptr);
+  } else {
+    if (md_batch->idx.named.grpc_status != nullptr) {
+      *status = grpc_get_status_code_from_metadata(
+          md_batch->idx.named.grpc_status->md);
+    } else {
+      *status = GRPC_STATUS_UNKNOWN;
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+static void recv_trailing_metadata_ready(void* arg, grpc_error* error) {
+  grpc_subchannel_call* call = static_cast<grpc_subchannel_call*>(arg);
+  GPR_ASSERT(call->recv_trailing_metadata != nullptr);
+  grpc_status_code status = GRPC_STATUS_OK;
+  grpc_metadata_batch* md_batch = call->recv_trailing_metadata;
+  get_call_status(call, md_batch, GRPC_ERROR_REF(error), &status);
+  grpc_core::channelz::SubchannelNode* channelz_subchannel =
+      call->connection->channelz_subchannel();
+  GPR_ASSERT(channelz_subchannel != nullptr);
+  if (status == GRPC_STATUS_OK) {
+    channelz_subchannel->RecordCallSucceeded();
+  } else {
+    channelz_subchannel->RecordCallFailed();
+  }
+  GRPC_CLOSURE_RUN(call->original_recv_trailing_metadata,
+                   GRPC_ERROR_REF(error));
+}
+
+// If channelz is enabled, intercept recv_trailing so that we may check the
+// status and associate it to a subchannel.
+static void maybe_intercept_recv_trailing_metadata(
+    grpc_subchannel_call* call, grpc_transport_stream_op_batch* batch) {
+  // only intercept payloads with recv trailing.
+  if (!batch->recv_trailing_metadata) {
+    return;
+  }
+  // only add interceptor is channelz is enabled.
+  if (call->connection->channelz_subchannel() == nullptr) {
+    return;
+  }
+  GRPC_CLOSURE_INIT(&call->recv_trailing_metadata_ready,
+                    recv_trailing_metadata_ready, call,
+                    grpc_schedule_on_exec_ctx);
+  // save some state needed for the interception callback.
+  GPR_ASSERT(call->recv_trailing_metadata == nullptr);
+  call->recv_trailing_metadata =
+      batch->payload->recv_trailing_metadata.recv_trailing_metadata;
+  call->original_recv_trailing_metadata =
+      batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+  batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+      &call->recv_trailing_metadata_ready;
+}
+
+void grpc_subchannel_call_process_op(grpc_subchannel_call* call,
+                                     grpc_transport_stream_op_batch* batch) {
+  GPR_TIMER_SCOPE("grpc_subchannel_call_process_op", 0);
+  maybe_intercept_recv_trailing_metadata(call, batch);
+  grpc_call_stack* call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call);
+  grpc_call_element* top_elem = grpc_call_stack_element(call_stack, 0);
+  GRPC_CALL_LOG_OP(GPR_INFO, top_elem, batch);
+  top_elem->filter->start_transport_stream_op_batch(top_elem, batch);
+}
+
+grpc_core::RefCountedPtr<grpc_core::ConnectedSubchannel>
+grpc_subchannel_get_connected_subchannel(grpc_subchannel* c) {
+  gpr_mu_lock(&c->mu);
+  auto copy = c->connected_subchannel;
+  gpr_mu_unlock(&c->mu);
+  return copy;
+}
+
+const grpc_subchannel_key* grpc_subchannel_get_key(
+    const grpc_subchannel* subchannel) {
+  return subchannel->key;
+}
+
+void* grpc_connected_subchannel_call_get_parent_data(
+    grpc_subchannel_call* subchannel_call) {
+  grpc_channel_stack* chanstk = subchannel_call->connection->channel_stack();
+  return (char*)subchannel_call + sizeof(grpc_subchannel_call) +
+         chanstk->call_stack_size;
+}
+
+grpc_call_stack* grpc_subchannel_call_get_call_stack(
+    grpc_subchannel_call* subchannel_call) {
+  return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call);
+}
+
+static void grpc_uri_to_sockaddr(const char* uri_str,
+                                 grpc_resolved_address* addr) {
+  grpc_uri* uri = grpc_uri_parse(uri_str, 0 /* suppress_errors */);
+  GPR_ASSERT(uri != nullptr);
+  if (!grpc_parse_uri(uri, addr)) memset(addr, 0, sizeof(*addr));
+  grpc_uri_destroy(uri);
+}
+
+void grpc_get_subchannel_address_arg(const grpc_channel_args* args,
+                                     grpc_resolved_address* addr) {
+  const char* addr_uri_str = grpc_get_subchannel_address_uri_arg(args);
+  memset(addr, 0, sizeof(*addr));
+  if (*addr_uri_str != '\0') {
+    grpc_uri_to_sockaddr(addr_uri_str, addr);
+  }
+}
+
+const char* grpc_subchannel_get_target(grpc_subchannel* subchannel) {
+  const grpc_arg* addr_arg =
+      grpc_channel_args_find(subchannel->args, GRPC_ARG_SUBCHANNEL_ADDRESS);
+  const char* addr_str = grpc_channel_arg_get_string(addr_arg);
+  GPR_ASSERT(addr_str != nullptr);  // Should have been set by LB policy.
+  return addr_str;
+}
+
+const char* grpc_get_subchannel_address_uri_arg(const grpc_channel_args* args) {
+  const grpc_arg* addr_arg =
+      grpc_channel_args_find(args, GRPC_ARG_SUBCHANNEL_ADDRESS);
+  const char* addr_str = grpc_channel_arg_get_string(addr_arg);
+  GPR_ASSERT(addr_str != nullptr);  // Should have been set by LB policy.
+  return addr_str;
+}
+
+grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address* addr) {
+  return grpc_channel_arg_string_create(
+      (char*)GRPC_ARG_SUBCHANNEL_ADDRESS,
+      addr->len > 0 ? grpc_sockaddr_to_uri(addr) : gpr_strdup(""));
+}
+
+namespace grpc_core {
+
+ConnectedSubchannel::ConnectedSubchannel(
+    grpc_channel_stack* channel_stack, const grpc_channel_args* args,
+    grpc_core::RefCountedPtr<grpc_core::channelz::SubchannelNode>
+        channelz_subchannel,
+    intptr_t socket_uuid)
+    : RefCounted<ConnectedSubchannel>(&grpc_trace_stream_refcount),
+      channel_stack_(channel_stack),
+      args_(grpc_channel_args_copy(args)),
+      channelz_subchannel_(std::move(channelz_subchannel)),
+      socket_uuid_(socket_uuid) {}
+
+ConnectedSubchannel::~ConnectedSubchannel() {
+  grpc_channel_args_destroy(args_);
+  GRPC_CHANNEL_STACK_UNREF(channel_stack_, "connected_subchannel_dtor");
+}
+
+void ConnectedSubchannel::NotifyOnStateChange(
+    grpc_pollset_set* interested_parties, grpc_connectivity_state* state,
+    grpc_closure* closure) {
+  grpc_transport_op* op = grpc_make_transport_op(nullptr);
+  grpc_channel_element* elem;
+  op->connectivity_state = state;
+  op->on_connectivity_state_change = closure;
+  op->bind_pollset_set = interested_parties;
+  elem = grpc_channel_stack_element(channel_stack_, 0);
+  elem->filter->start_transport_op(elem, op);
+}
+
+void ConnectedSubchannel::Ping(grpc_closure* on_initiate,
+                               grpc_closure* on_ack) {
+  grpc_transport_op* op = grpc_make_transport_op(nullptr);
+  grpc_channel_element* elem;
+  op->send_ping.on_initiate = on_initiate;
+  op->send_ping.on_ack = on_ack;
+  elem = grpc_channel_stack_element(channel_stack_, 0);
+  elem->filter->start_transport_op(elem, op);
+}
+
+grpc_error* ConnectedSubchannel::CreateCall(const CallArgs& args,
+                                            grpc_subchannel_call** call) {
+  const size_t allocation_size =
+      GetInitialCallSizeEstimate(args.parent_data_size);
+  *call = new (gpr_arena_alloc(args.arena, allocation_size))
+      grpc_subchannel_call(this, args);
+  grpc_call_stack* callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call);
+  RefCountedPtr<ConnectedSubchannel> connection =
+      Ref(DEBUG_LOCATION, "subchannel_call");
+  connection.release();  // Ref is passed to the grpc_subchannel_call object.
+  const grpc_call_element_args call_args = {
+      callstk,           /* call_stack */
+      nullptr,           /* server_transport_data */
+      args.context,      /* context */
+      args.path,         /* path */
+      args.start_time,   /* start_time */
+      args.deadline,     /* deadline */
+      args.arena,        /* arena */
+      args.call_combiner /* call_combiner */
+  };
+  grpc_error* error = grpc_call_stack_init(
+      channel_stack_, 1, subchannel_call_destroy, *call, &call_args);
+  if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) {
+    const char* error_string = grpc_error_string(error);
+    gpr_log(GPR_ERROR, "error: %s", error_string);
+    return error;
+  }
+  grpc_call_stack_set_pollset_or_pollset_set(callstk, args.pollent);
+  if (channelz_subchannel_ != nullptr) {
+    channelz_subchannel_->RecordCallStarted();
+  }
+  return GRPC_ERROR_NONE;
+}
+
+size_t ConnectedSubchannel::GetInitialCallSizeEstimate(
+    size_t parent_data_size) const {
+  size_t allocation_size =
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_subchannel_call));
+  if (parent_data_size > 0) {
+    allocation_size +=
+        GPR_ROUND_UP_TO_ALIGNMENT_SIZE(channel_stack_->call_stack_size) +
+        parent_data_size;
+  } else {
+    allocation_size += channel_stack_->call_stack_size;
+  }
+  return allocation_size;
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/subchannel.h b/third_party/grpc/src/core/ext/filters/client_channel/subchannel.h
new file mode 100644
index 0000000..14f87f2
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/subchannel.h
@@ -0,0 +1,218 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
+#include "src/core/ext/filters/client_channel/connector.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/metadata.h"
+
+// Channel arg containing a grpc_resolved_address to connect to.
+#define GRPC_ARG_SUBCHANNEL_ADDRESS "grpc.subchannel_address"
+
+/** A (sub-)channel that knows how to connect to exactly one target
+    address. Provides a target for load balancing. */
+typedef struct grpc_subchannel grpc_subchannel;
+typedef struct grpc_subchannel_call grpc_subchannel_call;
+typedef struct grpc_subchannel_args grpc_subchannel_args;
+typedef struct grpc_subchannel_key grpc_subchannel_key;
+
+#ifndef NDEBUG
+#define GRPC_SUBCHANNEL_REF(p, r) \
+  grpc_subchannel_ref((p), __FILE__, __LINE__, (r))
+#define GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(p, r) \
+  grpc_subchannel_ref_from_weak_ref((p), __FILE__, __LINE__, (r))
+#define GRPC_SUBCHANNEL_UNREF(p, r) \
+  grpc_subchannel_unref((p), __FILE__, __LINE__, (r))
+#define GRPC_SUBCHANNEL_WEAK_REF(p, r) \
+  grpc_subchannel_weak_ref((p), __FILE__, __LINE__, (r))
+#define GRPC_SUBCHANNEL_WEAK_UNREF(p, r) \
+  grpc_subchannel_weak_unref((p), __FILE__, __LINE__, (r))
+#define GRPC_SUBCHANNEL_CALL_REF(p, r) \
+  grpc_subchannel_call_ref((p), __FILE__, __LINE__, (r))
+#define GRPC_SUBCHANNEL_CALL_UNREF(p, r) \
+  grpc_subchannel_call_unref((p), __FILE__, __LINE__, (r))
+#define GRPC_SUBCHANNEL_REF_EXTRA_ARGS \
+  , const char *file, int line, const char *reason
+#else
+#define GRPC_SUBCHANNEL_REF(p, r) grpc_subchannel_ref((p))
+#define GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(p, r) \
+  grpc_subchannel_ref_from_weak_ref((p))
+#define GRPC_SUBCHANNEL_UNREF(p, r) grpc_subchannel_unref((p))
+#define GRPC_SUBCHANNEL_WEAK_REF(p, r) grpc_subchannel_weak_ref((p))
+#define GRPC_SUBCHANNEL_WEAK_UNREF(p, r) grpc_subchannel_weak_unref((p))
+#define GRPC_SUBCHANNEL_CALL_REF(p, r) grpc_subchannel_call_ref((p))
+#define GRPC_SUBCHANNEL_CALL_UNREF(p, r) grpc_subchannel_call_unref((p))
+#define GRPC_SUBCHANNEL_REF_EXTRA_ARGS
+#endif
+
+namespace grpc_core {
+
+class ConnectedSubchannel : public RefCounted<ConnectedSubchannel> {
+ public:
+  struct CallArgs {
+    grpc_polling_entity* pollent;
+    grpc_slice path;
+    gpr_timespec start_time;
+    grpc_millis deadline;
+    gpr_arena* arena;
+    grpc_call_context_element* context;
+    grpc_call_combiner* call_combiner;
+    size_t parent_data_size;
+  };
+
+  ConnectedSubchannel(
+      grpc_channel_stack* channel_stack, const grpc_channel_args* args,
+      grpc_core::RefCountedPtr<grpc_core::channelz::SubchannelNode>
+          channelz_subchannel,
+      intptr_t socket_uuid);
+  ~ConnectedSubchannel();
+
+  void NotifyOnStateChange(grpc_pollset_set* interested_parties,
+                           grpc_connectivity_state* state,
+                           grpc_closure* closure);
+  void Ping(grpc_closure* on_initiate, grpc_closure* on_ack);
+  grpc_error* CreateCall(const CallArgs& args, grpc_subchannel_call** call);
+
+  grpc_channel_stack* channel_stack() const { return channel_stack_; }
+  const grpc_channel_args* args() const { return args_; }
+  channelz::SubchannelNode* channelz_subchannel() const {
+    return channelz_subchannel_.get();
+  }
+  intptr_t socket_uuid() const { return socket_uuid_; }
+
+  size_t GetInitialCallSizeEstimate(size_t parent_data_size) const;
+
+ private:
+  grpc_channel_stack* channel_stack_;
+  grpc_channel_args* args_;
+  // ref counted pointer to the channelz node in this connected subchannel's
+  // owning subchannel.
+  grpc_core::RefCountedPtr<grpc_core::channelz::SubchannelNode>
+      channelz_subchannel_;
+  // uuid of this subchannel's socket. 0 if this subchannel is not connected.
+  const intptr_t socket_uuid_;
+};
+
+}  // namespace grpc_core
+
+grpc_subchannel* grpc_subchannel_ref(
+    grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
+grpc_subchannel* grpc_subchannel_ref_from_weak_ref(
+    grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
+void grpc_subchannel_unref(
+    grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
+grpc_subchannel* grpc_subchannel_weak_ref(
+    grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
+void grpc_subchannel_weak_unref(
+    grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
+grpc_subchannel_call* grpc_subchannel_call_ref(
+    grpc_subchannel_call* call GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
+void grpc_subchannel_call_unref(
+    grpc_subchannel_call* call GRPC_SUBCHANNEL_REF_EXTRA_ARGS);
+
+grpc_core::channelz::SubchannelNode* grpc_subchannel_get_channelz_node(
+    grpc_subchannel* subchannel);
+
+intptr_t grpc_subchannel_get_child_socket_uuid(grpc_subchannel* subchannel);
+
+/** Returns a pointer to the parent data associated with \a subchannel_call.
+    The data will be of the size specified in \a parent_data_size
+    field of the args passed to \a grpc_connected_subchannel_create_call(). */
+void* grpc_connected_subchannel_call_get_parent_data(
+    grpc_subchannel_call* subchannel_call);
+
+/** poll the current connectivity state of a channel */
+grpc_connectivity_state grpc_subchannel_check_connectivity(
+    grpc_subchannel* channel, grpc_error** error, bool inhibit_health_checking);
+
+/** Calls notify when the connectivity state of a channel becomes different
+    from *state.  Updates *state with the new state of the channel. */
+void grpc_subchannel_notify_on_state_change(
+    grpc_subchannel* channel, grpc_pollset_set* interested_parties,
+    grpc_connectivity_state* state, grpc_closure* notify,
+    bool inhibit_health_checks);
+
+/** retrieve the grpc_core::ConnectedSubchannel - or nullptr if not connected
+ * (which may happen before it initially connects or during transient failures)
+ * */
+grpc_core::RefCountedPtr<grpc_core::ConnectedSubchannel>
+grpc_subchannel_get_connected_subchannel(grpc_subchannel* c);
+
+/** return the subchannel index key for \a subchannel */
+const grpc_subchannel_key* grpc_subchannel_get_key(
+    const grpc_subchannel* subchannel);
+
+// Resets the connection backoff of the subchannel.
+// TODO(roth): Move connection backoff out of subchannels and up into LB
+// policy code (probably by adding a SubchannelGroup between
+// SubchannelList and SubchannelData), at which point this method can
+// go away.
+void grpc_subchannel_reset_backoff(grpc_subchannel* subchannel);
+
+/** continue processing a transport op */
+void grpc_subchannel_call_process_op(grpc_subchannel_call* subchannel_call,
+                                     grpc_transport_stream_op_batch* op);
+
+/** Must be called once per call. Sets the 'then_schedule_closure' argument for
+    call stack destruction. */
+void grpc_subchannel_call_set_cleanup_closure(
+    grpc_subchannel_call* subchannel_call, grpc_closure* closure);
+
+grpc_call_stack* grpc_subchannel_call_get_call_stack(
+    grpc_subchannel_call* subchannel_call);
+
+struct grpc_subchannel_args {
+  /* When updating this struct, also update subchannel_index.c */
+
+  /** Channel filters for this channel - wrapped factories will likely
+      want to mutate this */
+  const grpc_channel_filter** filters;
+  /** The number of filters in the above array */
+  size_t filter_count;
+  /** Channel arguments to be supplied to the newly created channel */
+  const grpc_channel_args* args;
+};
+
+/** create a subchannel given a connector */
+grpc_subchannel* grpc_subchannel_create(grpc_connector* connector,
+                                        const grpc_subchannel_args* args);
+
+/// Sets \a addr from \a args.
+void grpc_get_subchannel_address_arg(const grpc_channel_args* args,
+                                     grpc_resolved_address* addr);
+
+const char* grpc_subchannel_get_target(grpc_subchannel* subchannel);
+
+/// Returns the URI string for the address to connect to.
+const char* grpc_get_subchannel_address_uri_arg(const grpc_channel_args* args);
+
+/// Returns a new channel arg encoding the subchannel address as a string.
+/// Caller is responsible for freeing the string.
+grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address* addr);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H */
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.cc b/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.cc
new file mode 100644
index 0000000..aa8441f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.cc
@@ -0,0 +1,248 @@
+//
+//
+// Copyright 2016 gRPC authors.
+//
+// Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/subchannel_index.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/avl/avl.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/tls.h"
+
+// a map of subchannel_key --> subchannel, used for detecting connections
+// to the same destination in order to share them
+static grpc_avl g_subchannel_index;
+
+static gpr_mu g_mu;
+
+static gpr_refcount g_refcount;
+
+struct grpc_subchannel_key {
+  grpc_subchannel_args args;
+};
+
+static bool g_force_creation = false;
+
+static grpc_subchannel_key* create_key(
+    const grpc_subchannel_args* args,
+    grpc_channel_args* (*copy_channel_args)(const grpc_channel_args* args)) {
+  grpc_subchannel_key* k =
+      static_cast<grpc_subchannel_key*>(gpr_malloc(sizeof(*k)));
+  k->args.filter_count = args->filter_count;
+  if (k->args.filter_count > 0) {
+    k->args.filters = static_cast<const grpc_channel_filter**>(
+        gpr_malloc(sizeof(*k->args.filters) * k->args.filter_count));
+    memcpy(reinterpret_cast<grpc_channel_filter*>(k->args.filters),
+           args->filters, sizeof(*k->args.filters) * k->args.filter_count);
+  } else {
+    k->args.filters = nullptr;
+  }
+  k->args.args = copy_channel_args(args->args);
+  return k;
+}
+
+grpc_subchannel_key* grpc_subchannel_key_create(
+    const grpc_subchannel_args* args) {
+  return create_key(args, grpc_channel_args_normalize);
+}
+
+static grpc_subchannel_key* subchannel_key_copy(grpc_subchannel_key* k) {
+  return create_key(&k->args, grpc_channel_args_copy);
+}
+
+int grpc_subchannel_key_compare(const grpc_subchannel_key* a,
+                                const grpc_subchannel_key* b) {
+  // To pretend the keys are different, return a non-zero value.
+  if (GPR_UNLIKELY(g_force_creation)) return 1;
+  int c = GPR_ICMP(a->args.filter_count, b->args.filter_count);
+  if (c != 0) return c;
+  if (a->args.filter_count > 0) {
+    c = memcmp(a->args.filters, b->args.filters,
+               a->args.filter_count * sizeof(*a->args.filters));
+    if (c != 0) return c;
+  }
+  return grpc_channel_args_compare(a->args.args, b->args.args);
+}
+
+void grpc_subchannel_key_destroy(grpc_subchannel_key* k) {
+  gpr_free(reinterpret_cast<grpc_channel_args*>(k->args.filters));
+  grpc_channel_args_destroy(const_cast<grpc_channel_args*>(k->args.args));
+  gpr_free(k);
+}
+
+static void sck_avl_destroy(void* p, void* unused) {
+  grpc_subchannel_key_destroy(static_cast<grpc_subchannel_key*>(p));
+}
+
+static void* sck_avl_copy(void* p, void* unused) {
+  return subchannel_key_copy(static_cast<grpc_subchannel_key*>(p));
+}
+
+static long sck_avl_compare(void* a, void* b, void* unused) {
+  return grpc_subchannel_key_compare(static_cast<grpc_subchannel_key*>(a),
+                                     static_cast<grpc_subchannel_key*>(b));
+}
+
+static void scv_avl_destroy(void* p, void* unused) {
+  GRPC_SUBCHANNEL_WEAK_UNREF((grpc_subchannel*)p, "subchannel_index");
+}
+
+static void* scv_avl_copy(void* p, void* unused) {
+  GRPC_SUBCHANNEL_WEAK_REF((grpc_subchannel*)p, "subchannel_index");
+  return p;
+}
+
+static const grpc_avl_vtable subchannel_avl_vtable = {
+    sck_avl_destroy,  // destroy_key
+    sck_avl_copy,     // copy_key
+    sck_avl_compare,  // compare_keys
+    scv_avl_destroy,  // destroy_value
+    scv_avl_copy      // copy_value
+};
+
+void grpc_subchannel_index_init(void) {
+  g_subchannel_index = grpc_avl_create(&subchannel_avl_vtable);
+  gpr_mu_init(&g_mu);
+  gpr_ref_init(&g_refcount, 1);
+}
+
+void grpc_subchannel_index_shutdown(void) {
+  // TODO(juanlishen): This refcounting mechanism may lead to memory leackage.
+  // To solve that, we should force polling to flush any pending callbacks, then
+  // shutdown safely.
+  grpc_subchannel_index_unref();
+}
+
+void grpc_subchannel_index_unref(void) {
+  if (gpr_unref(&g_refcount)) {
+    gpr_mu_destroy(&g_mu);
+    grpc_avl_unref(g_subchannel_index, nullptr);
+  }
+}
+
+void grpc_subchannel_index_ref(void) { gpr_ref_non_zero(&g_refcount); }
+
+grpc_subchannel* grpc_subchannel_index_find(grpc_subchannel_key* key) {
+  // Lock, and take a reference to the subchannel index.
+  // We don't need to do the search under a lock as avl's are immutable.
+  gpr_mu_lock(&g_mu);
+  grpc_avl index = grpc_avl_ref(g_subchannel_index, nullptr);
+  gpr_mu_unlock(&g_mu);
+
+  grpc_subchannel* c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(
+      (grpc_subchannel*)grpc_avl_get(index, key, nullptr), "index_find");
+  grpc_avl_unref(index, nullptr);
+
+  return c;
+}
+
+grpc_subchannel* grpc_subchannel_index_register(grpc_subchannel_key* key,
+                                                grpc_subchannel* constructed) {
+  grpc_subchannel* c = nullptr;
+  bool need_to_unref_constructed = false;
+
+  while (c == nullptr) {
+    need_to_unref_constructed = false;
+
+    // Compare and swap loop:
+    // - take a reference to the current index
+    gpr_mu_lock(&g_mu);
+    grpc_avl index = grpc_avl_ref(g_subchannel_index, nullptr);
+    gpr_mu_unlock(&g_mu);
+
+    // - Check to see if a subchannel already exists
+    c = static_cast<grpc_subchannel*>(grpc_avl_get(index, key, nullptr));
+    if (c != nullptr) {
+      c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(c, "index_register");
+    }
+    if (c != nullptr) {
+      // yes -> we're done
+      need_to_unref_constructed = true;
+    } else {
+      // no -> update the avl and compare/swap
+      grpc_avl updated = grpc_avl_add(
+          grpc_avl_ref(index, nullptr), subchannel_key_copy(key),
+          GRPC_SUBCHANNEL_WEAK_REF(constructed, "index_register"), nullptr);
+
+      // it may happen (but it's expected to be unlikely)
+      // that some other thread has changed the index:
+      // compare/swap here to check that, and retry as necessary
+      gpr_mu_lock(&g_mu);
+      if (index.root == g_subchannel_index.root) {
+        GPR_SWAP(grpc_avl, updated, g_subchannel_index);
+        c = constructed;
+      }
+      gpr_mu_unlock(&g_mu);
+
+      grpc_avl_unref(updated, nullptr);
+    }
+    grpc_avl_unref(index, nullptr);
+  }
+
+  if (need_to_unref_constructed) {
+    GRPC_SUBCHANNEL_UNREF(constructed, "index_register");
+  }
+
+  return c;
+}
+
+void grpc_subchannel_index_unregister(grpc_subchannel_key* key,
+                                      grpc_subchannel* constructed) {
+  bool done = false;
+  while (!done) {
+    // Compare and swap loop:
+    // - take a reference to the current index
+    gpr_mu_lock(&g_mu);
+    grpc_avl index = grpc_avl_ref(g_subchannel_index, nullptr);
+    gpr_mu_unlock(&g_mu);
+
+    // Check to see if this key still refers to the previously
+    // registered subchannel
+    grpc_subchannel* c =
+        static_cast<grpc_subchannel*>(grpc_avl_get(index, key, nullptr));
+    if (c != constructed) {
+      grpc_avl_unref(index, nullptr);
+      break;
+    }
+
+    // compare and swap the update (some other thread may have
+    // mutated the index behind us)
+    grpc_avl updated =
+        grpc_avl_remove(grpc_avl_ref(index, nullptr), key, nullptr);
+
+    gpr_mu_lock(&g_mu);
+    if (index.root == g_subchannel_index.root) {
+      GPR_SWAP(grpc_avl, updated, g_subchannel_index);
+      done = true;
+    }
+    gpr_mu_unlock(&g_mu);
+
+    grpc_avl_unref(updated, nullptr);
+    grpc_avl_unref(index, nullptr);
+  }
+}
+
+void grpc_subchannel_index_test_only_set_force_creation(bool force_creation) {
+  g_force_creation = force_creation;
+}
diff --git a/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.h b/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.h
new file mode 100644
index 0000000..c135613
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H
+#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/subchannel.h"
+
+/** \file Provides an index of active subchannels so that they can be
+    shared amongst channels */
+
+/** Create a key that can be used to uniquely identify a subchannel */
+grpc_subchannel_key* grpc_subchannel_key_create(
+    const grpc_subchannel_args* args);
+
+/** Destroy a subchannel key */
+void grpc_subchannel_key_destroy(grpc_subchannel_key* key);
+
+/** Given a subchannel key, find the subchannel registered for it.
+    Returns NULL if no such channel exists.
+    Thread-safe. */
+grpc_subchannel* grpc_subchannel_index_find(grpc_subchannel_key* key);
+
+/** Register a subchannel against a key.
+    Takes ownership of \a constructed.
+    Returns the registered subchannel. This may be different from
+    \a constructed in the case of a registration race. */
+grpc_subchannel* grpc_subchannel_index_register(grpc_subchannel_key* key,
+                                                grpc_subchannel* constructed);
+
+/** Remove \a constructed as the registered subchannel for \a key. */
+void grpc_subchannel_index_unregister(grpc_subchannel_key* key,
+                                      grpc_subchannel* constructed);
+
+int grpc_subchannel_key_compare(const grpc_subchannel_key* a,
+                                const grpc_subchannel_key* b);
+
+/** Initialize the subchannel index (global) */
+void grpc_subchannel_index_init(void);
+/** Shutdown the subchannel index (global) */
+void grpc_subchannel_index_shutdown(void);
+
+/** Increment the refcount (non-zero) of subchannel index (global). */
+void grpc_subchannel_index_ref(void);
+
+/** Decrement the refcount of subchannel index (global). If the refcount drops
+    to zero, unref the subchannel index and destroy its mutex. */
+void grpc_subchannel_index_unref(void);
+
+/** \em TEST ONLY.
+ * If \a force_creation is true, all keys are regarded different, resulting in
+ * new subchannels always being created. Otherwise, the keys will be compared as
+ * usual.
+ *
+ * Tests using this function \em MUST run tests with and without \a
+ * force_creation set. */
+void grpc_subchannel_index_test_only_set_force_creation(bool force_creation);
+
+#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H */
diff --git a/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.cc b/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.cc
new file mode 100644
index 0000000..b4cb07f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.cc
@@ -0,0 +1,385 @@
+//
+// Copyright 2016 gRPC authors.
+//
+// Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/deadline/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/channel/channel_stack_builder.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/channel_init.h"
+
+//
+// grpc_deadline_state
+//
+
+// The on_complete callback used when sending a cancel_error batch down the
+// filter stack.  Yields the call combiner when the batch returns.
+static void yield_call_combiner(void* arg, grpc_error* ignored) {
+  grpc_deadline_state* deadline_state = static_cast<grpc_deadline_state*>(arg);
+  GRPC_CALL_COMBINER_STOP(deadline_state->call_combiner,
+                          "got on_complete from cancel_stream batch");
+  GRPC_CALL_STACK_UNREF(deadline_state->call_stack, "deadline_timer");
+}
+
+// This is called via the call combiner, so access to deadline_state is
+// synchronized.
+static void send_cancel_op_in_call_combiner(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  grpc_deadline_state* deadline_state =
+      static_cast<grpc_deadline_state*>(elem->call_data);
+  grpc_transport_stream_op_batch* batch = grpc_make_transport_stream_op(
+      GRPC_CLOSURE_INIT(&deadline_state->timer_callback, yield_call_combiner,
+                        deadline_state, grpc_schedule_on_exec_ctx));
+  batch->cancel_stream = true;
+  batch->payload->cancel_stream.cancel_error = GRPC_ERROR_REF(error);
+  elem->filter->start_transport_stream_op_batch(elem, batch);
+}
+
+// Timer callback.
+static void timer_callback(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  grpc_deadline_state* deadline_state =
+      static_cast<grpc_deadline_state*>(elem->call_data);
+  if (error != GRPC_ERROR_CANCELLED) {
+    error = grpc_error_set_int(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Deadline Exceeded"),
+        GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_DEADLINE_EXCEEDED);
+    grpc_call_combiner_cancel(deadline_state->call_combiner,
+                              GRPC_ERROR_REF(error));
+    GRPC_CLOSURE_INIT(&deadline_state->timer_callback,
+                      send_cancel_op_in_call_combiner, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CALL_COMBINER_START(deadline_state->call_combiner,
+                             &deadline_state->timer_callback, error,
+                             "deadline exceeded -- sending cancel_stream op");
+  } else {
+    GRPC_CALL_STACK_UNREF(deadline_state->call_stack, "deadline_timer");
+  }
+}
+
+// Starts the deadline timer.
+// This is called via the call combiner, so access to deadline_state is
+// synchronized.
+static void start_timer_if_needed(grpc_call_element* elem,
+                                  grpc_millis deadline) {
+  if (deadline == GRPC_MILLIS_INF_FUTURE) {
+    return;
+  }
+  grpc_deadline_state* deadline_state =
+      static_cast<grpc_deadline_state*>(elem->call_data);
+  grpc_closure* closure = nullptr;
+  switch (deadline_state->timer_state) {
+    case GRPC_DEADLINE_STATE_PENDING:
+      // Note: We do not start the timer if there is already a timer
+      return;
+    case GRPC_DEADLINE_STATE_FINISHED:
+      deadline_state->timer_state = GRPC_DEADLINE_STATE_PENDING;
+      // If we've already created and destroyed a timer, we always create a
+      // new closure: we have no other guarantee that the inlined closure is
+      // not in use (it may hold a pending call to timer_callback)
+      closure =
+          GRPC_CLOSURE_CREATE(timer_callback, elem, grpc_schedule_on_exec_ctx);
+      break;
+    case GRPC_DEADLINE_STATE_INITIAL:
+      deadline_state->timer_state = GRPC_DEADLINE_STATE_PENDING;
+      closure =
+          GRPC_CLOSURE_INIT(&deadline_state->timer_callback, timer_callback,
+                            elem, grpc_schedule_on_exec_ctx);
+      break;
+  }
+  GPR_ASSERT(closure != nullptr);
+  GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer");
+  grpc_timer_init(&deadline_state->timer, deadline, closure);
+}
+
+// Cancels the deadline timer.
+// This is called via the call combiner, so access to deadline_state is
+// synchronized.
+static void cancel_timer_if_needed(grpc_deadline_state* deadline_state) {
+  if (deadline_state->timer_state == GRPC_DEADLINE_STATE_PENDING) {
+    deadline_state->timer_state = GRPC_DEADLINE_STATE_FINISHED;
+    grpc_timer_cancel(&deadline_state->timer);
+  } else {
+    // timer was either in STATE_INITAL (nothing to cancel)
+    // OR in STATE_FINISHED (again nothing to cancel)
+  }
+}
+
+// Callback run when we receive trailing metadata.
+static void recv_trailing_metadata_ready(void* arg, grpc_error* error) {
+  grpc_deadline_state* deadline_state = static_cast<grpc_deadline_state*>(arg);
+  cancel_timer_if_needed(deadline_state);
+  // Invoke the original callback.
+  GRPC_CLOSURE_RUN(deadline_state->original_recv_trailing_metadata_ready,
+                   GRPC_ERROR_REF(error));
+}
+
+// Inject our own recv_trailing_metadata_ready callback into op.
+static void inject_recv_trailing_metadata_ready(
+    grpc_deadline_state* deadline_state, grpc_transport_stream_op_batch* op) {
+  deadline_state->original_recv_trailing_metadata_ready =
+      op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+  GRPC_CLOSURE_INIT(&deadline_state->recv_trailing_metadata_ready,
+                    recv_trailing_metadata_ready, deadline_state,
+                    grpc_schedule_on_exec_ctx);
+  op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+      &deadline_state->recv_trailing_metadata_ready;
+}
+
+// Callback and associated state for starting the timer after call stack
+// initialization has been completed.
+struct start_timer_after_init_state {
+  start_timer_after_init_state(grpc_call_element* elem, grpc_millis deadline)
+      : elem(elem), deadline(deadline) {}
+  ~start_timer_after_init_state() { start_timer_if_needed(elem, deadline); }
+
+  bool in_call_combiner = false;
+  grpc_call_element* elem;
+  grpc_millis deadline;
+  grpc_closure closure;
+};
+static void start_timer_after_init(void* arg, grpc_error* error) {
+  struct start_timer_after_init_state* state =
+      static_cast<struct start_timer_after_init_state*>(arg);
+  grpc_deadline_state* deadline_state =
+      static_cast<grpc_deadline_state*>(state->elem->call_data);
+  if (!state->in_call_combiner) {
+    // We are initially called without holding the call combiner, so we
+    // need to bounce ourselves into it.
+    state->in_call_combiner = true;
+    GRPC_CALL_COMBINER_START(deadline_state->call_combiner, &state->closure,
+                             GRPC_ERROR_REF(error),
+                             "scheduling deadline timer");
+    return;
+  }
+  grpc_core::Delete(state);
+  GRPC_CALL_COMBINER_STOP(deadline_state->call_combiner,
+                          "done scheduling deadline timer");
+}
+
+grpc_deadline_state::grpc_deadline_state(grpc_call_element* elem,
+                                         grpc_call_stack* call_stack,
+                                         grpc_call_combiner* call_combiner,
+                                         grpc_millis deadline)
+    : call_stack(call_stack), call_combiner(call_combiner) {
+  // Deadline will always be infinite on servers, so the timer will only be
+  // set on clients with a finite deadline.
+  if (deadline != GRPC_MILLIS_INF_FUTURE) {
+    // 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 =
+        grpc_core::New<start_timer_after_init_state>(elem, deadline);
+    GRPC_CLOSURE_INIT(&state->closure, start_timer_after_init, state,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_SCHED(&state->closure, GRPC_ERROR_NONE);
+  }
+}
+
+grpc_deadline_state::~grpc_deadline_state() { cancel_timer_if_needed(this); }
+
+void grpc_deadline_state_reset(grpc_call_element* elem,
+                               grpc_millis new_deadline) {
+  grpc_deadline_state* deadline_state =
+      static_cast<grpc_deadline_state*>(elem->call_data);
+  cancel_timer_if_needed(deadline_state);
+  start_timer_if_needed(elem, new_deadline);
+}
+
+void grpc_deadline_state_client_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
+  grpc_deadline_state* deadline_state =
+      static_cast<grpc_deadline_state*>(elem->call_data);
+  if (op->cancel_stream) {
+    cancel_timer_if_needed(deadline_state);
+  } else {
+    // Make sure we know when the call is complete, so that we can cancel
+    // the timer.
+    if (op->recv_trailing_metadata) {
+      inject_recv_trailing_metadata_ready(deadline_state, op);
+    }
+  }
+}
+
+//
+// filter code
+//
+
+// Constructor for channel_data.  Used for both client and server filters.
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  GPR_ASSERT(!args->is_last);
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for channel_data.  Used for both client and server filters.
+static void destroy_channel_elem(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_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  new (elem->call_data) grpc_deadline_state(
+      elem, args->call_stack, args->call_combiner, args->deadline);
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for call_data.  Used for both client and server filters.
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  grpc_deadline_state* deadline_state =
+      static_cast<grpc_deadline_state*>(elem->call_data);
+  deadline_state->~grpc_deadline_state();
+}
+
+// Method for starting a call op for client filter.
+static void client_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
+  grpc_deadline_state_client_start_transport_stream_op_batch(elem, op);
+  // Chain to next filter.
+  grpc_call_next_op(elem, op);
+}
+
+// Callback for receiving initial metadata on the server.
+static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  server_call_data* calld = static_cast<server_call_data*>(elem->call_data);
+  start_timer_if_needed(elem, calld->recv_initial_metadata->deadline);
+  // Invoke the next callback.
+  GRPC_CLOSURE_RUN(calld->next_recv_initial_metadata_ready,
+                   GRPC_ERROR_REF(error));
+}
+
+// Method for starting a call op for server filter.
+static void server_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
+  server_call_data* calld = static_cast<server_call_data*>(elem->call_data);
+  if (op->cancel_stream) {
+    cancel_timer_if_needed(&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) {
+      calld->next_recv_initial_metadata_ready =
+          op->payload->recv_initial_metadata.recv_initial_metadata_ready;
+      calld->recv_initial_metadata =
+          op->payload->recv_initial_metadata.recv_initial_metadata;
+      GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready,
+                        recv_initial_metadata_ready, elem,
+                        grpc_schedule_on_exec_ctx);
+      op->payload->recv_initial_metadata.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) {
+      inject_recv_trailing_metadata_ready(&calld->base.deadline_state, op);
+    }
+  }
+  // Chain to next filter.
+  grpc_call_next_op(elem, op);
+}
+
+const grpc_channel_filter grpc_client_deadline_filter = {
+    client_start_transport_stream_op_batch,
+    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_channel_next_get_info,
+    "deadline",
+};
+
+const grpc_channel_filter grpc_server_deadline_filter = {
+    server_start_transport_stream_op_batch,
+    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_channel_next_get_info,
+    "deadline",
+};
+
+bool grpc_deadline_checking_enabled(const grpc_channel_args* channel_args) {
+  return grpc_channel_arg_get_bool(
+      grpc_channel_args_find(channel_args, GRPC_ARG_ENABLE_DEADLINE_CHECKS),
+      !grpc_channel_args_want_minimal_stack(channel_args));
+}
+
+static bool maybe_add_deadline_filter(grpc_channel_stack_builder* builder,
+                                      void* arg) {
+  return grpc_deadline_checking_enabled(
+             grpc_channel_stack_builder_get_channel_arguments(builder))
+             ? grpc_channel_stack_builder_prepend_filter(
+                   builder, static_cast<const grpc_channel_filter*>(arg),
+                   nullptr, nullptr)
+             : true;
+}
+
+void grpc_deadline_filter_init(void) {
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      maybe_add_deadline_filter, (void*)&grpc_client_deadline_filter);
+  grpc_channel_init_register_stage(
+      GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      maybe_add_deadline_filter, (void*)&grpc_server_deadline_filter);
+}
+
+void grpc_deadline_filter_shutdown(void) {}
diff --git a/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.h b/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.h
new file mode 100644
index 0000000..e370329
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.h
@@ -0,0 +1,89 @@
+//
+// Copyright 2016 gRPC authors.
+//
+// Licensed 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.
+//
+
+#ifndef GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/iomgr/timer.h"
+
+enum grpc_deadline_timer_state {
+  GRPC_DEADLINE_STATE_INITIAL,
+  GRPC_DEADLINE_STATE_PENDING,
+  GRPC_DEADLINE_STATE_FINISHED
+};
+
+// State used for filters that enforce call deadlines.
+// Must be the first field in the filter's call_data.
+struct grpc_deadline_state {
+  grpc_deadline_state(grpc_call_element* elem, grpc_call_stack* call_stack,
+                      grpc_call_combiner* call_combiner, grpc_millis deadline);
+  ~grpc_deadline_state();
+
+  // We take a reference to the call stack for the timer callback.
+  grpc_call_stack* call_stack;
+  grpc_call_combiner* call_combiner;
+  grpc_deadline_timer_state timer_state = GRPC_DEADLINE_STATE_INITIAL;
+  grpc_timer timer;
+  grpc_closure timer_callback;
+  // Closure to invoke when we receive trailing metadata.
+  // We use this to cancel the timer.
+  grpc_closure recv_trailing_metadata_ready;
+  // The original recv_trailing_metadata_ready closure, which we chain to
+  // after our own closure is invoked.
+  grpc_closure* original_recv_trailing_metadata_ready;
+};
+
+//
+// NOTE: All of these functions require that the first field in
+// elem->call_data is a grpc_deadline_state.
+//
+
+// Cancels the existing timer and starts a new one with new_deadline.
+//
+// Note: It is generally safe to call this with an earlier deadline
+// value than the current one, but not the reverse.  No checks are done
+// to ensure that the timer callback is not invoked while it is in the
+// process of being reset, which means that attempting to increase the
+// deadline may result in the timer being called twice.
+//
+// Note: Must be called while holding the call combiner.
+void grpc_deadline_state_reset(grpc_call_element* elem,
+                               grpc_millis new_deadline);
+
+// To be called from the client-side filter's start_transport_stream_op_batch()
+// method.  Ensures that the deadline timer is cancelled when the call
+// is completed.
+//
+// Note: It is the caller's responsibility to chain to the next filter if
+// necessary after this function returns.
+//
+// Note: Must be called while holding the call combiner.
+void grpc_deadline_state_client_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op);
+
+// Should deadline checking be performed (according to channel args)
+bool grpc_deadline_checking_enabled(const grpc_channel_args* args);
+
+// 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_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H */
diff --git a/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.cc b/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.cc
new file mode 100644
index 0000000..bf9a01f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.cc
@@ -0,0 +1,598 @@
+/*
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <stdint.h>
+#include <string.h>
+#include "src/core/ext/filters/http/client/http_client_filter.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/b64.h"
+#include "src/core/lib/slice/percent_encoding.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/transport_impl.h"
+
+#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 constexpr size_t kMaxPayloadSizeForGet = 2048;
+
+static void recv_initial_metadata_ready(void* user_data, grpc_error* error);
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
+static void on_send_message_next_done(void* arg, grpc_error* error);
+static void send_message_on_complete(void* arg, grpc_error* error);
+
+namespace {
+struct call_data {
+  call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+      : call_combiner(args.call_combiner) {
+    GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
+                      ::recv_initial_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+                      ::recv_trailing_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&on_send_message_next_done, ::on_send_message_next_done,
+                      elem, grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&send_message_on_complete, ::send_message_on_complete,
+                      elem, grpc_schedule_on_exec_ctx);
+  }
+
+  ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); }
+
+  grpc_call_combiner* call_combiner;
+  // State for handling send_initial_metadata ops.
+  grpc_linked_mdelem method;
+  grpc_linked_mdelem scheme;
+  grpc_linked_mdelem authority;
+  grpc_linked_mdelem te_trailers;
+  grpc_linked_mdelem content_type;
+  grpc_linked_mdelem user_agent;
+  // State for handling recv_initial_metadata ops.
+  grpc_metadata_batch* recv_initial_metadata;
+  grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
+  grpc_closure* original_recv_initial_metadata_ready = nullptr;
+  grpc_closure recv_initial_metadata_ready;
+  // State for handling recv_trailing_metadata ops.
+  grpc_metadata_batch* recv_trailing_metadata;
+  grpc_closure* original_recv_trailing_metadata_ready;
+  grpc_closure recv_trailing_metadata_ready;
+  grpc_error* recv_trailing_metadata_error = GRPC_ERROR_NONE;
+  bool seen_recv_trailing_metadata_ready = false;
+  // State for handling send_message ops.
+  grpc_transport_stream_op_batch* send_message_batch;
+  size_t send_message_bytes_read = 0;
+  grpc_core::ManualConstructor<grpc_core::ByteStreamCache> send_message_cache;
+  grpc_core::ManualConstructor<grpc_core::ByteStreamCache::CachingByteStream>
+      send_message_caching_stream;
+  grpc_closure on_send_message_next_done;
+  grpc_closure* original_send_message_on_complete;
+  grpc_closure send_message_on_complete;
+};
+
+struct channel_data {
+  grpc_mdelem static_scheme;
+  grpc_mdelem user_agent;
+  size_t max_payload_size_for_get;
+};
+}  // namespace
+
+static grpc_error* client_filter_incoming_metadata(grpc_call_element* elem,
+                                                   grpc_metadata_batch* b) {
+  if (b->idx.named.status != nullptr) {
+    /* If both gRPC status and HTTP status are provided in the response, we
+     * should prefer the gRPC status code, as mentioned in
+     * https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md.
+     */
+    if (b->idx.named.grpc_status != nullptr ||
+        grpc_mdelem_eq(b->idx.named.status->md, GRPC_MDELEM_STATUS_200)) {
+      grpc_metadata_batch_remove(b, b->idx.named.status);
+    } else {
+      char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.status->md),
+                                  GPR_DUMP_ASCII);
+      char* msg;
+      gpr_asprintf(&msg, "Received http2 header with status: %s", val);
+      grpc_error* e = grpc_error_set_str(
+          grpc_error_set_int(
+              grpc_error_set_str(
+                  GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                      "Received http2 :status header with non-200 OK status"),
+                  GRPC_ERROR_STR_VALUE, grpc_slice_from_copied_string(val)),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED),
+          GRPC_ERROR_STR_GRPC_MESSAGE, grpc_slice_from_copied_string(msg));
+      gpr_free(val);
+      gpr_free(msg);
+      return e;
+    }
+  }
+
+  if (b->idx.named.grpc_message != nullptr) {
+    grpc_slice pct_decoded_msg = grpc_permissive_percent_decode_slice(
+        GRPC_MDVALUE(b->idx.named.grpc_message->md));
+    if (grpc_slice_is_equivalent(pct_decoded_msg,
+                                 GRPC_MDVALUE(b->idx.named.grpc_message->md))) {
+      grpc_slice_unref_internal(pct_decoded_msg);
+    } else {
+      grpc_metadata_batch_set_value(b->idx.named.grpc_message, pct_decoded_msg);
+    }
+  }
+
+  if (b->idx.named.content_type != nullptr) {
+    if (!grpc_mdelem_eq(b->idx.named.content_type->md,
+                        GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
+      if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md),
+                                  EXPECTED_CONTENT_TYPE,
+                                  EXPECTED_CONTENT_TYPE_LENGTH) &&
+          (GRPC_SLICE_START_PTR(GRPC_MDVALUE(
+               b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
+               '+' ||
+           GRPC_SLICE_START_PTR(GRPC_MDVALUE(
+               b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
+               ';')) {
+        /* Although the C implementation doesn't (currently) generate them,
+           any custom +-suffix is explicitly valid. */
+        /* TODO(klempner): We should consider preallocating common values such
+           as +proto or +json, or at least stashing them if we see them. */
+        /* TODO(klempner): Should we be surfacing this to application code? */
+      } else {
+        /* TODO(klempner): We're currently allowing this, but we shouldn't
+           see it without a proxy so log for now. */
+        char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md),
+                                    GPR_DUMP_ASCII);
+        gpr_log(GPR_INFO, "Unexpected content-type '%s'", val);
+        gpr_free(val);
+      }
+    }
+    grpc_metadata_batch_remove(b, b->idx.named.content_type);
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+static void recv_initial_metadata_ready(void* user_data, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (error == GRPC_ERROR_NONE) {
+    error = client_filter_incoming_metadata(elem, calld->recv_initial_metadata);
+    calld->recv_initial_metadata_error = GRPC_ERROR_REF(error);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  grpc_closure* closure = calld->original_recv_initial_metadata_ready;
+  calld->original_recv_initial_metadata_ready = nullptr;
+  if (calld->seen_recv_trailing_metadata_ready) {
+    GRPC_CALL_COMBINER_START(
+        calld->call_combiner, &calld->recv_trailing_metadata_ready,
+        calld->recv_trailing_metadata_error, "continue recv_trailing_metadata");
+  }
+  GRPC_CLOSURE_RUN(closure, error);
+}
+
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->original_recv_initial_metadata_ready != nullptr) {
+    calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error);
+    calld->seen_recv_trailing_metadata_ready = true;
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "deferring recv_trailing_metadata_ready until "
+                            "after recv_initial_metadata_ready");
+    return;
+  }
+  if (error == GRPC_ERROR_NONE) {
+    error =
+        client_filter_incoming_metadata(elem, calld->recv_trailing_metadata);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  error = grpc_error_add_child(
+      error, GRPC_ERROR_REF(calld->recv_initial_metadata_error));
+  GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error);
+}
+
+static void send_message_on_complete(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->send_message_cache.Destroy();
+  GRPC_CLOSURE_RUN(calld->original_send_message_on_complete,
+                   GRPC_ERROR_REF(error));
+}
+
+// Pulls a slice from the send_message byte stream, updating
+// calld->send_message_bytes_read.
+static grpc_error* pull_slice_from_send_message(call_data* calld) {
+  grpc_slice incoming_slice;
+  grpc_error* error = calld->send_message_caching_stream->Pull(&incoming_slice);
+  if (error == GRPC_ERROR_NONE) {
+    calld->send_message_bytes_read += GRPC_SLICE_LENGTH(incoming_slice);
+    grpc_slice_unref_internal(incoming_slice);
+  }
+  return error;
+}
+
+// Reads as many slices as possible from the send_message byte stream.
+// Upon successful return, if calld->send_message_bytes_read ==
+// calld->send_message_caching_stream->length(), then we have completed
+// reading from the byte stream; otherwise, an async read has been dispatched
+// and on_send_message_next_done() will be invoked when it is complete.
+static grpc_error* read_all_available_send_message_data(call_data* calld) {
+  while (calld->send_message_caching_stream->Next(
+      SIZE_MAX, &calld->on_send_message_next_done)) {
+    grpc_error* error = pull_slice_from_send_message(calld);
+    if (error != GRPC_ERROR_NONE) return error;
+    if (calld->send_message_bytes_read ==
+        calld->send_message_caching_stream->length()) {
+      break;
+    }
+  }
+  return GRPC_ERROR_NONE;
+}
+
+// Async callback for ByteStream::Next().
+static void on_send_message_next_done(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (error != GRPC_ERROR_NONE) {
+    grpc_transport_stream_op_batch_finish_with_failure(
+        calld->send_message_batch, error, calld->call_combiner);
+    return;
+  }
+  error = pull_slice_from_send_message(calld);
+  if (error != GRPC_ERROR_NONE) {
+    grpc_transport_stream_op_batch_finish_with_failure(
+        calld->send_message_batch, error, calld->call_combiner);
+    return;
+  }
+  // There may or may not be more to read, but we don't care.  If we got
+  // here, then we know that all of the data was not available
+  // synchronously, so we were not able to do a cached call.  Instead,
+  // we just reset the byte stream and then send down the batch as-is.
+  calld->send_message_caching_stream->Reset();
+  grpc_call_next_op(elem, calld->send_message_batch);
+}
+
+static char* slice_buffer_to_string(grpc_slice_buffer* slice_buffer) {
+  char* payload_bytes =
+      static_cast<char*>(gpr_malloc(slice_buffer->length + 1));
+  size_t offset = 0;
+  for (size_t i = 0; i < slice_buffer->count; ++i) {
+    memcpy(payload_bytes + offset,
+           GRPC_SLICE_START_PTR(slice_buffer->slices[i]),
+           GRPC_SLICE_LENGTH(slice_buffer->slices[i]));
+    offset += GRPC_SLICE_LENGTH(slice_buffer->slices[i]);
+  }
+  *(payload_bytes + offset) = '\0';
+  return payload_bytes;
+}
+
+// Modifies the path entry in the batch's send_initial_metadata to
+// append the base64-encoded query for a GET request.
+static grpc_error* update_path_for_get(grpc_call_element* elem,
+                                       grpc_transport_stream_op_batch* batch) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_slice path_slice =
+      GRPC_MDVALUE(batch->payload->send_initial_metadata.send_initial_metadata
+                       ->idx.named.path->md);
+  /* sum up individual component's lengths and allocate enough memory to
+   * hold combined path+query */
+  size_t estimated_len = GRPC_SLICE_LENGTH(path_slice);
+  estimated_len++; /* for the '?' */
+  estimated_len += grpc_base64_estimate_encoded_size(
+      batch->payload->send_message.send_message->length(), true /* url_safe */,
+      false /* multi_line */);
+  grpc_slice path_with_query_slice = GRPC_SLICE_MALLOC(estimated_len);
+  /* memcopy individual pieces into this slice */
+  char* write_ptr =
+      reinterpret_cast<char*> GRPC_SLICE_START_PTR(path_with_query_slice);
+  char* original_path =
+      reinterpret_cast<char*> GRPC_SLICE_START_PTR(path_slice);
+  memcpy(write_ptr, original_path, GRPC_SLICE_LENGTH(path_slice));
+  write_ptr += GRPC_SLICE_LENGTH(path_slice);
+  *write_ptr++ = '?';
+  char* payload_bytes =
+      slice_buffer_to_string(calld->send_message_cache->cache_buffer());
+  grpc_base64_encode_core(write_ptr, payload_bytes,
+                          batch->payload->send_message.send_message->length(),
+                          true /* url_safe */, false /* multi_line */);
+  gpr_free(payload_bytes);
+  /* remove trailing unused memory and add trailing 0 to terminate string */
+  char* t = reinterpret_cast<char*> GRPC_SLICE_START_PTR(path_with_query_slice);
+  /* safe to use strlen since base64_encode will always add '\0' */
+  path_with_query_slice =
+      grpc_slice_sub_no_ref(path_with_query_slice, 0, strlen(t));
+  /* substitute previous path with the new path+query */
+  grpc_mdelem mdelem_path_and_query =
+      grpc_mdelem_from_slices(GRPC_MDSTR_PATH, path_with_query_slice);
+  grpc_metadata_batch* b =
+      batch->payload->send_initial_metadata.send_initial_metadata;
+  return grpc_metadata_batch_substitute(b, b->idx.named.path,
+                                        mdelem_path_and_query);
+}
+
+static void remove_if_present(grpc_metadata_batch* batch,
+                              grpc_metadata_batch_callouts_index idx) {
+  if (batch->idx.array[idx] != nullptr) {
+    grpc_metadata_batch_remove(batch, batch->idx.array[idx]);
+  }
+}
+
+static void hc_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* channeld = static_cast<channel_data*>(elem->channel_data);
+  GPR_TIMER_SCOPE("hc_start_transport_stream_op_batch", 0);
+
+  if (batch->recv_initial_metadata) {
+    /* substitute our callback for the higher callback */
+    calld->recv_initial_metadata =
+        batch->payload->recv_initial_metadata.recv_initial_metadata;
+    calld->original_recv_initial_metadata_ready =
+        batch->payload->recv_initial_metadata.recv_initial_metadata_ready;
+    batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
+        &calld->recv_initial_metadata_ready;
+  }
+
+  if (batch->recv_trailing_metadata) {
+    /* substitute our callback for the higher callback */
+    calld->recv_trailing_metadata =
+        batch->payload->recv_trailing_metadata.recv_trailing_metadata;
+    calld->original_recv_trailing_metadata_ready =
+        batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &calld->recv_trailing_metadata_ready;
+  }
+
+  grpc_error* error = GRPC_ERROR_NONE;
+  bool batch_will_be_handled_asynchronously = false;
+  if (batch->send_initial_metadata) {
+    // 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;
+    if (batch->send_message &&
+        (batch->payload->send_initial_metadata.send_initial_metadata_flags &
+         GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) &&
+        batch->payload->send_message.send_message->length() <
+            channeld->max_payload_size_for_get) {
+      calld->send_message_bytes_read = 0;
+      calld->send_message_cache.Init(
+          std::move(batch->payload->send_message.send_message));
+      calld->send_message_caching_stream.Init(calld->send_message_cache.get());
+      batch->payload->send_message.send_message.reset(
+          calld->send_message_caching_stream.get());
+      calld->original_send_message_on_complete = batch->on_complete;
+      batch->on_complete = &calld->send_message_on_complete;
+      calld->send_message_batch = batch;
+      error = read_all_available_send_message_data(calld);
+      if (error != GRPC_ERROR_NONE) goto done;
+      // If all the data has been read, then we can use GET.
+      if (calld->send_message_bytes_read ==
+          calld->send_message_caching_stream->length()) {
+        method = GRPC_MDELEM_METHOD_GET;
+        error = update_path_for_get(elem, batch);
+        if (error != GRPC_ERROR_NONE) goto done;
+        batch->send_message = false;
+        calld->send_message_caching_stream->Orphan();
+      } else {
+        // Not all data is available.  The batch will be sent down
+        // asynchronously in on_send_message_next_done().
+        batch_will_be_handled_asynchronously = true;
+        // Fall back to POST.
+        gpr_log(GPR_DEBUG,
+                "Request is marked Cacheable but not all data is available.  "
+                "Falling back to POST");
+      }
+    } else if (batch->payload->send_initial_metadata
+                   .send_initial_metadata_flags &
+               GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) {
+      method = GRPC_MDELEM_METHOD_PUT;
+    }
+
+    remove_if_present(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        GRPC_BATCH_METHOD);
+    remove_if_present(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        GRPC_BATCH_SCHEME);
+    remove_if_present(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        GRPC_BATCH_TE);
+    remove_if_present(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        GRPC_BATCH_CONTENT_TYPE);
+    remove_if_present(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        GRPC_BATCH_USER_AGENT);
+
+    /* Send : prefixed headers, which have to be before any application
+       layer headers. */
+    error = grpc_metadata_batch_add_head(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        &calld->method, method);
+    if (error != GRPC_ERROR_NONE) goto done;
+    error = grpc_metadata_batch_add_head(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        &calld->scheme, channeld->static_scheme);
+    if (error != GRPC_ERROR_NONE) goto done;
+    error = grpc_metadata_batch_add_tail(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        &calld->te_trailers, GRPC_MDELEM_TE_TRAILERS);
+    if (error != GRPC_ERROR_NONE) goto done;
+    error = grpc_metadata_batch_add_tail(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        &calld->content_type, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
+    if (error != GRPC_ERROR_NONE) goto done;
+    error = grpc_metadata_batch_add_tail(
+        batch->payload->send_initial_metadata.send_initial_metadata,
+        &calld->user_agent, GRPC_MDELEM_REF(channeld->user_agent));
+    if (error != GRPC_ERROR_NONE) goto done;
+  }
+
+done:
+  if (error != GRPC_ERROR_NONE) {
+    grpc_transport_stream_op_batch_finish_with_failure(
+        calld->send_message_batch, error, calld->call_combiner);
+  } else if (!batch_will_be_handled_asynchronously) {
+    grpc_call_next_op(elem, batch);
+  }
+}
+
+/* Constructor for call_data */
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  new (elem->call_data) call_data(elem, *args);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for call_data */
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->~call_data();
+}
+
+static grpc_mdelem scheme_from_args(const grpc_channel_args* args) {
+  unsigned i;
+  size_t j;
+  grpc_mdelem valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
+                                 GRPC_MDELEM_SCHEME_HTTPS};
+  if (args != nullptr) {
+    for (i = 0; i < args->num_args; ++i) {
+      if (args->args[i].type == GRPC_ARG_STRING &&
+          strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) {
+        for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) {
+          if (0 == grpc_slice_str_cmp(GRPC_MDVALUE(valid_schemes[j]),
+                                      args->args[i].value.string)) {
+            return valid_schemes[j];
+          }
+        }
+      }
+    }
+  }
+  return GRPC_MDELEM_SCHEME_HTTP;
+}
+
+static size_t max_payload_size_from_args(const grpc_channel_args* args) {
+  if (args != nullptr) {
+    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 static_cast<size_t>(args->args[i].value.integer);
+        }
+      }
+    }
+  }
+  return kMaxPayloadSizeForGet;
+}
+
+static grpc_slice user_agent_from_args(const grpc_channel_args* args,
+                                       const char* transport_name) {
+  gpr_strvec v;
+  size_t i;
+  int is_first = 1;
+  char* tmp;
+  grpc_slice result;
+
+  gpr_strvec_init(&v);
+
+  for (i = 0; args && i < args->num_args; i++) {
+    if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) {
+      if (args->args[i].type != GRPC_ARG_STRING) {
+        gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
+                GRPC_ARG_PRIMARY_USER_AGENT_STRING);
+      } else {
+        if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
+        is_first = 0;
+        gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
+      }
+    }
+  }
+
+  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);
+
+  for (i = 0; args && i < args->num_args; i++) {
+    if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) {
+      if (args->args[i].type != GRPC_ARG_STRING) {
+        gpr_log(GPR_ERROR, "Channel argument '%s' should be a string",
+                GRPC_ARG_SECONDARY_USER_AGENT_STRING);
+      } else {
+        if (!is_first) gpr_strvec_add(&v, gpr_strdup(" "));
+        is_first = 0;
+        gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string));
+      }
+    }
+  }
+
+  tmp = gpr_strvec_flatten(&v, nullptr);
+  gpr_strvec_destroy(&v);
+  result = grpc_slice_intern(grpc_slice_from_static_string(tmp));
+  gpr_free(tmp);
+
+  return result;
+}
+
+/* Constructor for channel_data */
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  GPR_ASSERT(!args->is_last);
+  GPR_ASSERT(args->optional_transport != nullptr);
+  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_slices(
+      GRPC_MDSTR_USER_AGENT,
+      user_agent_from_args(args->channel_args,
+                           args->optional_transport->vtable->name));
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for channel data */
+static void destroy_channel_elem(grpc_channel_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  GRPC_MDELEM_UNREF(chand->user_agent);
+}
+
+const grpc_channel_filter grpc_http_client_filter = {
+    hc_start_transport_stream_op_batch,
+    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_channel_next_get_info,
+    "http-client"};
diff --git a/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.h b/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.h
new file mode 100644
index 0000000..b7cef33
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+/* Processes metadata on the client side for HTTP2 transports */
+extern const grpc_channel_filter grpc_http_client_filter;
+
+/* 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_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H */
diff --git a/third_party/grpc/src/core/ext/filters/http/client_authority_filter.cc b/third_party/grpc/src/core/ext/filters/http/client_authority_filter.cc
new file mode 100644
index 0000000..6383f12
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/client_authority_filter.cc
@@ -0,0 +1,156 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <assert.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/ext/filters/http/client_authority_filter.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/surface/channel_stack_type.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+namespace {
+
+struct call_data {
+  grpc_linked_mdelem authority_storage;
+  grpc_call_combiner* call_combiner;
+};
+
+struct channel_data {
+  grpc_slice default_authority;
+};
+
+void authority_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Handle send_initial_metadata.
+  auto* initial_metadata =
+      batch->payload->send_initial_metadata.send_initial_metadata;
+  // If the initial metadata doesn't already contain :authority, add it.
+  if (batch->send_initial_metadata &&
+      initial_metadata->idx.named.authority == nullptr) {
+    grpc_error* error = grpc_metadata_batch_add_head(
+        initial_metadata, &calld->authority_storage,
+        grpc_mdelem_create(GRPC_MDSTR_AUTHORITY, chand->default_authority,
+                           nullptr));
+    if (error != GRPC_ERROR_NONE) {
+      grpc_transport_stream_op_batch_finish_with_failure(batch, error,
+                                                         calld->call_combiner);
+      return;
+    }
+  }
+  // Pass control down the stack.
+  grpc_call_next_op(elem, batch);
+}
+
+/* Constructor for call_data */
+grpc_error* init_call_elem(grpc_call_element* elem,
+                           const grpc_call_element_args* args) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->call_combiner = args->call_combiner;
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for call_data */
+void destroy_call_elem(grpc_call_element* elem,
+                       const grpc_call_final_info* final_info,
+                       grpc_closure* ignored) {}
+
+/* Constructor for channel_data */
+grpc_error* init_channel_elem(grpc_channel_element* elem,
+                              grpc_channel_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  const grpc_arg* default_authority_arg =
+      grpc_channel_args_find(args->channel_args, GRPC_ARG_DEFAULT_AUTHORITY);
+  if (default_authority_arg == nullptr) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "GRPC_ARG_DEFAULT_AUTHORITY channel arg. not found. Note that direct "
+        "channels must explicitly specify a value for this argument.");
+  }
+  const char* default_authority_str =
+      grpc_channel_arg_get_string(default_authority_arg);
+  if (default_authority_str == nullptr) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "GRPC_ARG_DEFAULT_AUTHORITY channel arg. must be a string");
+  }
+  chand->default_authority =
+      grpc_slice_intern(grpc_slice_from_static_string(default_authority_str));
+  GPR_ASSERT(!args->is_last);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for channel data */
+void destroy_channel_elem(grpc_channel_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  grpc_slice_unref_internal(chand->default_authority);
+}
+}  // namespace
+
+const grpc_channel_filter grpc_client_authority_filter = {
+    authority_start_transport_stream_op_batch,
+    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_channel_next_get_info,
+    "authority"};
+
+static bool add_client_authority_filter(grpc_channel_stack_builder* builder,
+                                        void* arg) {
+  const grpc_channel_args* channel_args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  const grpc_arg* disable_client_authority_filter_arg = grpc_channel_args_find(
+      channel_args, GRPC_ARG_DISABLE_CLIENT_AUTHORITY_FILTER);
+  if (disable_client_authority_filter_arg != nullptr) {
+    const bool is_client_authority_filter_disabled =
+        grpc_channel_arg_get_bool(disable_client_authority_filter_arg, false);
+    if (is_client_authority_filter_disabled) {
+      return true;
+    }
+  }
+  return grpc_channel_stack_builder_prepend_filter(
+      builder, static_cast<const grpc_channel_filter*>(arg), nullptr, nullptr);
+}
+
+void grpc_client_authority_filter_init(void) {
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX,
+                                   add_client_authority_filter,
+                                   (void*)&grpc_client_authority_filter);
+  grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX,
+                                   add_client_authority_filter,
+                                   (void*)&grpc_client_authority_filter);
+}
+
+void grpc_client_authority_filter_shutdown(void) {}
diff --git a/third_party/grpc/src/core/ext/filters/http/client_authority_filter.h b/third_party/grpc/src/core/ext/filters/http/client_authority_filter.h
new file mode 100644
index 0000000..5824e91
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/client_authority_filter.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_AUTHORITY_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_AUTHORITY_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/compression_types.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+/// Filter responsible for setting the authority header, if not already set. It
+/// uses the value of the GRPC_ARG_DEFAULT_AUTHORITY channel arg if the initial
+/// metadata doesn't already contain an authority value.
+
+extern const grpc_channel_filter grpc_client_authority_filter;
+
+#endif /* GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_AUTHORITY_FILTER_H */
diff --git a/third_party/grpc/src/core/ext/filters/http/http_filters_plugin.cc b/third_party/grpc/src/core/ext/filters/http/http_filters_plugin.cc
new file mode 100644
index 0000000..f03fa01
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/http_filters_plugin.cc
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include "src/core/ext/filters/http/client/http_client_filter.h"
+#include "src/core/ext/filters/http/message_compress/message_compress_filter.h"
+#include "src/core/ext/filters/http/server/http_server_filter.h"
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/transport_impl.h"
+
+typedef struct {
+  const grpc_channel_filter* filter;
+  const char* control_channel_arg;
+} optional_filter;
+
+static optional_filter compress_filter = {
+    &grpc_message_compress_filter, GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION};
+
+static bool is_building_http_like_transport(
+    grpc_channel_stack_builder* builder) {
+  grpc_transport* t = grpc_channel_stack_builder_get_transport(builder);
+  return t != nullptr && strstr(t->vtable->name, "http");
+}
+
+static bool maybe_add_optional_filter(grpc_channel_stack_builder* builder,
+                                      void* arg) {
+  if (!is_building_http_like_transport(builder)) return true;
+  optional_filter* filtarg = static_cast<optional_filter*>(arg);
+  const grpc_channel_args* channel_args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  bool enable = grpc_channel_arg_get_bool(
+      grpc_channel_args_find(channel_args, filtarg->control_channel_arg),
+      !grpc_channel_args_want_minimal_stack(channel_args));
+  return enable ? grpc_channel_stack_builder_prepend_filter(
+                      builder, filtarg->filter, nullptr, nullptr)
+                : true;
+}
+
+static bool maybe_add_required_filter(grpc_channel_stack_builder* builder,
+                                      void* arg) {
+  return is_building_http_like_transport(builder)
+             ? grpc_channel_stack_builder_prepend_filter(
+                   builder, static_cast<const grpc_channel_filter*>(arg),
+                   nullptr, nullptr)
+             : true;
+}
+
+void grpc_http_filters_init(void) {
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_optional_filter, &compress_filter);
+  grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_optional_filter, &compress_filter);
+  grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_optional_filter, &compress_filter);
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      maybe_add_required_filter, (void*)&grpc_http_client_filter);
+  grpc_channel_init_register_stage(
+      GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      maybe_add_required_filter, (void*)&grpc_http_client_filter);
+  grpc_channel_init_register_stage(
+      GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+      maybe_add_required_filter, (void*)&grpc_http_server_filter);
+}
+
+void grpc_http_filters_shutdown(void) {}
diff --git a/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.cc
new file mode 100644
index 0000000..9c8c8d9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.cc
@@ -0,0 +1,511 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/ext/filters/http/message_compress/message_compress_filter.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/algorithm_metadata.h"
+#include "src/core/lib/compression/compression_internal.h"
+#include "src/core/lib/compression/message_compress.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+static void start_send_message_batch(void* arg, grpc_error* unused);
+static void send_message_on_complete(void* arg, grpc_error* error);
+static void on_send_message_next_done(void* arg, grpc_error* error);
+
+namespace {
+enum initial_metadata_state {
+  // Initial metadata not yet seen.
+  INITIAL_METADATA_UNSEEN = 0,
+  // Initial metadata seen; compression algorithm set.
+  HAS_COMPRESSION_ALGORITHM,
+  // Initial metadata seen; no compression algorithm set.
+  NO_COMPRESSION_ALGORITHM,
+};
+
+struct call_data {
+  call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+      : call_combiner(args.call_combiner) {
+    GRPC_CLOSURE_INIT(&start_send_message_batch_in_call_combiner,
+                      start_send_message_batch, elem,
+                      grpc_schedule_on_exec_ctx);
+    grpc_slice_buffer_init(&slices);
+    GRPC_CLOSURE_INIT(&send_message_on_complete, ::send_message_on_complete,
+                      elem, grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&on_send_message_next_done, ::on_send_message_next_done,
+                      elem, grpc_schedule_on_exec_ctx);
+  }
+
+  ~call_data() {
+    grpc_slice_buffer_destroy_internal(&slices);
+    GRPC_ERROR_UNREF(cancel_error);
+  }
+
+  grpc_call_combiner* call_combiner;
+  grpc_linked_mdelem compression_algorithm_storage;
+  grpc_linked_mdelem stream_compression_algorithm_storage;
+  grpc_linked_mdelem accept_encoding_storage;
+  grpc_linked_mdelem accept_stream_encoding_storage;
+  /** Compression algorithm we'll try to use. It may be given by incoming
+   * metadata, or by the channel's default compression settings. */
+  grpc_message_compression_algorithm message_compression_algorithm =
+      GRPC_MESSAGE_COMPRESS_NONE;
+  initial_metadata_state send_initial_metadata_state = INITIAL_METADATA_UNSEEN;
+  grpc_error* cancel_error = GRPC_ERROR_NONE;
+  grpc_closure start_send_message_batch_in_call_combiner;
+  grpc_transport_stream_op_batch* send_message_batch = nullptr;
+  grpc_slice_buffer slices; /**< Buffers up input slices to be compressed */
+  grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream>
+      replacement_stream;
+  grpc_closure* original_send_message_on_complete;
+  grpc_closure send_message_on_complete;
+  grpc_closure on_send_message_next_done;
+};
+
+struct channel_data {
+  /** The default, channel-level, compression algorithm */
+  grpc_compression_algorithm default_compression_algorithm;
+  /** Bitset of enabled compression algorithms */
+  uint32_t enabled_algorithms_bitset;
+  /** Supported compression algorithms */
+  uint32_t supported_message_compression_algorithms;
+  /** Supported stream compression algorithms */
+  uint32_t supported_stream_compression_algorithms;
+};
+}  // namespace
+
+static bool skip_compression(grpc_call_element* elem, uint32_t flags,
+                             bool has_compression_algorithm) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* channeld = static_cast<channel_data*>(elem->channel_data);
+
+  if (flags & (GRPC_WRITE_NO_COMPRESS | GRPC_WRITE_INTERNAL_COMPRESS)) {
+    return true;
+  }
+  if (has_compression_algorithm) {
+    if (calld->message_compression_algorithm == GRPC_MESSAGE_COMPRESS_NONE) {
+      return true;
+    }
+    return false; /* we have an actual call-specific algorithm */
+  }
+  /* no per-call compression override */
+  return channeld->default_compression_algorithm == GRPC_COMPRESS_NONE;
+}
+
+/** Filter initial metadata */
+static grpc_error* process_send_initial_metadata(
+    grpc_call_element* elem, grpc_metadata_batch* initial_metadata,
+    bool* has_compression_algorithm) GRPC_MUST_USE_RESULT;
+static grpc_error* process_send_initial_metadata(
+    grpc_call_element* elem, grpc_metadata_batch* initial_metadata,
+    bool* has_compression_algorithm) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* channeld = static_cast<channel_data*>(elem->channel_data);
+  *has_compression_algorithm = false;
+  grpc_compression_algorithm compression_algorithm;
+  grpc_stream_compression_algorithm stream_compression_algorithm =
+      GRPC_STREAM_COMPRESS_NONE;
+  if (initial_metadata->idx.named.grpc_internal_encoding_request != nullptr) {
+    grpc_mdelem md =
+        initial_metadata->idx.named.grpc_internal_encoding_request->md;
+    if (GPR_UNLIKELY(!grpc_compression_algorithm_parse(
+            GRPC_MDVALUE(md), &compression_algorithm))) {
+      char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+      gpr_log(GPR_ERROR,
+              "Invalid compression algorithm: '%s' (unknown). Ignoring.", val);
+      gpr_free(val);
+      calld->message_compression_algorithm = GRPC_MESSAGE_COMPRESS_NONE;
+      stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE;
+    }
+    if (GPR_UNLIKELY(!GPR_BITGET(channeld->enabled_algorithms_bitset,
+                                 compression_algorithm))) {
+      char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+      gpr_log(GPR_ERROR,
+              "Invalid compression algorithm: '%s' (previously disabled). "
+              "Ignoring.",
+              val);
+      gpr_free(val);
+      calld->message_compression_algorithm = GRPC_MESSAGE_COMPRESS_NONE;
+      stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE;
+    }
+    *has_compression_algorithm = true;
+    grpc_metadata_batch_remove(
+        initial_metadata,
+        initial_metadata->idx.named.grpc_internal_encoding_request);
+    calld->message_compression_algorithm =
+        grpc_compression_algorithm_to_message_compression_algorithm(
+            compression_algorithm);
+    stream_compression_algorithm =
+        grpc_compression_algorithm_to_stream_compression_algorithm(
+            compression_algorithm);
+  } else {
+    /* If no algorithm was found in the metadata and we aren't
+     * exceptionally skipping compression, fall back to the channel
+     * default */
+    if (channeld->default_compression_algorithm != GRPC_COMPRESS_NONE) {
+      calld->message_compression_algorithm =
+          grpc_compression_algorithm_to_message_compression_algorithm(
+              channeld->default_compression_algorithm);
+      stream_compression_algorithm =
+          grpc_compression_algorithm_to_stream_compression_algorithm(
+              channeld->default_compression_algorithm);
+    }
+    *has_compression_algorithm = true;
+  }
+
+  grpc_error* error = GRPC_ERROR_NONE;
+  /* hint compression algorithm */
+  if (stream_compression_algorithm != GRPC_STREAM_COMPRESS_NONE) {
+    error = grpc_metadata_batch_add_tail(
+        initial_metadata, &calld->stream_compression_algorithm_storage,
+        grpc_stream_compression_encoding_mdelem(stream_compression_algorithm));
+  } else if (calld->message_compression_algorithm !=
+             GRPC_MESSAGE_COMPRESS_NONE) {
+    error = grpc_metadata_batch_add_tail(
+        initial_metadata, &calld->compression_algorithm_storage,
+        grpc_message_compression_encoding_mdelem(
+            calld->message_compression_algorithm));
+  }
+
+  if (error != GRPC_ERROR_NONE) return error;
+
+  /* convey supported compression algorithms */
+  error = grpc_metadata_batch_add_tail(
+      initial_metadata, &calld->accept_encoding_storage,
+      GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(
+          channeld->supported_message_compression_algorithms));
+
+  if (error != GRPC_ERROR_NONE) return error;
+
+  /* Do not overwrite accept-encoding header if it already presents (e.g. added
+   * by some proxy). */
+  if (!initial_metadata->idx.named.accept_encoding) {
+    error = grpc_metadata_batch_add_tail(
+        initial_metadata, &calld->accept_stream_encoding_storage,
+        GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS(
+            channeld->supported_stream_compression_algorithms));
+  }
+
+  return error;
+}
+
+static void send_message_on_complete(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_slice_buffer_reset_and_unref_internal(&calld->slices);
+  GRPC_CLOSURE_RUN(calld->original_send_message_on_complete,
+                   GRPC_ERROR_REF(error));
+}
+
+static void send_message_batch_continue(grpc_call_element* elem) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Note: The call to grpc_call_next_op() results in yielding the
+  // call combiner, so we need to clear calld->send_message_batch
+  // before we do that.
+  grpc_transport_stream_op_batch* send_message_batch =
+      calld->send_message_batch;
+  calld->send_message_batch = nullptr;
+  grpc_call_next_op(elem, send_message_batch);
+}
+
+static void finish_send_message(grpc_call_element* elem) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Compress the data if appropriate.
+  grpc_slice_buffer tmp;
+  grpc_slice_buffer_init(&tmp);
+  uint32_t send_flags =
+      calld->send_message_batch->payload->send_message.send_message->flags();
+  bool did_compress = grpc_msg_compress(calld->message_compression_algorithm,
+                                        &calld->slices, &tmp);
+  if (did_compress) {
+    if (grpc_compression_trace.enabled()) {
+      const char* algo_name;
+      const size_t before_size = calld->slices.length;
+      const size_t after_size = tmp.length;
+      const float savings_ratio = 1.0f - static_cast<float>(after_size) /
+                                             static_cast<float>(before_size);
+      GPR_ASSERT(grpc_message_compression_algorithm_name(
+          calld->message_compression_algorithm, &algo_name));
+      gpr_log(GPR_INFO,
+              "Compressed[%s] %" PRIuPTR " bytes vs. %" PRIuPTR
+              " bytes (%.2f%% savings)",
+              algo_name, before_size, after_size, 100 * savings_ratio);
+    }
+    grpc_slice_buffer_swap(&calld->slices, &tmp);
+    send_flags |= GRPC_WRITE_INTERNAL_COMPRESS;
+  } else {
+    if (grpc_compression_trace.enabled()) {
+      const char* algo_name;
+      GPR_ASSERT(grpc_message_compression_algorithm_name(
+          calld->message_compression_algorithm, &algo_name));
+      gpr_log(GPR_INFO,
+              "Algorithm '%s' enabled but decided not to compress. Input size: "
+              "%" PRIuPTR,
+              algo_name, calld->slices.length);
+    }
+  }
+  grpc_slice_buffer_destroy_internal(&tmp);
+  // Swap out the original byte stream with our new one and send the
+  // batch down.
+  calld->replacement_stream.Init(&calld->slices, send_flags);
+  calld->send_message_batch->payload->send_message.send_message.reset(
+      calld->replacement_stream.get());
+  calld->original_send_message_on_complete =
+      calld->send_message_batch->on_complete;
+  calld->send_message_batch->on_complete = &calld->send_message_on_complete;
+  send_message_batch_continue(elem);
+}
+
+static void fail_send_message_batch_in_call_combiner(void* arg,
+                                                     grpc_error* error) {
+  call_data* calld = static_cast<call_data*>(arg);
+  if (calld->send_message_batch != nullptr) {
+    grpc_transport_stream_op_batch_finish_with_failure(
+        calld->send_message_batch, GRPC_ERROR_REF(error), calld->call_combiner);
+    calld->send_message_batch = nullptr;
+  }
+}
+
+// Pulls a slice from the send_message byte stream and adds it to calld->slices.
+static grpc_error* pull_slice_from_send_message(call_data* calld) {
+  grpc_slice incoming_slice;
+  grpc_error* error =
+      calld->send_message_batch->payload->send_message.send_message->Pull(
+          &incoming_slice);
+  if (error == GRPC_ERROR_NONE) {
+    grpc_slice_buffer_add(&calld->slices, incoming_slice);
+  }
+  return error;
+}
+
+// Reads as many slices as possible from the send_message byte stream.
+// If all data has been read, invokes finish_send_message().  Otherwise,
+// an async call to ByteStream::Next() has been started, which will
+// eventually result in calling on_send_message_next_done().
+static void continue_reading_send_message(grpc_call_element* elem) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  while (calld->send_message_batch->payload->send_message.send_message->Next(
+      ~static_cast<size_t>(0), &calld->on_send_message_next_done)) {
+    grpc_error* error = pull_slice_from_send_message(calld);
+    if (error != GRPC_ERROR_NONE) {
+      // Closure callback; does not take ownership of error.
+      fail_send_message_batch_in_call_combiner(calld, error);
+      GRPC_ERROR_UNREF(error);
+      return;
+    }
+    if (calld->slices.length == calld->send_message_batch->payload->send_message
+                                    .send_message->length()) {
+      finish_send_message(elem);
+      break;
+    }
+  }
+}
+
+// Async callback for ByteStream::Next().
+static void on_send_message_next_done(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (error != GRPC_ERROR_NONE) {
+    // Closure callback; does not take ownership of error.
+    fail_send_message_batch_in_call_combiner(calld, error);
+    return;
+  }
+  error = pull_slice_from_send_message(calld);
+  if (error != GRPC_ERROR_NONE) {
+    // Closure callback; does not take ownership of error.
+    fail_send_message_batch_in_call_combiner(calld, error);
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  if (calld->slices.length ==
+      calld->send_message_batch->payload->send_message.send_message->length()) {
+    finish_send_message(elem);
+  } else {
+    continue_reading_send_message(elem);
+  }
+}
+
+static void start_send_message_batch(void* arg, grpc_error* unused) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (skip_compression(
+          elem,
+          calld->send_message_batch->payload->send_message.send_message
+              ->flags(),
+          calld->send_initial_metadata_state == HAS_COMPRESSION_ALGORITHM)) {
+    send_message_batch_continue(elem);
+  } else {
+    continue_reading_send_message(elem);
+  }
+}
+
+static void compress_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  GPR_TIMER_SCOPE("compress_start_transport_stream_op_batch", 0);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Handle cancel_stream.
+  if (batch->cancel_stream) {
+    GRPC_ERROR_UNREF(calld->cancel_error);
+    calld->cancel_error =
+        GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error);
+    if (calld->send_message_batch != nullptr) {
+      if (calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN) {
+        GRPC_CALL_COMBINER_START(
+            calld->call_combiner,
+            GRPC_CLOSURE_CREATE(fail_send_message_batch_in_call_combiner, calld,
+                                grpc_schedule_on_exec_ctx),
+            GRPC_ERROR_REF(calld->cancel_error), "failing send_message op");
+      } else {
+        calld->send_message_batch->payload->send_message.send_message->Shutdown(
+            GRPC_ERROR_REF(calld->cancel_error));
+      }
+    }
+  } else if (calld->cancel_error != GRPC_ERROR_NONE) {
+    grpc_transport_stream_op_batch_finish_with_failure(
+        batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner);
+    return;
+  }
+  // Handle send_initial_metadata.
+  if (batch->send_initial_metadata) {
+    GPR_ASSERT(calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN);
+    bool has_compression_algorithm;
+    grpc_error* error = process_send_initial_metadata(
+        elem, batch->payload->send_initial_metadata.send_initial_metadata,
+        &has_compression_algorithm);
+    if (error != GRPC_ERROR_NONE) {
+      grpc_transport_stream_op_batch_finish_with_failure(batch, error,
+                                                         calld->call_combiner);
+      return;
+    }
+    calld->send_initial_metadata_state = has_compression_algorithm
+                                             ? HAS_COMPRESSION_ALGORITHM
+                                             : NO_COMPRESSION_ALGORITHM;
+    // If we had previously received a batch containing a send_message op,
+    // handle it now.  Note that we need to re-enter the call combiner
+    // for this, since we can't send two batches down while holding the
+    // call combiner, since the connected_channel filter (at the bottom of
+    // the call stack) will release the call combiner for each batch it sees.
+    if (calld->send_message_batch != nullptr) {
+      GRPC_CALL_COMBINER_START(
+          calld->call_combiner,
+          &calld->start_send_message_batch_in_call_combiner, GRPC_ERROR_NONE,
+          "starting send_message after send_initial_metadata");
+    }
+  }
+  // Handle send_message.
+  if (batch->send_message) {
+    GPR_ASSERT(calld->send_message_batch == nullptr);
+    calld->send_message_batch = batch;
+    // If we have not yet seen send_initial_metadata, then we have to
+    // wait.  We save the batch in calld and then drop the call
+    // combiner, which we'll have to pick up again later when we get
+    // send_initial_metadata.
+    if (calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN) {
+      GRPC_CALL_COMBINER_STOP(
+          calld->call_combiner,
+          "send_message batch pending send_initial_metadata");
+      return;
+    }
+    start_send_message_batch(elem, GRPC_ERROR_NONE);
+  } else {
+    // Pass control down the stack.
+    grpc_call_next_op(elem, batch);
+  }
+}
+
+/* Constructor for call_data */
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  new (elem->call_data) call_data(elem, *args);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for call_data */
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->~call_data();
+}
+
+/* Constructor for channel_data */
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  channel_data* channeld = static_cast<channel_data*>(elem->channel_data);
+
+  channeld->enabled_algorithms_bitset =
+      grpc_channel_args_compression_algorithm_get_states(args->channel_args);
+  channeld->default_compression_algorithm =
+      grpc_channel_args_get_compression_algorithm(args->channel_args);
+
+  /* Make sure the default isn't disabled. */
+  if (!GPR_BITGET(channeld->enabled_algorithms_bitset,
+                  channeld->default_compression_algorithm)) {
+    gpr_log(GPR_DEBUG,
+            "compression algorithm %d not enabled: switching to none",
+            channeld->default_compression_algorithm);
+    channeld->default_compression_algorithm = GRPC_COMPRESS_NONE;
+  }
+
+  uint32_t supported_compression_algorithms =
+      (((1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1) &
+       channeld->enabled_algorithms_bitset) |
+      1u;
+
+  channeld->supported_message_compression_algorithms =
+      grpc_compression_bitset_to_message_bitset(
+          supported_compression_algorithms);
+
+  channeld->supported_stream_compression_algorithms =
+      grpc_compression_bitset_to_stream_bitset(
+          supported_compression_algorithms);
+
+  GPR_ASSERT(!args->is_last);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for channel data */
+static void destroy_channel_elem(grpc_channel_element* elem) {}
+
+const grpc_channel_filter grpc_message_compress_filter = {
+    compress_start_transport_stream_op_batch,
+    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_channel_next_get_info,
+    "message_compress"};
diff --git a/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.h b/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.h
new file mode 100644
index 0000000..e163e3c
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/compression_types.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+/** Compression filter for outgoing data.
+ *
+ * See <grpc/compression.h> for the available compression settings.
+ *
+ * Compression settings may come from:
+ *  - Channel configuration, as established at channel creation time.
+ *  - The metadata accompanying the outgoing data to be compressed. This is
+ *    taken as a request only. We may choose not to honor it. The metadata key
+ *    is given by \a GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY.
+ *
+ * Compression can be disabled for concrete messages (for instance in order to
+ * prevent CRIME/BEAST type attacks) by having the GRPC_WRITE_NO_COMPRESS set in
+ * the BEGIN_MESSAGE flags.
+ *
+ * The attempted compression mechanism is added to the resulting initial
+ * metadata under the'grpc-encoding' key.
+ *
+ * If compression is actually performed, BEGIN_MESSAGE's flag is modified to
+ * incorporate GRPC_WRITE_INTERNAL_COMPRESS. Otherwise, and regardless of the
+ * aforementioned 'grpc-encoding' metadata value, data will pass through
+ * uncompressed. */
+
+extern const grpc_channel_filter grpc_message_compress_filter;
+
+#endif /* GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.cc b/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.cc
new file mode 100644
index 0000000..ce1be83
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.cc
@@ -0,0 +1,495 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/http/server/http_server_filter.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <string.h>
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/b64.h"
+#include "src/core/lib/slice/percent_encoding.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#define EXPECTED_CONTENT_TYPE "application/grpc"
+#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
+
+static void hs_recv_initial_metadata_ready(void* user_data, grpc_error* err);
+static void hs_recv_trailing_metadata_ready(void* user_data, grpc_error* err);
+static void hs_recv_message_ready(void* user_data, grpc_error* err);
+
+namespace {
+
+struct call_data {
+  call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+      : call_combiner(args.call_combiner) {
+    GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
+                      hs_recv_initial_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&recv_message_ready, hs_recv_message_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+                      hs_recv_trailing_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+  }
+
+  ~call_data() {
+    GRPC_ERROR_UNREF(recv_initial_metadata_ready_error);
+    if (have_read_stream) {
+      read_stream->Orphan();
+    }
+  }
+
+  grpc_call_combiner* call_combiner;
+
+  // Outgoing headers to add to send_initial_metadata.
+  grpc_linked_mdelem status;
+  grpc_linked_mdelem content_type;
+
+  // If we see the recv_message contents in the GET query string, we
+  // store it here.
+  grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> read_stream;
+  bool have_read_stream = false;
+
+  // State for intercepting recv_initial_metadata.
+  grpc_closure recv_initial_metadata_ready;
+  grpc_error* recv_initial_metadata_ready_error = GRPC_ERROR_NONE;
+  grpc_closure* original_recv_initial_metadata_ready;
+  grpc_metadata_batch* recv_initial_metadata = nullptr;
+  uint32_t* recv_initial_metadata_flags;
+  bool seen_recv_initial_metadata_ready = false;
+
+  // State for intercepting recv_message.
+  grpc_closure* original_recv_message_ready;
+  grpc_closure recv_message_ready;
+  grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message;
+  bool seen_recv_message_ready = false;
+
+  // State for intercepting recv_trailing_metadata
+  grpc_closure recv_trailing_metadata_ready;
+  grpc_closure* original_recv_trailing_metadata_ready;
+  grpc_error* recv_trailing_metadata_ready_error;
+  bool seen_recv_trailing_metadata_ready = false;
+};
+
+struct channel_data {
+  bool surface_user_agent;
+};
+
+}  // namespace
+
+static grpc_error* hs_filter_outgoing_metadata(grpc_call_element* elem,
+                                               grpc_metadata_batch* b) {
+  if (b->idx.named.grpc_message != nullptr) {
+    grpc_slice pct_encoded_msg = grpc_percent_encode_slice(
+        GRPC_MDVALUE(b->idx.named.grpc_message->md),
+        grpc_compatible_percent_encoding_unreserved_bytes);
+    if (grpc_slice_is_equivalent(pct_encoded_msg,
+                                 GRPC_MDVALUE(b->idx.named.grpc_message->md))) {
+      grpc_slice_unref_internal(pct_encoded_msg);
+    } else {
+      grpc_metadata_batch_set_value(b->idx.named.grpc_message, pct_encoded_msg);
+    }
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static void hs_add_error(const char* error_name, grpc_error** cumulative,
+                         grpc_error* new_err) {
+  if (new_err == GRPC_ERROR_NONE) return;
+  if (*cumulative == GRPC_ERROR_NONE) {
+    *cumulative = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_name);
+  }
+  *cumulative = grpc_error_add_child(*cumulative, new_err);
+}
+
+static grpc_error* hs_filter_incoming_metadata(grpc_call_element* elem,
+                                               grpc_metadata_batch* b) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_error* error = GRPC_ERROR_NONE;
+  static const char* error_name = "Failed processing incoming headers";
+
+  if (b->idx.named.method != nullptr) {
+    if (grpc_mdelem_eq(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) {
+      *calld->recv_initial_metadata_flags &=
+          ~(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST |
+            GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST);
+    } else if (grpc_mdelem_eq(b->idx.named.method->md,
+                              GRPC_MDELEM_METHOD_PUT)) {
+      *calld->recv_initial_metadata_flags &=
+          ~GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
+      *calld->recv_initial_metadata_flags |=
+          GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
+    } else if (grpc_mdelem_eq(b->idx.named.method->md,
+                              GRPC_MDELEM_METHOD_GET)) {
+      *calld->recv_initial_metadata_flags |=
+          GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
+      *calld->recv_initial_metadata_flags &=
+          ~GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
+    } else {
+      hs_add_error(error_name, &error,
+                   grpc_attach_md_to_error(
+                       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
+                       b->idx.named.method->md));
+    }
+    grpc_metadata_batch_remove(b, b->idx.named.method);
+  } else {
+    hs_add_error(
+        error_name, &error,
+        grpc_error_set_str(
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
+            GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":method")));
+  }
+
+  if (b->idx.named.te != nullptr) {
+    if (!grpc_mdelem_eq(b->idx.named.te->md, GRPC_MDELEM_TE_TRAILERS)) {
+      hs_add_error(error_name, &error,
+                   grpc_attach_md_to_error(
+                       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
+                       b->idx.named.te->md));
+    }
+    grpc_metadata_batch_remove(b, b->idx.named.te);
+  } else {
+    hs_add_error(error_name, &error,
+                 grpc_error_set_str(
+                     GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
+                     GRPC_ERROR_STR_KEY, grpc_slice_from_static_string("te")));
+  }
+
+  if (b->idx.named.scheme != nullptr) {
+    if (!grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTP) &&
+        !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTPS) &&
+        !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_GRPC)) {
+      hs_add_error(error_name, &error,
+                   grpc_attach_md_to_error(
+                       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"),
+                       b->idx.named.scheme->md));
+    }
+    grpc_metadata_batch_remove(b, b->idx.named.scheme);
+  } else {
+    hs_add_error(
+        error_name, &error,
+        grpc_error_set_str(
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
+            GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":scheme")));
+  }
+
+  if (b->idx.named.content_type != nullptr) {
+    if (!grpc_mdelem_eq(b->idx.named.content_type->md,
+                        GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) {
+      if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md),
+                                  EXPECTED_CONTENT_TYPE,
+                                  EXPECTED_CONTENT_TYPE_LENGTH) &&
+          (GRPC_SLICE_START_PTR(GRPC_MDVALUE(
+               b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
+               '+' ||
+           GRPC_SLICE_START_PTR(GRPC_MDVALUE(
+               b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] ==
+               ';')) {
+        /* Although the C implementation doesn't (currently) generate them,
+           any custom +-suffix is explicitly valid. */
+        /* TODO(klempner): We should consider preallocating common values such
+           as +proto or +json, or at least stashing them if we see them. */
+        /* TODO(klempner): Should we be surfacing this to application code? */
+      } else {
+        /* TODO(klempner): We're currently allowing this, but we shouldn't
+           see it without a proxy so log for now. */
+        char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md),
+                                    GPR_DUMP_ASCII);
+        gpr_log(GPR_INFO, "Unexpected content-type '%s'", val);
+        gpr_free(val);
+      }
+    }
+    grpc_metadata_batch_remove(b, b->idx.named.content_type);
+  }
+
+  if (b->idx.named.path == nullptr) {
+    hs_add_error(
+        error_name, &error,
+        grpc_error_set_str(
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
+            GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":path")));
+  } else if (*calld->recv_initial_metadata_flags &
+             GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) {
+    /* We have a cacheable request made with GET verb. The path contains the
+     * query parameter which is base64 encoded request payload. */
+    const char k_query_separator = '?';
+    grpc_slice path_slice = GRPC_MDVALUE(b->idx.named.path->md);
+    uint8_t* path_ptr = GRPC_SLICE_START_PTR(path_slice);
+    size_t path_length = GRPC_SLICE_LENGTH(path_slice);
+    /* offset of the character '?' */
+    size_t offset = 0;
+    for (offset = 0; offset < path_length && *path_ptr != k_query_separator;
+         path_ptr++, offset++)
+      ;
+    if (offset < path_length) {
+      grpc_slice query_slice =
+          grpc_slice_sub(path_slice, offset + 1, path_length);
+
+      /* substitute path metadata with just the path (not query) */
+      grpc_mdelem mdelem_path_without_query = grpc_mdelem_from_slices(
+          GRPC_MDSTR_PATH, grpc_slice_sub(path_slice, 0, offset));
+
+      grpc_metadata_batch_substitute(b, b->idx.named.path,
+                                     mdelem_path_without_query);
+
+      /* decode payload from query and add to the slice buffer to be returned */
+      const int k_url_safe = 1;
+      grpc_slice_buffer read_slice_buffer;
+      grpc_slice_buffer_init(&read_slice_buffer);
+      grpc_slice_buffer_add(
+          &read_slice_buffer,
+          grpc_base64_decode_with_len(
+              reinterpret_cast<const char*> GRPC_SLICE_START_PTR(query_slice),
+              GRPC_SLICE_LENGTH(query_slice), k_url_safe));
+      calld->read_stream.Init(&read_slice_buffer, 0);
+      grpc_slice_buffer_destroy_internal(&read_slice_buffer);
+      calld->have_read_stream = true;
+      grpc_slice_unref_internal(query_slice);
+    } else {
+      gpr_log(GPR_ERROR, "GET request without QUERY");
+    }
+  }
+
+  if (b->idx.named.host != nullptr && b->idx.named.authority == nullptr) {
+    grpc_linked_mdelem* el = b->idx.named.host;
+    grpc_mdelem md = GRPC_MDELEM_REF(el->md);
+    grpc_metadata_batch_remove(b, el);
+    hs_add_error(error_name, &error,
+                 grpc_metadata_batch_add_head(
+                     b, el,
+                     grpc_mdelem_from_slices(
+                         GRPC_MDSTR_AUTHORITY,
+                         grpc_slice_ref_internal(GRPC_MDVALUE(md)))));
+    GRPC_MDELEM_UNREF(md);
+  }
+
+  if (b->idx.named.authority == nullptr) {
+    hs_add_error(
+        error_name, &error,
+        grpc_error_set_str(
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
+            GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":authority")));
+  }
+
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  if (!chand->surface_user_agent && b->idx.named.user_agent != nullptr) {
+    grpc_metadata_batch_remove(b, b->idx.named.user_agent);
+  }
+
+  return error;
+}
+
+static void hs_recv_initial_metadata_ready(void* user_data, grpc_error* err) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->seen_recv_initial_metadata_ready = true;
+  if (err == GRPC_ERROR_NONE) {
+    err = hs_filter_incoming_metadata(elem, calld->recv_initial_metadata);
+    calld->recv_initial_metadata_ready_error = GRPC_ERROR_REF(err);
+    if (calld->seen_recv_message_ready) {
+      // We've already seen the recv_message callback, but we previously
+      // deferred it, so we need to return it here.
+      // Replace the recv_message byte stream if needed.
+      if (calld->have_read_stream) {
+        calld->recv_message->reset(calld->read_stream.get());
+        calld->have_read_stream = false;
+      }
+      // Re-enter call combiner for original_recv_message_ready, since the
+      // surface code will release the call combiner for each callback it
+      // receives.
+      GRPC_CALL_COMBINER_START(
+          calld->call_combiner, calld->original_recv_message_ready,
+          GRPC_ERROR_REF(err),
+          "resuming recv_message_ready from recv_initial_metadata_ready");
+    }
+  } else {
+    GRPC_ERROR_REF(err);
+  }
+  if (calld->seen_recv_trailing_metadata_ready) {
+    GRPC_CALL_COMBINER_START(calld->call_combiner,
+                             &calld->recv_trailing_metadata_ready,
+                             calld->recv_trailing_metadata_ready_error,
+                             "resuming hs_recv_trailing_metadata_ready from "
+                             "hs_recv_initial_metadata_ready");
+  }
+  GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, err);
+}
+
+static void hs_recv_message_ready(void* user_data, grpc_error* err) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->seen_recv_message_ready = true;
+  if (calld->seen_recv_initial_metadata_ready) {
+    // We've already seen the recv_initial_metadata callback, so
+    // replace the recv_message byte stream if needed and invoke the
+    // original recv_message callback immediately.
+    if (calld->have_read_stream) {
+      calld->recv_message->reset(calld->read_stream.get());
+      calld->have_read_stream = false;
+    }
+    GRPC_CLOSURE_RUN(calld->original_recv_message_ready, GRPC_ERROR_REF(err));
+  } else {
+    // We have not yet seen the recv_initial_metadata callback, so we
+    // need to wait to see if this is a GET request.
+    // Note that we release the call combiner here, so that other
+    // callbacks can run.
+    GRPC_CALL_COMBINER_STOP(
+        calld->call_combiner,
+        "pausing recv_message_ready until recv_initial_metadata_ready");
+  }
+}
+
+static void hs_recv_trailing_metadata_ready(void* user_data, grpc_error* err) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (!calld->seen_recv_initial_metadata_ready) {
+    calld->recv_trailing_metadata_ready_error = GRPC_ERROR_REF(err);
+    calld->seen_recv_trailing_metadata_ready = true;
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "deferring hs_recv_trailing_metadata_ready until "
+                            "ater hs_recv_initial_metadata_ready");
+    return;
+  }
+  err = grpc_error_add_child(
+      GRPC_ERROR_REF(err),
+      GRPC_ERROR_REF(calld->recv_initial_metadata_ready_error));
+  GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, err);
+}
+
+static grpc_error* hs_mutate_op(grpc_call_element* elem,
+                                grpc_transport_stream_op_batch* op) {
+  /* grab pointers to our data from the call element */
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+
+  if (op->send_initial_metadata) {
+    grpc_error* error = GRPC_ERROR_NONE;
+    static const char* error_name = "Failed sending initial metadata";
+    hs_add_error(error_name, &error,
+                 grpc_metadata_batch_add_head(
+                     op->payload->send_initial_metadata.send_initial_metadata,
+                     &calld->status, GRPC_MDELEM_STATUS_200));
+    hs_add_error(error_name, &error,
+                 grpc_metadata_batch_add_tail(
+                     op->payload->send_initial_metadata.send_initial_metadata,
+                     &calld->content_type,
+                     GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC));
+    hs_add_error(
+        error_name, &error,
+        hs_filter_outgoing_metadata(
+            elem, op->payload->send_initial_metadata.send_initial_metadata));
+    if (error != GRPC_ERROR_NONE) return error;
+  }
+
+  if (op->recv_initial_metadata) {
+    /* substitute our callback for the higher callback */
+    GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags != nullptr);
+    calld->recv_initial_metadata =
+        op->payload->recv_initial_metadata.recv_initial_metadata;
+    calld->recv_initial_metadata_flags =
+        op->payload->recv_initial_metadata.recv_flags;
+    calld->original_recv_initial_metadata_ready =
+        op->payload->recv_initial_metadata.recv_initial_metadata_ready;
+    op->payload->recv_initial_metadata.recv_initial_metadata_ready =
+        &calld->recv_initial_metadata_ready;
+  }
+
+  if (op->recv_message) {
+    calld->recv_message = op->payload->recv_message.recv_message;
+    calld->original_recv_message_ready =
+        op->payload->recv_message.recv_message_ready;
+    op->payload->recv_message.recv_message_ready = &calld->recv_message_ready;
+  }
+
+  if (op->recv_trailing_metadata) {
+    calld->original_recv_trailing_metadata_ready =
+        op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &calld->recv_trailing_metadata_ready;
+  }
+
+  if (op->send_trailing_metadata) {
+    grpc_error* error = hs_filter_outgoing_metadata(
+        elem, op->payload->send_trailing_metadata.send_trailing_metadata);
+    if (error != GRPC_ERROR_NONE) return error;
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+static void hs_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
+  GPR_TIMER_SCOPE("hs_start_transport_stream_op_batch", 0);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_error* error = hs_mutate_op(elem, op);
+  if (error != GRPC_ERROR_NONE) {
+    grpc_transport_stream_op_batch_finish_with_failure(op, error,
+                                                       calld->call_combiner);
+  } else {
+    grpc_call_next_op(elem, op);
+  }
+}
+
+/* Constructor for call_data */
+static grpc_error* hs_init_call_elem(grpc_call_element* elem,
+                                     const grpc_call_element_args* args) {
+  new (elem->call_data) call_data(elem, *args);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for call_data */
+static void hs_destroy_call_elem(grpc_call_element* elem,
+                                 const grpc_call_final_info* final_info,
+                                 grpc_closure* ignored) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->~call_data();
+}
+
+/* Constructor for channel_data */
+static grpc_error* hs_init_channel_elem(grpc_channel_element* elem,
+                                        grpc_channel_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  GPR_ASSERT(!args->is_last);
+  chand->surface_user_agent = grpc_channel_arg_get_bool(
+      grpc_channel_args_find(args->channel_args,
+                             const_cast<char*>(GRPC_ARG_SURFACE_USER_AGENT)),
+      true);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for channel data */
+static void hs_destroy_channel_elem(grpc_channel_element* elem) {}
+
+const grpc_channel_filter grpc_http_server_filter = {
+    hs_start_transport_stream_op_batch,
+    grpc_channel_next_op,
+    sizeof(call_data),
+    hs_init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    hs_destroy_call_elem,
+    sizeof(channel_data),
+    hs_init_channel_elem,
+    hs_destroy_channel_elem,
+    grpc_channel_next_get_info,
+    "http-server"};
diff --git a/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.h b/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.h
new file mode 100644
index 0000000..e4e0546
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+/* Processes metadata on the server side for HTTP2 transports */
+extern const grpc_channel_filter grpc_http_server_filter;
+
+#endif /* GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H */
diff --git a/third_party/grpc/src/core/ext/filters/load_reporting/registered_opencensus_objects.h b/third_party/grpc/src/core/ext/filters/load_reporting/registered_opencensus_objects.h
new file mode 100644
index 0000000..4eacda7
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/load_reporting/registered_opencensus_objects.h
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_REGISTERED_OPENCENSUS_OBJECTS_H
+#define GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_REGISTERED_OPENCENSUS_OBJECTS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "opencensus/stats/stats.h"
+
+#include "src/cpp/server/load_reporter/constants.h"
+
+namespace grpc {
+namespace load_reporter {
+
+// Note that the functions here are specified as inline to share the static
+// objects across all the translation units including this header. See more
+// details on https://en.cppreference.com/w/cpp/language/inline.
+
+// Measures.
+
+inline ::opencensus::stats::MeasureInt64 MeasureStartCount() {
+  static const ::opencensus::stats::MeasureInt64 measure =
+      ::opencensus::stats::MeasureInt64::Register(
+          kMeasureStartCount, kMeasureStartCount, kMeasureStartCount);
+  return measure;
+}
+
+inline ::opencensus::stats::MeasureInt64 MeasureEndCount() {
+  static const ::opencensus::stats::MeasureInt64 measure =
+      ::opencensus::stats::MeasureInt64::Register(
+          kMeasureEndCount, kMeasureEndCount, kMeasureEndCount);
+  return measure;
+}
+
+inline ::opencensus::stats::MeasureInt64 MeasureEndBytesSent() {
+  static const ::opencensus::stats::MeasureInt64 measure =
+      ::opencensus::stats::MeasureInt64::Register(
+          kMeasureEndBytesSent, kMeasureEndBytesSent, kMeasureEndBytesSent);
+  return measure;
+}
+
+inline ::opencensus::stats::MeasureInt64 MeasureEndBytesReceived() {
+  static const ::opencensus::stats::MeasureInt64 measure =
+      ::opencensus::stats::MeasureInt64::Register(kMeasureEndBytesReceived,
+                                                  kMeasureEndBytesReceived,
+                                                  kMeasureEndBytesReceived);
+  return measure;
+}
+
+inline ::opencensus::stats::MeasureInt64 MeasureEndLatencyMs() {
+  static const ::opencensus::stats::MeasureInt64 measure =
+      ::opencensus::stats::MeasureInt64::Register(
+          kMeasureEndLatencyMs, kMeasureEndLatencyMs, kMeasureEndLatencyMs);
+  return measure;
+}
+
+inline ::opencensus::stats::MeasureDouble MeasureOtherCallMetric() {
+  static const ::opencensus::stats::MeasureDouble measure =
+      ::opencensus::stats::MeasureDouble::Register(kMeasureOtherCallMetric,
+                                                   kMeasureOtherCallMetric,
+                                                   kMeasureOtherCallMetric);
+  return measure;
+}
+
+// Tags.
+
+inline ::opencensus::stats::TagKey TagKeyToken() {
+  static const ::opencensus::stats::TagKey token =
+      opencensus::stats::TagKey::Register(kTagKeyToken);
+  return token;
+}
+
+inline ::opencensus::stats::TagKey TagKeyHost() {
+  static const ::opencensus::stats::TagKey token =
+      opencensus::stats::TagKey::Register(kTagKeyHost);
+  return token;
+}
+
+inline ::opencensus::stats::TagKey TagKeyUserId() {
+  static const ::opencensus::stats::TagKey token =
+      opencensus::stats::TagKey::Register(kTagKeyUserId);
+  return token;
+}
+
+inline ::opencensus::stats::TagKey TagKeyStatus() {
+  static const ::opencensus::stats::TagKey token =
+      opencensus::stats::TagKey::Register(kTagKeyStatus);
+  return token;
+}
+
+inline ::opencensus::stats::TagKey TagKeyMetricName() {
+  static const ::opencensus::stats::TagKey token =
+      opencensus::stats::TagKey::Register(kTagKeyMetricName);
+  return token;
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif /* GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_REGISTERED_OPENCENSUS_OBJECTS_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc b/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
new file mode 100644
index 0000000..6a7231f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc
@@ -0,0 +1,363 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/parse_address.h"
+#include "src/core/ext/filters/load_reporting/registered_opencensus_objects.h"
+#include "src/core/ext/filters/load_reporting/server_load_reporting_filter.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/context.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr_posix.h"
+#include "src/core/lib/iomgr/socket_utils.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+namespace grpc {
+
+constexpr char kEncodedIpv4AddressLengthString[] = "08";
+constexpr char kEncodedIpv6AddressLengthString[] = "32";
+constexpr char kEmptyAddressLengthString[] = "00";
+constexpr size_t kLengthPrefixSize = 2;
+
+grpc_error* ServerLoadReportingChannelData::Init(
+    grpc_channel_element* /* elem */, grpc_channel_element_args* args) {
+  GPR_ASSERT(!args->is_last);
+  // Find and record the peer_identity.
+  const grpc_auth_context* auth_context =
+      grpc_find_auth_context_in_args(args->channel_args);
+  if (auth_context != nullptr &&
+      grpc_auth_context_peer_is_authenticated(auth_context)) {
+    grpc_auth_property_iterator auth_it =
+        grpc_auth_context_peer_identity(auth_context);
+    const grpc_auth_property* auth_property =
+        grpc_auth_property_iterator_next(&auth_it);
+    if (auth_property != nullptr) {
+      peer_identity_ = auth_property->value;
+      peer_identity_len_ = auth_property->value_length;
+    }
+  }
+  return GRPC_ERROR_NONE;
+}
+
+void ServerLoadReportingCallData::Destroy(
+    grpc_call_element* elem, const grpc_call_final_info* final_info,
+    grpc_closure* then_call_closure) {
+  ServerLoadReportingChannelData* chand =
+      reinterpret_cast<ServerLoadReportingChannelData*>(elem->channel_data);
+  // Only record an end if we've recorded its corresponding start, which is
+  // indicated by a non-null client_ip_and_lr_token_. Note that it's possible
+  // that we attempt to record the call end before we have recorded the call
+  // start, because the data needed for recording the start comes from the
+  // initial metadata, which may not be ready before the call finishes.
+  if (client_ip_and_lr_token_ != nullptr) {
+    opencensus::stats::Record(
+        {{::grpc::load_reporter::MeasureEndCount(), 1},
+         {::grpc::load_reporter::MeasureEndBytesSent(),
+          final_info->stats.transport_stream_stats.outgoing.data_bytes},
+         {::grpc::load_reporter::MeasureEndBytesReceived(),
+          final_info->stats.transport_stream_stats.incoming.data_bytes},
+         {::grpc::load_reporter::MeasureEndLatencyMs(),
+          gpr_time_to_millis(final_info->stats.latency)}},
+        {{::grpc::load_reporter::TagKeyToken(),
+          {client_ip_and_lr_token_, client_ip_and_lr_token_len_}},
+         {::grpc::load_reporter::TagKeyHost(),
+          {target_host_, target_host_len_}},
+         {::grpc::load_reporter::TagKeyUserId(),
+          {chand->peer_identity(), chand->peer_identity_len()}},
+         {::grpc::load_reporter::TagKeyStatus(),
+          GetStatusTagForStatus(final_info->final_status)}});
+    gpr_free(client_ip_and_lr_token_);
+  }
+  gpr_free(target_host_);
+  grpc_slice_unref_internal(service_method_);
+}
+
+void ServerLoadReportingCallData::StartTransportStreamOpBatch(
+    grpc_call_element* elem, TransportStreamOpBatch* op) {
+  GPR_TIMER_SCOPE("lr_start_transport_stream_op", 0);
+  if (op->recv_initial_metadata() != nullptr) {
+    // Save some fields to use when initial metadata is ready.
+    peer_string_ = op->get_peer_string();
+    recv_initial_metadata_ =
+        op->op()->payload->recv_initial_metadata.recv_initial_metadata;
+    original_recv_initial_metadata_ready_ = op->recv_initial_metadata_ready();
+    // Substitute the original closure for the wrapper closure.
+    op->set_recv_initial_metadata_ready(&recv_initial_metadata_ready_);
+  } else if (op->send_trailing_metadata() != nullptr) {
+    GRPC_LOG_IF_ERROR(
+        "server_load_reporting_filter",
+        grpc_metadata_batch_filter(op->send_trailing_metadata()->batch(),
+                                   SendTrailingMetadataFilter, elem,
+                                   "send_trailing_metadata filtering error"));
+  }
+  grpc_call_next_op(elem, op->op());
+}
+
+void ServerLoadReportingCallData::GetCensusSafeClientIpString(
+    char** client_ip_string, size_t* size) {
+  // Find the client URI string.
+  const char* client_uri_str =
+      reinterpret_cast<const char*>(gpr_atm_acq_load(peer_string_));
+  if (client_uri_str == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Unable to extract client URI string (peer string) from gRPC "
+            "metadata.");
+    *client_ip_string = nullptr;
+    *size = 0;
+    return;
+  }
+  // Parse the client URI string into grpc_uri.
+  grpc_uri* client_uri = grpc_uri_parse(client_uri_str, true);
+  if (client_uri == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Unable to parse the client URI string (peer string) to a client "
+            "URI.");
+    *client_ip_string = nullptr;
+    *size = 0;
+    return;
+  }
+  // Parse the client URI into grpc_resolved_address.
+  grpc_resolved_address resolved_address;
+  bool success = grpc_parse_uri(client_uri, &resolved_address);
+  grpc_uri_destroy(client_uri);
+  if (!success) {
+    gpr_log(GPR_ERROR,
+            "Unable to parse client URI into a grpc_resolved_address.");
+    *client_ip_string = nullptr;
+    *size = 0;
+    return;
+  }
+  // Convert the socket address in the grpc_resolved_address into a hex string
+  // according to the address family.
+  grpc_sockaddr* addr = reinterpret_cast<grpc_sockaddr*>(resolved_address.addr);
+  if (addr->sa_family == GRPC_AF_INET) {
+    grpc_sockaddr_in* addr4 = reinterpret_cast<grpc_sockaddr_in*>(addr);
+    gpr_asprintf(client_ip_string, "%08x", grpc_ntohl(addr4->sin_addr.s_addr));
+    *size = 8;
+  } else if (addr->sa_family == GRPC_AF_INET6) {
+    grpc_sockaddr_in6* addr6 = reinterpret_cast<grpc_sockaddr_in6*>(addr);
+    *client_ip_string = static_cast<char*>(gpr_malloc(32 + 1));
+    uint32_t* addr6_next_long = reinterpret_cast<uint32_t*>(&addr6->sin6_addr);
+    for (size_t i = 0; i < 4; ++i) {
+      snprintf(*client_ip_string + 8 * i, 8 + 1, "%08x",
+               grpc_ntohl(*addr6_next_long++));
+    }
+    *size = 32;
+  } else {
+    GPR_UNREACHABLE_CODE();
+  }
+}
+
+void ServerLoadReportingCallData::StoreClientIpAndLrToken(const char* lr_token,
+                                                          size_t lr_token_len) {
+  char* client_ip;
+  size_t client_ip_len;
+  GetCensusSafeClientIpString(&client_ip, &client_ip_len);
+  client_ip_and_lr_token_len_ =
+      kLengthPrefixSize + client_ip_len + lr_token_len;
+  client_ip_and_lr_token_ = static_cast<char*>(
+      gpr_zalloc(client_ip_and_lr_token_len_ * sizeof(char)));
+  char* cur_pos = client_ip_and_lr_token_;
+  // Store the IP length prefix.
+  if (client_ip_len == 0) {
+    strncpy(cur_pos, kEmptyAddressLengthString, kLengthPrefixSize);
+  } else if (client_ip_len == 8) {
+    strncpy(cur_pos, kEncodedIpv4AddressLengthString, kLengthPrefixSize);
+  } else if (client_ip_len == 32) {
+    strncpy(cur_pos, kEncodedIpv6AddressLengthString, kLengthPrefixSize);
+  } else {
+    GPR_UNREACHABLE_CODE();
+  }
+  cur_pos += kLengthPrefixSize;
+  // Store the IP.
+  if (client_ip_len != 0) {
+    strncpy(cur_pos, client_ip, client_ip_len);
+  }
+  gpr_free(client_ip);
+  cur_pos += client_ip_len;
+  // Store the LR token.
+  if (lr_token_len != 0) {
+    strncpy(cur_pos, lr_token, lr_token_len);
+  }
+  GPR_ASSERT(cur_pos + lr_token_len - client_ip_and_lr_token_ ==
+             client_ip_and_lr_token_len_);
+}
+
+grpc_filtered_mdelem ServerLoadReportingCallData::RecvInitialMetadataFilter(
+    void* user_data, grpc_mdelem md) {
+  grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data);
+  ServerLoadReportingCallData* calld =
+      reinterpret_cast<ServerLoadReportingCallData*>(elem->call_data);
+  if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_PATH)) {
+    calld->service_method_ = grpc_slice_ref_internal(GRPC_MDVALUE(md));
+  } else if (calld->target_host_ == nullptr &&
+             grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_AUTHORITY)) {
+    grpc_slice target_host_slice = GRPC_MDVALUE(md);
+    calld->target_host_len_ = GRPC_SLICE_LENGTH(target_host_slice);
+    calld->target_host_ =
+        reinterpret_cast<char*>(gpr_zalloc(calld->target_host_len_));
+    for (size_t i = 0; i < calld->target_host_len_; ++i) {
+      calld->target_host_[i] = static_cast<char>(
+          tolower(GRPC_SLICE_START_PTR(target_host_slice)[i]));
+    }
+  } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_LB_TOKEN)) {
+    if (calld->client_ip_and_lr_token_ == nullptr) {
+      calld->StoreClientIpAndLrToken(
+          reinterpret_cast<const char*> GRPC_SLICE_START_PTR(GRPC_MDVALUE(md)),
+          GRPC_SLICE_LENGTH(GRPC_MDVALUE(md)));
+    }
+    return GRPC_FILTERED_REMOVE();
+  }
+  return GRPC_FILTERED_MDELEM(md);
+}
+
+void ServerLoadReportingCallData::RecvInitialMetadataReady(void* arg,
+                                                           grpc_error* err) {
+  grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(arg);
+  ServerLoadReportingCallData* calld =
+      reinterpret_cast<ServerLoadReportingCallData*>(elem->call_data);
+  ServerLoadReportingChannelData* chand =
+      reinterpret_cast<ServerLoadReportingChannelData*>(elem->channel_data);
+  if (err == GRPC_ERROR_NONE) {
+    GRPC_LOG_IF_ERROR(
+        "server_load_reporting_filter",
+        grpc_metadata_batch_filter(calld->recv_initial_metadata_,
+                                   RecvInitialMetadataFilter, elem,
+                                   "recv_initial_metadata filtering error"));
+    // If the LB token was not found in the recv_initial_metadata, only the
+    // client IP part will be recorded (with an empty LB token).
+    if (calld->client_ip_and_lr_token_ == nullptr) {
+      calld->StoreClientIpAndLrToken(nullptr, 0);
+    }
+    opencensus::stats::Record(
+        {{::grpc::load_reporter::MeasureStartCount(), 1}},
+        {{::grpc::load_reporter::TagKeyToken(),
+          {calld->client_ip_and_lr_token_, calld->client_ip_and_lr_token_len_}},
+         {::grpc::load_reporter::TagKeyHost(),
+          {calld->target_host_, calld->target_host_len_}},
+         {::grpc::load_reporter::TagKeyUserId(),
+          {chand->peer_identity(), chand->peer_identity_len()}}});
+  }
+  GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready_,
+                   GRPC_ERROR_REF(err));
+}
+
+grpc_error* ServerLoadReportingCallData::Init(
+    grpc_call_element* elem, const grpc_call_element_args* args) {
+  service_method_ = grpc_empty_slice();
+  GRPC_CLOSURE_INIT(&recv_initial_metadata_ready_, RecvInitialMetadataReady,
+                    elem, grpc_schedule_on_exec_ctx);
+  return GRPC_ERROR_NONE;
+}
+
+grpc_filtered_mdelem ServerLoadReportingCallData::SendTrailingMetadataFilter(
+    void* user_data, grpc_mdelem md) {
+  grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data);
+  ServerLoadReportingCallData* calld =
+      reinterpret_cast<ServerLoadReportingCallData*>(elem->call_data);
+  ServerLoadReportingChannelData* chand =
+      reinterpret_cast<ServerLoadReportingChannelData*>(elem->channel_data);
+  if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_LB_COST_BIN)) {
+    const grpc_slice value = GRPC_MDVALUE(md);
+    const size_t cost_entry_size = GRPC_SLICE_LENGTH(value);
+    if (cost_entry_size < sizeof(double)) {
+      gpr_log(GPR_ERROR,
+              "Cost metadata value too small (%zu bytes) to hold valid data. "
+              "Ignoring.",
+              cost_entry_size);
+      return GRPC_FILTERED_REMOVE();
+    }
+    const double* cost_entry_ptr =
+        reinterpret_cast<const double*>(GRPC_SLICE_START_PTR(value));
+    double cost_value = *cost_entry_ptr++;
+    const char* cost_name = reinterpret_cast<const char*>(cost_entry_ptr);
+    const size_t cost_name_len = cost_entry_size - sizeof(double);
+    opencensus::stats::Record(
+        {{::grpc::load_reporter::MeasureOtherCallMetric(), cost_value}},
+        {{::grpc::load_reporter::TagKeyToken(),
+          {calld->client_ip_and_lr_token_, calld->client_ip_and_lr_token_len_}},
+         {::grpc::load_reporter::TagKeyHost(),
+          {calld->target_host_, calld->target_host_len_}},
+         {::grpc::load_reporter::TagKeyUserId(),
+          {chand->peer_identity(), chand->peer_identity_len()}},
+         {::grpc::load_reporter::TagKeyMetricName(),
+          {cost_name, cost_name_len}}});
+    return GRPC_FILTERED_REMOVE();
+  }
+  return GRPC_FILTERED_MDELEM(md);
+}
+
+const char* ServerLoadReportingCallData::GetStatusTagForStatus(
+    grpc_status_code status) {
+  switch (status) {
+    case GRPC_STATUS_OK:
+      return ::grpc::load_reporter::kCallStatusOk;
+    case GRPC_STATUS_UNKNOWN:
+    case GRPC_STATUS_DEADLINE_EXCEEDED:
+    case GRPC_STATUS_UNIMPLEMENTED:
+    case GRPC_STATUS_INTERNAL:
+    case GRPC_STATUS_UNAVAILABLE:
+    case GRPC_STATUS_DATA_LOSS:
+      return ::grpc::load_reporter::kCallStatusServerError;
+    default:
+      return ::grpc::load_reporter::kCallStatusClientError;
+  }
+}
+
+namespace {
+bool MaybeAddServerLoadReportingFilter(const grpc_channel_args& args) {
+  return grpc_channel_arg_get_bool(
+      grpc_channel_args_find(&args, GRPC_ARG_ENABLE_LOAD_REPORTING), false);
+}
+}  // namespace
+
+// TODO(juanlishen): We should register the filter during grpc initialization
+// time once OpenCensus is compatible with our build system. For now, we force
+// registration of the server load reporting filter at static initialization
+// time if we build with the filter target.
+struct ServerLoadReportingFilterStaticRegistrar {
+  ServerLoadReportingFilterStaticRegistrar() {
+    static std::atomic_bool registered{false};
+    if (registered) return;
+    RegisterChannelFilter<ServerLoadReportingChannelData,
+                          ServerLoadReportingCallData>(
+        "server_load_reporting", GRPC_SERVER_CHANNEL, INT_MAX,
+        MaybeAddServerLoadReportingFilter);
+    // Access measures to ensure they are initialized. Otherwise, we can't
+    // create any valid view before the first RPC.
+    ::grpc::load_reporter::MeasureStartCount();
+    ::grpc::load_reporter::MeasureEndCount();
+    ::grpc::load_reporter::MeasureEndBytesSent();
+    ::grpc::load_reporter::MeasureEndBytesReceived();
+    ::grpc::load_reporter::MeasureEndLatencyMs();
+    ::grpc::load_reporter::MeasureOtherCallMetric();
+    registered = true;
+  }
+} server_load_reporting_filter_static_registrar;
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.h b/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.h
new file mode 100644
index 0000000..10baf1f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.h
@@ -0,0 +1,120 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/cpp/common/channel_filter.h"
+
+namespace grpc {
+
+class ServerLoadReportingChannelData : public ChannelData {
+ public:
+  grpc_error* Init(grpc_channel_element* elem,
+                   grpc_channel_element_args* args) override;
+
+  // Getters.
+  const char* peer_identity() { return peer_identity_; }
+  size_t peer_identity_len() { return peer_identity_len_; }
+
+ private:
+  // The peer's authenticated identity.
+  char* peer_identity_ = nullptr;
+  size_t peer_identity_len_ = 0;
+};
+
+class ServerLoadReportingCallData : public CallData {
+ public:
+  grpc_error* Init(grpc_call_element* elem,
+                   const grpc_call_element_args* args) override;
+
+  void Destroy(grpc_call_element* elem, const grpc_call_final_info* final_info,
+               grpc_closure* then_call_closure) override;
+
+  void StartTransportStreamOpBatch(grpc_call_element* elem,
+                                   TransportStreamOpBatch* op) override;
+
+ private:
+  // From the peer_string_ in calld, extracts the client IP string (owned by
+  // caller), e.g., "01020a0b". Upon failure, set the output pointer to null and
+  // size to zero.
+  void GetCensusSafeClientIpString(char** client_ip_string, size_t* size);
+
+  // Concatenates the client IP address and the load reporting token, then
+  // stores the result into the call data.
+  void StoreClientIpAndLrToken(const char* lr_token, size_t lr_token_len);
+
+  // This matches the classification of the status codes in
+  // googleapis/google/rpc/code.proto.
+  static const char* GetStatusTagForStatus(grpc_status_code status);
+
+  // Records the call start.
+  static void RecvInitialMetadataReady(void* arg, grpc_error* err);
+
+  // From the initial metadata, extracts the service_method_, target_host_, and
+  // client_ip_and_lr_token_.
+  static grpc_filtered_mdelem RecvInitialMetadataFilter(void* user_data,
+                                                        grpc_mdelem md);
+
+  // Records the other call metrics.
+  static grpc_filtered_mdelem SendTrailingMetadataFilter(void* user_data,
+                                                         grpc_mdelem md);
+
+  // The peer string (a member of the recv_initial_metadata op). Note that
+  // gpr_atm itself is a pointer type here, making "peer_string_" effectively a
+  // double pointer.
+  const gpr_atm* peer_string_;
+
+  // The received initial metadata (a member of the recv_initial_metadata op).
+  // When it is ready, we will extract some data from it via
+  // recv_initial_metadata_ready_ closure, before the original
+  // recv_initial_metadata_ready closure.
+  grpc_metadata_batch* recv_initial_metadata_;
+
+  // The original recv_initial_metadata closure, which is wrapped by our own
+  // closure (recv_initial_metadata_ready_) to capture the incoming initial
+  // metadata.
+  grpc_closure* original_recv_initial_metadata_ready_;
+
+  // The closure that wraps the original closure. Scheduled when
+  // recv_initial_metadata_ is ready.
+  grpc_closure recv_initial_metadata_ready_;
+
+  // Corresponds to the :path header.
+  grpc_slice service_method_;
+
+  // The backend host that the client thinks it's talking to. This may be
+  // different from the actual backend in the case of, for example,
+  // load-balanced targets. We store a copy of the metadata slice in order to
+  // lowercase it. */
+  char* target_host_;
+  size_t target_host_len_;
+
+  // The client IP address (including a length prefix) and the load reporting
+  // token.
+  char* client_ip_and_lr_token_;
+  size_t client_ip_and_lr_token_len_;
+};
+
+}  // namespace grpc
+
+#endif /* GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.cc b/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.cc
new file mode 100644
index 0000000..4314726
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.cc
@@ -0,0 +1,542 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/max_age/max_age_filter.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+#define DEFAULT_MAX_CONNECTION_AGE_MS INT_MAX
+#define DEFAULT_MAX_CONNECTION_AGE_GRACE_MS INT_MAX
+#define DEFAULT_MAX_CONNECTION_IDLE_MS INT_MAX
+#define MAX_CONNECTION_AGE_JITTER 0.1
+
+#define MAX_CONNECTION_AGE_INTEGER_OPTIONS \
+  { DEFAULT_MAX_CONNECTION_AGE_MS, 1, INT_MAX }
+#define MAX_CONNECTION_IDLE_INTEGER_OPTIONS \
+  { DEFAULT_MAX_CONNECTION_IDLE_MS, 1, INT_MAX }
+
+/* States for idle_state in channel_data */
+#define MAX_IDLE_STATE_INIT ((gpr_atm)0)
+#define MAX_IDLE_STATE_SEEN_EXIT_IDLE ((gpr_atm)1)
+#define MAX_IDLE_STATE_SEEN_ENTER_IDLE ((gpr_atm)2)
+#define MAX_IDLE_STATE_TIMER_SET ((gpr_atm)3)
+
+namespace {
+struct channel_data {
+  /* We take a reference to the channel stack for the timer callback */
+  grpc_channel_stack* channel_stack;
+  /* Guards access to max_age_timer, max_age_timer_pending, max_age_grace_timer
+     and max_age_grace_timer_pending */
+  gpr_mu max_age_timer_mu;
+  /* True if the max_age timer callback is currently pending */
+  bool max_age_timer_pending;
+  /* True if the max_age_grace timer callback is currently pending */
+  bool max_age_grace_timer_pending;
+  /* The timer for checking if the channel has reached its max age */
+  grpc_timer max_age_timer;
+  /* The timer for checking if the max-aged channel has uesed up the grace
+     period */
+  grpc_timer max_age_grace_timer;
+  /* The timer for checking if the channel's idle duration reaches
+     max_connection_idle */
+  grpc_timer max_idle_timer;
+  /* Allowed max time a channel may have no outstanding rpcs */
+  grpc_millis max_connection_idle;
+  /* Allowed max time a channel may exist */
+  grpc_millis max_connection_age;
+  /* Allowed grace period after the channel reaches its max age */
+  grpc_millis max_connection_age_grace;
+  /* Closure to run when the channel's idle duration reaches max_connection_idle
+     and should be closed gracefully */
+  grpc_closure max_idle_timer_cb;
+  /* Closure to run when the channel reaches its max age and should be closed
+     gracefully */
+  grpc_closure close_max_age_channel;
+  /* Closure to run the channel uses up its max age grace time and should be
+     closed forcibly */
+  grpc_closure force_close_max_age_channel;
+  /* Closure to run when the init fo channel stack is done and the max_idle
+     timer should be started */
+  grpc_closure start_max_idle_timer_after_init;
+  /* Closure to run when the init fo channel stack is done and the max_age timer
+     should be started */
+  grpc_closure start_max_age_timer_after_init;
+  /* Closure to run when the goaway op is finished and the max_age_timer */
+  grpc_closure start_max_age_grace_timer_after_goaway_op;
+  /* Closure to run when the channel connectivity state changes */
+  grpc_closure channel_connectivity_changed;
+  /* Records the current connectivity state */
+  grpc_connectivity_state connectivity_state;
+  /* Number of active calls */
+  gpr_atm call_count;
+  /* TODO(zyc): C++lize this state machine */
+  /* 'idle_state' holds the states of max_idle_timer and channel idleness.
+      It can contain one of the following values:
+     +--------------------------------+----------------+---------+
+     |           idle_state           | max_idle_timer | channel |
+     +--------------------------------+----------------+---------+
+     | MAX_IDLE_STATE_INIT            | unset          | busy    |
+     | MAX_IDLE_STATE_TIMER_SET       | set, valid     | idle    |
+     | MAX_IDLE_STATE_SEEN_EXIT_IDLE  | set, invalid   | busy    |
+     | MAX_IDLE_STATE_SEEN_ENTER_IDLE | set, invalid   | idle    |
+     +--------------------------------+----------------+---------+
+
+     MAX_IDLE_STATE_INIT: The initial and final state of 'idle_state'. The
+     channel has 1 or 1+ active calls, and the the timer is not set. Note that
+     we may put a virtual call to hold this state at channel initialization or
+     shutdown, so that the channel won't enter other states.
+
+     MAX_IDLE_STATE_TIMER_SET: The state after the timer is set and no calls
+     have arrived after the timer is set. The channel must have 0 active call in
+     this state. If the timer is fired in this state, we will close the channel
+     due to idleness.
+
+     MAX_IDLE_STATE_SEEN_EXIT_IDLE: The state after the timer is set and at
+     least one call has arrived after the timer is set. The channel must have 1
+     or 1+ active calls in this state. If the timer is fired in this state, we
+     won't reschudle it.
+
+     MAX_IDLE_STATE_SEEN_ENTER_IDLE: The state after the timer is set and the at
+     least one call has arrived after the timer is set, BUT the channel
+     currently has 1 or 1+ active calls. If the timer is fired in this state, we
+     will reschudle it.
+
+     max_idle_timer will not be cancelled (unless the channel is shutting down).
+     If the timer callback is called when the max_idle_timer is valid (i.e.
+     idle_state is MAX_IDLE_STATE_TIMER_SET), the channel will be closed due to
+     idleness, otherwise the channel won't be changed.
+
+     State transitions:
+         MAX_IDLE_STATE_INIT <-------3------ MAX_IDLE_STATE_SEEN_EXIT_IDLE
+              ^    |                              ^     ^    |
+              |    |                              |     |    |
+              1    2     +-----------4------------+     6    7
+              |    |     |                              |    |
+              |    v     |                              |    v
+       MAX_IDLE_STATE_TIMER_SET <----5------ MAX_IDLE_STATE_SEEN_ENTER_IDLE
+
+     For 1, 3, 5 :  See max_idle_timer_cb() function
+     For 2, 7    :  See decrease_call_count() function
+     For 4, 6    :  See increase_call_count() function */
+  gpr_atm idle_state;
+  /* Time when the channel finished its last outstanding call, in grpc_millis */
+  gpr_atm last_enter_idle_time_millis;
+};
+}  // namespace
+
+/* Increase the nubmer of active calls. Before the increasement, if there are no
+   calls, the max_idle_timer should be cancelled. */
+static void increase_call_count(channel_data* chand) {
+  /* Exit idle */
+  if (gpr_atm_full_fetch_add(&chand->call_count, 1) == 0) {
+    while (true) {
+      gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state);
+      switch (idle_state) {
+        case MAX_IDLE_STATE_TIMER_SET:
+          /* max_idle_timer_cb may have already set idle_state to
+             MAX_IDLE_STATE_INIT, in this case, we don't need to set it to
+             MAX_IDLE_STATE_SEEN_EXIT_IDLE */
+          gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_TIMER_SET,
+                          MAX_IDLE_STATE_SEEN_EXIT_IDLE);
+          return;
+        case MAX_IDLE_STATE_SEEN_ENTER_IDLE:
+          gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE);
+          return;
+        default:
+          /* try again */
+          break;
+      }
+    }
+  }
+}
+
+/* Decrease the nubmer of active calls. After the decrement, if there are no
+   calls, the max_idle_timer should be started. */
+static void decrease_call_count(channel_data* chand) {
+  /* Enter idle */
+  if (gpr_atm_full_fetch_add(&chand->call_count, -1) == 1) {
+    gpr_atm_no_barrier_store(&chand->last_enter_idle_time_millis,
+                             (gpr_atm)grpc_core::ExecCtx::Get()->Now());
+    while (true) {
+      gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state);
+      switch (idle_state) {
+        case MAX_IDLE_STATE_INIT:
+          GRPC_CHANNEL_STACK_REF(chand->channel_stack,
+                                 "max_age max_idle_timer");
+          grpc_timer_init(
+              &chand->max_idle_timer,
+              grpc_core::ExecCtx::Get()->Now() + chand->max_connection_idle,
+              &chand->max_idle_timer_cb);
+          gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_TIMER_SET);
+          return;
+        case MAX_IDLE_STATE_SEEN_EXIT_IDLE:
+          if (gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE,
+                              MAX_IDLE_STATE_SEEN_ENTER_IDLE)) {
+            return;
+          }
+          break;
+        default:
+          /* try again */
+          break;
+      }
+    }
+  }
+}
+
+static void start_max_idle_timer_after_init(void* arg, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  /* Decrease call_count. If there are no active calls at this time,
+     max_idle_timer will start here. If the number of active calls is not 0,
+     max_idle_timer will start after all the active calls end. */
+  decrease_call_count(chand);
+  GRPC_CHANNEL_STACK_UNREF(chand->channel_stack,
+                           "max_age start_max_idle_timer_after_init");
+}
+
+static void start_max_age_timer_after_init(void* arg, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  gpr_mu_lock(&chand->max_age_timer_mu);
+  chand->max_age_timer_pending = true;
+  GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_timer");
+  grpc_timer_init(&chand->max_age_timer,
+                  grpc_core::ExecCtx::Get()->Now() + chand->max_connection_age,
+                  &chand->close_max_age_channel);
+  gpr_mu_unlock(&chand->max_age_timer_mu);
+  grpc_transport_op* op = grpc_make_transport_op(nullptr);
+  op->on_connectivity_state_change = &chand->channel_connectivity_changed;
+  op->connectivity_state = &chand->connectivity_state;
+  grpc_channel_next_op(grpc_channel_stack_element(chand->channel_stack, 0), op);
+  GRPC_CHANNEL_STACK_UNREF(chand->channel_stack,
+                           "max_age start_max_age_timer_after_init");
+}
+
+static void start_max_age_grace_timer_after_goaway_op(void* arg,
+                                                      grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  gpr_mu_lock(&chand->max_age_timer_mu);
+  chand->max_age_grace_timer_pending = true;
+  GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_grace_timer");
+  grpc_timer_init(
+      &chand->max_age_grace_timer,
+      chand->max_connection_age_grace == GRPC_MILLIS_INF_FUTURE
+          ? GRPC_MILLIS_INF_FUTURE
+          : grpc_core::ExecCtx::Get()->Now() + chand->max_connection_age_grace,
+      &chand->force_close_max_age_channel);
+  gpr_mu_unlock(&chand->max_age_timer_mu);
+  GRPC_CHANNEL_STACK_UNREF(chand->channel_stack,
+                           "max_age start_max_age_grace_timer_after_goaway_op");
+}
+
+static void close_max_idle_channel(channel_data* chand) {
+  /* Prevent the max idle timer from being set again */
+  gpr_atm_no_barrier_fetch_add(&chand->call_count, 1);
+  grpc_transport_op* op = grpc_make_transport_op(nullptr);
+  op->goaway_error =
+      grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_idle"),
+                         GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR);
+  grpc_channel_element* elem =
+      grpc_channel_stack_element(chand->channel_stack, 0);
+  elem->filter->start_transport_op(elem, op);
+}
+
+static void max_idle_timer_cb(void* arg, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  if (error == GRPC_ERROR_NONE) {
+    bool try_again = true;
+    while (try_again) {
+      gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state);
+      switch (idle_state) {
+        case MAX_IDLE_STATE_TIMER_SET:
+          close_max_idle_channel(chand);
+          /* This MAX_IDLE_STATE_INIT is a final state, we don't have to check
+           * if idle_state has been changed */
+          gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_INIT);
+          try_again = false;
+          break;
+        case MAX_IDLE_STATE_SEEN_EXIT_IDLE:
+          if (gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE,
+                              MAX_IDLE_STATE_INIT)) {
+            try_again = false;
+          }
+          break;
+        case MAX_IDLE_STATE_SEEN_ENTER_IDLE:
+          GRPC_CHANNEL_STACK_REF(chand->channel_stack,
+                                 "max_age max_idle_timer");
+          grpc_timer_init(&chand->max_idle_timer,
+                          static_cast<grpc_millis>(gpr_atm_no_barrier_load(
+                              &chand->last_enter_idle_time_millis)) +
+                              chand->max_connection_idle,
+                          &chand->max_idle_timer_cb);
+          /* idle_state may have already been set to
+             MAX_IDLE_STATE_SEEN_EXIT_IDLE by increase_call_count(), in this
+             case, we don't need to set it to MAX_IDLE_STATE_TIMER_SET */
+          gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_ENTER_IDLE,
+                          MAX_IDLE_STATE_TIMER_SET);
+          try_again = false;
+          break;
+        default:
+          /* try again */
+          break;
+      }
+    }
+  }
+  GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_idle_timer");
+}
+
+static void close_max_age_channel(void* arg, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  gpr_mu_lock(&chand->max_age_timer_mu);
+  chand->max_age_timer_pending = false;
+  gpr_mu_unlock(&chand->max_age_timer_mu);
+  if (error == GRPC_ERROR_NONE) {
+    GRPC_CHANNEL_STACK_REF(chand->channel_stack,
+                           "max_age start_max_age_grace_timer_after_goaway_op");
+    grpc_transport_op* op = grpc_make_transport_op(
+        &chand->start_max_age_grace_timer_after_goaway_op);
+    op->goaway_error =
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_age"),
+                           GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR);
+    grpc_channel_element* elem =
+        grpc_channel_stack_element(chand->channel_stack, 0);
+    elem->filter->start_transport_op(elem, op);
+  } else if (error != GRPC_ERROR_CANCELLED) {
+    GRPC_LOG_IF_ERROR("close_max_age_channel", error);
+  }
+  GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_age_timer");
+}
+
+static void force_close_max_age_channel(void* arg, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  gpr_mu_lock(&chand->max_age_timer_mu);
+  chand->max_age_grace_timer_pending = false;
+  gpr_mu_unlock(&chand->max_age_timer_mu);
+  if (error == GRPC_ERROR_NONE) {
+    grpc_transport_op* op = grpc_make_transport_op(nullptr);
+    op->disconnect_with_error =
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel reaches max age");
+    grpc_channel_element* elem =
+        grpc_channel_stack_element(chand->channel_stack, 0);
+    elem->filter->start_transport_op(elem, op);
+  } else if (error != GRPC_ERROR_CANCELLED) {
+    GRPC_LOG_IF_ERROR("force_close_max_age_channel", error);
+  }
+  GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_age_grace_timer");
+}
+
+static void channel_connectivity_changed(void* arg, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(arg);
+  if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) {
+    grpc_transport_op* op = grpc_make_transport_op(nullptr);
+    op->on_connectivity_state_change = &chand->channel_connectivity_changed;
+    op->connectivity_state = &chand->connectivity_state;
+    grpc_channel_next_op(grpc_channel_stack_element(chand->channel_stack, 0),
+                         op);
+  } else {
+    gpr_mu_lock(&chand->max_age_timer_mu);
+    if (chand->max_age_timer_pending) {
+      grpc_timer_cancel(&chand->max_age_timer);
+      chand->max_age_timer_pending = false;
+    }
+    if (chand->max_age_grace_timer_pending) {
+      grpc_timer_cancel(&chand->max_age_grace_timer);
+      chand->max_age_grace_timer_pending = false;
+    }
+    gpr_mu_unlock(&chand->max_age_timer_mu);
+    /* If there are no active calls, this increasement will cancel
+       max_idle_timer, and prevent max_idle_timer from being started in the
+       future. */
+    increase_call_count(chand);
+    if (gpr_atm_acq_load(&chand->idle_state) == MAX_IDLE_STATE_SEEN_EXIT_IDLE) {
+      grpc_timer_cancel(&chand->max_idle_timer);
+    }
+  }
+}
+
+/* A random jitter of +/-10% will be added to MAX_CONNECTION_AGE to spread out
+   connection storms. Note that the MAX_CONNECTION_AGE option without jitter
+   would not create connection storms by itself, but if there happened to be a
+   connection storm it could cause it to repeat at a fixed period. */
+static grpc_millis
+add_random_max_connection_age_jitter_and_convert_to_grpc_millis(int value) {
+  /* generate a random number between 1 - MAX_CONNECTION_AGE_JITTER and
+     1 + MAX_CONNECTION_AGE_JITTER */
+  double multiplier = rand() * MAX_CONNECTION_AGE_JITTER * 2.0 / RAND_MAX +
+                      1.0 - MAX_CONNECTION_AGE_JITTER;
+  double result = multiplier * value;
+  /* INT_MAX - 0.5 converts the value to float, so that result will not be
+     cast to int implicitly before the comparison. */
+  return result > (static_cast<double>(GRPC_MILLIS_INF_FUTURE)) - 0.5
+             ? GRPC_MILLIS_INF_FUTURE
+             : static_cast<grpc_millis>(result);
+}
+
+/* Constructor for call_data. */
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  increase_call_count(chand);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for call_data. */
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  decrease_call_count(chand);
+}
+
+/* Constructor for channel_data. */
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  gpr_mu_init(&chand->max_age_timer_mu);
+  chand->max_age_timer_pending = false;
+  chand->max_age_grace_timer_pending = false;
+  chand->channel_stack = args->channel_stack;
+  chand->max_connection_age =
+      add_random_max_connection_age_jitter_and_convert_to_grpc_millis(
+          DEFAULT_MAX_CONNECTION_AGE_MS);
+  chand->max_connection_age_grace =
+      DEFAULT_MAX_CONNECTION_AGE_GRACE_MS == INT_MAX
+          ? GRPC_MILLIS_INF_FUTURE
+          : DEFAULT_MAX_CONNECTION_AGE_GRACE_MS;
+  chand->max_connection_idle = DEFAULT_MAX_CONNECTION_IDLE_MS == INT_MAX
+                                   ? GRPC_MILLIS_INF_FUTURE
+                                   : DEFAULT_MAX_CONNECTION_IDLE_MS;
+  chand->idle_state = MAX_IDLE_STATE_INIT;
+  gpr_atm_no_barrier_store(&chand->last_enter_idle_time_millis, GPR_ATM_MIN);
+  for (size_t i = 0; i < args->channel_args->num_args; ++i) {
+    if (0 == strcmp(args->channel_args->args[i].key,
+                    GRPC_ARG_MAX_CONNECTION_AGE_MS)) {
+      const int value = grpc_channel_arg_get_integer(
+          &args->channel_args->args[i], MAX_CONNECTION_AGE_INTEGER_OPTIONS);
+      chand->max_connection_age =
+          add_random_max_connection_age_jitter_and_convert_to_grpc_millis(
+              value);
+    } else if (0 == strcmp(args->channel_args->args[i].key,
+                           GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS)) {
+      const int value = grpc_channel_arg_get_integer(
+          &args->channel_args->args[i],
+          {DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, 0, INT_MAX});
+      chand->max_connection_age_grace =
+          value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
+    } else if (0 == strcmp(args->channel_args->args[i].key,
+                           GRPC_ARG_MAX_CONNECTION_IDLE_MS)) {
+      const int value = grpc_channel_arg_get_integer(
+          &args->channel_args->args[i], MAX_CONNECTION_IDLE_INTEGER_OPTIONS);
+      chand->max_connection_idle =
+          value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
+    }
+  }
+  GRPC_CLOSURE_INIT(&chand->max_idle_timer_cb, max_idle_timer_cb, chand,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&chand->close_max_age_channel, close_max_age_channel, chand,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&chand->force_close_max_age_channel,
+                    force_close_max_age_channel, chand,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&chand->start_max_idle_timer_after_init,
+                    start_max_idle_timer_after_init, chand,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&chand->start_max_age_timer_after_init,
+                    start_max_age_timer_after_init, chand,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&chand->start_max_age_grace_timer_after_goaway_op,
+                    start_max_age_grace_timer_after_goaway_op, chand,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed,
+                    channel_connectivity_changed, chand,
+                    grpc_schedule_on_exec_ctx);
+
+  if (chand->max_connection_age != GRPC_MILLIS_INF_FUTURE) {
+    /* When the channel reaches its max age, we send down an op with
+       goaway_error set.  However, we can't send down any ops until after the
+       channel stack is fully initialized.  If we start the timer here, we have
+       no guarantee that the timer won't pop before channel 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. */
+    GRPC_CHANNEL_STACK_REF(chand->channel_stack,
+                           "max_age start_max_age_timer_after_init");
+    GRPC_CLOSURE_SCHED(&chand->start_max_age_timer_after_init, GRPC_ERROR_NONE);
+  }
+
+  /* Initialize the number of calls as 1, so that the max_idle_timer will not
+     start until start_max_idle_timer_after_init is invoked. */
+  gpr_atm_rel_store(&chand->call_count, 1);
+  if (chand->max_connection_idle != GRPC_MILLIS_INF_FUTURE) {
+    GRPC_CHANNEL_STACK_REF(chand->channel_stack,
+                           "max_age start_max_idle_timer_after_init");
+    GRPC_CLOSURE_SCHED(&chand->start_max_idle_timer_after_init,
+                       GRPC_ERROR_NONE);
+  }
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for channel_data. */
+static void destroy_channel_elem(grpc_channel_element* elem) {}
+
+const grpc_channel_filter grpc_max_age_filter = {
+    grpc_call_next_op,
+    grpc_channel_next_op,
+    0, /* 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_channel_next_get_info,
+    "max_age"};
+
+static bool maybe_add_max_age_filter(grpc_channel_stack_builder* builder,
+                                     void* arg) {
+  const grpc_channel_args* channel_args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  bool enable =
+      grpc_channel_arg_get_integer(
+          grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_AGE_MS),
+          MAX_CONNECTION_AGE_INTEGER_OPTIONS) != INT_MAX ||
+      grpc_channel_arg_get_integer(
+          grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_IDLE_MS),
+          MAX_CONNECTION_IDLE_INTEGER_OPTIONS) != INT_MAX;
+  if (enable) {
+    return grpc_channel_stack_builder_prepend_filter(
+        builder, &grpc_max_age_filter, nullptr, nullptr);
+  } else {
+    return true;
+  }
+}
+
+void grpc_max_age_filter_init(void) {
+  grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_max_age_filter, nullptr);
+}
+
+void grpc_max_age_filter_shutdown(void) {}
diff --git a/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.h b/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.h
new file mode 100644
index 0000000..9893222
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.h
@@ -0,0 +1,26 @@
+//
+// Copyright 2017 gRPC authors.
+//
+// Licensed 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.
+//
+
+#ifndef GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_max_age_filter;
+
+#endif /* GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H */
diff --git a/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.cc b/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.cc
new file mode 100644
index 0000000..94d6942
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.cc
@@ -0,0 +1,385 @@
+//
+// Copyright 2016 gRPC authors.
+//
+// Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/message_size/message_size_filter.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/impl/codegen/grpc_types.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"
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/service_config.h"
+
+typedef struct {
+  int max_send_size;
+  int max_recv_size;
+} message_size_limits;
+
+namespace grpc_core {
+namespace {
+
+class MessageSizeLimits : public RefCounted<MessageSizeLimits> {
+ public:
+  static RefCountedPtr<MessageSizeLimits> CreateFromJson(const grpc_json* json);
+
+  const message_size_limits& limits() const { return limits_; }
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T, typename... Args>
+  friend T* grpc_core::New(Args&&... args);
+
+  MessageSizeLimits(int max_send_size, int max_recv_size) {
+    limits_.max_send_size = max_send_size;
+    limits_.max_recv_size = max_recv_size;
+  }
+
+  message_size_limits limits_;
+};
+
+RefCountedPtr<MessageSizeLimits> MessageSizeLimits::CreateFromJson(
+    const grpc_json* json) {
+  int max_request_message_bytes = -1;
+  int max_response_message_bytes = -1;
+  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
+    if (field->key == nullptr) continue;
+    if (strcmp(field->key, "maxRequestMessageBytes") == 0) {
+      if (max_request_message_bytes >= 0) return nullptr;  // Duplicate.
+      if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) {
+        return nullptr;
+      }
+      max_request_message_bytes = gpr_parse_nonnegative_int(field->value);
+      if (max_request_message_bytes == -1) return nullptr;
+    } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) {
+      if (max_response_message_bytes >= 0) return nullptr;  // Duplicate.
+      if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) {
+        return nullptr;
+      }
+      max_response_message_bytes = gpr_parse_nonnegative_int(field->value);
+      if (max_response_message_bytes == -1) return nullptr;
+    }
+  }
+  return MakeRefCounted<MessageSizeLimits>(max_request_message_bytes,
+                                           max_response_message_bytes);
+}
+
+}  // namespace
+}  // namespace grpc_core
+
+static void recv_message_ready(void* user_data, grpc_error* error);
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
+
+namespace {
+
+struct channel_data {
+  message_size_limits limits;
+  // Maps path names to refcounted_message_size_limits structs.
+  grpc_core::RefCountedPtr<grpc_core::SliceHashTable<
+      grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits>>>
+      method_limit_table;
+};
+
+struct call_data {
+  call_data(grpc_call_element* elem, const channel_data& chand,
+            const grpc_call_element_args& args)
+      : call_combiner(args.call_combiner), limits(chand.limits) {
+    GRPC_CLOSURE_INIT(&recv_message_ready, ::recv_message_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+                      ::recv_trailing_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    // Get max sizes from channel data, then merge in per-method config values.
+    // Note: Per-method config is only available on the client, so we
+    // apply the max request size to the send limit and the max response
+    // size to the receive limit.
+    if (chand.method_limit_table != nullptr) {
+      grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits> limits =
+          grpc_core::ServiceConfig::MethodConfigTableLookup(
+              *chand.method_limit_table, args.path);
+      if (limits != nullptr) {
+        if (limits->limits().max_send_size >= 0 &&
+            (limits->limits().max_send_size < this->limits.max_send_size ||
+             this->limits.max_send_size < 0)) {
+          this->limits.max_send_size = limits->limits().max_send_size;
+        }
+        if (limits->limits().max_recv_size >= 0 &&
+            (limits->limits().max_recv_size < this->limits.max_recv_size ||
+             this->limits.max_recv_size < 0)) {
+          this->limits.max_recv_size = limits->limits().max_recv_size;
+        }
+      }
+    }
+  }
+
+  ~call_data() { GRPC_ERROR_UNREF(error); }
+
+  grpc_call_combiner* call_combiner;
+  message_size_limits limits;
+  // 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;
+  grpc_closure recv_trailing_metadata_ready;
+  // The error caused by a message that is too large, or GRPC_ERROR_NONE
+  grpc_error* error = GRPC_ERROR_NONE;
+  // Used by recv_message_ready.
+  grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message = nullptr;
+  // Original recv_message_ready callback, invoked after our own.
+  grpc_closure* next_recv_message_ready = nullptr;
+  // Original recv_trailing_metadata callback, invoked after our own.
+  grpc_closure* original_recv_trailing_metadata_ready;
+  bool seen_recv_trailing_metadata = false;
+  grpc_error* recv_trailing_metadata_error;
+};
+
+}  // namespace
+
+// Callback invoked when we receive a message.  Here we check the max
+// receive message size.
+static void recv_message_ready(void* user_data, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (*calld->recv_message != nullptr && calld->limits.max_recv_size >= 0 &&
+      (*calld->recv_message)->length() >
+          static_cast<size_t>(calld->limits.max_recv_size)) {
+    char* message_string;
+    gpr_asprintf(&message_string,
+                 "Received message larger than max (%u vs. %d)",
+                 (*calld->recv_message)->length(), calld->limits.max_recv_size);
+    grpc_error* new_error = grpc_error_set_int(
+        GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string),
+        GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED);
+    GRPC_ERROR_UNREF(calld->error);
+    if (error == GRPC_ERROR_NONE) {
+      error = new_error;
+    } else {
+      error = grpc_error_add_child(error, new_error);
+    }
+    calld->error = GRPC_ERROR_REF(error);
+    gpr_free(message_string);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  // Invoke the next callback.
+  grpc_closure* closure = calld->next_recv_message_ready;
+  calld->next_recv_message_ready = nullptr;
+  if (calld->seen_recv_trailing_metadata) {
+    /* We might potentially see another RECV_MESSAGE op. In that case, we do not
+     * want to run the recv_trailing_metadata_ready closure again. The newer
+     * RECV_MESSAGE op cannot cause any errors since the transport has already
+     * invoked the recv_trailing_metadata_ready closure and all further
+     * RECV_MESSAGE ops will get null payloads. */
+    calld->seen_recv_trailing_metadata = false;
+    GRPC_CALL_COMBINER_START(calld->call_combiner,
+                             &calld->recv_trailing_metadata_ready,
+                             calld->recv_trailing_metadata_error,
+                             "continue recv_trailing_metadata_ready");
+  }
+  GRPC_CLOSURE_RUN(closure, error);
+}
+
+// Callback invoked on completion of recv_trailing_metadata
+// Notifies the recv_trailing_metadata batch of any message size failures
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->next_recv_message_ready != nullptr) {
+    calld->seen_recv_trailing_metadata = true;
+    calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error);
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "deferring recv_trailing_metadata_ready until "
+                            "after recv_message_ready");
+    return;
+  }
+  error =
+      grpc_error_add_child(GRPC_ERROR_REF(error), GRPC_ERROR_REF(calld->error));
+  // Invoke the next callback.
+  GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error);
+}
+
+// Start transport stream op.
+static void start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // Check max send message size.
+  if (op->send_message && calld->limits.max_send_size >= 0 &&
+      op->payload->send_message.send_message->length() >
+          static_cast<size_t>(calld->limits.max_send_size)) {
+    char* message_string;
+    gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)",
+                 op->payload->send_message.send_message->length(),
+                 calld->limits.max_send_size);
+    grpc_transport_stream_op_batch_finish_with_failure(
+        op,
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string),
+                           GRPC_ERROR_INT_GRPC_STATUS,
+                           GRPC_STATUS_RESOURCE_EXHAUSTED),
+        calld->call_combiner);
+    gpr_free(message_string);
+    return;
+  }
+  // Inject callback for receiving a message.
+  if (op->recv_message) {
+    calld->next_recv_message_ready =
+        op->payload->recv_message.recv_message_ready;
+    calld->recv_message = op->payload->recv_message.recv_message;
+    op->payload->recv_message.recv_message_ready = &calld->recv_message_ready;
+  }
+  // Inject callback for receiving trailing metadata.
+  if (op->recv_trailing_metadata) {
+    calld->original_recv_trailing_metadata_ready =
+        op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &calld->recv_trailing_metadata_ready;
+  }
+  // Chain to the next filter.
+  grpc_call_next_op(elem, op);
+}
+
+// Constructor for call_data.
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  new (elem->call_data) call_data(elem, *chand, *args);
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for call_data.
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  call_data* calld = (call_data*)elem->call_data;
+  calld->~call_data();
+}
+
+static int default_size(const grpc_channel_args* args,
+                        int without_minimal_stack) {
+  if (grpc_channel_args_want_minimal_stack(args)) {
+    return -1;
+  }
+  return without_minimal_stack;
+}
+
+message_size_limits get_message_size_limits(
+    const grpc_channel_args* channel_args) {
+  message_size_limits lim;
+  lim.max_send_size =
+      default_size(channel_args, GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH);
+  lim.max_recv_size =
+      default_size(channel_args, GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH);
+  for (size_t i = 0; i < channel_args->num_args; ++i) {
+    if (strcmp(channel_args->args[i].key, GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) ==
+        0) {
+      const grpc_integer_options options = {lim.max_send_size, -1, INT_MAX};
+      lim.max_send_size =
+          grpc_channel_arg_get_integer(&channel_args->args[i], options);
+    }
+    if (strcmp(channel_args->args[i].key,
+               GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) == 0) {
+      const grpc_integer_options options = {lim.max_recv_size, -1, INT_MAX};
+      lim.max_recv_size =
+          grpc_channel_arg_get_integer(&channel_args->args[i], options);
+    }
+  }
+  return lim;
+}
+
+// Constructor for channel_data.
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  GPR_ASSERT(!args->is_last);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  chand->limits = get_message_size_limits(args->channel_args);
+  // Get method config table from channel args.
+  const grpc_arg* channel_arg =
+      grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG);
+  const char* service_config_str = grpc_channel_arg_get_string(channel_arg);
+  if (service_config_str != nullptr) {
+    grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config =
+        grpc_core::ServiceConfig::Create(service_config_str);
+    if (service_config != nullptr) {
+      chand->method_limit_table = service_config->CreateMethodConfigTable(
+          grpc_core::MessageSizeLimits::CreateFromJson);
+    }
+  }
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for channel_data.
+static void destroy_channel_elem(grpc_channel_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  chand->method_limit_table.reset();
+}
+
+const grpc_channel_filter grpc_message_size_filter = {
+    start_transport_stream_op_batch,
+    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_channel_next_get_info,
+    "message_size"};
+
+static bool maybe_add_message_size_filter(grpc_channel_stack_builder* builder,
+                                          void* arg) {
+  const grpc_channel_args* channel_args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  bool enable = false;
+  message_size_limits lim = get_message_size_limits(channel_args);
+  if (lim.max_send_size != -1 || lim.max_recv_size != -1) {
+    enable = true;
+  }
+  const grpc_arg* a =
+      grpc_channel_args_find(channel_args, GRPC_ARG_SERVICE_CONFIG);
+  if (a != nullptr) {
+    enable = true;
+  }
+  if (enable) {
+    return grpc_channel_stack_builder_prepend_filter(
+        builder, &grpc_message_size_filter, nullptr, nullptr);
+  } else {
+    return true;
+  }
+}
+
+void grpc_message_size_filter_init(void) {
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_message_size_filter, nullptr);
+  grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_message_size_filter, nullptr);
+  grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   maybe_add_message_size_filter, nullptr);
+}
+
+void grpc_message_size_filter_shutdown(void) {}
diff --git a/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.h b/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.h
new file mode 100644
index 0000000..f66636e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.h
@@ -0,0 +1,26 @@
+//
+// Copyright 2016 gRPC authors.
+//
+// Licensed 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.
+//
+
+#ifndef GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_message_size_filter;
+
+#endif /* GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H */
diff --git a/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc b/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc
new file mode 100644
index 0000000..c7070d4
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc
@@ -0,0 +1,208 @@
+//
+// Copyright 2017 gRPC authors.
+//
+// Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/ext/filters/workarounds/workaround_utils.h"
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/metadata.h"
+
+namespace {
+struct call_data {
+  // Receive closures are chained: we inject this closure as the
+  // recv_initial_metadata_ready up-call on transport_stream_op, and remember to
+  // call our next_recv_initial_metadata_ready member after handling it.
+  grpc_closure recv_initial_metadata_ready;
+  // Used by recv_initial_metadata_ready.
+  grpc_metadata_batch* recv_initial_metadata;
+  // Original recv_initial_metadata_ready callback, invoked after our own.
+  grpc_closure* next_recv_initial_metadata_ready;
+
+  // Marks whether the workaround is active
+  bool workaround_active;
+};
+}  // namespace
+
+// Find the user agent metadata element in the batch
+static bool get_user_agent_mdelem(const grpc_metadata_batch* batch,
+                                  grpc_mdelem* md) {
+  if (batch->idx.named.user_agent != nullptr) {
+    *md = batch->idx.named.user_agent->md;
+    return true;
+  }
+  return false;
+}
+
+// Callback invoked when we receive an initial metadata.
+static void recv_initial_metadata_ready(void* user_data, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+
+  if (GRPC_ERROR_NONE == error) {
+    grpc_mdelem md;
+    if (get_user_agent_mdelem(calld->recv_initial_metadata, &md)) {
+      grpc_workaround_user_agent_md* user_agent_md = grpc_parse_user_agent(md);
+      if (user_agent_md
+              ->workaround_active[GRPC_WORKAROUND_ID_CRONET_COMPRESSION]) {
+        calld->workaround_active = true;
+      }
+    }
+  }
+
+  // Invoke the next callback.
+  GRPC_CLOSURE_RUN(calld->next_recv_initial_metadata_ready,
+                   GRPC_ERROR_REF(error));
+}
+
+// Start transport stream op.
+static void start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+
+  // Inject callback for receiving initial metadata
+  if (op->recv_initial_metadata) {
+    calld->next_recv_initial_metadata_ready =
+        op->payload->recv_initial_metadata.recv_initial_metadata_ready;
+    op->payload->recv_initial_metadata.recv_initial_metadata_ready =
+        &calld->recv_initial_metadata_ready;
+    calld->recv_initial_metadata =
+        op->payload->recv_initial_metadata.recv_initial_metadata;
+  }
+
+  if (op->send_message) {
+    /* Send message happens after client's user-agent (initial metadata) is
+     * received, so workaround_active must be set already */
+    if (calld->workaround_active) {
+      op->payload->send_message.send_message->set_flags(
+          op->payload->send_message.send_message->flags() |
+          GRPC_WRITE_NO_COMPRESS);
+    }
+  }
+
+  // Chain to the next filter.
+  grpc_call_next_op(elem, op);
+}
+
+// Constructor for call_data.
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->next_recv_initial_metadata_ready = nullptr;
+  calld->workaround_active = false;
+  GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready,
+                    recv_initial_metadata_ready, elem,
+                    grpc_schedule_on_exec_ctx);
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for call_data.
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {}
+
+// Constructor for channel_data.
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  return GRPC_ERROR_NONE;
+}
+
+// Destructor for channel_data.
+static void destroy_channel_elem(grpc_channel_element* elem) {}
+
+// Parse the user agent
+static bool parse_user_agent(grpc_mdelem md) {
+  const char grpc_objc_specifier[] = "grpc-objc/";
+  const size_t grpc_objc_specifier_len = sizeof(grpc_objc_specifier) - 1;
+  const char cronet_specifier[] = "cronet_http";
+  const size_t cronet_specifier_len = sizeof(cronet_specifier) - 1;
+
+  char* user_agent_str = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+  bool grpc_objc_specifier_seen = false;
+  bool cronet_specifier_seen = false;
+  char *major_version_str = user_agent_str, *minor_version_str;
+  long major_version = 0, minor_version = 0;
+
+  char* head = strtok(user_agent_str, " ");
+  while (head != nullptr) {
+    if (!grpc_objc_specifier_seen &&
+        0 == strncmp(head, grpc_objc_specifier, grpc_objc_specifier_len)) {
+      major_version_str = head + grpc_objc_specifier_len;
+      grpc_objc_specifier_seen = true;
+    } else if (grpc_objc_specifier_seen &&
+               0 == strncmp(head, cronet_specifier, cronet_specifier_len)) {
+      cronet_specifier_seen = true;
+      break;
+    }
+
+    head = strtok(nullptr, " ");
+  }
+  if (grpc_objc_specifier_seen) {
+    major_version_str = strtok(major_version_str, ".");
+    minor_version_str = strtok(nullptr, ".");
+    major_version = atol(major_version_str);
+    minor_version = atol(minor_version_str);
+  }
+
+  gpr_free(user_agent_str);
+  return (grpc_objc_specifier_seen && cronet_specifier_seen &&
+          (major_version < 1 || (major_version == 1 && minor_version <= 3)));
+}
+
+const grpc_channel_filter grpc_workaround_cronet_compression_filter = {
+    start_transport_stream_op_batch,
+    grpc_channel_next_op,
+    sizeof(call_data),
+    init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    destroy_call_elem,
+    0,
+    init_channel_elem,
+    destroy_channel_elem,
+    grpc_channel_next_get_info,
+    "workaround_cronet_compression"};
+
+static bool register_workaround_cronet_compression(
+    grpc_channel_stack_builder* builder, void* arg) {
+  const grpc_channel_args* channel_args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  const grpc_arg* a = grpc_channel_args_find(
+      channel_args, GRPC_ARG_WORKAROUND_CRONET_COMPRESSION);
+  if (a == nullptr) {
+    return true;
+  }
+  if (grpc_channel_arg_get_bool(a, false) == false) {
+    return true;
+  }
+  return grpc_channel_stack_builder_prepend_filter(
+      builder, &grpc_workaround_cronet_compression_filter, nullptr, nullptr);
+}
+
+void grpc_workaround_cronet_compression_filter_init(void) {
+  grpc_channel_init_register_stage(
+      GRPC_SERVER_CHANNEL, GRPC_WORKAROUND_PRIORITY_HIGH,
+      register_workaround_cronet_compression, nullptr);
+  grpc_register_workaround(GRPC_WORKAROUND_ID_CRONET_COMPRESSION,
+                           parse_user_agent);
+}
+
+void grpc_workaround_cronet_compression_filter_shutdown(void) {}
diff --git a/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h b/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h
new file mode 100644
index 0000000..94d20f0
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h
@@ -0,0 +1,27 @@
+//
+// Copyright 2017 gRPC authors.
+//
+// Licensed 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.
+//
+
+#ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H
+#define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_workaround_cronet_compression_filter;
+
+#endif /* GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H \
+        */
diff --git a/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.cc b/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.cc
new file mode 100644
index 0000000..4dabe89
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.cc
@@ -0,0 +1,53 @@
+//
+// Copyright 2017 gRPC authors.
+//
+// Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/workarounds/workaround_utils.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+user_agent_parser ua_parser[GRPC_MAX_WORKAROUND_ID];
+
+static void destroy_user_agent_md(void* user_agent_md) {
+  gpr_free(user_agent_md);
+}
+
+grpc_workaround_user_agent_md* grpc_parse_user_agent(grpc_mdelem md) {
+  grpc_workaround_user_agent_md* user_agent_md =
+      static_cast<grpc_workaround_user_agent_md*>(
+          grpc_mdelem_get_user_data(md, destroy_user_agent_md));
+
+  if (nullptr != user_agent_md) {
+    return user_agent_md;
+  }
+  user_agent_md = static_cast<grpc_workaround_user_agent_md*>(
+      gpr_malloc(sizeof(grpc_workaround_user_agent_md)));
+  for (int i = 0; i < GRPC_MAX_WORKAROUND_ID; i++) {
+    if (ua_parser[i]) {
+      user_agent_md->workaround_active[i] = ua_parser[i](md);
+    }
+  }
+  grpc_mdelem_set_user_data(md, destroy_user_agent_md, (void*)user_agent_md);
+
+  return user_agent_md;
+}
+
+void grpc_register_workaround(uint32_t id, user_agent_parser parser) {
+  GPR_ASSERT(id < GRPC_MAX_WORKAROUND_ID);
+  ua_parser[id] = parser;
+}
diff --git a/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.h b/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.h
new file mode 100644
index 0000000..f172ccc
--- /dev/null
+++ b/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.h
@@ -0,0 +1,39 @@
+//
+// Copyright 2017 gRPC authors.
+//
+// Licensed 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.
+//
+
+#ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H
+#define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/workaround_list.h>
+
+#include "src/core/lib/transport/metadata.h"
+
+#define GRPC_WORKAROUND_PRIORITY_HIGH 10001
+#define GRPC_WORKAROUND_PROIRITY_LOW 9999
+
+typedef struct grpc_workaround_user_agent_md {
+  bool workaround_active[GRPC_MAX_WORKAROUND_ID];
+} grpc_workaround_user_agent_md;
+
+grpc_workaround_user_agent_md* grpc_parse_user_agent(grpc_mdelem md);
+
+typedef bool (*user_agent_parser)(grpc_mdelem);
+
+void grpc_register_workaround(uint32_t id, user_agent_parser parser);
+
+#endif /* GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H */
diff --git a/third_party/grpc/src/core/ext/transport/README.md b/third_party/grpc/src/core/ext/transport/README.md
new file mode 100644
index 0000000..2290568
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/README.md
@@ -0,0 +1 @@
+Transports for gRPC
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/README.md b/third_party/grpc/src/core/ext/transport/chttp2/README.md
new file mode 100644
index 0000000..8880a47
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/README.md
@@ -0,0 +1 @@
+CHTTP2 - gRPC's implementation of a HTTP2 based transport
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.cc b/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.cc
new file mode 100644
index 0000000..1fdab76
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.cc
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include "src/core/ext/transport/chttp2/alpn/alpn.h"
+
+#include "src/core/lib/gpr/useful.h"
+
+/* in order of preference */
+static const char* const supported_versions[] = {"grpc-exp", "h2"};
+
+int grpc_chttp2_is_alpn_version_supported(const char* version, size_t size) {
+  size_t i;
+  for (i = 0; i < GPR_ARRAY_SIZE(supported_versions); i++) {
+    if (!strncmp(version, supported_versions[i], size)) return 1;
+  }
+  return 0;
+}
+
+size_t grpc_chttp2_num_alpn_versions(void) {
+  return GPR_ARRAY_SIZE(supported_versions);
+}
+
+const char* grpc_chttp2_get_alpn_version_index(size_t i) {
+  GPR_ASSERT(i < GPR_ARRAY_SIZE(supported_versions));
+  return supported_versions[i];
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.h b/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.h
new file mode 100644
index 0000000..0042eaf
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H
+
+#include <grpc/support/port_platform.h>
+
+#include <string.h>
+
+/* Retuns 1 if the version is supported, 0 otherwise. */
+int grpc_chttp2_is_alpn_version_supported(const char* version, size_t size);
+
+/* Returns the number of protocol versions to advertise */
+size_t grpc_chttp2_num_alpn_versions(void);
+
+/* Returns the protocol version at index i (0 <= i <
+ * grpc_chttp2_num_alpn_versions()) */
+const char* grpc_chttp2_get_alpn_version_index(size_t i);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/authority.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/authority.cc
new file mode 100644
index 0000000..bad3153
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/authority.cc
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/client/authority.h"
+
+grpc_channel_args* grpc_default_authority_add_if_not_present(
+    const grpc_channel_args* args) {
+  const bool has_default_authority =
+      grpc_channel_args_find(args, GRPC_ARG_DEFAULT_AUTHORITY) != nullptr;
+  grpc_arg new_args[1];
+  size_t num_new_args = 0;
+  grpc_core::UniquePtr<char> default_authority;
+  if (!has_default_authority) {
+    const grpc_arg* server_uri_arg =
+        grpc_channel_args_find(args, GRPC_ARG_SERVER_URI);
+    const char* server_uri_str = grpc_channel_arg_get_string(server_uri_arg);
+    GPR_ASSERT(server_uri_str != nullptr);
+    default_authority =
+        grpc_core::ResolverRegistry::GetDefaultAuthority(server_uri_str);
+    GPR_ASSERT(default_authority != nullptr);
+    new_args[num_new_args++] = grpc_channel_arg_string_create(
+        const_cast<char*>(GRPC_ARG_DEFAULT_AUTHORITY), default_authority.get());
+  }
+  return grpc_channel_args_copy_and_add(args, new_args, num_new_args);
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/authority.h b/third_party/grpc/src/core/ext/transport/chttp2/client/authority.h
new file mode 100644
index 0000000..642584e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/authority.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_AUTHORITY_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_AUTHORITY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/memory.h"
+
+/// Returns a copy of \a args with the default authority channel arg set if it
+/// wasn't already present.
+grpc_channel_args* grpc_default_authority_add_if_not_present(
+    const grpc_channel_args* args);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_AUTHORITY_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.cc
new file mode 100644
index 0000000..42a2e2e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.cc
@@ -0,0 +1,239 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/client/chttp2_connector.h"
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/connector.h"
+#include "src/core/ext/filters/client_channel/http_connect_handshaker.h"
+#include "src/core/ext/filters/client_channel/subchannel.h"
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+typedef struct {
+  grpc_connector base;
+
+  gpr_mu mu;
+  gpr_refcount refs;
+
+  bool shutdown;
+  bool connecting;
+
+  grpc_closure* notify;
+  grpc_connect_in_args args;
+  grpc_connect_out_args* result;
+
+  grpc_endpoint* endpoint;  // Non-NULL until handshaking starts.
+
+  grpc_closure connected;
+
+  grpc_handshake_manager* handshake_mgr;
+} chttp2_connector;
+
+static void chttp2_connector_ref(grpc_connector* con) {
+  chttp2_connector* c = reinterpret_cast<chttp2_connector*>(con);
+  gpr_ref(&c->refs);
+}
+
+static void chttp2_connector_unref(grpc_connector* con) {
+  chttp2_connector* c = reinterpret_cast<chttp2_connector*>(con);
+  if (gpr_unref(&c->refs)) {
+    gpr_mu_destroy(&c->mu);
+    // If handshaking is not yet in progress, destroy the endpoint.
+    // Otherwise, the handshaker will do this for us.
+    if (c->endpoint != nullptr) grpc_endpoint_destroy(c->endpoint);
+    gpr_free(c);
+  }
+}
+
+static void chttp2_connector_shutdown(grpc_connector* con, grpc_error* why) {
+  chttp2_connector* c = reinterpret_cast<chttp2_connector*>(con);
+  gpr_mu_lock(&c->mu);
+  c->shutdown = true;
+  if (c->handshake_mgr != nullptr) {
+    grpc_handshake_manager_shutdown(c->handshake_mgr, GRPC_ERROR_REF(why));
+  }
+  // If handshaking is not yet in progress, shutdown the endpoint.
+  // Otherwise, the handshaker will do this for us.
+  if (!c->connecting && c->endpoint != nullptr) {
+    grpc_endpoint_shutdown(c->endpoint, GRPC_ERROR_REF(why));
+  }
+  gpr_mu_unlock(&c->mu);
+  GRPC_ERROR_UNREF(why);
+}
+
+static void on_handshake_done(void* arg, grpc_error* error) {
+  grpc_handshaker_args* args = static_cast<grpc_handshaker_args*>(arg);
+  chttp2_connector* c = static_cast<chttp2_connector*>(args->user_data);
+  gpr_mu_lock(&c->mu);
+  if (error != GRPC_ERROR_NONE || c->shutdown) {
+    if (error == GRPC_ERROR_NONE) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown");
+      // We were shut down after handshaking completed successfully, so
+      // destroy the endpoint here.
+      // TODO(ctiller): It is currently necessary to shutdown endpoints
+      // before destroying them, even if we know that there are no
+      // pending read/write callbacks.  This should be fixed, at which
+      // point this can be removed.
+      grpc_endpoint_shutdown(args->endpoint, GRPC_ERROR_REF(error));
+      grpc_endpoint_destroy(args->endpoint);
+      grpc_channel_args_destroy(args->args);
+      grpc_slice_buffer_destroy_internal(args->read_buffer);
+      gpr_free(args->read_buffer);
+    } else {
+      error = GRPC_ERROR_REF(error);
+    }
+    memset(c->result, 0, sizeof(*c->result));
+  } else {
+    grpc_endpoint_delete_from_pollset_set(args->endpoint,
+                                          c->args.interested_parties);
+    c->result->transport =
+        grpc_create_chttp2_transport(args->args, args->endpoint, true);
+    grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode> socket_node =
+        grpc_chttp2_transport_get_socket_node(c->result->transport);
+    c->result->socket_uuid = socket_node == nullptr ? 0 : socket_node->uuid();
+    GPR_ASSERT(c->result->transport);
+    // TODO(roth): We ideally want to wait until we receive HTTP/2
+    // settings from the server before we consider the connection
+    // established.  If that doesn't happen before the connection
+    // timeout expires, then we should consider the connection attempt a
+    // failure and feed that information back into the backoff code.
+    // We could pass a notify_on_receive_settings callback to
+    // grpc_chttp2_transport_start_reading() to let us know when
+    // settings are received, but we would need to figure out how to use
+    // that information here.
+    //
+    // Unfortunately, we don't currently have a way to split apart the two
+    // effects of scheduling c->notify: we start sending RPCs immediately
+    // (which we want to do) and we consider the connection attempt successful
+    // (which we don't want to do until we get the notify_on_receive_settings
+    // callback from the transport).  If we could split those things
+    // apart, then we could start sending RPCs but then wait for our
+    // timeout before deciding if the connection attempt is successful.
+    // If the attempt is not successful, then we would tear down the
+    // transport and feed the failure back into the backoff code.
+    //
+    // In addition, even if we did that, we would probably not want to do
+    // so until after transparent retries is implemented.  Otherwise, any
+    // RPC that we attempt to send on the connection before the timeout
+    // would fail instead of being retried on a subsequent attempt.
+    grpc_chttp2_transport_start_reading(c->result->transport, args->read_buffer,
+                                        nullptr);
+    c->result->channel_args = args->args;
+  }
+  grpc_closure* notify = c->notify;
+  c->notify = nullptr;
+  GRPC_CLOSURE_SCHED(notify, error);
+  grpc_handshake_manager_destroy(c->handshake_mgr);
+  c->handshake_mgr = nullptr;
+  gpr_mu_unlock(&c->mu);
+  chttp2_connector_unref(reinterpret_cast<grpc_connector*>(c));
+}
+
+static void start_handshake_locked(chttp2_connector* c) {
+  c->handshake_mgr = grpc_handshake_manager_create();
+  grpc_handshakers_add(HANDSHAKER_CLIENT, c->args.channel_args,
+                       c->args.interested_parties, c->handshake_mgr);
+  grpc_endpoint_add_to_pollset_set(c->endpoint, c->args.interested_parties);
+  grpc_handshake_manager_do_handshake(
+      c->handshake_mgr, c->endpoint, c->args.channel_args, c->args.deadline,
+      nullptr /* acceptor */, on_handshake_done, c);
+  c->endpoint = nullptr;  // Endpoint handed off to handshake manager.
+}
+
+static void connected(void* arg, grpc_error* error) {
+  chttp2_connector* c = static_cast<chttp2_connector*>(arg);
+  gpr_mu_lock(&c->mu);
+  GPR_ASSERT(c->connecting);
+  c->connecting = false;
+  if (error != GRPC_ERROR_NONE || c->shutdown) {
+    if (error == GRPC_ERROR_NONE) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown");
+    } else {
+      error = GRPC_ERROR_REF(error);
+    }
+    memset(c->result, 0, sizeof(*c->result));
+    grpc_closure* notify = c->notify;
+    c->notify = nullptr;
+    GRPC_CLOSURE_SCHED(notify, error);
+    if (c->endpoint != nullptr) {
+      grpc_endpoint_shutdown(c->endpoint, GRPC_ERROR_REF(error));
+    }
+    gpr_mu_unlock(&c->mu);
+    chttp2_connector_unref(static_cast<grpc_connector*>(arg));
+  } else {
+    GPR_ASSERT(c->endpoint != nullptr);
+    start_handshake_locked(c);
+    gpr_mu_unlock(&c->mu);
+  }
+}
+
+static void chttp2_connector_connect(grpc_connector* con,
+                                     const grpc_connect_in_args* args,
+                                     grpc_connect_out_args* result,
+                                     grpc_closure* notify) {
+  chttp2_connector* c = reinterpret_cast<chttp2_connector*>(con);
+  grpc_resolved_address addr;
+  grpc_get_subchannel_address_arg(args->channel_args, &addr);
+  gpr_mu_lock(&c->mu);
+  GPR_ASSERT(c->notify == nullptr);
+  c->notify = notify;
+  c->args = *args;
+  c->result = result;
+  GPR_ASSERT(c->endpoint == nullptr);
+  chttp2_connector_ref(con);  // Ref taken for callback.
+  GRPC_CLOSURE_INIT(&c->connected, connected, c, grpc_schedule_on_exec_ctx);
+  GPR_ASSERT(!c->connecting);
+  c->connecting = true;
+  grpc_closure* closure = &c->connected;
+  grpc_endpoint** ep = &c->endpoint;
+  gpr_mu_unlock(&c->mu);
+  // In some implementations, the closure can be flushed before
+  // grpc_tcp_client_connect and since the closure requires access to c->mu,
+  // this can result in a deadlock. Refer
+  // https://github.com/grpc/grpc/issues/16427
+  // grpc_tcp_client_connect would fill c->endpoint with proper contents and we
+  // make sure that we would still exist at that point by taking a ref.
+  grpc_tcp_client_connect(closure, ep, args->interested_parties,
+                          args->channel_args, &addr, args->deadline);
+}
+
+static const grpc_connector_vtable chttp2_connector_vtable = {
+    chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown,
+    chttp2_connector_connect};
+
+grpc_connector* grpc_chttp2_connector_create() {
+  chttp2_connector* c = static_cast<chttp2_connector*>(gpr_zalloc(sizeof(*c)));
+  c->base.vtable = &chttp2_connector_vtable;
+  gpr_mu_init(&c->mu);
+  gpr_ref_init(&c->refs, 1);
+  return &c->base;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.h b/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.h
new file mode 100644
index 0000000..04da441
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/filters/client_channel/connector.h"
+
+grpc_connector* grpc_chttp2_connector_create();
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/README.md b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/README.md
new file mode 100644
index 0000000..fa11463
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/README.md
@@ -0,0 +1 @@
+Plugin for creating insecure channels using chttp2
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create.cc
new file mode 100644
index 0000000..e6c8c38
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create.cc
@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/transport/chttp2/client/authority.h"
+#include "src/core/ext/transport/chttp2/client/chttp2_connector.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/channel.h"
+
+static void client_channel_factory_ref(
+    grpc_client_channel_factory* cc_factory) {}
+
+static void client_channel_factory_unref(
+    grpc_client_channel_factory* cc_factory) {}
+
+static grpc_subchannel* client_channel_factory_create_subchannel(
+    grpc_client_channel_factory* cc_factory, const grpc_subchannel_args* args) {
+  grpc_subchannel_args final_sc_args;
+  memcpy(&final_sc_args, args, sizeof(*args));
+  final_sc_args.args = grpc_default_authority_add_if_not_present(args->args);
+  grpc_connector* connector = grpc_chttp2_connector_create();
+  grpc_subchannel* s = grpc_subchannel_create(connector, &final_sc_args);
+  grpc_connector_unref(connector);
+  grpc_channel_args_destroy(const_cast<grpc_channel_args*>(final_sc_args.args));
+  return s;
+}
+
+static grpc_channel* client_channel_factory_create_channel(
+    grpc_client_channel_factory* cc_factory, const char* target,
+    grpc_client_channel_type type, const grpc_channel_args* args) {
+  if (target == nullptr) {
+    gpr_log(GPR_ERROR, "cannot create channel with NULL target name");
+    return nullptr;
+  }
+  // Add channel arg containing the server URI.
+  grpc_core::UniquePtr<char> canonical_target =
+      grpc_core::ResolverRegistry::AddDefaultPrefixIfNeeded(target);
+  grpc_arg arg = grpc_channel_arg_string_create(
+      const_cast<char*>(GRPC_ARG_SERVER_URI), canonical_target.get());
+  const char* to_remove[] = {GRPC_ARG_SERVER_URI};
+  grpc_channel_args* new_args =
+      grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1);
+  grpc_channel* channel =
+      grpc_channel_create(target, new_args, GRPC_CLIENT_CHANNEL, nullptr);
+  grpc_channel_args_destroy(new_args);
+  return channel;
+}
+
+static const grpc_client_channel_factory_vtable client_channel_factory_vtable =
+    {client_channel_factory_ref, client_channel_factory_unref,
+     client_channel_factory_create_subchannel,
+     client_channel_factory_create_channel};
+
+static grpc_client_channel_factory client_channel_factory = {
+    &client_channel_factory_vtable};
+
+/* Create a client channel:
+   Asynchronously: - resolve target
+                   - connect to it (trying alternatives as presented)
+                   - perform handshakes */
+grpc_channel* grpc_insecure_channel_create(const char* target,
+                                           const grpc_channel_args* args,
+                                           void* reserved) {
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE(
+      "grpc_insecure_channel_create(target=%s, args=%p, reserved=%p)", 3,
+      (target, args, reserved));
+  GPR_ASSERT(reserved == nullptr);
+  // Add channel arg containing the client channel factory.
+  grpc_arg arg =
+      grpc_client_channel_factory_create_channel_arg(&client_channel_factory);
+  grpc_channel_args* new_args = grpc_channel_args_copy_and_add(args, &arg, 1);
+  // Create channel.
+  grpc_channel* channel = client_channel_factory_create_channel(
+      &client_channel_factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR,
+      new_args);
+  // Clean up.
+  grpc_channel_args_destroy(new_args);
+
+  return channel != nullptr ? channel
+                            : grpc_lame_client_channel_create(
+                                  target, GRPC_STATUS_INTERNAL,
+                                  "Failed to create client channel");
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
new file mode 100644
index 0000000..5bdcb38
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_posix.h>
+#include <grpc/support/log.h>
+
+#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
+
+#include <fcntl.h>
+
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/tcp_client_posix.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/transport.h"
+
+grpc_channel* grpc_insecure_channel_create_from_fd(
+    const char* target, int fd, const grpc_channel_args* args) {
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE("grpc_insecure_channel_create(target=%p, fd=%d, args=%p)", 3,
+                 (target, fd, args));
+
+  grpc_arg default_authority_arg = grpc_channel_arg_string_create(
+      (char*)GRPC_ARG_DEFAULT_AUTHORITY, (char*)"test.authority");
+  grpc_channel_args* final_args =
+      grpc_channel_args_copy_and_add(args, &default_authority_arg, 1);
+
+  int flags = fcntl(fd, F_GETFL, 0);
+  GPR_ASSERT(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0);
+
+  grpc_endpoint* client = grpc_tcp_client_create_from_fd(
+      grpc_fd_create(fd, "client", true), args, "fd-client");
+
+  grpc_transport* transport =
+      grpc_create_chttp2_transport(final_args, client, true);
+  GPR_ASSERT(transport);
+  grpc_channel* channel = grpc_channel_create(
+      target, final_args, GRPC_CLIENT_DIRECT_CHANNEL, transport);
+  grpc_channel_args_destroy(final_args);
+  grpc_chttp2_transport_start_reading(transport, nullptr, nullptr);
+
+  grpc_core::ExecCtx::Get()->Flush();
+
+  return channel != nullptr ? channel
+                            : grpc_lame_client_channel_create(
+                                  target, GRPC_STATUS_INTERNAL,
+                                  "Failed to create client channel");
+}
+
+#else  // !GPR_SUPPORT_CHANNELS_FROM_FD
+
+grpc_channel* grpc_insecure_channel_create_from_fd(
+    const char* target, int fd, const grpc_channel_args* args) {
+  GPR_ASSERT(0);
+  return nullptr;
+}
+
+#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/secure/README.md b/third_party/grpc/src/core/ext/transport/chttp2/client/secure/README.md
new file mode 100644
index 0000000..405a86e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/secure/README.md
@@ -0,0 +1 @@
+Plugin for creating secure channels using chttp2
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
new file mode 100644
index 0000000..9612698
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc
@@ -0,0 +1,229 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/ext/transport/chttp2/client/chttp2_connector.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
+#include "src/core/lib/slice/slice_hash_table.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+static void client_channel_factory_ref(
+    grpc_client_channel_factory* cc_factory) {}
+
+static void client_channel_factory_unref(
+    grpc_client_channel_factory* cc_factory) {}
+
+static grpc_subchannel_args* get_secure_naming_subchannel_args(
+    const grpc_subchannel_args* args) {
+  grpc_channel_credentials* channel_credentials =
+      grpc_channel_credentials_find_in_args(args->args);
+  if (channel_credentials == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Can't create subchannel: channel credentials missing for secure "
+            "channel.");
+    return nullptr;
+  }
+  // Make sure security connector does not already exist in args.
+  if (grpc_security_connector_find_in_args(args->args) != nullptr) {
+    gpr_log(GPR_ERROR,
+            "Can't create subchannel: security connector already present in "
+            "channel args.");
+    return nullptr;
+  }
+  // To which address are we connecting? By default, use the server URI.
+  const grpc_arg* server_uri_arg =
+      grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI);
+  const char* server_uri_str = grpc_channel_arg_get_string(server_uri_arg);
+  GPR_ASSERT(server_uri_str != nullptr);
+  grpc_uri* server_uri =
+      grpc_uri_parse(server_uri_str, true /* supress errors */);
+  GPR_ASSERT(server_uri != nullptr);
+  const grpc_core::TargetAuthorityTable* target_authority_table =
+      grpc_core::FindTargetAuthorityTableInArgs(args->args);
+  grpc_core::UniquePtr<char> authority;
+  if (target_authority_table != nullptr) {
+    // Find the authority for the target.
+    const char* target_uri_str =
+        grpc_get_subchannel_address_uri_arg(args->args);
+    grpc_uri* target_uri =
+        grpc_uri_parse(target_uri_str, false /* suppress errors */);
+    GPR_ASSERT(target_uri != nullptr);
+    if (target_uri->path[0] != '\0') {  // "path" may be empty
+      const grpc_slice key = grpc_slice_from_static_string(
+          target_uri->path[0] == '/' ? target_uri->path + 1 : target_uri->path);
+      const grpc_core::UniquePtr<char>* value =
+          target_authority_table->Get(key);
+      if (value != nullptr) authority.reset(gpr_strdup(value->get()));
+      grpc_slice_unref_internal(key);
+    }
+    grpc_uri_destroy(target_uri);
+  }
+  // If the authority hasn't already been set (either because no target
+  // authority table was present or because the target was not present
+  // in the table), fall back to using the original server URI.
+  if (authority == nullptr) {
+    authority =
+        grpc_core::ResolverRegistry::GetDefaultAuthority(server_uri_str);
+  }
+  grpc_arg args_to_add[2];
+  size_t num_args_to_add = 0;
+  if (grpc_channel_args_find(args->args, GRPC_ARG_DEFAULT_AUTHORITY) ==
+      nullptr) {
+    // If the channel args don't already contain GRPC_ARG_DEFAULT_AUTHORITY, add
+    // the arg, setting it to the value just obtained.
+    args_to_add[num_args_to_add++] = grpc_channel_arg_string_create(
+        const_cast<char*>(GRPC_ARG_DEFAULT_AUTHORITY), authority.get());
+  }
+  grpc_channel_args* args_with_authority =
+      grpc_channel_args_copy_and_add(args->args, args_to_add, num_args_to_add);
+  grpc_uri_destroy(server_uri);
+  // Create the security connector using the credentials and target name.
+  grpc_channel_args* new_args_from_connector = nullptr;
+  grpc_core::RefCountedPtr<grpc_channel_security_connector>
+      subchannel_security_connector =
+          channel_credentials->create_security_connector(
+              /*call_creds=*/nullptr, authority.get(), args_with_authority,
+              &new_args_from_connector);
+  if (subchannel_security_connector == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Failed to create secure subchannel for secure name '%s'",
+            authority.get());
+    grpc_channel_args_destroy(args_with_authority);
+    return nullptr;
+  }
+  grpc_arg new_security_connector_arg =
+      grpc_security_connector_to_arg(subchannel_security_connector.get());
+
+  grpc_channel_args* new_args = grpc_channel_args_copy_and_add(
+      new_args_from_connector != nullptr ? new_args_from_connector
+                                         : args_with_authority,
+      &new_security_connector_arg, 1);
+
+  subchannel_security_connector.reset(DEBUG_LOCATION, "lb_channel_create");
+  if (new_args_from_connector != nullptr) {
+    grpc_channel_args_destroy(new_args_from_connector);
+  }
+  grpc_channel_args_destroy(args_with_authority);
+  grpc_subchannel_args* final_sc_args =
+      static_cast<grpc_subchannel_args*>(gpr_malloc(sizeof(*final_sc_args)));
+  memcpy(final_sc_args, args, sizeof(*args));
+  final_sc_args->args = new_args;
+  return final_sc_args;
+}
+
+static grpc_subchannel* client_channel_factory_create_subchannel(
+    grpc_client_channel_factory* cc_factory, const grpc_subchannel_args* args) {
+  grpc_subchannel_args* subchannel_args =
+      get_secure_naming_subchannel_args(args);
+  if (subchannel_args == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Failed to create subchannel arguments during subchannel creation.");
+    return nullptr;
+  }
+  grpc_connector* connector = grpc_chttp2_connector_create();
+  grpc_subchannel* s = grpc_subchannel_create(connector, subchannel_args);
+  grpc_connector_unref(connector);
+  grpc_channel_args_destroy(
+      const_cast<grpc_channel_args*>(subchannel_args->args));
+  gpr_free(subchannel_args);
+  return s;
+}
+
+static grpc_channel* client_channel_factory_create_channel(
+    grpc_client_channel_factory* cc_factory, const char* target,
+    grpc_client_channel_type type, const grpc_channel_args* args) {
+  if (target == nullptr) {
+    gpr_log(GPR_ERROR, "cannot create channel with NULL target name");
+    return nullptr;
+  }
+  // Add channel arg containing the server URI.
+  grpc_core::UniquePtr<char> canonical_target =
+      grpc_core::ResolverRegistry::AddDefaultPrefixIfNeeded(target);
+  grpc_arg arg = grpc_channel_arg_string_create((char*)GRPC_ARG_SERVER_URI,
+                                                canonical_target.get());
+  const char* to_remove[] = {GRPC_ARG_SERVER_URI};
+  grpc_channel_args* new_args =
+      grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1);
+  grpc_channel* channel =
+      grpc_channel_create(target, new_args, GRPC_CLIENT_CHANNEL, nullptr);
+  grpc_channel_args_destroy(new_args);
+  return channel;
+}
+
+static const grpc_client_channel_factory_vtable client_channel_factory_vtable =
+    {client_channel_factory_ref, client_channel_factory_unref,
+     client_channel_factory_create_subchannel,
+     client_channel_factory_create_channel};
+
+static grpc_client_channel_factory client_channel_factory = {
+    &client_channel_factory_vtable};
+
+// Create a secure client channel:
+//   Asynchronously: - resolve target
+//                   - connect to it (trying alternatives as presented)
+//                   - perform handshakes
+grpc_channel* grpc_secure_channel_create(grpc_channel_credentials* creds,
+                                         const char* target,
+                                         const grpc_channel_args* args,
+                                         void* reserved) {
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE(
+      "grpc_secure_channel_create(creds=%p, target=%s, args=%p, "
+      "reserved=%p)",
+      4, ((void*)creds, target, (void*)args, (void*)reserved));
+  GPR_ASSERT(reserved == nullptr);
+  grpc_channel* channel = nullptr;
+  if (creds != nullptr) {
+    // Add channel args containing the client channel factory and channel
+    // credentials.
+    grpc_arg args_to_add[] = {
+        grpc_client_channel_factory_create_channel_arg(&client_channel_factory),
+        grpc_channel_credentials_to_arg(creds)};
+    grpc_channel_args* new_args = grpc_channel_args_copy_and_add(
+        args, args_to_add, GPR_ARRAY_SIZE(args_to_add));
+    // Create channel.
+    channel = client_channel_factory_create_channel(
+        &client_channel_factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR,
+        new_args);
+    // Clean up.
+    grpc_channel_args_destroy(new_args);
+  }
+  return channel != nullptr ? channel
+                            : grpc_lame_client_channel_create(
+                                  target, GRPC_STATUS_INTERNAL,
+                                  "Failed to create secure client channel");
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.cc b/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.cc
new file mode 100644
index 0000000..3d09187
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.cc
@@ -0,0 +1,403 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/server/chttp2_server.h"
+
+#include <grpc/grpc.h>
+
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/ext/filters/http/server/http_server_filter.h"
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/resource_quota.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/server.h"
+
+typedef struct {
+  grpc_server* server;
+  grpc_tcp_server* tcp_server;
+  grpc_channel_args* args;
+  gpr_mu mu;
+  bool shutdown;
+  grpc_closure tcp_server_shutdown_complete;
+  grpc_closure* server_destroy_listener_done;
+  grpc_handshake_manager* pending_handshake_mgrs;
+  grpc_core::RefCountedPtr<grpc_core::channelz::ListenSocketNode>
+      channelz_listen_socket;
+} server_state;
+
+typedef struct {
+  gpr_refcount refs;
+  server_state* svr_state;
+  grpc_pollset* accepting_pollset;
+  grpc_tcp_server_acceptor* acceptor;
+  grpc_handshake_manager* handshake_mgr;
+  // State for enforcing handshake timeout on receiving HTTP/2 settings.
+  grpc_chttp2_transport* transport;
+  grpc_millis deadline;
+  grpc_timer timer;
+  grpc_closure on_timeout;
+  grpc_closure on_receive_settings;
+  grpc_pollset_set* interested_parties;
+} server_connection_state;
+
+static void server_connection_state_unref(
+    server_connection_state* connection_state) {
+  if (gpr_unref(&connection_state->refs)) {
+    if (connection_state->transport != nullptr) {
+      GRPC_CHTTP2_UNREF_TRANSPORT(connection_state->transport,
+                                  "receive settings timeout");
+    }
+    grpc_pollset_set_del_pollset(connection_state->interested_parties,
+                                 connection_state->accepting_pollset);
+    grpc_pollset_set_destroy(connection_state->interested_parties);
+    gpr_free(connection_state);
+  }
+}
+
+static void on_timeout(void* arg, grpc_error* error) {
+  server_connection_state* connection_state =
+      static_cast<server_connection_state*>(arg);
+  // Note that we may be called with GRPC_ERROR_NONE when the timer fires
+  // or with an error indicating that the timer system is being shut down.
+  if (error != GRPC_ERROR_CANCELLED) {
+    grpc_transport_op* op = grpc_make_transport_op(nullptr);
+    op->disconnect_with_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Did not receive HTTP/2 settings before handshake timeout");
+    grpc_transport_perform_op(&connection_state->transport->base, op);
+  }
+  server_connection_state_unref(connection_state);
+}
+
+static void on_receive_settings(void* arg, grpc_error* error) {
+  server_connection_state* connection_state =
+      static_cast<server_connection_state*>(arg);
+  if (error == GRPC_ERROR_NONE) {
+    grpc_timer_cancel(&connection_state->timer);
+  }
+  server_connection_state_unref(connection_state);
+}
+
+static void on_handshake_done(void* arg, grpc_error* error) {
+  grpc_handshaker_args* args = static_cast<grpc_handshaker_args*>(arg);
+  server_connection_state* connection_state =
+      static_cast<server_connection_state*>(args->user_data);
+  gpr_mu_lock(&connection_state->svr_state->mu);
+  grpc_resource_user* resource_user = grpc_server_get_default_resource_user(
+      connection_state->svr_state->server);
+  if (error != GRPC_ERROR_NONE || connection_state->svr_state->shutdown) {
+    const char* error_str = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "Handshaking failed: %s", error_str);
+    grpc_resource_user* resource_user = grpc_server_get_default_resource_user(
+        connection_state->svr_state->server);
+    if (resource_user != nullptr) {
+      grpc_resource_user_free(resource_user, GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
+    }
+    if (error == GRPC_ERROR_NONE && args->endpoint != nullptr) {
+      // We were shut down after handshaking completed successfully, so
+      // destroy the endpoint here.
+      // TODO(ctiller): It is currently necessary to shutdown endpoints
+      // before destroying them, even if we know that there are no
+      // pending read/write callbacks.  This should be fixed, at which
+      // point this can be removed.
+      grpc_endpoint_shutdown(args->endpoint, GRPC_ERROR_NONE);
+      grpc_endpoint_destroy(args->endpoint);
+      grpc_channel_args_destroy(args->args);
+      grpc_slice_buffer_destroy_internal(args->read_buffer);
+      gpr_free(args->read_buffer);
+    }
+  } else {
+    // If the handshaking succeeded but there is no endpoint, then the
+    // handshaker may have handed off the connection to some external
+    // code, so we can just clean up here without creating a transport.
+    if (args->endpoint != nullptr) {
+      grpc_transport* transport = grpc_create_chttp2_transport(
+          args->args, args->endpoint, false, resource_user);
+      grpc_server_setup_transport(
+          connection_state->svr_state->server, transport,
+          connection_state->accepting_pollset, args->args,
+          grpc_chttp2_transport_get_socket_node(transport), resource_user);
+      // Use notify_on_receive_settings callback to enforce the
+      // handshake deadline.
+      connection_state->transport =
+          reinterpret_cast<grpc_chttp2_transport*>(transport);
+      gpr_ref(&connection_state->refs);
+      GRPC_CLOSURE_INIT(&connection_state->on_receive_settings,
+                        on_receive_settings, connection_state,
+                        grpc_schedule_on_exec_ctx);
+      grpc_chttp2_transport_start_reading(
+          transport, args->read_buffer, &connection_state->on_receive_settings);
+      grpc_channel_args_destroy(args->args);
+      gpr_ref(&connection_state->refs);
+      GRPC_CHTTP2_REF_TRANSPORT((grpc_chttp2_transport*)transport,
+                                "receive settings timeout");
+      GRPC_CLOSURE_INIT(&connection_state->on_timeout, on_timeout,
+                        connection_state, grpc_schedule_on_exec_ctx);
+      grpc_timer_init(&connection_state->timer, connection_state->deadline,
+                      &connection_state->on_timeout);
+    } else {
+      if (resource_user != nullptr) {
+        grpc_resource_user_free(resource_user,
+                                GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
+      }
+    }
+  }
+  grpc_handshake_manager_pending_list_remove(
+      &connection_state->svr_state->pending_handshake_mgrs,
+      connection_state->handshake_mgr);
+  gpr_mu_unlock(&connection_state->svr_state->mu);
+  grpc_handshake_manager_destroy(connection_state->handshake_mgr);
+  gpr_free(connection_state->acceptor);
+  grpc_tcp_server_unref(connection_state->svr_state->tcp_server);
+  server_connection_state_unref(connection_state);
+}
+
+static void on_accept(void* arg, grpc_endpoint* tcp,
+                      grpc_pollset* accepting_pollset,
+                      grpc_tcp_server_acceptor* acceptor) {
+  server_state* state = static_cast<server_state*>(arg);
+  gpr_mu_lock(&state->mu);
+  if (state->shutdown) {
+    gpr_mu_unlock(&state->mu);
+    grpc_endpoint_shutdown(tcp, GRPC_ERROR_NONE);
+    grpc_endpoint_destroy(tcp);
+    gpr_free(acceptor);
+    return;
+  }
+  grpc_resource_user* resource_user =
+      grpc_server_get_default_resource_user(state->server);
+  if (resource_user != nullptr &&
+      !grpc_resource_user_safe_alloc(resource_user,
+                                     GRPC_RESOURCE_QUOTA_CHANNEL_SIZE)) {
+    gpr_log(
+        GPR_ERROR,
+        "Memory quota exhausted, rejecting the connection, no handshaking.");
+    gpr_mu_unlock(&state->mu);
+    grpc_endpoint_shutdown(tcp, GRPC_ERROR_NONE);
+    grpc_endpoint_destroy(tcp);
+    gpr_free(acceptor);
+    return;
+  }
+  grpc_handshake_manager* handshake_mgr = grpc_handshake_manager_create();
+  grpc_handshake_manager_pending_list_add(&state->pending_handshake_mgrs,
+                                          handshake_mgr);
+  grpc_tcp_server_ref(state->tcp_server);
+  gpr_mu_unlock(&state->mu);
+  server_connection_state* connection_state =
+      static_cast<server_connection_state*>(
+          gpr_zalloc(sizeof(*connection_state)));
+  gpr_ref_init(&connection_state->refs, 1);
+  connection_state->svr_state = state;
+  connection_state->accepting_pollset = accepting_pollset;
+  connection_state->acceptor = acceptor;
+  connection_state->handshake_mgr = handshake_mgr;
+  connection_state->interested_parties = grpc_pollset_set_create();
+  grpc_pollset_set_add_pollset(connection_state->interested_parties,
+                               connection_state->accepting_pollset);
+  grpc_handshakers_add(HANDSHAKER_SERVER, state->args,
+                       connection_state->interested_parties,
+                       connection_state->handshake_mgr);
+  const grpc_arg* timeout_arg =
+      grpc_channel_args_find(state->args, GRPC_ARG_SERVER_HANDSHAKE_TIMEOUT_MS);
+  connection_state->deadline =
+      grpc_core::ExecCtx::Get()->Now() +
+      grpc_channel_arg_get_integer(timeout_arg,
+                                   {120 * GPR_MS_PER_SEC, 1, INT_MAX});
+  grpc_handshake_manager_do_handshake(connection_state->handshake_mgr, tcp,
+                                      state->args, connection_state->deadline,
+                                      acceptor, on_handshake_done,
+                                      connection_state);
+}
+
+/* Server callback: start listening on our ports */
+static void server_start_listener(grpc_server* server, void* arg,
+                                  grpc_pollset** pollsets,
+                                  size_t pollset_count) {
+  server_state* state = static_cast<server_state*>(arg);
+  gpr_mu_lock(&state->mu);
+  state->shutdown = false;
+  gpr_mu_unlock(&state->mu);
+  grpc_tcp_server_start(state->tcp_server, pollsets, pollset_count, on_accept,
+                        state);
+}
+
+static void tcp_server_shutdown_complete(void* arg, grpc_error* error) {
+  server_state* state = static_cast<server_state*>(arg);
+  /* ensure all threads have unlocked */
+  gpr_mu_lock(&state->mu);
+  grpc_closure* destroy_done = state->server_destroy_listener_done;
+  GPR_ASSERT(state->shutdown);
+  grpc_handshake_manager_pending_list_shutdown_all(
+      state->pending_handshake_mgrs, GRPC_ERROR_REF(error));
+  state->channelz_listen_socket.reset();
+  gpr_mu_unlock(&state->mu);
+  // Flush queued work before destroying handshaker factory, since that
+  // may do a synchronous unref.
+  grpc_core::ExecCtx::Get()->Flush();
+  if (destroy_done != nullptr) {
+    destroy_done->cb(destroy_done->cb_arg, GRPC_ERROR_REF(error));
+    grpc_core::ExecCtx::Get()->Flush();
+  }
+  grpc_channel_args_destroy(state->args);
+  gpr_mu_destroy(&state->mu);
+  gpr_free(state);
+}
+
+/* Server callback: destroy the tcp listener (so we don't generate further
+   callbacks) */
+static void server_destroy_listener(grpc_server* server, void* arg,
+                                    grpc_closure* destroy_done) {
+  server_state* state = static_cast<server_state*>(arg);
+  gpr_mu_lock(&state->mu);
+  state->shutdown = true;
+  state->server_destroy_listener_done = destroy_done;
+  grpc_tcp_server* tcp_server = state->tcp_server;
+  gpr_mu_unlock(&state->mu);
+  grpc_tcp_server_shutdown_listeners(tcp_server);
+  grpc_tcp_server_unref(tcp_server);
+}
+
+grpc_error* grpc_chttp2_server_add_port(grpc_server* server, const char* addr,
+                                        grpc_channel_args* args,
+                                        int* port_num) {
+  grpc_resolved_addresses* resolved = nullptr;
+  grpc_tcp_server* tcp_server = nullptr;
+  size_t i;
+  size_t count = 0;
+  int port_temp;
+  grpc_error* err = GRPC_ERROR_NONE;
+  server_state* state = nullptr;
+  grpc_error** errors = nullptr;
+  size_t naddrs = 0;
+  const grpc_arg* arg = nullptr;
+  intptr_t socket_uuid = 0;
+
+  *port_num = -1;
+
+  /* resolve address */
+  err = grpc_blocking_resolve_address(addr, "https", &resolved);
+  if (err != GRPC_ERROR_NONE) {
+    goto error;
+  }
+  state = static_cast<server_state*>(gpr_zalloc(sizeof(*state)));
+  GRPC_CLOSURE_INIT(&state->tcp_server_shutdown_complete,
+                    tcp_server_shutdown_complete, state,
+                    grpc_schedule_on_exec_ctx);
+  err = grpc_tcp_server_create(&state->tcp_server_shutdown_complete, args,
+                               &tcp_server);
+  if (err != GRPC_ERROR_NONE) {
+    goto error;
+  }
+
+  state->server = server;
+  state->tcp_server = tcp_server;
+  state->args = args;
+  state->shutdown = true;
+  gpr_mu_init(&state->mu);
+
+  naddrs = resolved->naddrs;
+  errors = static_cast<grpc_error**>(gpr_malloc(sizeof(*errors) * naddrs));
+  for (i = 0; i < naddrs; i++) {
+    errors[i] =
+        grpc_tcp_server_add_port(tcp_server, &resolved->addrs[i], &port_temp);
+    if (errors[i] == GRPC_ERROR_NONE) {
+      if (*port_num == -1) {
+        *port_num = port_temp;
+      } else {
+        GPR_ASSERT(*port_num == port_temp);
+      }
+      count++;
+    }
+  }
+  if (count == 0) {
+    char* msg;
+    gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved",
+                 naddrs);
+    err = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, errors, naddrs);
+    gpr_free(msg);
+    goto error;
+  } else if (count != naddrs) {
+    char* msg;
+    gpr_asprintf(&msg,
+                 "Only %" PRIuPTR " addresses added out of total %" PRIuPTR
+                 " resolved",
+                 count, naddrs);
+    err = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, errors, naddrs);
+    gpr_free(msg);
+
+    const char* warning_message = grpc_error_string(err);
+    gpr_log(GPR_INFO, "WARNING: %s", warning_message);
+
+    /* we managed to bind some addresses: continue */
+  }
+  grpc_resolved_addresses_destroy(resolved);
+
+  arg = grpc_channel_args_find(args, GRPC_ARG_ENABLE_CHANNELZ);
+  if (grpc_channel_arg_get_bool(arg, GRPC_ENABLE_CHANNELZ_DEFAULT)) {
+    state->channelz_listen_socket =
+        grpc_core::MakeRefCounted<grpc_core::channelz::ListenSocketNode>(
+            grpc_core::UniquePtr<char>(gpr_strdup(addr)));
+    socket_uuid = state->channelz_listen_socket->uuid();
+  }
+
+  /* Register with the server only upon success */
+  grpc_server_add_listener(server, state, server_start_listener,
+                           server_destroy_listener, socket_uuid);
+  goto done;
+
+/* Error path: cleanup and return */
+error:
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
+  if (resolved) {
+    grpc_resolved_addresses_destroy(resolved);
+  }
+  if (tcp_server) {
+    grpc_tcp_server_unref(tcp_server);
+  } else {
+    grpc_channel_args_destroy(args);
+    gpr_free(state);
+  }
+  *port_num = 0;
+
+done:
+  if (errors != nullptr) {
+    for (i = 0; i < naddrs; i++) {
+      GRPC_ERROR_UNREF(errors[i]);
+    }
+    gpr_free(errors);
+  }
+  return err;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.h b/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.h
new file mode 100644
index 0000000..6e51001
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/iomgr/error.h"
+
+/// Adds a port to \a server.  Sets \a port_num to the port number.
+/// Takes ownership of \a args.
+grpc_error* grpc_chttp2_server_add_port(grpc_server* server, const char* addr,
+                                        grpc_channel_args* args, int* port_num);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/README.md b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/README.md
new file mode 100644
index 0000000..fc0bc14
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/README.md
@@ -0,0 +1 @@
+Plugin for creating insecure servers using chttp2
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
new file mode 100644
index 0000000..99f18cd
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/ext/transport/chttp2/server/chttp2_server.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/server.h"
+
+int grpc_server_add_insecure_http2_port(grpc_server* server, const char* addr) {
+  grpc_core::ExecCtx exec_ctx;
+  int port_num = 0;
+  GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2,
+                 (server, addr));
+  grpc_error* err = grpc_chttp2_server_add_port(
+      server, addr,
+      grpc_channel_args_copy(grpc_server_get_channel_args(server)), &port_num);
+  if (err != GRPC_ERROR_NONE) {
+    const char* msg = grpc_error_string(err);
+    gpr_log(GPR_ERROR, "%s", msg);
+
+    GRPC_ERROR_UNREF(err);
+  }
+  return port_num;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
new file mode 100644
index 0000000..c29c1e5
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_posix.h>
+#include <grpc/support/log.h>
+
+#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/surface/server.h"
+
+void grpc_server_add_insecure_channel_from_fd(grpc_server* server,
+                                              void* reserved, int fd) {
+  GPR_ASSERT(reserved == nullptr);
+
+  grpc_core::ExecCtx exec_ctx;
+  char* name;
+  gpr_asprintf(&name, "fd:%d", fd);
+
+  grpc_endpoint* server_endpoint =
+      grpc_tcp_create(grpc_fd_create(fd, name, true),
+                      grpc_server_get_channel_args(server), name);
+
+  gpr_free(name);
+
+  const grpc_channel_args* server_args = grpc_server_get_channel_args(server);
+  grpc_transport* transport = grpc_create_chttp2_transport(
+      server_args, server_endpoint, false /* is_client */);
+
+  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(server_endpoint, pollsets[i]);
+  }
+
+  grpc_server_setup_transport(server, transport, nullptr, server_args, nullptr);
+  grpc_chttp2_transport_start_reading(transport, nullptr, nullptr);
+}
+
+#else  // !GPR_SUPPORT_CHANNELS_FROM_FD
+
+void grpc_server_add_insecure_channel_from_fd(grpc_server* server,
+                                              void* reserved, int fd) {
+  GPR_ASSERT(0);
+}
+
+#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/secure/README.md b/third_party/grpc/src/core/ext/transport/chttp2/server/secure/README.md
new file mode 100644
index 0000000..6bda696
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/server/secure/README.md
@@ -0,0 +1 @@
+Plugin for creating secure servers using chttp2
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc b/third_party/grpc/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
new file mode 100644
index 0000000..98fdb62
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc
@@ -0,0 +1,86 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/server/chttp2_server.h"
+
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/server.h"
+
+int grpc_server_add_secure_http2_port(grpc_server* server, const char* addr,
+                                      grpc_server_credentials* creds) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_error* err = GRPC_ERROR_NONE;
+  grpc_core::RefCountedPtr<grpc_server_security_connector> sc;
+  int port_num = 0;
+  grpc_channel_args* args = nullptr;
+  GRPC_API_TRACE(
+      "grpc_server_add_secure_http2_port("
+      "server=%p, addr=%s, creds=%p)",
+      3, (server, addr, creds));
+  // Create security context.
+  if (creds == nullptr) {
+    err = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "No credentials specified for secure server port (creds==NULL)");
+    goto done;
+  }
+  sc = creds->create_security_connector();
+  if (sc == nullptr) {
+    char* msg;
+    gpr_asprintf(&msg,
+                 "Unable to create secure server with credentials of type %s.",
+                 creds->type());
+    err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    goto done;
+  }
+  // Create channel args.
+  grpc_arg args_to_add[2];
+  args_to_add[0] = grpc_server_credentials_to_arg(creds);
+  args_to_add[1] = grpc_security_connector_to_arg(sc.get());
+  args =
+      grpc_channel_args_copy_and_add(grpc_server_get_channel_args(server),
+                                     args_to_add, GPR_ARRAY_SIZE(args_to_add));
+  // Add server port.
+  err = grpc_chttp2_server_add_port(server, addr, args, &port_num);
+done:
+  sc.reset(DEBUG_LOCATION, "server");
+
+  if (err != GRPC_ERROR_NONE) {
+    const char* msg = grpc_error_string(err);
+    gpr_log(GPR_ERROR, "%s", msg);
+
+    GRPC_ERROR_UNREF(err);
+  }
+  return port_num;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/README.md b/third_party/grpc/src/core/ext/transport/chttp2/transport/README.md
new file mode 100644
index 0000000..4684e58
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/README.md
@@ -0,0 +1,4 @@
+chttp2 transport plugin - implements grpc over http2
+
+Used by chttp2/{client,server}/{insecure,secure} plugins to implement most of
+their functionality
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.cc
new file mode 100644
index 0000000..b660a45
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.cc
@@ -0,0 +1,250 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/ext/transport/chttp2/transport/bin_decoder.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+static uint8_t decode_table[] = {
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 62,   0x40, 0x40, 0x40, 63,
+    52,   53,   54,   55,   56,   57,   58,   59,   60,   61,   0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0,    1,    2,    3,    4,    5,    6,
+    7,    8,    9,    10,   11,   12,   13,   14,   15,   16,   17,   18,
+    19,   20,   21,   22,   23,   24,   25,   0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 26,   27,   28,   29,   30,   31,   32,   33,   34,   35,   36,
+    37,   38,   39,   40,   41,   42,   43,   44,   45,   46,   47,   48,
+    49,   50,   51,   0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+    0x40, 0x40, 0x40, 0x40};
+
+static const uint8_t tail_xtra[4] = {0, 0, 1, 2};
+
+static bool input_is_valid(uint8_t* input_ptr, size_t length) {
+  size_t i;
+
+  for (i = 0; i < length; ++i) {
+    if (GPR_UNLIKELY((decode_table[input_ptr[i]] & 0xC0) != 0)) {
+      gpr_log(GPR_ERROR,
+              "Base64 decoding failed, invalid character '%c' in base64 "
+              "input.\n",
+              static_cast<char>(*input_ptr));
+      return false;
+    }
+  }
+  return true;
+}
+
+#define COMPOSE_OUTPUT_BYTE_0(input_ptr)        \
+  (uint8_t)((decode_table[input_ptr[0]] << 2) | \
+            (decode_table[input_ptr[1]] >> 4))
+
+#define COMPOSE_OUTPUT_BYTE_1(input_ptr)        \
+  (uint8_t)((decode_table[input_ptr[1]] << 4) | \
+            (decode_table[input_ptr[2]] >> 2))
+
+#define COMPOSE_OUTPUT_BYTE_2(input_ptr) \
+  (uint8_t)((decode_table[input_ptr[2]] << 6) | decode_table[input_ptr[3]])
+
+// By RFC 4648, if the length of the encoded string without padding is 4n+r,
+// the length of decoded string is: 1) 3n if r = 0, 2) 3n + 1 if r = 2, 3, or
+// 3) invalid if r = 1.
+size_t grpc_chttp2_base64_infer_length_after_decode(const grpc_slice& slice) {
+  size_t len = GRPC_SLICE_LENGTH(slice);
+  const uint8_t* bytes = GRPC_SLICE_START_PTR(slice);
+  while (len > 0 && bytes[len - 1] == '=') {
+    len--;
+  }
+  if (GPR_UNLIKELY(GRPC_SLICE_LENGTH(slice) - len > 2)) {
+    gpr_log(GPR_ERROR,
+            "Base64 decoding failed. Input has more than 2 paddings.");
+    return 0;
+  }
+  size_t tuples = len / 4;
+  size_t tail_case = len % 4;
+  if (GPR_UNLIKELY(tail_case == 1)) {
+    gpr_log(GPR_ERROR,
+            "Base64 decoding failed. Input has a length of %zu (without"
+            " padding), which is invalid.\n",
+            len);
+    return 0;
+  }
+  return tuples * 3 + tail_xtra[tail_case];
+}
+
+bool grpc_base64_decode_partial(struct grpc_base64_decode_context* ctx) {
+  size_t input_tail;
+
+  if (ctx->input_cur > ctx->input_end || ctx->output_cur > ctx->output_end) {
+    return false;
+  }
+
+  // Process a block of 4 input characters and 3 output bytes
+  while (ctx->input_end >= ctx->input_cur + 4 &&
+         ctx->output_end >= ctx->output_cur + 3) {
+    if (!input_is_valid(ctx->input_cur, 4)) return false;
+    ctx->output_cur[0] = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur);
+    ctx->output_cur[1] = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur);
+    ctx->output_cur[2] = COMPOSE_OUTPUT_BYTE_2(ctx->input_cur);
+    ctx->output_cur += 3;
+    ctx->input_cur += 4;
+  }
+
+  // Process the tail of input data
+  input_tail = static_cast<size_t>(ctx->input_end - ctx->input_cur);
+  if (input_tail == 4) {
+    // Process the input data with pad chars
+    if (ctx->input_cur[3] == '=') {
+      if (ctx->input_cur[2] == '=' && ctx->output_end >= ctx->output_cur + 1) {
+        if (!input_is_valid(ctx->input_cur, 2)) return false;
+        *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur);
+        ctx->input_cur += 4;
+      } else if (ctx->output_end >= ctx->output_cur + 2) {
+        if (!input_is_valid(ctx->input_cur, 3)) return false;
+        *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur);
+        *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur);
+        ;
+        ctx->input_cur += 4;
+      }
+    }
+
+  } else if (ctx->contains_tail && input_tail > 1) {
+    // Process the input data without pad chars, but constains_tail is set
+    if (ctx->output_end >= ctx->output_cur + tail_xtra[input_tail]) {
+      if (!input_is_valid(ctx->input_cur, input_tail)) return false;
+      switch (input_tail) {
+        case 3:
+          ctx->output_cur[1] = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur);
+        /* fallthrough */
+        case 2:
+          ctx->output_cur[0] = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur);
+      }
+      ctx->output_cur += tail_xtra[input_tail];
+      ctx->input_cur += input_tail;
+    }
+  }
+
+  return true;
+}
+
+grpc_slice grpc_chttp2_base64_decode(grpc_slice input) {
+  size_t input_length = GRPC_SLICE_LENGTH(input);
+  size_t output_length = input_length / 4 * 3;
+  struct grpc_base64_decode_context ctx;
+  grpc_slice output;
+
+  if (GPR_UNLIKELY(input_length % 4 != 0)) {
+    gpr_log(GPR_ERROR,
+            "Base64 decoding failed, input of "
+            "grpc_chttp2_base64_decode has a length of %d, which is not a "
+            "multiple of 4.\n",
+            static_cast<int>(input_length));
+    return grpc_empty_slice();
+  }
+
+  if (input_length > 0) {
+    uint8_t* input_end = GRPC_SLICE_END_PTR(input);
+    if (*(--input_end) == '=') {
+      output_length--;
+      if (*(--input_end) == '=') {
+        output_length--;
+      }
+    }
+  }
+  output = GRPC_SLICE_MALLOC(output_length);
+
+  ctx.input_cur = GRPC_SLICE_START_PTR(input);
+  ctx.input_end = GRPC_SLICE_END_PTR(input);
+  ctx.output_cur = GRPC_SLICE_START_PTR(output);
+  ctx.output_end = GRPC_SLICE_END_PTR(output);
+  ctx.contains_tail = false;
+
+  if (GPR_UNLIKELY(!grpc_base64_decode_partial(&ctx))) {
+    char* s = grpc_slice_to_c_string(input);
+    gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s);
+    gpr_free(s);
+    grpc_slice_unref_internal(output);
+    return grpc_empty_slice();
+  }
+  GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output));
+  GPR_ASSERT(ctx.input_cur == GRPC_SLICE_END_PTR(input));
+  return output;
+}
+
+grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input,
+                                                 size_t output_length) {
+  size_t input_length = GRPC_SLICE_LENGTH(input);
+  grpc_slice output = GRPC_SLICE_MALLOC(output_length);
+  struct grpc_base64_decode_context ctx;
+
+  // The length of a base64 string cannot be 4 * n + 1
+  if (GPR_UNLIKELY(input_length % 4 == 1)) {
+    gpr_log(GPR_ERROR,
+            "Base64 decoding failed, input of "
+            "grpc_chttp2_base64_decode_with_length has a length of %d, which "
+            "has a tail of 1 byte.\n",
+            static_cast<int>(input_length));
+    grpc_slice_unref_internal(output);
+    return grpc_empty_slice();
+  }
+
+  if (GPR_UNLIKELY(output_length >
+                   input_length / 4 * 3 + tail_xtra[input_length % 4])) {
+    gpr_log(
+        GPR_ERROR,
+        "Base64 decoding failed, output_length %d is longer "
+        "than the max possible output length %d.\n",
+        static_cast<int>(output_length),
+        static_cast<int>(input_length / 4 * 3 + tail_xtra[input_length % 4]));
+    grpc_slice_unref_internal(output);
+    return grpc_empty_slice();
+  }
+
+  ctx.input_cur = GRPC_SLICE_START_PTR(input);
+  ctx.input_end = GRPC_SLICE_END_PTR(input);
+  ctx.output_cur = GRPC_SLICE_START_PTR(output);
+  ctx.output_end = GRPC_SLICE_END_PTR(output);
+  ctx.contains_tail = true;
+
+  if (GPR_UNLIKELY(!grpc_base64_decode_partial(&ctx))) {
+    char* s = grpc_slice_to_c_string(input);
+    gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s);
+    gpr_free(s);
+    grpc_slice_unref_internal(output);
+    return grpc_empty_slice();
+  }
+  GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output));
+  GPR_ASSERT(ctx.input_cur <= GRPC_SLICE_END_PTR(input));
+  return output;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.h
new file mode 100644
index 0000000..8a4d4a7
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include <stdbool.h>
+
+struct grpc_base64_decode_context {
+  /* input/output: */
+  uint8_t* input_cur;
+  uint8_t* input_end;
+  uint8_t* output_cur;
+  uint8_t* output_end;
+  /* Indicate if the decoder should handle the tail of input data*/
+  bool contains_tail;
+};
+
+/* base64 decode a grpc_base64_decode_context util either input_end is reached
+   or output_end is reached. When input_end is reached, (input_end - input_cur)
+   is less than 4. When output_end is reached, (output_end - output_cur) is less
+   than 3. Returns false if decoding is failed. */
+bool grpc_base64_decode_partial(struct grpc_base64_decode_context* ctx);
+
+/* base64 decode a slice with pad chars. Returns a new slice, does not take
+   ownership of the input. Returns an empty slice if decoding is failed. */
+grpc_slice grpc_chttp2_base64_decode(grpc_slice input);
+
+/* base64 decode a slice without pad chars, data length is needed. Returns a new
+   slice, does not take ownership of the input. Returns an empty slice if
+   decoding is failed. */
+grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input,
+                                                 size_t output_length);
+
+/* Infer the length of decoded data from encoded data. */
+size_t grpc_chttp2_base64_infer_length_after_decode(const grpc_slice& slice);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.cc
new file mode 100644
index 0000000..bad29e3
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.cc
@@ -0,0 +1,231 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include "src/core/ext/transport/chttp2/transport/huffsyms.h"
+
+static const char alphabet[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+typedef struct {
+  uint16_t bits;
+  uint8_t length;
+} b64_huff_sym;
+
+static const b64_huff_sym huff_alphabet[64] = {
+    {0x21, 6}, {0x5d, 7}, {0x5e, 7},   {0x5f, 7}, {0x60, 7}, {0x61, 7},
+    {0x62, 7}, {0x63, 7}, {0x64, 7},   {0x65, 7}, {0x66, 7}, {0x67, 7},
+    {0x68, 7}, {0x69, 7}, {0x6a, 7},   {0x6b, 7}, {0x6c, 7}, {0x6d, 7},
+    {0x6e, 7}, {0x6f, 7}, {0x70, 7},   {0x71, 7}, {0x72, 7}, {0xfc, 8},
+    {0x73, 7}, {0xfd, 8}, {0x3, 5},    {0x23, 6}, {0x4, 5},  {0x24, 6},
+    {0x5, 5},  {0x25, 6}, {0x26, 6},   {0x27, 6}, {0x6, 5},  {0x74, 7},
+    {0x75, 7}, {0x28, 6}, {0x29, 6},   {0x2a, 6}, {0x7, 5},  {0x2b, 6},
+    {0x76, 7}, {0x2c, 6}, {0x8, 5},    {0x9, 5},  {0x2d, 6}, {0x77, 7},
+    {0x78, 7}, {0x79, 7}, {0x7a, 7},   {0x7b, 7}, {0x0, 5},  {0x1, 5},
+    {0x2, 5},  {0x19, 6}, {0x1a, 6},   {0x1b, 6}, {0x1c, 6}, {0x1d, 6},
+    {0x1e, 6}, {0x1f, 6}, {0x7fb, 11}, {0x18, 6}};
+
+static const uint8_t tail_xtra[3] = {0, 2, 3};
+
+grpc_slice grpc_chttp2_base64_encode(grpc_slice input) {
+  size_t input_length = GRPC_SLICE_LENGTH(input);
+  size_t input_triplets = input_length / 3;
+  size_t tail_case = input_length % 3;
+  size_t output_length = input_triplets * 4 + tail_xtra[tail_case];
+  grpc_slice output = GRPC_SLICE_MALLOC(output_length);
+  uint8_t* in = GRPC_SLICE_START_PTR(input);
+  char* out = reinterpret_cast<char*> GRPC_SLICE_START_PTR(output);
+  size_t i;
+
+  /* encode full triplets */
+  for (i = 0; i < input_triplets; i++) {
+    out[0] = alphabet[in[0] >> 2];
+    out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)];
+    out[2] = alphabet[((in[1] & 0xf) << 2) | (in[2] >> 6)];
+    out[3] = alphabet[in[2] & 0x3f];
+    out += 4;
+    in += 3;
+  }
+
+  /* encode the remaining bytes */
+  switch (tail_case) {
+    case 0:
+      break;
+    case 1:
+      out[0] = alphabet[in[0] >> 2];
+      out[1] = alphabet[(in[0] & 0x3) << 4];
+      out += 2;
+      in += 1;
+      break;
+    case 2:
+      out[0] = alphabet[in[0] >> 2];
+      out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)];
+      out[2] = alphabet[(in[1] & 0xf) << 2];
+      out += 3;
+      in += 2;
+      break;
+  }
+
+  GPR_ASSERT(out == (char*)GRPC_SLICE_END_PTR(output));
+  GPR_ASSERT(in == GRPC_SLICE_END_PTR(input));
+  return output;
+}
+
+grpc_slice grpc_chttp2_huffman_compress(grpc_slice input) {
+  size_t nbits;
+  uint8_t* in;
+  uint8_t* out;
+  grpc_slice output;
+  uint32_t temp = 0;
+  uint32_t temp_length = 0;
+
+  nbits = 0;
+  for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input);
+       ++in) {
+    nbits += grpc_chttp2_huffsyms[*in].length;
+  }
+
+  output = GRPC_SLICE_MALLOC(nbits / 8 + (nbits % 8 != 0));
+  out = GRPC_SLICE_START_PTR(output);
+  for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input);
+       ++in) {
+    int sym = *in;
+    temp <<= grpc_chttp2_huffsyms[sym].length;
+    temp |= grpc_chttp2_huffsyms[sym].bits;
+    temp_length += grpc_chttp2_huffsyms[sym].length;
+
+    while (temp_length > 8) {
+      temp_length -= 8;
+      *out++ = static_cast<uint8_t>(temp >> temp_length);
+    }
+  }
+
+  if (temp_length) {
+    /* 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 */
+    *out++ =
+        static_cast<uint8_t>(static_cast<uint8_t>(temp << (8u - temp_length)) |
+                             static_cast<uint8_t>(0xffu >> temp_length));
+  }
+
+  GPR_ASSERT(out == GRPC_SLICE_END_PTR(output));
+
+  return output;
+}
+
+typedef struct {
+  uint32_t temp;
+  uint32_t temp_length;
+  uint8_t* out;
+} huff_out;
+
+static void enc_flush_some(huff_out* out) {
+  while (out->temp_length > 8) {
+    out->temp_length -= 8;
+    *out->out++ = static_cast<uint8_t>(out->temp >> out->temp_length);
+  }
+}
+
+static void enc_add2(huff_out* out, uint8_t a, uint8_t b) {
+  b64_huff_sym sa = huff_alphabet[a];
+  b64_huff_sym sb = huff_alphabet[b];
+  out->temp = (out->temp << (sa.length + sb.length)) |
+              (static_cast<uint32_t>(sa.bits) << sb.length) | sb.bits;
+  out->temp_length +=
+      static_cast<uint32_t>(sa.length) + static_cast<uint32_t>(sb.length);
+  enc_flush_some(out);
+}
+
+static void enc_add1(huff_out* out, uint8_t a) {
+  b64_huff_sym sa = huff_alphabet[a];
+  out->temp = (out->temp << sa.length) | sa.bits;
+  out->temp_length += sa.length;
+  enc_flush_some(out);
+}
+
+grpc_slice grpc_chttp2_base64_encode_and_huffman_compress(grpc_slice input) {
+  size_t input_length = GRPC_SLICE_LENGTH(input);
+  size_t input_triplets = input_length / 3;
+  size_t tail_case = input_length % 3;
+  size_t output_syms = input_triplets * 4 + tail_xtra[tail_case];
+  size_t max_output_bits = 11 * output_syms;
+  size_t max_output_length = max_output_bits / 8 + (max_output_bits % 8 != 0);
+  grpc_slice output = GRPC_SLICE_MALLOC(max_output_length);
+  uint8_t* in = GRPC_SLICE_START_PTR(input);
+  uint8_t* start_out = GRPC_SLICE_START_PTR(output);
+  huff_out out;
+  size_t i;
+
+  out.temp = 0;
+  out.temp_length = 0;
+  out.out = start_out;
+
+  /* encode full triplets */
+  for (i = 0; i < input_triplets; i++) {
+    const uint8_t low_to_high = static_cast<uint8_t>((in[0] & 0x3) << 4);
+    const uint8_t high_to_low = in[1] >> 4;
+    enc_add2(&out, in[0] >> 2, low_to_high | high_to_low);
+
+    const uint8_t a = static_cast<uint8_t>((in[1] & 0xf) << 2);
+    const uint8_t b = (in[2] >> 6);
+    enc_add2(&out, a | b, in[2] & 0x3f);
+    in += 3;
+  }
+
+  /* encode the remaining bytes */
+  switch (tail_case) {
+    case 0:
+      break;
+    case 1:
+      enc_add2(&out, in[0] >> 2, static_cast<uint8_t>((in[0] & 0x3) << 4));
+      in += 1;
+      break;
+    case 2: {
+      const uint8_t low_to_high = static_cast<uint8_t>((in[0] & 0x3) << 4);
+      const uint8_t high_to_low = in[1] >> 4;
+      enc_add2(&out, in[0] >> 2, low_to_high | high_to_low);
+      enc_add1(&out, static_cast<uint8_t>((in[1] & 0xf) << 2));
+      in += 2;
+      break;
+    }
+  }
+
+  if (out.temp_length) {
+    /* 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 */
+    *out.out++ = static_cast<uint8_t>(
+        static_cast<uint8_t>(out.temp << (8u - out.temp_length)) |
+        static_cast<uint8_t>(0xffu >> out.temp_length));
+  }
+
+  GPR_ASSERT(out.out <= GRPC_SLICE_END_PTR(output));
+  GRPC_SLICE_SET_LENGTH(output, out.out - start_out);
+
+  GPR_ASSERT(in == GRPC_SLICE_END_PTR(input));
+  return output;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.h
new file mode 100644
index 0000000..1b7bb15
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+
+/* base64 encode a slice. Returns a new slice, does not take ownership of the
+   input */
+grpc_slice grpc_chttp2_base64_encode(grpc_slice input);
+
+/* Compress a slice with the static huffman encoder detailed in the hpack
+   standard. Returns a new slice, does not take ownership of the input */
+grpc_slice grpc_chttp2_huffman_compress(grpc_slice input);
+
+/* equivalent to:
+   grpc_slice x = grpc_chttp2_base64_encode(input);
+   grpc_slice y = grpc_chttp2_huffman_compress(x);
+   grpc_slice_unref_internal( x);
+   return y; */
+grpc_slice grpc_chttp2_base64_encode_and_huffman_compress(grpc_slice input);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
new file mode 100644
index 0000000..531ea73
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/transport/metadata.h"
+
+void grpc_chttp2_plugin_init(void) {
+  g_flow_control_enabled = true;
+  char* env_variable = gpr_getenv("GRPC_EXPERIMENTAL_DISABLE_FLOW_CONTROL");
+  if (env_variable != nullptr) {
+    g_flow_control_enabled = false;
+    gpr_free(env_variable);
+  }
+}
+
+void grpc_chttp2_plugin_shutdown(void) {}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
new file mode 100644
index 0000000..7f4627f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -0,0 +1,3181 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/transport/context_list.h"
+#include "src/core/ext/transport/chttp2/transport/frame_data.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+#include "src/core/ext/transport/chttp2/transport/varint.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/compression/stream_compression.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/transport/http2_errors.h"
+#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/status_conversion.h"
+#include "src/core/lib/transport/timeout_encoding.h"
+#include "src/core/lib/transport/transport.h"
+#include "src/core/lib/transport/transport_impl.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+#define DEFAULT_CONNECTION_WINDOW_TARGET (1024 * 1024)
+#define MAX_WINDOW 0x7fffffffu
+#define MAX_WRITE_BUFFER_SIZE (64 * 1024 * 1024)
+#define DEFAULT_MAX_HEADER_LIST_SIZE (8 * 1024)
+
+#define DEFAULT_CLIENT_KEEPALIVE_TIME_MS INT_MAX
+#define DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_MS 20000 /* 20 seconds */
+#define DEFAULT_SERVER_KEEPALIVE_TIME_MS 7200000  /* 2 hours */
+#define DEFAULT_SERVER_KEEPALIVE_TIMEOUT_MS 20000 /* 20 seconds */
+#define DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS false
+#define KEEPALIVE_TIME_BACKOFF_MULTIPLIER 2
+
+#define DEFAULT_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS 300000 /* 5 minutes */
+#define DEFAULT_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS 300000 /* 5 minutes */
+#define DEFAULT_MAX_PINGS_BETWEEN_DATA 2
+#define DEFAULT_MAX_PING_STRIKES 2
+
+static int g_default_client_keepalive_time_ms =
+    DEFAULT_CLIENT_KEEPALIVE_TIME_MS;
+static int g_default_client_keepalive_timeout_ms =
+    DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_MS;
+static int g_default_server_keepalive_time_ms =
+    DEFAULT_SERVER_KEEPALIVE_TIME_MS;
+static int g_default_server_keepalive_timeout_ms =
+    DEFAULT_SERVER_KEEPALIVE_TIMEOUT_MS;
+static bool g_default_client_keepalive_permit_without_calls =
+    DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS;
+static bool g_default_server_keepalive_permit_without_calls =
+    DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS;
+
+static int g_default_min_sent_ping_interval_without_data_ms =
+    DEFAULT_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS;
+static int g_default_min_recv_ping_interval_without_data_ms =
+    DEFAULT_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS;
+static int g_default_max_pings_without_data = DEFAULT_MAX_PINGS_BETWEEN_DATA;
+static int g_default_max_ping_strikes = DEFAULT_MAX_PING_STRIKES;
+
+#define MAX_CLIENT_STREAM_ID 0x7fffffffu
+grpc_core::TraceFlag grpc_http_trace(false, "http");
+grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount(false,
+                                                         "chttp2_refcount");
+
+/* forward declarations of various callbacks that we'll build closures around */
+static void write_action_begin_locked(void* t, grpc_error* error);
+static void write_action(void* t, grpc_error* error);
+static void write_action_end_locked(void* t, grpc_error* error);
+
+static void read_action_locked(void* t, grpc_error* error);
+
+static void complete_fetch_locked(void* gs, grpc_error* error);
+/** Set a transport level setting, and push it to our peer */
+static void queue_setting_update(grpc_chttp2_transport* t,
+                                 grpc_chttp2_setting_id id, uint32_t value);
+
+static void close_from_api(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                           grpc_error* error);
+
+/** Start new streams that have been created if we can */
+static void maybe_start_some_streams(grpc_chttp2_transport* t);
+
+static void connectivity_state_set(grpc_chttp2_transport* t,
+                                   grpc_connectivity_state state,
+                                   grpc_error* error, const char* reason);
+
+static void benign_reclaimer_locked(void* t, grpc_error* error);
+static void destructive_reclaimer_locked(void* t, grpc_error* error);
+
+static void post_benign_reclaimer(grpc_chttp2_transport* t);
+static void post_destructive_reclaimer(grpc_chttp2_transport* t);
+
+static void close_transport_locked(grpc_chttp2_transport* t, grpc_error* error);
+static void end_all_the_calls(grpc_chttp2_transport* t, grpc_error* error);
+
+static void schedule_bdp_ping_locked(grpc_chttp2_transport* t);
+static void start_bdp_ping_locked(void* tp, grpc_error* error);
+static void finish_bdp_ping_locked(void* tp, grpc_error* error);
+static void next_bdp_ping_timer_expired_locked(void* tp, grpc_error* error);
+
+static void cancel_pings(grpc_chttp2_transport* t, grpc_error* error);
+static void send_ping_locked(grpc_chttp2_transport* t,
+                             grpc_closure* on_initiate,
+                             grpc_closure* on_complete);
+static void retry_initiate_ping_locked(void* tp, grpc_error* error);
+
+/** keepalive-relevant functions */
+static void init_keepalive_ping_locked(void* arg, grpc_error* error);
+static void start_keepalive_ping_locked(void* arg, grpc_error* error);
+static void finish_keepalive_ping_locked(void* arg, grpc_error* error);
+static void keepalive_watchdog_fired_locked(void* arg, grpc_error* error);
+
+static void reset_byte_stream(void* arg, grpc_error* error);
+
+// Flow control default enabled. Can be disabled by setting
+// GRPC_EXPERIMENTAL_DISABLE_FLOW_CONTROL
+bool g_flow_control_enabled = true;
+
+/*******************************************************************************
+ * CONSTRUCTION/DESTRUCTION/REFCOUNTING
+ */
+
+grpc_chttp2_transport::~grpc_chttp2_transport() {
+  size_t i;
+
+  if (channelz_socket != nullptr) {
+    channelz_socket.reset();
+  }
+
+  grpc_endpoint_destroy(ep);
+
+  grpc_slice_buffer_destroy_internal(&qbuf);
+
+  grpc_slice_buffer_destroy_internal(&outbuf);
+  grpc_chttp2_hpack_compressor_destroy(&hpack_compressor);
+
+  grpc_error* error =
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed");
+  // ContextList::Execute follows semantics of a callback function and does not
+  // take a ref on error
+  grpc_core::ContextList::Execute(cl, nullptr, error);
+  GRPC_ERROR_UNREF(error);
+  cl = nullptr;
+
+  grpc_slice_buffer_destroy_internal(&read_buffer);
+  grpc_chttp2_hpack_parser_destroy(&hpack_parser);
+  grpc_chttp2_goaway_parser_destroy(&goaway_parser);
+
+  for (i = 0; i < STREAM_LIST_COUNT; i++) {
+    GPR_ASSERT(lists[i].head == nullptr);
+    GPR_ASSERT(lists[i].tail == nullptr);
+  }
+
+  GRPC_ERROR_UNREF(goaway_error);
+
+  GPR_ASSERT(grpc_chttp2_stream_map_size(&stream_map) == 0);
+
+  grpc_chttp2_stream_map_destroy(&stream_map);
+  grpc_connectivity_state_destroy(&channel_callback.state_tracker);
+
+  GRPC_COMBINER_UNREF(combiner, "chttp2_transport");
+
+  cancel_pings(this,
+               GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"));
+
+  while (write_cb_pool) {
+    grpc_chttp2_write_cb* next = write_cb_pool->next;
+    gpr_free(write_cb_pool);
+    write_cb_pool = next;
+  }
+
+  flow_control.Destroy();
+
+  GRPC_ERROR_UNREF(closed_with_error);
+  gpr_free(ping_acks);
+  gpr_free(peer_string);
+}
+
+static const grpc_transport_vtable* get_vtable(void);
+
+/* Returns whether bdp is enabled */
+static bool read_channel_args(grpc_chttp2_transport* t,
+                              const grpc_channel_args* channel_args,
+                              bool is_client) {
+  bool enable_bdp = true;
+  bool channelz_enabled = GRPC_ENABLE_CHANNELZ_DEFAULT;
+  size_t i;
+  int j;
+
+  for (i = 0; i < channel_args->num_args; i++) {
+    if (0 == strcmp(channel_args->args[i].key,
+                    GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) {
+      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->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->next_stream_id & 1,
+                  is_client ? "client" : "server");
+        } else {
+          t->next_stream_id = static_cast<uint32_t>(value);
+        }
+      }
+    } else if (0 == strcmp(channel_args->args[i].key,
+                           GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) {
+      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->hpack_compressor, static_cast<uint32_t>(value));
+      }
+    } else if (0 == strcmp(channel_args->args[i].key,
+                           GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) {
+      t->ping_policy.max_pings_without_data = grpc_channel_arg_get_integer(
+          &channel_args->args[i],
+          {g_default_max_pings_without_data, 0, INT_MAX});
+    } else if (0 == strcmp(channel_args->args[i].key,
+                           GRPC_ARG_HTTP2_MAX_PING_STRIKES)) {
+      t->ping_policy.max_ping_strikes = grpc_channel_arg_get_integer(
+          &channel_args->args[i], {g_default_max_ping_strikes, 0, INT_MAX});
+    } else if (0 ==
+               strcmp(channel_args->args[i].key,
+                      GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) {
+      t->ping_policy.min_sent_ping_interval_without_data =
+          grpc_channel_arg_get_integer(
+              &channel_args->args[i],
+              grpc_integer_options{
+                  g_default_min_sent_ping_interval_without_data_ms, 0,
+                  INT_MAX});
+    } else if (0 ==
+               strcmp(channel_args->args[i].key,
+                      GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) {
+      t->ping_policy.min_recv_ping_interval_without_data =
+          grpc_channel_arg_get_integer(
+              &channel_args->args[i],
+              grpc_integer_options{
+                  g_default_min_recv_ping_interval_without_data_ms, 0,
+                  INT_MAX});
+    } else if (0 == strcmp(channel_args->args[i].key,
+                           GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE)) {
+      t->write_buffer_size = static_cast<uint32_t>(grpc_channel_arg_get_integer(
+          &channel_args->args[i], {0, 0, MAX_WRITE_BUFFER_SIZE}));
+    } else if (0 ==
+               strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_BDP_PROBE)) {
+      enable_bdp = grpc_channel_arg_get_bool(&channel_args->args[i], true);
+    } else if (0 ==
+               strcmp(channel_args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) {
+      const int value = grpc_channel_arg_get_integer(
+          &channel_args->args[i],
+          grpc_integer_options{t->is_client
+                                   ? g_default_client_keepalive_time_ms
+                                   : g_default_server_keepalive_time_ms,
+                               1, INT_MAX});
+      t->keepalive_time = value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
+    } else if (0 == strcmp(channel_args->args[i].key,
+                           GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) {
+      const int value = grpc_channel_arg_get_integer(
+          &channel_args->args[i],
+          grpc_integer_options{t->is_client
+                                   ? g_default_client_keepalive_timeout_ms
+                                   : g_default_server_keepalive_timeout_ms,
+                               0, INT_MAX});
+      t->keepalive_timeout = value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
+    } else if (0 == strcmp(channel_args->args[i].key,
+                           GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) {
+      t->keepalive_permit_without_calls = static_cast<uint32_t>(
+          grpc_channel_arg_get_integer(&channel_args->args[i], {0, 0, 1}));
+    } else if (0 == strcmp(channel_args->args[i].key,
+                           GRPC_ARG_OPTIMIZATION_TARGET)) {
+      if (channel_args->args[i].type != GRPC_ARG_STRING) {
+        gpr_log(GPR_ERROR, "%s should be a string",
+                GRPC_ARG_OPTIMIZATION_TARGET);
+      } else if (0 == strcmp(channel_args->args[i].value.string, "blend")) {
+        t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY;
+      } else if (0 == strcmp(channel_args->args[i].value.string, "latency")) {
+        t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY;
+      } else if (0 ==
+                 strcmp(channel_args->args[i].value.string, "throughput")) {
+        t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT;
+      } else {
+        gpr_log(GPR_ERROR, "%s value '%s' unknown, assuming 'blend'",
+                GRPC_ARG_OPTIMIZATION_TARGET,
+                channel_args->args[i].value.string);
+      }
+    } else if (0 ==
+               strcmp(channel_args->args[i].key, GRPC_ARG_ENABLE_CHANNELZ)) {
+      channelz_enabled = grpc_channel_arg_get_bool(
+          &channel_args->args[i], GRPC_ENABLE_CHANNELZ_DEFAULT);
+    } else {
+      static const struct {
+        const char* channel_arg_name;
+        grpc_chttp2_setting_id setting_id;
+        grpc_integer_options integer_options;
+        bool availability[2] /* server, client */;
+      } settings_map[] = {{GRPC_ARG_MAX_CONCURRENT_STREAMS,
+                           GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
+                           {-1, 0, INT32_MAX},
+                           {true, false}},
+                          {GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER,
+                           GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE,
+                           {-1, 0, INT32_MAX},
+                           {true, true}},
+                          {GRPC_ARG_MAX_METADATA_SIZE,
+                           GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
+                           {-1, 0, INT32_MAX},
+                           {true, true}},
+                          {GRPC_ARG_HTTP2_MAX_FRAME_SIZE,
+                           GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE,
+                           {-1, 16384, 16777215},
+                           {true, true}},
+                          {GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY,
+                           GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA,
+                           {1, 0, 1},
+                           {true, true}},
+                          {GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES,
+                           GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
+                           {-1, 5, INT32_MAX},
+                           {true, true}}};
+      for (j = 0; j < static_cast<int> GPR_ARRAY_SIZE(settings_map); j++) {
+        if (0 == strcmp(channel_args->args[i].key,
+                        settings_map[j].channel_arg_name)) {
+          if (!settings_map[j].availability[is_client]) {
+            gpr_log(GPR_DEBUG, "%s is not available on %s",
+                    settings_map[j].channel_arg_name,
+                    is_client ? "clients" : "servers");
+          } else {
+            int value = grpc_channel_arg_get_integer(
+                &channel_args->args[i], settings_map[j].integer_options);
+            if (value >= 0) {
+              queue_setting_update(t, settings_map[j].setting_id,
+                                   static_cast<uint32_t>(value));
+            }
+          }
+          break;
+        }
+      }
+    }
+  }
+  if (channelz_enabled) {
+    // TODO(ncteisen): add an API to endpoint to query for local addr, and pass
+    // it in here, so SocketNode knows its own address.
+    t->channelz_socket =
+        grpc_core::MakeRefCounted<grpc_core::channelz::SocketNode>(
+            grpc_core::UniquePtr<char>(),
+            grpc_core::UniquePtr<char>(gpr_strdup(t->peer_string)));
+  }
+  return enable_bdp;
+}
+
+static void init_transport_closures(grpc_chttp2_transport* t) {
+  GRPC_CLOSURE_INIT(&t->read_action_locked, read_action_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->benign_reclaimer_locked, benign_reclaimer_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->destructive_reclaimer_locked,
+                    destructive_reclaimer_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->retry_initiate_ping_locked, retry_initiate_ping_locked,
+                    t, grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->start_bdp_ping_locked, start_bdp_ping_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->finish_bdp_ping_locked, finish_bdp_ping_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->next_bdp_ping_timer_expired_locked,
+                    next_bdp_ping_timer_expired_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->init_keepalive_ping_locked, init_keepalive_ping_locked,
+                    t, grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->start_keepalive_ping_locked,
+                    start_keepalive_ping_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->finish_keepalive_ping_locked,
+                    finish_keepalive_ping_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+  GRPC_CLOSURE_INIT(&t->keepalive_watchdog_fired_locked,
+                    keepalive_watchdog_fired_locked, t,
+                    grpc_combiner_scheduler(t->combiner));
+}
+
+static void init_transport_keepalive_settings(grpc_chttp2_transport* t) {
+  if (t->is_client) {
+    t->keepalive_time = g_default_client_keepalive_time_ms == INT_MAX
+                            ? GRPC_MILLIS_INF_FUTURE
+                            : g_default_client_keepalive_time_ms;
+    t->keepalive_timeout = g_default_client_keepalive_timeout_ms == INT_MAX
+                               ? GRPC_MILLIS_INF_FUTURE
+                               : g_default_client_keepalive_timeout_ms;
+    t->keepalive_permit_without_calls =
+        g_default_client_keepalive_permit_without_calls;
+  } else {
+    t->keepalive_time = g_default_server_keepalive_time_ms == INT_MAX
+                            ? GRPC_MILLIS_INF_FUTURE
+                            : g_default_server_keepalive_time_ms;
+    t->keepalive_timeout = g_default_server_keepalive_timeout_ms == INT_MAX
+                               ? GRPC_MILLIS_INF_FUTURE
+                               : g_default_server_keepalive_timeout_ms;
+    t->keepalive_permit_without_calls =
+        g_default_server_keepalive_permit_without_calls;
+  }
+}
+
+static void configure_transport_ping_policy(grpc_chttp2_transport* t) {
+  t->ping_policy.max_pings_without_data = g_default_max_pings_without_data;
+  t->ping_policy.min_sent_ping_interval_without_data =
+      g_default_min_sent_ping_interval_without_data_ms;
+  t->ping_policy.max_ping_strikes = g_default_max_ping_strikes;
+  t->ping_policy.min_recv_ping_interval_without_data =
+      g_default_min_recv_ping_interval_without_data_ms;
+}
+
+static void init_keepalive_pings_if_enabled(grpc_chttp2_transport* t) {
+  if (t->keepalive_time != GRPC_MILLIS_INF_FUTURE) {
+    t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
+    GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
+    grpc_timer_init(&t->keepalive_ping_timer,
+                    grpc_core::ExecCtx::Get()->Now() + t->keepalive_time,
+                    &t->init_keepalive_ping_locked);
+  } else {
+    /* Use GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED to indicate there are no
+       inflight keeaplive timers */
+    t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED;
+  }
+}
+
+grpc_chttp2_transport::grpc_chttp2_transport(
+    const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client,
+    grpc_resource_user* resource_user)
+    : refs(1, &grpc_trace_chttp2_refcount),
+      ep(ep),
+      peer_string(grpc_endpoint_get_peer(ep)),
+      resource_user(resource_user),
+      combiner(grpc_combiner_create()),
+      is_client(is_client),
+      next_stream_id(is_client ? 1 : 2),
+      deframe_state(is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0) {
+  GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) ==
+             GRPC_CHTTP2_CLIENT_CONNECT_STRLEN);
+  base.vtable = get_vtable();
+  /* 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
+     needed.
+     TODO(ctiller): tune this */
+  grpc_chttp2_stream_map_init(&stream_map, 8);
+
+  grpc_slice_buffer_init(&read_buffer);
+  grpc_connectivity_state_init(
+      &channel_callback.state_tracker, GRPC_CHANNEL_READY,
+      is_client ? "client_transport" : "server_transport");
+  grpc_slice_buffer_init(&outbuf);
+  if (is_client) {
+    grpc_slice_buffer_add(&outbuf, grpc_slice_from_copied_string(
+                                       GRPC_CHTTP2_CLIENT_CONNECT_STRING));
+  }
+  grpc_chttp2_hpack_compressor_init(&hpack_compressor);
+  grpc_slice_buffer_init(&qbuf);
+  /* copy in initial settings to all setting sets */
+  size_t i;
+  int j;
+  for (i = 0; i < GRPC_CHTTP2_NUM_SETTINGS; i++) {
+    for (j = 0; j < GRPC_NUM_SETTING_SETS; j++) {
+      settings[j][i] = grpc_chttp2_settings_parameters[i].default_value;
+    }
+  }
+  grpc_chttp2_hpack_parser_init(&hpack_parser);
+  grpc_chttp2_goaway_parser_init(&goaway_parser);
+
+  init_transport_closures(this);
+
+  /* configure http2 the way we like it */
+  if (is_client) {
+    queue_setting_update(this, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0);
+    queue_setting_update(this, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0);
+  }
+  queue_setting_update(this, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
+                       DEFAULT_MAX_HEADER_LIST_SIZE);
+  queue_setting_update(this,
+                       GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, 1);
+
+  configure_transport_ping_policy(this);
+  init_transport_keepalive_settings(this);
+
+  bool enable_bdp = true;
+  if (channel_args) {
+    enable_bdp = read_channel_args(this, channel_args, is_client);
+  }
+
+  if (g_flow_control_enabled) {
+    flow_control.Init<grpc_core::chttp2::TransportFlowControl>(this,
+                                                               enable_bdp);
+  } else {
+    flow_control.Init<grpc_core::chttp2::TransportFlowControlDisabled>(this);
+    enable_bdp = false;
+  }
+
+  /* No pings allowed before receiving a header or data frame. */
+  ping_state.pings_before_data_required = 0;
+  ping_state.is_delayed_ping_timer_set = false;
+  ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST;
+
+  ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
+  ping_recv_state.ping_strikes = 0;
+
+  init_keepalive_pings_if_enabled(this);
+
+  if (enable_bdp) {
+    GRPC_CHTTP2_REF_TRANSPORT(this, "bdp_ping");
+    schedule_bdp_ping_locked(this);
+    grpc_chttp2_act_on_flowctl_action(flow_control->PeriodicUpdate(), this,
+                                      nullptr);
+  }
+
+  grpc_chttp2_initiate_write(this, GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE);
+  post_benign_reclaimer(this);
+}
+
+static void destroy_transport_locked(void* tp, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+  t->destroying = 1;
+  close_transport_locked(
+      t, grpc_error_set_int(
+             GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"),
+             GRPC_ERROR_INT_OCCURRED_DURING_WRITE, t->write_state));
+  // Must be the last line.
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "destroy");
+}
+
+static void destroy_transport(grpc_transport* gt) {
+  grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
+  GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(destroy_transport_locked, t,
+                                         grpc_combiner_scheduler(t->combiner)),
+                     GRPC_ERROR_NONE);
+}
+
+static void close_transport_locked(grpc_chttp2_transport* t,
+                                   grpc_error* error) {
+  end_all_the_calls(t, GRPC_ERROR_REF(error));
+  cancel_pings(t, GRPC_ERROR_REF(error));
+  if (t->closed_with_error == GRPC_ERROR_NONE) {
+    if (!grpc_error_has_clear_grpc_status(error)) {
+      error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS,
+                                 GRPC_STATUS_UNAVAILABLE);
+    }
+    if (t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE) {
+      if (t->close_transport_on_writes_finished == nullptr) {
+        t->close_transport_on_writes_finished =
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "Delayed close due to in-progress write");
+      }
+      t->close_transport_on_writes_finished =
+          grpc_error_add_child(t->close_transport_on_writes_finished, error);
+      return;
+    }
+    GPR_ASSERT(error != GRPC_ERROR_NONE);
+    t->closed_with_error = GRPC_ERROR_REF(error);
+    connectivity_state_set(t, GRPC_CHANNEL_SHUTDOWN, GRPC_ERROR_REF(error),
+                           "close_transport");
+    if (t->ping_state.is_delayed_ping_timer_set) {
+      grpc_timer_cancel(&t->ping_state.delayed_ping_timer);
+    }
+    if (t->have_next_bdp_ping_timer) {
+      grpc_timer_cancel(&t->next_bdp_ping_timer);
+    }
+    switch (t->keepalive_state) {
+      case GRPC_CHTTP2_KEEPALIVE_STATE_WAITING:
+        grpc_timer_cancel(&t->keepalive_ping_timer);
+        break;
+      case GRPC_CHTTP2_KEEPALIVE_STATE_PINGING:
+        grpc_timer_cancel(&t->keepalive_ping_timer);
+        grpc_timer_cancel(&t->keepalive_watchdog_timer);
+        break;
+      case GRPC_CHTTP2_KEEPALIVE_STATE_DYING:
+      case GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED:
+        /* keepalive timers are not set in these two states */
+        break;
+    }
+
+    /* flush writable stream list to avoid dangling references */
+    grpc_chttp2_stream* s;
+    while (grpc_chttp2_list_pop_writable_stream(t, &s)) {
+      GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:close");
+    }
+    GPR_ASSERT(t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE);
+    grpc_endpoint_shutdown(t->ep, GRPC_ERROR_REF(error));
+  }
+  if (t->notify_on_receive_settings != nullptr) {
+    GRPC_CLOSURE_SCHED(t->notify_on_receive_settings, GRPC_ERROR_CANCELLED);
+    t->notify_on_receive_settings = nullptr;
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+#ifndef NDEBUG
+void grpc_chttp2_stream_ref(grpc_chttp2_stream* s, const char* reason) {
+  grpc_stream_ref(s->refcount, reason);
+}
+void grpc_chttp2_stream_unref(grpc_chttp2_stream* s, const char* reason) {
+  grpc_stream_unref(s->refcount, reason);
+}
+#else
+void grpc_chttp2_stream_ref(grpc_chttp2_stream* s) {
+  grpc_stream_ref(s->refcount);
+}
+void grpc_chttp2_stream_unref(grpc_chttp2_stream* s) {
+  grpc_stream_unref(s->refcount);
+}
+#endif
+
+grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t,
+                                       grpc_stream_refcount* refcount,
+                                       const void* server_data,
+                                       gpr_arena* arena)
+    : t(t), refcount(refcount), metadata_buffer{{arena}, {arena}} {
+  /* We reserve one 'active stream' that's dropped when the stream is
+     read-closed. The others are for Chttp2IncomingByteStreams that are
+     actively reading */
+  GRPC_CHTTP2_STREAM_REF(this, "chttp2");
+  GRPC_CHTTP2_REF_TRANSPORT(t, "stream");
+
+  if (server_data) {
+    id = static_cast<uint32_t>((uintptr_t)server_data);
+    *t->accepting_stream = this;
+    grpc_chttp2_stream_map_add(&t->stream_map, id, this);
+    post_destructive_reclaimer(t);
+  }
+  if (t->flow_control->flow_control_enabled()) {
+    flow_control.Init<grpc_core::chttp2::StreamFlowControl>(
+        static_cast<grpc_core::chttp2::TransportFlowControl*>(
+            t->flow_control.get()),
+        this);
+  } else {
+    flow_control.Init<grpc_core::chttp2::StreamFlowControlDisabled>();
+  }
+
+  grpc_slice_buffer_init(&frame_storage);
+  grpc_slice_buffer_init(&unprocessed_incoming_frames_buffer);
+  grpc_slice_buffer_init(&flow_controlled_buffer);
+  grpc_slice_buffer_init(&compressed_data_buffer);
+  grpc_slice_buffer_init(&decompressed_data_buffer);
+
+  GRPC_CLOSURE_INIT(&complete_fetch_locked, ::complete_fetch_locked, this,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&reset_byte_stream, ::reset_byte_stream, this,
+                    grpc_combiner_scheduler(t->combiner));
+}
+
+grpc_chttp2_stream::~grpc_chttp2_stream() {
+  if (t->channelz_socket != nullptr) {
+    if ((t->is_client && eos_received) || (!t->is_client && eos_sent)) {
+      t->channelz_socket->RecordStreamSucceeded();
+    } else {
+      t->channelz_socket->RecordStreamFailed();
+    }
+  }
+
+  GPR_ASSERT((write_closed && read_closed) || id == 0);
+  if (id != 0) {
+    GPR_ASSERT(grpc_chttp2_stream_map_find(&t->stream_map, id) == nullptr);
+  }
+
+  grpc_slice_buffer_destroy_internal(&unprocessed_incoming_frames_buffer);
+  grpc_slice_buffer_destroy_internal(&frame_storage);
+  grpc_slice_buffer_destroy_internal(&compressed_data_buffer);
+  grpc_slice_buffer_destroy_internal(&decompressed_data_buffer);
+
+  grpc_chttp2_list_remove_stalled_by_transport(t, this);
+  grpc_chttp2_list_remove_stalled_by_stream(t, this);
+
+  for (int i = 0; i < STREAM_LIST_COUNT; i++) {
+    if (GPR_UNLIKELY(included[i])) {
+      gpr_log(GPR_ERROR, "%s stream %d still included in list %d",
+              t->is_client ? "client" : "server", id, i);
+      abort();
+    }
+  }
+
+  GPR_ASSERT(send_initial_metadata_finished == nullptr);
+  GPR_ASSERT(fetching_send_message == nullptr);
+  GPR_ASSERT(send_trailing_metadata_finished == nullptr);
+  GPR_ASSERT(recv_initial_metadata_ready == nullptr);
+  GPR_ASSERT(recv_message_ready == nullptr);
+  GPR_ASSERT(recv_trailing_metadata_finished == nullptr);
+  grpc_slice_buffer_destroy_internal(&flow_controlled_buffer);
+  GRPC_ERROR_UNREF(read_closed_error);
+  GRPC_ERROR_UNREF(write_closed_error);
+  GRPC_ERROR_UNREF(byte_stream_error);
+
+  flow_control.Destroy();
+
+  if (t->resource_user != nullptr) {
+    grpc_resource_user_free(t->resource_user, GRPC_RESOURCE_QUOTA_CALL_SIZE);
+  }
+
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "stream");
+  GRPC_CLOSURE_SCHED(destroy_stream_arg, GRPC_ERROR_NONE);
+}
+
+static int init_stream(grpc_transport* gt, grpc_stream* gs,
+                       grpc_stream_refcount* refcount, const void* server_data,
+                       gpr_arena* arena) {
+  GPR_TIMER_SCOPE("init_stream", 0);
+  grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
+  new (gs) grpc_chttp2_stream(t, refcount, server_data, arena);
+  return 0;
+}
+
+static void destroy_stream_locked(void* sp, grpc_error* error) {
+  GPR_TIMER_SCOPE("destroy_stream", 0);
+  grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(sp);
+  s->~grpc_chttp2_stream();
+}
+
+static void destroy_stream(grpc_transport* gt, grpc_stream* gs,
+                           grpc_closure* then_schedule_closure) {
+  GPR_TIMER_SCOPE("destroy_stream", 0);
+  grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
+  grpc_chttp2_stream* s = reinterpret_cast<grpc_chttp2_stream*>(gs);
+
+  if (s->stream_compression_ctx != nullptr) {
+    grpc_stream_compression_context_destroy(s->stream_compression_ctx);
+    s->stream_compression_ctx = nullptr;
+  }
+  if (s->stream_decompression_ctx != nullptr) {
+    grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
+    s->stream_decompression_ctx = nullptr;
+  }
+
+  s->destroy_stream_arg = then_schedule_closure;
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&s->destroy_stream, destroy_stream_locked, s,
+                        grpc_combiner_scheduler(t->combiner)),
+      GRPC_ERROR_NONE);
+}
+
+grpc_chttp2_stream* grpc_chttp2_parsing_lookup_stream(grpc_chttp2_transport* t,
+                                                      uint32_t id) {
+  return static_cast<grpc_chttp2_stream*>(
+      grpc_chttp2_stream_map_find(&t->stream_map, id));
+}
+
+grpc_chttp2_stream* grpc_chttp2_parsing_accept_stream(grpc_chttp2_transport* t,
+                                                      uint32_t id) {
+  if (t->channel_callback.accept_stream == nullptr) {
+    return nullptr;
+  }
+  // Don't accept the stream if memory quota doesn't allow. Note that we should
+  // simply refuse the stream here instead of canceling the stream after it's
+  // accepted since the latter will create the call which costs much memory.
+  if (t->resource_user != nullptr &&
+      !grpc_resource_user_safe_alloc(t->resource_user,
+                                     GRPC_RESOURCE_QUOTA_CALL_SIZE)) {
+    gpr_log(GPR_ERROR, "Memory exhausted, rejecting the stream.");
+    grpc_slice_buffer_add(
+        &t->qbuf,
+        grpc_chttp2_rst_stream_create(
+            id, static_cast<uint32_t>(GRPC_HTTP2_REFUSED_STREAM), nullptr));
+    grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM);
+    return nullptr;
+  }
+  grpc_chttp2_stream* accepting = nullptr;
+  GPR_ASSERT(t->accepting_stream == nullptr);
+  t->accepting_stream = &accepting;
+  t->channel_callback.accept_stream(t->channel_callback.accept_stream_user_data,
+                                    &t->base,
+                                    (void*)static_cast<uintptr_t>(id));
+  t->accepting_stream = nullptr;
+  return accepting;
+}
+
+/*******************************************************************************
+ * OUTPUT PROCESSING
+ */
+
+static const char* write_state_name(grpc_chttp2_write_state st) {
+  switch (st) {
+    case GRPC_CHTTP2_WRITE_STATE_IDLE:
+      return "IDLE";
+    case GRPC_CHTTP2_WRITE_STATE_WRITING:
+      return "WRITING";
+    case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE:
+      return "WRITING+MORE";
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
+
+static void set_write_state(grpc_chttp2_transport* t,
+                            grpc_chttp2_write_state st, const char* reason) {
+  GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "W:%p %s state %s -> %s [%s]", t,
+                                 t->is_client ? "CLIENT" : "SERVER",
+                                 write_state_name(t->write_state),
+                                 write_state_name(st), reason));
+  t->write_state = st;
+  /* If the state is being reset back to idle, it means a write was just
+   * finished. Make sure all the run_after_write closures are scheduled.
+   *
+   * This is also our chance to close the transport if the transport was marked
+   * to be closed after all writes finish (for example, if we received a go-away
+   * from peer while we had some pending writes) */
+  if (st == GRPC_CHTTP2_WRITE_STATE_IDLE) {
+    GRPC_CLOSURE_LIST_SCHED(&t->run_after_write);
+    if (t->close_transport_on_writes_finished != nullptr) {
+      grpc_error* err = t->close_transport_on_writes_finished;
+      t->close_transport_on_writes_finished = nullptr;
+      close_transport_locked(t, err);
+    }
+  }
+}
+
+static void inc_initiate_write_reason(
+    grpc_chttp2_initiate_write_reason reason) {
+  switch (reason) {
+    case GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE();
+      break;
+    case GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM:
+      GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM();
+      break;
+  }
+}
+
+void grpc_chttp2_initiate_write(grpc_chttp2_transport* t,
+                                grpc_chttp2_initiate_write_reason reason) {
+  GPR_TIMER_SCOPE("grpc_chttp2_initiate_write", 0);
+
+  switch (t->write_state) {
+    case GRPC_CHTTP2_WRITE_STATE_IDLE:
+      inc_initiate_write_reason(reason);
+      set_write_state(t, GRPC_CHTTP2_WRITE_STATE_WRITING,
+                      grpc_chttp2_initiate_write_reason_string(reason));
+      t->is_first_write_in_batch = true;
+      GRPC_CHTTP2_REF_TRANSPORT(t, "writing");
+      /* Note that the 'write_action_begin_locked' closure is being scheduled
+       * on the 'finally_scheduler' of t->combiner. This means that
+       * 'write_action_begin_locked' is called only *after* all the other
+       * closures (some of which are potentially initiating more writes on the
+       * transport) are executed on the t->combiner.
+       *
+       * The reason for scheduling on finally_scheduler is to make sure we batch
+       * as many writes as possible. 'write_action_begin_locked' is the function
+       * that gathers all the relevant bytes (which are at various places in the
+       * grpc_chttp2_transport structure) and append them to 'outbuf' field in
+       * grpc_chttp2_transport thereby batching what would have been potentially
+       * multiple write operations.
+       *
+       * Also, 'write_action_begin_locked' only gathers the bytes into outbuf.
+       * It does not call the endpoint to write the bytes. That is done by the
+       * 'write_action' (which is scheduled by 'write_action_begin_locked') */
+      GRPC_CLOSURE_SCHED(
+          GRPC_CLOSURE_INIT(&t->write_action_begin_locked,
+                            write_action_begin_locked, t,
+                            grpc_combiner_finally_scheduler(t->combiner)),
+          GRPC_ERROR_NONE);
+      break;
+    case GRPC_CHTTP2_WRITE_STATE_WRITING:
+      set_write_state(t, GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE,
+                      grpc_chttp2_initiate_write_reason_string(reason));
+      break;
+    case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE:
+      break;
+  }
+}
+
+void grpc_chttp2_mark_stream_writable(grpc_chttp2_transport* t,
+                                      grpc_chttp2_stream* s) {
+  if (t->closed_with_error == GRPC_ERROR_NONE &&
+      grpc_chttp2_list_add_writable_stream(t, s)) {
+    GRPC_CHTTP2_STREAM_REF(s, "chttp2_writing:become");
+  }
+}
+
+static grpc_closure_scheduler* write_scheduler(grpc_chttp2_transport* t,
+                                               bool early_results_scheduled,
+                                               bool partial_write) {
+  /* if it's not the first write in a batch, always offload to the executor:
+     we'll probably end up queuing against the kernel anyway, so we'll likely
+     get better latency overall if we switch writing work elsewhere and continue
+     with application work above */
+  if (!t->is_first_write_in_batch) {
+    return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT);
+  }
+  /* equivalently, if it's a partial write, we *know* we're going to be taking a
+     thread jump to write it because of the above, may as well do so
+     immediately */
+  if (partial_write) {
+    return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT);
+  }
+  switch (t->opt_target) {
+    case GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT:
+      /* executor gives us the largest probability of being able to batch a
+       * write with others on this transport */
+      return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT);
+    case GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY:
+      return grpc_schedule_on_exec_ctx;
+  }
+  GPR_UNREACHABLE_CODE(return nullptr);
+}
+
+#define WRITE_STATE_TUPLE_TO_INT(p, i) (2 * (int)(p) + (int)(i))
+static const char* begin_writing_desc(bool partial, bool inlined) {
+  switch (WRITE_STATE_TUPLE_TO_INT(partial, inlined)) {
+    case WRITE_STATE_TUPLE_TO_INT(false, false):
+      return "begin write in background";
+    case WRITE_STATE_TUPLE_TO_INT(false, true):
+      return "begin write in current thread";
+    case WRITE_STATE_TUPLE_TO_INT(true, false):
+      return "begin partial write in background";
+    case WRITE_STATE_TUPLE_TO_INT(true, true):
+      return "begin partial write in current thread";
+  }
+  GPR_UNREACHABLE_CODE(return "bad state tuple");
+}
+
+static void write_action_begin_locked(void* gt, grpc_error* error_ignored) {
+  GPR_TIMER_SCOPE("write_action_begin_locked", 0);
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(gt);
+  GPR_ASSERT(t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE);
+  grpc_chttp2_begin_write_result r;
+  if (t->closed_with_error != GRPC_ERROR_NONE) {
+    r.writing = false;
+  } else {
+    r = grpc_chttp2_begin_write(t);
+  }
+  if (r.writing) {
+    if (r.partial) {
+      GRPC_STATS_INC_HTTP2_PARTIAL_WRITES();
+    }
+    if (!t->is_first_write_in_batch) {
+      GRPC_STATS_INC_HTTP2_WRITES_CONTINUED();
+    }
+    grpc_closure_scheduler* scheduler =
+        write_scheduler(t, r.early_results_scheduled, r.partial);
+    if (scheduler != grpc_schedule_on_exec_ctx) {
+      GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED();
+    }
+    set_write_state(
+        t,
+        r.partial ? GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE
+                  : GRPC_CHTTP2_WRITE_STATE_WRITING,
+        begin_writing_desc(r.partial, scheduler == grpc_schedule_on_exec_ctx));
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_INIT(&t->write_action, write_action, t, scheduler),
+        GRPC_ERROR_NONE);
+  } else {
+    GRPC_STATS_INC_HTTP2_SPURIOUS_WRITES_BEGUN();
+    set_write_state(t, GRPC_CHTTP2_WRITE_STATE_IDLE, "begin writing nothing");
+    GRPC_CHTTP2_UNREF_TRANSPORT(t, "writing");
+  }
+}
+
+static void write_action(void* gt, grpc_error* error) {
+  GPR_TIMER_SCOPE("write_action", 0);
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(gt);
+  void* cl = t->cl;
+  t->cl = nullptr;
+  grpc_endpoint_write(
+      t->ep, &t->outbuf,
+      GRPC_CLOSURE_INIT(&t->write_action_end_locked, write_action_end_locked, t,
+                        grpc_combiner_scheduler(t->combiner)),
+      cl);
+}
+
+/* Callback from the grpc_endpoint after bytes have been written by calling
+ * sendmsg */
+static void write_action_end_locked(void* tp, grpc_error* error) {
+  GPR_TIMER_SCOPE("terminate_writing_with_lock", 0);
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+
+  if (error != GRPC_ERROR_NONE) {
+    close_transport_locked(t, GRPC_ERROR_REF(error));
+  }
+
+  if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED) {
+    t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SENT;
+    if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
+      close_transport_locked(
+          t, GRPC_ERROR_CREATE_FROM_STATIC_STRING("goaway sent"));
+    }
+  }
+
+  switch (t->write_state) {
+    case GRPC_CHTTP2_WRITE_STATE_IDLE:
+      GPR_UNREACHABLE_CODE(break);
+    case GRPC_CHTTP2_WRITE_STATE_WRITING:
+      GPR_TIMER_MARK("state=writing", 0);
+      set_write_state(t, GRPC_CHTTP2_WRITE_STATE_IDLE, "finish writing");
+      break;
+    case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE:
+      GPR_TIMER_MARK("state=writing_stale_no_poller", 0);
+      set_write_state(t, GRPC_CHTTP2_WRITE_STATE_WRITING, "continue writing");
+      t->is_first_write_in_batch = false;
+      GRPC_CHTTP2_REF_TRANSPORT(t, "writing");
+      GRPC_CLOSURE_RUN(
+          GRPC_CLOSURE_INIT(&t->write_action_begin_locked,
+                            write_action_begin_locked, t,
+                            grpc_combiner_finally_scheduler(t->combiner)),
+          GRPC_ERROR_NONE);
+      break;
+  }
+
+  grpc_chttp2_end_write(t, GRPC_ERROR_REF(error));
+
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "writing");
+}
+
+// Dirties an HTTP2 setting to be sent out next time a writing path occurs.
+// If the change needs to occur immediately, manually initiate a write.
+static void queue_setting_update(grpc_chttp2_transport* t,
+                                 grpc_chttp2_setting_id id, uint32_t value) {
+  const grpc_chttp2_setting_parameters* sp =
+      &grpc_chttp2_settings_parameters[id];
+  uint32_t use_value = GPR_CLAMP(value, sp->min_value, sp->max_value);
+  if (use_value != value) {
+    gpr_log(GPR_INFO, "Requested parameter %s clamped from %d to %d", sp->name,
+            value, use_value);
+  }
+  if (use_value != t->settings[GRPC_LOCAL_SETTINGS][id]) {
+    t->settings[GRPC_LOCAL_SETTINGS][id] = use_value;
+    t->dirtied_local_settings = 1;
+  }
+}
+
+void grpc_chttp2_add_incoming_goaway(grpc_chttp2_transport* t,
+                                     uint32_t goaway_error,
+                                     grpc_slice goaway_text) {
+  // GRPC_CHTTP2_IF_TRACING(
+  //     gpr_log(GPR_INFO, "got goaway [%d]: %s", goaway_error, msg));
+
+  // Discard the error from a previous goaway frame (if any)
+  if (t->goaway_error != GRPC_ERROR_NONE) {
+    GRPC_ERROR_UNREF(t->goaway_error);
+  }
+  t->goaway_error = grpc_error_set_str(
+      grpc_error_set_int(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("GOAWAY received"),
+          GRPC_ERROR_INT_HTTP2_ERROR, static_cast<intptr_t>(goaway_error)),
+      GRPC_ERROR_STR_RAW_BYTES, goaway_text);
+
+  /* When a client receives a GOAWAY with error code ENHANCE_YOUR_CALM and debug
+   * data equal to "too_many_pings", it should log the occurrence at a log level
+   * that is enabled by default and double the configured KEEPALIVE_TIME used
+   * for new connections on that channel. */
+  if (GPR_UNLIKELY(t->is_client &&
+                   goaway_error == GRPC_HTTP2_ENHANCE_YOUR_CALM &&
+                   grpc_slice_str_cmp(goaway_text, "too_many_pings") == 0)) {
+    gpr_log(GPR_ERROR,
+            "Received a GOAWAY with error code ENHANCE_YOUR_CALM and debug "
+            "data equal to \"too_many_pings\"");
+    double current_keepalive_time_ms = static_cast<double>(t->keepalive_time);
+    t->keepalive_time =
+        current_keepalive_time_ms > INT_MAX / KEEPALIVE_TIME_BACKOFF_MULTIPLIER
+            ? GRPC_MILLIS_INF_FUTURE
+            : static_cast<grpc_millis>(current_keepalive_time_ms *
+                                       KEEPALIVE_TIME_BACKOFF_MULTIPLIER);
+  }
+
+  /* lie: use transient failure from the transport to indicate goaway has been
+   * received */
+  connectivity_state_set(t, GRPC_CHANNEL_TRANSIENT_FAILURE,
+                         GRPC_ERROR_REF(t->goaway_error), "got_goaway");
+}
+
+static void maybe_start_some_streams(grpc_chttp2_transport* t) {
+  grpc_chttp2_stream* s;
+  /* start streams where we have free grpc_chttp2_stream ids and free
+   * concurrency */
+  while (t->next_stream_id <= MAX_CLIENT_STREAM_ID &&
+         grpc_chttp2_stream_map_size(&t->stream_map) <
+             t->settings[GRPC_PEER_SETTINGS]
+                        [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] &&
+         grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) {
+    /* safe since we can't (legally) be parsing this stream yet */
+    GRPC_CHTTP2_IF_TRACING(gpr_log(
+        GPR_INFO, "HTTP:%s: Allocating new grpc_chttp2_stream %p to id %d",
+        t->is_client ? "CLI" : "SVR", s, t->next_stream_id));
+
+    GPR_ASSERT(s->id == 0);
+    s->id = t->next_stream_id;
+    t->next_stream_id += 2;
+
+    if (t->next_stream_id >= MAX_CLIENT_STREAM_ID) {
+      connectivity_state_set(
+          t, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"),
+          "no_more_stream_ids");
+    }
+
+    grpc_chttp2_stream_map_add(&t->stream_map, s->id, s);
+    post_destructive_reclaimer(t);
+    grpc_chttp2_mark_stream_writable(t, s);
+    grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM);
+  }
+  /* cancel out streams that will never be started */
+  while (t->next_stream_id >= MAX_CLIENT_STREAM_ID &&
+         grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) {
+    grpc_chttp2_cancel_stream(
+        t, s,
+        grpc_error_set_int(
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"),
+            GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
+  }
+}
+
+/* Flag that this closure barrier may be covering a write in a pollset, and so
+   we should not complete this closure until we can prove that the write got
+   scheduled */
+#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 0)
+/* First bit of the reference count, stored in the high order bits (with the low
+   bits being used for flags defined above) */
+#define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16)
+
+static grpc_closure* add_closure_barrier(grpc_closure* closure) {
+  closure->next_data.scratch += CLOSURE_BARRIER_FIRST_REF_BIT;
+  return closure;
+}
+
+static void null_then_run_closure(grpc_closure** closure, grpc_error* error) {
+  grpc_closure* c = *closure;
+  *closure = nullptr;
+  GRPC_CLOSURE_RUN(c, error);
+}
+
+void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t,
+                                       grpc_chttp2_stream* s,
+                                       grpc_closure** pclosure,
+                                       grpc_error* error, const char* desc) {
+  grpc_closure* closure = *pclosure;
+  *pclosure = nullptr;
+  if (closure == nullptr) {
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT;
+  if (grpc_http_trace.enabled()) {
+    const char* errstr = grpc_error_string(error);
+    gpr_log(
+        GPR_INFO,
+        "complete_closure_step: t=%p %p refs=%d flags=0x%04x desc=%s err=%s "
+        "write_state=%s",
+        t, closure,
+        static_cast<int>(closure->next_data.scratch /
+                         CLOSURE_BARRIER_FIRST_REF_BIT),
+        static_cast<int>(closure->next_data.scratch %
+                         CLOSURE_BARRIER_FIRST_REF_BIT),
+        desc, errstr, write_state_name(t->write_state));
+  }
+  if (error != GRPC_ERROR_NONE) {
+    if (closure->error_data.error == GRPC_ERROR_NONE) {
+      closure->error_data.error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "Error in HTTP transport completing operation");
+      closure->error_data.error = grpc_error_set_str(
+          closure->error_data.error, GRPC_ERROR_STR_TARGET_ADDRESS,
+          grpc_slice_from_copied_string(t->peer_string));
+    }
+    closure->error_data.error =
+        grpc_error_add_child(closure->error_data.error, error);
+  }
+  if (closure->next_data.scratch < CLOSURE_BARRIER_FIRST_REF_BIT) {
+    if ((t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE) ||
+        !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) {
+      GRPC_CLOSURE_RUN(closure, closure->error_data.error);
+    } else {
+      grpc_closure_list_append(&t->run_after_write, closure,
+                               closure->error_data.error);
+    }
+  }
+}
+
+static bool contains_non_ok_status(grpc_metadata_batch* batch) {
+  if (batch->idx.named.grpc_status != nullptr) {
+    return !grpc_mdelem_eq(batch->idx.named.grpc_status->md,
+                           GRPC_MDELEM_GRPC_STATUS_0);
+  }
+  return false;
+}
+
+static void maybe_become_writable_due_to_send_msg(grpc_chttp2_transport* t,
+                                                  grpc_chttp2_stream* s) {
+  if (s->id != 0 && (!s->write_buffering ||
+                     s->flow_controlled_buffer.length > t->write_buffer_size)) {
+    grpc_chttp2_mark_stream_writable(t, s);
+    grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE);
+  }
+}
+
+static void add_fetched_slice_locked(grpc_chttp2_transport* t,
+                                     grpc_chttp2_stream* s) {
+  s->fetched_send_message_length +=
+      static_cast<uint32_t> GRPC_SLICE_LENGTH(s->fetching_slice);
+  grpc_slice_buffer_add(&s->flow_controlled_buffer, s->fetching_slice);
+  maybe_become_writable_due_to_send_msg(t, s);
+}
+
+static void continue_fetching_send_locked(grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream* s) {
+  for (;;) {
+    if (s->fetching_send_message == nullptr) {
+      /* Stream was cancelled before message fetch completed */
+      abort(); /* TODO(ctiller): what cleanup here? */
+      return;  /* early out */
+    }
+    if (s->fetched_send_message_length == s->fetching_send_message->length()) {
+      int64_t notify_offset = s->next_message_end_offset;
+      if (notify_offset <= s->flow_controlled_bytes_written) {
+        grpc_chttp2_complete_closure_step(
+            t, s, &s->fetching_send_message_finished, GRPC_ERROR_NONE,
+            "fetching_send_message_finished");
+      } else {
+        grpc_chttp2_write_cb* cb = t->write_cb_pool;
+        if (cb == nullptr) {
+          cb = static_cast<grpc_chttp2_write_cb*>(gpr_malloc(sizeof(*cb)));
+        } else {
+          t->write_cb_pool = cb->next;
+        }
+        cb->call_at_byte = notify_offset;
+        cb->closure = s->fetching_send_message_finished;
+        s->fetching_send_message_finished = nullptr;
+        grpc_chttp2_write_cb** list =
+            s->fetching_send_message->flags() & GRPC_WRITE_THROUGH
+                ? &s->on_write_finished_cbs
+                : &s->on_flow_controlled_cbs;
+        cb->next = *list;
+        *list = cb;
+      }
+      s->fetching_send_message.reset();
+      return; /* early out */
+    } else if (s->fetching_send_message->Next(UINT32_MAX,
+                                              &s->complete_fetch_locked)) {
+      grpc_error* error = s->fetching_send_message->Pull(&s->fetching_slice);
+      if (error != GRPC_ERROR_NONE) {
+        s->fetching_send_message.reset();
+        grpc_chttp2_cancel_stream(t, s, error);
+      } else {
+        add_fetched_slice_locked(t, s);
+      }
+    }
+  }
+}
+
+static void complete_fetch_locked(void* gs, grpc_error* error) {
+  grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(gs);
+  grpc_chttp2_transport* t = s->t;
+  if (error == GRPC_ERROR_NONE) {
+    error = s->fetching_send_message->Pull(&s->fetching_slice);
+    if (error == GRPC_ERROR_NONE) {
+      add_fetched_slice_locked(t, s);
+      continue_fetching_send_locked(t, s);
+    }
+  }
+  if (error != GRPC_ERROR_NONE) {
+    s->fetching_send_message.reset();
+    grpc_chttp2_cancel_stream(t, s, error);
+  }
+}
+
+static void do_nothing(void* arg, grpc_error* error) {}
+
+static void log_metadata(const grpc_metadata_batch* md_batch, uint32_t id,
+                         bool is_client, bool is_initial) {
+  for (grpc_linked_mdelem* md = md_batch->list.head; md != nullptr;
+       md = md->next) {
+    char* key = grpc_slice_to_c_string(GRPC_MDKEY(md->md));
+    char* value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md));
+    gpr_log(GPR_INFO, "HTTP:%d:%s:%s: %s: %s", id, is_initial ? "HDR" : "TRL",
+            is_client ? "CLI" : "SVR", key, value);
+    gpr_free(key);
+    gpr_free(value);
+  }
+}
+
+static void perform_stream_op_locked(void* stream_op,
+                                     grpc_error* error_ignored) {
+  GPR_TIMER_SCOPE("perform_stream_op_locked", 0);
+
+  grpc_transport_stream_op_batch* op =
+      static_cast<grpc_transport_stream_op_batch*>(stream_op);
+  grpc_chttp2_stream* s =
+      static_cast<grpc_chttp2_stream*>(op->handler_private.extra_arg);
+  grpc_transport_stream_op_batch_payload* op_payload = op->payload;
+  grpc_chttp2_transport* t = s->t;
+
+  GRPC_STATS_INC_HTTP2_OP_BATCHES();
+
+  s->context = op->payload->context;
+  s->traced = op->is_traced;
+  if (grpc_http_trace.enabled()) {
+    char* str = grpc_transport_stream_op_batch_string(op);
+    gpr_log(GPR_INFO, "perform_stream_op_locked: %s; on_complete = %p", str,
+            op->on_complete);
+    gpr_free(str);
+    if (op->send_initial_metadata) {
+      log_metadata(op_payload->send_initial_metadata.send_initial_metadata,
+                   s->id, t->is_client, true);
+    }
+    if (op->send_trailing_metadata) {
+      log_metadata(op_payload->send_trailing_metadata.send_trailing_metadata,
+                   s->id, t->is_client, false);
+    }
+  }
+
+  grpc_closure* on_complete = op->on_complete;
+  // TODO(roth): This is a hack needed because we use data inside of the
+  // closure itself to do the barrier calculation (i.e., to ensure that
+  // we don't schedule the closure until all ops in the batch have been
+  // completed).  This can go away once we move to a new C++ closure API
+  // that provides the ability to create a barrier closure.
+  if (on_complete == nullptr) {
+    on_complete = GRPC_CLOSURE_INIT(&op->handler_private.closure, do_nothing,
+                                    nullptr, grpc_schedule_on_exec_ctx);
+  }
+
+  /* use final_data as a barrier until enqueue time; the inital counter is
+     dropped at the end of this function */
+  on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT;
+  on_complete->error_data.error = GRPC_ERROR_NONE;
+
+  if (op->cancel_stream) {
+    GRPC_STATS_INC_HTTP2_OP_CANCEL();
+    grpc_chttp2_cancel_stream(t, s, op_payload->cancel_stream.cancel_error);
+  }
+
+  if (op->send_initial_metadata) {
+    if (t->is_client && t->channelz_socket != nullptr) {
+      t->channelz_socket->RecordStreamStartedFromLocal();
+    }
+    GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA();
+    GPR_ASSERT(s->send_initial_metadata_finished == nullptr);
+    on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
+
+    /* Identify stream compression */
+    if (op_payload->send_initial_metadata.send_initial_metadata->idx.named
+                .content_encoding == nullptr ||
+        grpc_stream_compression_method_parse(
+            GRPC_MDVALUE(
+                op_payload->send_initial_metadata.send_initial_metadata->idx
+                    .named.content_encoding->md),
+            true, &s->stream_compression_method) == 0) {
+      s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
+    }
+
+    s->send_initial_metadata_finished = add_closure_barrier(on_complete);
+    s->send_initial_metadata =
+        op_payload->send_initial_metadata.send_initial_metadata;
+    const size_t metadata_size =
+        grpc_metadata_batch_size(s->send_initial_metadata);
+    const size_t metadata_peer_limit =
+        t->settings[GRPC_PEER_SETTINGS]
+                   [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
+    if (t->is_client) {
+      s->deadline = GPR_MIN(s->deadline, s->send_initial_metadata->deadline);
+    }
+    if (metadata_size > metadata_peer_limit) {
+      grpc_chttp2_cancel_stream(
+          t, s,
+          grpc_error_set_int(
+              grpc_error_set_int(
+                  grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                         "to-be-sent initial metadata size "
+                                         "exceeds peer limit"),
+                                     GRPC_ERROR_INT_SIZE,
+                                     static_cast<intptr_t>(metadata_size)),
+                  GRPC_ERROR_INT_LIMIT,
+                  static_cast<intptr_t>(metadata_peer_limit)),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
+    } else {
+      if (contains_non_ok_status(s->send_initial_metadata)) {
+        s->seen_error = true;
+      }
+      if (!s->write_closed) {
+        if (t->is_client) {
+          if (t->closed_with_error == GRPC_ERROR_NONE) {
+            GPR_ASSERT(s->id == 0);
+            grpc_chttp2_list_add_waiting_for_concurrency(t, s);
+            maybe_start_some_streams(t);
+          } else {
+            grpc_chttp2_cancel_stream(
+                t, s,
+                grpc_error_set_int(
+                    GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                        "Transport closed", &t->closed_with_error, 1),
+                    GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
+          }
+        } else {
+          GPR_ASSERT(s->id != 0);
+          grpc_chttp2_mark_stream_writable(t, s);
+          if (!(op->send_message &&
+                (op->payload->send_message.send_message->flags() &
+                 GRPC_WRITE_BUFFER_HINT))) {
+            grpc_chttp2_initiate_write(
+                t, GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA);
+          }
+        }
+      } else {
+        s->send_initial_metadata = nullptr;
+        grpc_chttp2_complete_closure_step(
+            t, s, &s->send_initial_metadata_finished,
+            GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                "Attempt to send initial metadata after stream was closed",
+                &s->write_closed_error, 1),
+            "send_initial_metadata_finished");
+      }
+    }
+    if (op_payload->send_initial_metadata.peer_string != nullptr) {
+      gpr_atm_rel_store(op_payload->send_initial_metadata.peer_string,
+                        (gpr_atm)t->peer_string);
+    }
+  }
+
+  if (op->send_message) {
+    GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE();
+    t->num_messages_in_next_write++;
+    GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE(
+        op->payload->send_message.send_message->length());
+    on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
+    s->fetching_send_message_finished = add_closure_barrier(op->on_complete);
+    if (s->write_closed) {
+      // Return an error unless the client has already received trailing
+      // metadata from the server, since an application using a
+      // streaming call might send another message before getting a
+      // recv_message failure, breaking out of its loop, and then
+      // starting recv_trailing_metadata.
+      op->payload->send_message.send_message.reset();
+      grpc_chttp2_complete_closure_step(
+          t, s, &s->fetching_send_message_finished,
+          t->is_client && s->received_trailing_metadata
+              ? GRPC_ERROR_NONE
+              : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                    "Attempt to send message after stream was closed",
+                    &s->write_closed_error, 1),
+          "fetching_send_message_finished");
+    } else {
+      GPR_ASSERT(s->fetching_send_message == nullptr);
+      uint8_t* frame_hdr = grpc_slice_buffer_tiny_add(
+          &s->flow_controlled_buffer, GRPC_HEADER_SIZE_IN_BYTES);
+      uint32_t flags = op_payload->send_message.send_message->flags();
+      frame_hdr[0] = (flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0;
+      size_t len = op_payload->send_message.send_message->length();
+      frame_hdr[1] = static_cast<uint8_t>(len >> 24);
+      frame_hdr[2] = static_cast<uint8_t>(len >> 16);
+      frame_hdr[3] = static_cast<uint8_t>(len >> 8);
+      frame_hdr[4] = static_cast<uint8_t>(len);
+      s->fetching_send_message =
+          std::move(op_payload->send_message.send_message);
+      s->fetched_send_message_length = 0;
+      s->next_message_end_offset =
+          s->flow_controlled_bytes_written +
+          static_cast<int64_t>(s->flow_controlled_buffer.length) +
+          static_cast<int64_t>(len);
+      if (flags & GRPC_WRITE_BUFFER_HINT) {
+        s->next_message_end_offset -= t->write_buffer_size;
+        s->write_buffering = true;
+      } else {
+        s->write_buffering = false;
+      }
+      continue_fetching_send_locked(t, s);
+      maybe_become_writable_due_to_send_msg(t, s);
+    }
+  }
+
+  if (op->send_trailing_metadata) {
+    GRPC_STATS_INC_HTTP2_OP_SEND_TRAILING_METADATA();
+    GPR_ASSERT(s->send_trailing_metadata_finished == nullptr);
+    on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
+    s->send_trailing_metadata_finished = add_closure_barrier(on_complete);
+    s->send_trailing_metadata =
+        op_payload->send_trailing_metadata.send_trailing_metadata;
+    s->write_buffering = false;
+    const size_t metadata_size =
+        grpc_metadata_batch_size(s->send_trailing_metadata);
+    const size_t metadata_peer_limit =
+        t->settings[GRPC_PEER_SETTINGS]
+                   [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
+    if (metadata_size > metadata_peer_limit) {
+      grpc_chttp2_cancel_stream(
+          t, s,
+          grpc_error_set_int(
+              grpc_error_set_int(
+                  grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                         "to-be-sent trailing metadata size "
+                                         "exceeds peer limit"),
+                                     GRPC_ERROR_INT_SIZE,
+                                     static_cast<intptr_t>(metadata_size)),
+                  GRPC_ERROR_INT_LIMIT,
+                  static_cast<intptr_t>(metadata_peer_limit)),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
+    } else {
+      if (contains_non_ok_status(s->send_trailing_metadata)) {
+        s->seen_error = true;
+      }
+      if (s->write_closed) {
+        s->send_trailing_metadata = nullptr;
+        grpc_chttp2_complete_closure_step(
+            t, s, &s->send_trailing_metadata_finished,
+            grpc_metadata_batch_is_empty(
+                op->payload->send_trailing_metadata.send_trailing_metadata)
+                ? GRPC_ERROR_NONE
+                : GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                      "Attempt to send trailing metadata after "
+                      "stream was closed"),
+            "send_trailing_metadata_finished");
+      } else if (s->id != 0) {
+        /* TODO(ctiller): check if there's flow control for any outstanding
+           bytes before going writable */
+        grpc_chttp2_mark_stream_writable(t, s);
+        grpc_chttp2_initiate_write(
+            t, GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA);
+      }
+    }
+  }
+
+  if (op->recv_initial_metadata) {
+    GRPC_STATS_INC_HTTP2_OP_RECV_INITIAL_METADATA();
+    GPR_ASSERT(s->recv_initial_metadata_ready == nullptr);
+    s->recv_initial_metadata_ready =
+        op_payload->recv_initial_metadata.recv_initial_metadata_ready;
+    s->recv_initial_metadata =
+        op_payload->recv_initial_metadata.recv_initial_metadata;
+    s->trailing_metadata_available =
+        op_payload->recv_initial_metadata.trailing_metadata_available;
+    if (op_payload->recv_initial_metadata.peer_string != nullptr) {
+      gpr_atm_rel_store(op_payload->recv_initial_metadata.peer_string,
+                        (gpr_atm)t->peer_string);
+    }
+    grpc_chttp2_maybe_complete_recv_initial_metadata(t, s);
+  }
+
+  if (op->recv_message) {
+    GRPC_STATS_INC_HTTP2_OP_RECV_MESSAGE();
+    size_t before = 0;
+    GPR_ASSERT(s->recv_message_ready == nullptr);
+    GPR_ASSERT(!s->pending_byte_stream);
+    s->recv_message_ready = op_payload->recv_message.recv_message_ready;
+    s->recv_message = op_payload->recv_message.recv_message;
+    if (s->id != 0) {
+      if (!s->read_closed) {
+        before = s->frame_storage.length +
+                 s->unprocessed_incoming_frames_buffer.length;
+      }
+    }
+    grpc_chttp2_maybe_complete_recv_message(t, s);
+    if (s->id != 0) {
+      if (!s->read_closed && s->frame_storage.length == 0) {
+        size_t after = s->frame_storage.length +
+                       s->unprocessed_incoming_frames_buffer_cached_length;
+        s->flow_control->IncomingByteStreamUpdate(GRPC_HEADER_SIZE_IN_BYTES,
+                                                  before - after);
+        grpc_chttp2_act_on_flowctl_action(s->flow_control->MakeAction(), t, s);
+      }
+    }
+  }
+
+  if (op->recv_trailing_metadata) {
+    GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA();
+    GPR_ASSERT(s->collecting_stats == nullptr);
+    s->collecting_stats = op_payload->recv_trailing_metadata.collect_stats;
+    GPR_ASSERT(s->recv_trailing_metadata_finished == nullptr);
+    s->recv_trailing_metadata_finished =
+        op_payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    s->recv_trailing_metadata =
+        op_payload->recv_trailing_metadata.recv_trailing_metadata;
+    s->final_metadata_requested = true;
+    grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s);
+  }
+
+  grpc_chttp2_complete_closure_step(t, s, &on_complete, GRPC_ERROR_NONE,
+                                    "op->on_complete");
+
+  GRPC_CHTTP2_STREAM_UNREF(s, "perform_stream_op");
+}
+
+static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
+                              grpc_transport_stream_op_batch* op) {
+  GPR_TIMER_SCOPE("perform_stream_op", 0);
+  grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
+  grpc_chttp2_stream* s = reinterpret_cast<grpc_chttp2_stream*>(gs);
+
+  if (!t->is_client) {
+    if (op->send_initial_metadata) {
+      grpc_millis deadline =
+          op->payload->send_initial_metadata.send_initial_metadata->deadline;
+      GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE);
+    }
+    if (op->send_trailing_metadata) {
+      grpc_millis deadline =
+          op->payload->send_trailing_metadata.send_trailing_metadata->deadline;
+      GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE);
+    }
+  }
+
+  if (grpc_http_trace.enabled()) {
+    char* str = grpc_transport_stream_op_batch_string(op);
+    gpr_log(GPR_INFO, "perform_stream_op[s=%p]: %s", s, str);
+    gpr_free(str);
+  }
+
+  GRPC_CHTTP2_STREAM_REF(s, "perform_stream_op");
+  op->handler_private.extra_arg = gs;
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&op->handler_private.closure, perform_stream_op_locked,
+                        op, grpc_combiner_scheduler(t->combiner)),
+      GRPC_ERROR_NONE);
+}
+
+static void cancel_pings(grpc_chttp2_transport* t, grpc_error* error) {
+  /* callback remaining pings: they're not allowed to call into the transpot,
+     and maybe they hold resources that need to be freed */
+  grpc_chttp2_ping_queue* pq = &t->ping_queue;
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  for (size_t j = 0; j < GRPC_CHTTP2_PCL_COUNT; j++) {
+    grpc_closure_list_fail_all(&pq->lists[j], GRPC_ERROR_REF(error));
+    GRPC_CLOSURE_LIST_SCHED(&pq->lists[j]);
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+static void send_ping_locked(grpc_chttp2_transport* t,
+                             grpc_closure* on_initiate, grpc_closure* on_ack) {
+  if (t->closed_with_error != GRPC_ERROR_NONE) {
+    GRPC_CLOSURE_SCHED(on_initiate, GRPC_ERROR_REF(t->closed_with_error));
+    GRPC_CLOSURE_SCHED(on_ack, GRPC_ERROR_REF(t->closed_with_error));
+    return;
+  }
+  grpc_chttp2_ping_queue* pq = &t->ping_queue;
+  grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INITIATE], on_initiate,
+                           GRPC_ERROR_NONE);
+  grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_NEXT], on_ack,
+                           GRPC_ERROR_NONE);
+}
+
+/*
+ * Specialized form of send_ping_locked for keepalive ping. If there is already
+ * a ping in progress, the keepalive ping would piggyback onto that ping,
+ * instead of waiting for that ping to complete and then starting a new ping.
+ */
+static void send_keepalive_ping_locked(grpc_chttp2_transport* t) {
+  if (t->closed_with_error != GRPC_ERROR_NONE) {
+    GRPC_CLOSURE_RUN(&t->start_keepalive_ping_locked,
+                     GRPC_ERROR_REF(t->closed_with_error));
+    GRPC_CLOSURE_RUN(&t->finish_keepalive_ping_locked,
+                     GRPC_ERROR_REF(t->closed_with_error));
+    return;
+  }
+  grpc_chttp2_ping_queue* pq = &t->ping_queue;
+  if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) {
+    /* There is a ping in flight. Add yourself to the inflight closure list. */
+    GRPC_CLOSURE_RUN(&t->start_keepalive_ping_locked, GRPC_ERROR_NONE);
+    grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INFLIGHT],
+                             &t->finish_keepalive_ping_locked, GRPC_ERROR_NONE);
+    return;
+  }
+  grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INITIATE],
+                           &t->start_keepalive_ping_locked, GRPC_ERROR_NONE);
+  grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_NEXT],
+                           &t->finish_keepalive_ping_locked, GRPC_ERROR_NONE);
+}
+
+static void retry_initiate_ping_locked(void* tp, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+  t->ping_state.is_delayed_ping_timer_set = false;
+  if (error == GRPC_ERROR_NONE) {
+    grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING);
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "retry_initiate_ping_locked");
+}
+
+void grpc_chttp2_ack_ping(grpc_chttp2_transport* t, uint64_t id) {
+  grpc_chttp2_ping_queue* pq = &t->ping_queue;
+  if (pq->inflight_id != id) {
+    char* from = grpc_endpoint_get_peer(t->ep);
+    gpr_log(GPR_DEBUG, "Unknown ping response from %s: %" PRIx64, from, id);
+    gpr_free(from);
+    return;
+  }
+  GRPC_CLOSURE_LIST_SCHED(&pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]);
+  if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) {
+    grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS);
+  }
+}
+
+static void send_goaway(grpc_chttp2_transport* t, grpc_error* error) {
+  t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED;
+  grpc_http2_error_code http_error;
+  grpc_slice slice;
+  grpc_error_get_status(error, GRPC_MILLIS_INF_FUTURE, nullptr, &slice,
+                        &http_error, nullptr);
+  grpc_chttp2_goaway_append(t->last_new_stream_id,
+                            static_cast<uint32_t>(http_error),
+                            grpc_slice_ref_internal(slice), &t->qbuf);
+  grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT);
+  GRPC_ERROR_UNREF(error);
+}
+
+void grpc_chttp2_add_ping_strike(grpc_chttp2_transport* t) {
+  if (++t->ping_recv_state.ping_strikes > t->ping_policy.max_ping_strikes &&
+      t->ping_policy.max_ping_strikes != 0) {
+    send_goaway(t,
+                grpc_error_set_int(
+                    GRPC_ERROR_CREATE_FROM_STATIC_STRING("too_many_pings"),
+                    GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM));
+    /*The transport will be closed after the write is done */
+    close_transport_locked(
+        t, grpc_error_set_int(
+               GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many pings"),
+               GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
+  }
+}
+
+static void perform_transport_op_locked(void* stream_op,
+                                        grpc_error* error_ignored) {
+  grpc_transport_op* op = static_cast<grpc_transport_op*>(stream_op);
+  grpc_chttp2_transport* t =
+      static_cast<grpc_chttp2_transport*>(op->handler_private.extra_arg);
+
+  if (op->goaway_error) {
+    send_goaway(t, op->goaway_error);
+  }
+
+  if (op->set_accept_stream) {
+    t->channel_callback.accept_stream = op->set_accept_stream_fn;
+    t->channel_callback.accept_stream_user_data =
+        op->set_accept_stream_user_data;
+  }
+
+  if (op->bind_pollset) {
+    grpc_endpoint_add_to_pollset(t->ep, op->bind_pollset);
+  }
+
+  if (op->bind_pollset_set) {
+    grpc_endpoint_add_to_pollset_set(t->ep, op->bind_pollset_set);
+  }
+
+  if (op->send_ping.on_initiate != nullptr || op->send_ping.on_ack != nullptr) {
+    send_ping_locked(t, op->send_ping.on_initiate, op->send_ping.on_ack);
+    grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING);
+  }
+
+  if (op->on_connectivity_state_change != nullptr) {
+    grpc_connectivity_state_notify_on_state_change(
+        &t->channel_callback.state_tracker, op->connectivity_state,
+        op->on_connectivity_state_change);
+  }
+
+  if (op->disconnect_with_error != GRPC_ERROR_NONE) {
+    close_transport_locked(t, op->disconnect_with_error);
+  }
+
+  GRPC_CLOSURE_RUN(op->on_consumed, GRPC_ERROR_NONE);
+
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "transport_op");
+}
+
+static void perform_transport_op(grpc_transport* gt, grpc_transport_op* op) {
+  grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
+  if (grpc_http_trace.enabled()) {
+    char* msg = grpc_transport_op_string(op);
+    gpr_log(GPR_INFO, "perform_transport_op[t=%p]: %s", t, msg);
+    gpr_free(msg);
+  }
+  op->handler_private.extra_arg = gt;
+  GRPC_CHTTP2_REF_TRANSPORT(t, "transport_op");
+  GRPC_CLOSURE_SCHED(GRPC_CLOSURE_INIT(&op->handler_private.closure,
+                                       perform_transport_op_locked, op,
+                                       grpc_combiner_scheduler(t->combiner)),
+                     GRPC_ERROR_NONE);
+}
+
+/*******************************************************************************
+ * INPUT PROCESSING - GENERAL
+ */
+
+void grpc_chttp2_maybe_complete_recv_initial_metadata(grpc_chttp2_transport* t,
+                                                      grpc_chttp2_stream* s) {
+  if (s->recv_initial_metadata_ready != nullptr &&
+      s->published_metadata[0] != GRPC_METADATA_NOT_PUBLISHED) {
+    if (s->seen_error) {
+      grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
+      if (!s->pending_byte_stream) {
+        grpc_slice_buffer_reset_and_unref_internal(
+            &s->unprocessed_incoming_frames_buffer);
+      }
+    }
+    grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[0],
+                                                 s->recv_initial_metadata);
+    null_then_run_closure(&s->recv_initial_metadata_ready, GRPC_ERROR_NONE);
+  }
+}
+
+void grpc_chttp2_maybe_complete_recv_message(grpc_chttp2_transport* t,
+                                             grpc_chttp2_stream* s) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (s->recv_message_ready != nullptr) {
+    *s->recv_message = nullptr;
+    if (s->final_metadata_requested && s->seen_error) {
+      grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
+      if (!s->pending_byte_stream) {
+        grpc_slice_buffer_reset_and_unref_internal(
+            &s->unprocessed_incoming_frames_buffer);
+      }
+    }
+    if (!s->pending_byte_stream) {
+      while (s->unprocessed_incoming_frames_buffer.length > 0 ||
+             s->frame_storage.length > 0) {
+        if (s->unprocessed_incoming_frames_buffer.length == 0) {
+          grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer,
+                                 &s->frame_storage);
+          s->unprocessed_incoming_frames_decompressed = false;
+        }
+        if (!s->unprocessed_incoming_frames_decompressed &&
+            s->stream_decompression_method !=
+                GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) {
+          GPR_ASSERT(s->decompressed_data_buffer.length == 0);
+          bool end_of_context;
+          if (!s->stream_decompression_ctx) {
+            s->stream_decompression_ctx =
+                grpc_stream_compression_context_create(
+                    s->stream_decompression_method);
+          }
+          if (!grpc_stream_decompress(
+                  s->stream_decompression_ctx,
+                  &s->unprocessed_incoming_frames_buffer,
+                  &s->decompressed_data_buffer, nullptr,
+                  GRPC_HEADER_SIZE_IN_BYTES - s->decompressed_header_bytes,
+                  &end_of_context)) {
+            grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
+            grpc_slice_buffer_reset_and_unref_internal(
+                &s->unprocessed_incoming_frames_buffer);
+            error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "Stream decompression error.");
+          } else {
+            s->decompressed_header_bytes += s->decompressed_data_buffer.length;
+            if (s->decompressed_header_bytes == GRPC_HEADER_SIZE_IN_BYTES) {
+              s->decompressed_header_bytes = 0;
+            }
+            error = grpc_deframe_unprocessed_incoming_frames(
+                &s->data_parser, s, &s->decompressed_data_buffer, nullptr,
+                s->recv_message);
+            if (end_of_context) {
+              grpc_stream_compression_context_destroy(
+                  s->stream_decompression_ctx);
+              s->stream_decompression_ctx = nullptr;
+            }
+          }
+        } else {
+          error = grpc_deframe_unprocessed_incoming_frames(
+              &s->data_parser, s, &s->unprocessed_incoming_frames_buffer,
+              nullptr, s->recv_message);
+        }
+        if (error != GRPC_ERROR_NONE) {
+          s->seen_error = true;
+          grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
+          grpc_slice_buffer_reset_and_unref_internal(
+              &s->unprocessed_incoming_frames_buffer);
+          break;
+        } else if (*s->recv_message != nullptr) {
+          break;
+        }
+      }
+    }
+    // save the length of the buffer before handing control back to application
+    // threads. Needed to support correct flow control bookkeeping
+    s->unprocessed_incoming_frames_buffer_cached_length =
+        s->unprocessed_incoming_frames_buffer.length;
+    if (error == GRPC_ERROR_NONE && *s->recv_message != nullptr) {
+      null_then_run_closure(&s->recv_message_ready, GRPC_ERROR_NONE);
+    } else if (s->published_metadata[1] != GRPC_METADATA_NOT_PUBLISHED) {
+      *s->recv_message = nullptr;
+      null_then_run_closure(&s->recv_message_ready, GRPC_ERROR_NONE);
+    }
+    GRPC_ERROR_UNREF(error);
+  }
+}
+
+void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t,
+                                                       grpc_chttp2_stream* s) {
+  grpc_chttp2_maybe_complete_recv_message(t, s);
+  if (s->recv_trailing_metadata_finished != nullptr && s->read_closed &&
+      s->write_closed) {
+    if (s->seen_error || !t->is_client) {
+      grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
+      if (!s->pending_byte_stream) {
+        grpc_slice_buffer_reset_and_unref_internal(
+            &s->unprocessed_incoming_frames_buffer);
+      }
+    }
+    bool pending_data = s->pending_byte_stream ||
+                        s->unprocessed_incoming_frames_buffer.length > 0;
+    if (s->read_closed && s->frame_storage.length > 0 && !pending_data &&
+        !s->seen_error && s->recv_trailing_metadata_finished != nullptr) {
+      /* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and
+       * maybe decompress the next 5 bytes in the stream. */
+      bool end_of_context;
+      if (!s->stream_decompression_ctx) {
+        s->stream_decompression_ctx = grpc_stream_compression_context_create(
+            s->stream_decompression_method);
+      }
+      if (!grpc_stream_decompress(
+              s->stream_decompression_ctx, &s->frame_storage,
+              &s->unprocessed_incoming_frames_buffer, nullptr,
+              GRPC_HEADER_SIZE_IN_BYTES, &end_of_context)) {
+        grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage);
+        grpc_slice_buffer_reset_and_unref_internal(
+            &s->unprocessed_incoming_frames_buffer);
+        s->seen_error = true;
+      } else {
+        if (s->unprocessed_incoming_frames_buffer.length > 0) {
+          s->unprocessed_incoming_frames_decompressed = true;
+          pending_data = true;
+        }
+        if (end_of_context) {
+          grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
+          s->stream_decompression_ctx = nullptr;
+        }
+      }
+    }
+    if (s->read_closed && s->frame_storage.length == 0 && !pending_data &&
+        s->recv_trailing_metadata_finished != nullptr) {
+      grpc_transport_move_stats(&s->stats, s->collecting_stats);
+      s->collecting_stats = nullptr;
+      grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[1],
+                                                   s->recv_trailing_metadata);
+      null_then_run_closure(&s->recv_trailing_metadata_finished,
+                            GRPC_ERROR_NONE);
+    }
+  }
+}
+
+static void remove_stream(grpc_chttp2_transport* t, uint32_t id,
+                          grpc_error* error) {
+  grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(
+      grpc_chttp2_stream_map_delete(&t->stream_map, id));
+  GPR_ASSERT(s);
+  if (t->incoming_stream == s) {
+    t->incoming_stream = nullptr;
+    grpc_chttp2_parsing_become_skip_parser(t);
+  }
+  if (s->pending_byte_stream) {
+    if (s->on_next != nullptr) {
+      grpc_core::Chttp2IncomingByteStream* bs = s->data_parser.parsing_frame;
+      if (error == GRPC_ERROR_NONE) {
+        error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message");
+      }
+      bs->PublishError(error);
+      bs->Unref();
+      s->data_parser.parsing_frame = nullptr;
+    } else {
+      GRPC_ERROR_UNREF(s->byte_stream_error);
+      s->byte_stream_error = GRPC_ERROR_REF(error);
+    }
+  }
+
+  if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
+    post_benign_reclaimer(t);
+    if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SENT) {
+      close_transport_locked(
+          t, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                 "Last stream closed after sending GOAWAY", &error, 1));
+    }
+  }
+  if (grpc_chttp2_list_remove_writable_stream(t, s)) {
+    GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:remove_stream");
+  }
+
+  GRPC_ERROR_UNREF(error);
+
+  maybe_start_some_streams(t);
+}
+
+void grpc_chttp2_cancel_stream(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                               grpc_error* due_to_error) {
+  if (!t->is_client && !s->sent_trailing_metadata &&
+      grpc_error_has_clear_grpc_status(due_to_error)) {
+    close_from_api(t, s, due_to_error);
+    return;
+  }
+
+  if (!s->read_closed || !s->write_closed) {
+    if (s->id != 0) {
+      grpc_http2_error_code http_error;
+      grpc_error_get_status(due_to_error, s->deadline, nullptr, nullptr,
+                            &http_error, nullptr);
+      grpc_slice_buffer_add(
+          &t->qbuf,
+          grpc_chttp2_rst_stream_create(
+              s->id, static_cast<uint32_t>(http_error), &s->stats.outgoing));
+      grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM);
+    }
+  }
+  if (due_to_error != GRPC_ERROR_NONE && !s->seen_error) {
+    s->seen_error = true;
+  }
+  grpc_chttp2_mark_stream_closed(t, s, 1, 1, due_to_error);
+}
+
+void grpc_chttp2_fake_status(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                             grpc_error* error) {
+  grpc_status_code status;
+  grpc_slice slice;
+  grpc_error_get_status(error, s->deadline, &status, &slice, nullptr, nullptr);
+  if (status != GRPC_STATUS_OK) {
+    s->seen_error = true;
+  }
+  /* stream_global->recv_trailing_metadata_finished gives us a
+     last chance replacement: we've received trailing metadata,
+     but something more important has become available to signal
+     to the upper layers - drop what we've got, and then publish
+     what we want - which is safe because we haven't told anyone
+     about the metadata yet */
+  if (s->published_metadata[1] == GRPC_METADATA_NOT_PUBLISHED ||
+      s->recv_trailing_metadata_finished != nullptr) {
+    char status_string[GPR_LTOA_MIN_BUFSIZE];
+    gpr_ltoa(status, status_string);
+    GRPC_LOG_IF_ERROR("add_status",
+                      grpc_chttp2_incoming_metadata_buffer_replace_or_add(
+                          &s->metadata_buffer[1],
+                          grpc_mdelem_from_slices(
+                              GRPC_MDSTR_GRPC_STATUS,
+                              grpc_slice_from_copied_string(status_string))));
+    if (!GRPC_SLICE_IS_EMPTY(slice)) {
+      GRPC_LOG_IF_ERROR(
+          "add_status_message",
+          grpc_chttp2_incoming_metadata_buffer_replace_or_add(
+              &s->metadata_buffer[1],
+              grpc_mdelem_create(GRPC_MDSTR_GRPC_MESSAGE, slice, nullptr)));
+    }
+    s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE;
+    grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s);
+  }
+
+  GRPC_ERROR_UNREF(error);
+}
+
+static void add_error(grpc_error* error, grpc_error** refs, size_t* nrefs) {
+  if (error == GRPC_ERROR_NONE) return;
+  for (size_t i = 0; i < *nrefs; i++) {
+    if (error == refs[i]) {
+      return;
+    }
+  }
+  refs[*nrefs] = error;
+  ++*nrefs;
+}
+
+static grpc_error* removal_error(grpc_error* extra_error, grpc_chttp2_stream* s,
+                                 const char* master_error_msg) {
+  grpc_error* refs[3];
+  size_t nrefs = 0;
+  add_error(s->read_closed_error, refs, &nrefs);
+  add_error(s->write_closed_error, refs, &nrefs);
+  add_error(extra_error, refs, &nrefs);
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (nrefs > 0) {
+    error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(master_error_msg,
+                                                             refs, nrefs);
+  }
+  GRPC_ERROR_UNREF(extra_error);
+  return error;
+}
+
+static void flush_write_list(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                             grpc_chttp2_write_cb** list, grpc_error* error) {
+  while (*list) {
+    grpc_chttp2_write_cb* cb = *list;
+    *list = cb->next;
+    grpc_chttp2_complete_closure_step(t, s, &cb->closure, GRPC_ERROR_REF(error),
+                                      "on_write_finished_cb");
+    cb->next = t->write_cb_pool;
+    t->write_cb_pool = cb;
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+void grpc_chttp2_fail_pending_writes(grpc_chttp2_transport* t,
+                                     grpc_chttp2_stream* s, grpc_error* error) {
+  error =
+      removal_error(error, s, "Pending writes failed due to stream closure");
+  s->send_initial_metadata = nullptr;
+  grpc_chttp2_complete_closure_step(t, s, &s->send_initial_metadata_finished,
+                                    GRPC_ERROR_REF(error),
+                                    "send_initial_metadata_finished");
+
+  s->send_trailing_metadata = nullptr;
+  grpc_chttp2_complete_closure_step(t, s, &s->send_trailing_metadata_finished,
+                                    GRPC_ERROR_REF(error),
+                                    "send_trailing_metadata_finished");
+
+  s->fetching_send_message.reset();
+  grpc_chttp2_complete_closure_step(t, s, &s->fetching_send_message_finished,
+                                    GRPC_ERROR_REF(error),
+                                    "fetching_send_message_finished");
+  flush_write_list(t, s, &s->on_write_finished_cbs, GRPC_ERROR_REF(error));
+  flush_write_list(t, s, &s->on_flow_controlled_cbs, error);
+}
+
+void grpc_chttp2_mark_stream_closed(grpc_chttp2_transport* t,
+                                    grpc_chttp2_stream* s, int close_reads,
+                                    int close_writes, grpc_error* error) {
+  if (s->read_closed && s->write_closed) {
+    /* already closed */
+    grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s);
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  bool closed_read = false;
+  bool became_closed = false;
+  if (close_reads && !s->read_closed) {
+    s->read_closed_error = GRPC_ERROR_REF(error);
+    s->read_closed = true;
+    closed_read = true;
+  }
+  if (close_writes && !s->write_closed) {
+    s->write_closed_error = GRPC_ERROR_REF(error);
+    s->write_closed = true;
+    grpc_chttp2_fail_pending_writes(t, s, GRPC_ERROR_REF(error));
+  }
+  if (s->read_closed && s->write_closed) {
+    became_closed = true;
+    grpc_error* overall_error =
+        removal_error(GRPC_ERROR_REF(error), s, "Stream removed");
+    if (s->id != 0) {
+      remove_stream(t, s->id, GRPC_ERROR_REF(overall_error));
+    } else {
+      /* Purge streams waiting on concurrency still waiting for id assignment */
+      grpc_chttp2_list_remove_waiting_for_concurrency(t, s);
+    }
+    if (overall_error != GRPC_ERROR_NONE) {
+      grpc_chttp2_fake_status(t, s, overall_error);
+    }
+  }
+  if (closed_read) {
+    for (int i = 0; i < 2; i++) {
+      if (s->published_metadata[i] == GRPC_METADATA_NOT_PUBLISHED) {
+        s->published_metadata[i] = GPRC_METADATA_PUBLISHED_AT_CLOSE;
+      }
+    }
+    grpc_chttp2_maybe_complete_recv_initial_metadata(t, s);
+    grpc_chttp2_maybe_complete_recv_message(t, s);
+  }
+  if (became_closed) {
+    grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s);
+    GRPC_CHTTP2_STREAM_UNREF(s, "chttp2");
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+static void close_from_api(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                           grpc_error* error) {
+  grpc_slice hdr;
+  grpc_slice status_hdr;
+  grpc_slice http_status_hdr;
+  grpc_slice content_type_hdr;
+  grpc_slice message_pfx;
+  uint8_t* p;
+  uint32_t len = 0;
+  grpc_status_code grpc_status;
+  grpc_slice slice;
+  grpc_error_get_status(error, s->deadline, &grpc_status, &slice, nullptr,
+                        nullptr);
+
+  GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100);
+
+  /* Hand roll a header block.
+     This is unnecessarily ugly - at some point we should find a more
+     elegant solution.
+     It's complicated by the fact that our send machinery would be dead by
+     the time we got around to sending this, so instead we ignore HPACK
+     compression and just write the uncompressed bytes onto the wire. */
+  if (!s->sent_initial_metadata) {
+    http_status_hdr = GRPC_SLICE_MALLOC(13);
+    p = GRPC_SLICE_START_PTR(http_status_hdr);
+    *p++ = 0x00;
+    *p++ = 7;
+    *p++ = ':';
+    *p++ = 's';
+    *p++ = 't';
+    *p++ = 'a';
+    *p++ = 't';
+    *p++ = 'u';
+    *p++ = 's';
+    *p++ = 3;
+    *p++ = '2';
+    *p++ = '0';
+    *p++ = '0';
+    GPR_ASSERT(p == GRPC_SLICE_END_PTR(http_status_hdr));
+    len += static_cast<uint32_t> GRPC_SLICE_LENGTH(http_status_hdr);
+
+    content_type_hdr = GRPC_SLICE_MALLOC(31);
+    p = GRPC_SLICE_START_PTR(content_type_hdr);
+    *p++ = 0x00;
+    *p++ = 12;
+    *p++ = 'c';
+    *p++ = 'o';
+    *p++ = 'n';
+    *p++ = 't';
+    *p++ = 'e';
+    *p++ = 'n';
+    *p++ = 't';
+    *p++ = '-';
+    *p++ = 't';
+    *p++ = 'y';
+    *p++ = 'p';
+    *p++ = 'e';
+    *p++ = 16;
+    *p++ = 'a';
+    *p++ = 'p';
+    *p++ = 'p';
+    *p++ = 'l';
+    *p++ = 'i';
+    *p++ = 'c';
+    *p++ = 'a';
+    *p++ = 't';
+    *p++ = 'i';
+    *p++ = 'o';
+    *p++ = 'n';
+    *p++ = '/';
+    *p++ = 'g';
+    *p++ = 'r';
+    *p++ = 'p';
+    *p++ = 'c';
+    GPR_ASSERT(p == GRPC_SLICE_END_PTR(content_type_hdr));
+    len += static_cast<uint32_t> GRPC_SLICE_LENGTH(content_type_hdr);
+  }
+
+  status_hdr = GRPC_SLICE_MALLOC(15 + (grpc_status >= 10));
+  p = GRPC_SLICE_START_PTR(status_hdr);
+  *p++ = 0x00; /* literal header, not indexed */
+  *p++ = 11;   /* len(grpc-status) */
+  *p++ = 'g';
+  *p++ = 'r';
+  *p++ = 'p';
+  *p++ = 'c';
+  *p++ = '-';
+  *p++ = 's';
+  *p++ = 't';
+  *p++ = 'a';
+  *p++ = 't';
+  *p++ = 'u';
+  *p++ = 's';
+  if (grpc_status < 10) {
+    *p++ = 1;
+    *p++ = static_cast<uint8_t>('0' + grpc_status);
+  } else {
+    *p++ = 2;
+    *p++ = static_cast<uint8_t>('0' + (grpc_status / 10));
+    *p++ = static_cast<uint8_t>('0' + (grpc_status % 10));
+  }
+  GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr));
+  len += static_cast<uint32_t> GRPC_SLICE_LENGTH(status_hdr);
+
+  size_t msg_len = GRPC_SLICE_LENGTH(slice);
+  GPR_ASSERT(msg_len <= UINT32_MAX);
+  uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 1);
+  message_pfx = GRPC_SLICE_MALLOC(14 + msg_len_len);
+  p = GRPC_SLICE_START_PTR(message_pfx);
+  *p++ = 0x00; /* literal header, not indexed */
+  *p++ = 12;   /* len(grpc-message) */
+  *p++ = 'g';
+  *p++ = 'r';
+  *p++ = 'p';
+  *p++ = 'c';
+  *p++ = '-';
+  *p++ = 'm';
+  *p++ = 'e';
+  *p++ = 's';
+  *p++ = 's';
+  *p++ = 'a';
+  *p++ = 'g';
+  *p++ = 'e';
+  GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 1, 0, p, (uint32_t)msg_len_len);
+  p += msg_len_len;
+  GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx));
+  len += static_cast<uint32_t> GRPC_SLICE_LENGTH(message_pfx);
+  len += static_cast<uint32_t>(msg_len);
+
+  hdr = GRPC_SLICE_MALLOC(9);
+  p = GRPC_SLICE_START_PTR(hdr);
+  *p++ = static_cast<uint8_t>(len >> 16);
+  *p++ = static_cast<uint8_t>(len >> 8);
+  *p++ = static_cast<uint8_t>(len);
+  *p++ = GRPC_CHTTP2_FRAME_HEADER;
+  *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS;
+  *p++ = static_cast<uint8_t>(s->id >> 24);
+  *p++ = static_cast<uint8_t>(s->id >> 16);
+  *p++ = static_cast<uint8_t>(s->id >> 8);
+  *p++ = static_cast<uint8_t>(s->id);
+  GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr));
+
+  grpc_slice_buffer_add(&t->qbuf, hdr);
+  if (!s->sent_initial_metadata) {
+    grpc_slice_buffer_add(&t->qbuf, http_status_hdr);
+    grpc_slice_buffer_add(&t->qbuf, content_type_hdr);
+  }
+  grpc_slice_buffer_add(&t->qbuf, status_hdr);
+  grpc_slice_buffer_add(&t->qbuf, message_pfx);
+  grpc_slice_buffer_add(&t->qbuf, grpc_slice_ref_internal(slice));
+  grpc_slice_buffer_add(
+      &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR,
+                                              &s->stats.outgoing));
+
+  grpc_chttp2_mark_stream_closed(t, s, 1, 1, error);
+  grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API);
+}
+
+typedef struct {
+  grpc_error* error;
+  grpc_chttp2_transport* t;
+} cancel_stream_cb_args;
+
+static void cancel_stream_cb(void* user_data, uint32_t key, void* stream) {
+  cancel_stream_cb_args* args = static_cast<cancel_stream_cb_args*>(user_data);
+  grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(stream);
+  grpc_chttp2_cancel_stream(args->t, s, GRPC_ERROR_REF(args->error));
+}
+
+static void end_all_the_calls(grpc_chttp2_transport* t, grpc_error* error) {
+  cancel_stream_cb_args args = {error, t};
+  grpc_chttp2_stream_map_for_each(&t->stream_map, cancel_stream_cb, &args);
+  GRPC_ERROR_UNREF(error);
+}
+
+/*******************************************************************************
+ * INPUT PROCESSING - PARSING
+ */
+
+template <class F>
+static void WithUrgency(grpc_chttp2_transport* t,
+                        grpc_core::chttp2::FlowControlAction::Urgency urgency,
+                        grpc_chttp2_initiate_write_reason reason, F action) {
+  switch (urgency) {
+    case grpc_core::chttp2::FlowControlAction::Urgency::NO_ACTION_NEEDED:
+      break;
+    case grpc_core::chttp2::FlowControlAction::Urgency::UPDATE_IMMEDIATELY:
+      grpc_chttp2_initiate_write(t, reason);
+    // fallthrough
+    case grpc_core::chttp2::FlowControlAction::Urgency::QUEUE_UPDATE:
+      action();
+      break;
+  }
+}
+
+void grpc_chttp2_act_on_flowctl_action(
+    const grpc_core::chttp2::FlowControlAction& action,
+    grpc_chttp2_transport* t, grpc_chttp2_stream* s) {
+  WithUrgency(t, action.send_stream_update(),
+              GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL,
+              [t, s]() { grpc_chttp2_mark_stream_writable(t, s); });
+  WithUrgency(t, action.send_transport_update(),
+              GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL, []() {});
+  WithUrgency(t, action.send_initial_window_update(),
+              GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, [t, &action]() {
+                queue_setting_update(t,
+                                     GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
+                                     action.initial_window_size());
+              });
+  WithUrgency(t, action.send_max_frame_size_update(),
+              GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, [t, &action]() {
+                queue_setting_update(t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE,
+                                     action.max_frame_size());
+              });
+}
+
+static grpc_error* try_http_parsing(grpc_chttp2_transport* t) {
+  grpc_http_parser parser;
+  size_t i = 0;
+  grpc_error* error = GRPC_ERROR_NONE;
+  grpc_http_response response;
+  memset(&response, 0, sizeof(response));
+
+  grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response);
+
+  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], nullptr);
+  }
+  if (parse_error == GRPC_ERROR_NONE &&
+      (parse_error = grpc_http_parser_eof(&parser)) == GRPC_ERROR_NONE) {
+    error = grpc_error_set_int(
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "Trying to connect an http1.x server"),
+                           GRPC_ERROR_INT_HTTP_STATUS, response.status),
+        GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE);
+  }
+  GRPC_ERROR_UNREF(parse_error);
+
+  grpc_http_parser_destroy(&parser);
+  grpc_http_response_destroy(&response);
+  return error;
+}
+
+static void read_action_locked(void* tp, grpc_error* error) {
+  GPR_TIMER_SCOPE("reading_action_locked", 0);
+
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+
+  GRPC_ERROR_REF(error);
+
+  grpc_error* err = error;
+  if (err != GRPC_ERROR_NONE) {
+    err = grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                                 "Endpoint read failed", &err, 1),
+                             GRPC_ERROR_INT_OCCURRED_DURING_WRITE,
+                             t->write_state);
+  }
+  GPR_SWAP(grpc_error*, err, error);
+  GRPC_ERROR_UNREF(err);
+  if (t->closed_with_error == GRPC_ERROR_NONE) {
+    GPR_TIMER_SCOPE("reading_action.parse", 0);
+    size_t i = 0;
+    grpc_error* errors[3] = {GRPC_ERROR_REF(error), GRPC_ERROR_NONE,
+                             GRPC_ERROR_NONE};
+    for (; i < t->read_buffer.count && errors[1] == GRPC_ERROR_NONE; i++) {
+      grpc_core::BdpEstimator* bdp_est = t->flow_control->bdp_estimator();
+      if (bdp_est) {
+        bdp_est->AddIncomingBytes(
+            static_cast<int64_t> GRPC_SLICE_LENGTH(t->read_buffer.slices[i]));
+      }
+      errors[1] = grpc_chttp2_perform_read(t, t->read_buffer.slices[i]);
+    }
+    if (errors[1] != GRPC_ERROR_NONE) {
+      errors[2] = try_http_parsing(t);
+      GRPC_ERROR_UNREF(error);
+      error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+          "Failed parsing HTTP/2", errors, GPR_ARRAY_SIZE(errors));
+    }
+    for (i = 0; i < GPR_ARRAY_SIZE(errors); i++) {
+      GRPC_ERROR_UNREF(errors[i]);
+    }
+
+    GPR_TIMER_SCOPE("post_parse_locked", 0);
+    if (t->initial_window_update != 0) {
+      if (t->initial_window_update > 0) {
+        grpc_chttp2_stream* s;
+        while (grpc_chttp2_list_pop_stalled_by_stream(t, &s)) {
+          grpc_chttp2_mark_stream_writable(t, s);
+          grpc_chttp2_initiate_write(
+              t, GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING);
+        }
+      }
+      t->initial_window_update = 0;
+    }
+  }
+
+  GPR_TIMER_SCOPE("post_reading_action_locked", 0);
+  bool keep_reading = false;
+  if (error == GRPC_ERROR_NONE && t->closed_with_error != GRPC_ERROR_NONE) {
+    error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+        "Transport closed", &t->closed_with_error, 1);
+  }
+  if (error != GRPC_ERROR_NONE) {
+    /* If a goaway frame was received, this might be the reason why the read
+     * failed. Add this info to the error */
+    if (t->goaway_error != GRPC_ERROR_NONE) {
+      error = grpc_error_add_child(error, GRPC_ERROR_REF(t->goaway_error));
+    }
+
+    close_transport_locked(t, GRPC_ERROR_REF(error));
+    t->endpoint_reading = 0;
+  } else if (t->closed_with_error == GRPC_ERROR_NONE) {
+    keep_reading = true;
+    GRPC_CHTTP2_REF_TRANSPORT(t, "keep_reading");
+  }
+  grpc_slice_buffer_reset_and_unref_internal(&t->read_buffer);
+
+  if (keep_reading) {
+    grpc_endpoint_read(t->ep, &t->read_buffer, &t->read_action_locked);
+    grpc_chttp2_act_on_flowctl_action(t->flow_control->MakeAction(), t,
+                                      nullptr);
+    GRPC_CHTTP2_UNREF_TRANSPORT(t, "keep_reading");
+  } else {
+    GRPC_CHTTP2_UNREF_TRANSPORT(t, "reading_action");
+  }
+
+  GRPC_ERROR_UNREF(error);
+}
+
+// t is reffed prior to calling the first time, and once the callback chain
+// that kicks off finishes, it's unreffed
+static void schedule_bdp_ping_locked(grpc_chttp2_transport* t) {
+  t->flow_control->bdp_estimator()->SchedulePing();
+  send_ping_locked(t, &t->start_bdp_ping_locked, &t->finish_bdp_ping_locked);
+}
+
+static void start_bdp_ping_locked(void* tp, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+  if (grpc_http_trace.enabled()) {
+    gpr_log(GPR_INFO, "%s: Start BDP ping err=%s", t->peer_string,
+            grpc_error_string(error));
+  }
+  /* Reset the keepalive ping timer */
+  if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING) {
+    grpc_timer_cancel(&t->keepalive_ping_timer);
+  }
+  t->flow_control->bdp_estimator()->StartPing();
+}
+
+static void finish_bdp_ping_locked(void* tp, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+  if (grpc_http_trace.enabled()) {
+    gpr_log(GPR_INFO, "%s: Complete BDP ping err=%s", t->peer_string,
+            grpc_error_string(error));
+  }
+  if (error != GRPC_ERROR_NONE) {
+    GRPC_CHTTP2_UNREF_TRANSPORT(t, "bdp_ping");
+    return;
+  }
+  grpc_millis next_ping = t->flow_control->bdp_estimator()->CompletePing();
+  grpc_chttp2_act_on_flowctl_action(t->flow_control->PeriodicUpdate(), t,
+                                    nullptr);
+  GPR_ASSERT(!t->have_next_bdp_ping_timer);
+  t->have_next_bdp_ping_timer = true;
+  grpc_timer_init(&t->next_bdp_ping_timer, next_ping,
+                  &t->next_bdp_ping_timer_expired_locked);
+}
+
+static void next_bdp_ping_timer_expired_locked(void* tp, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+  GPR_ASSERT(t->have_next_bdp_ping_timer);
+  t->have_next_bdp_ping_timer = false;
+  if (error != GRPC_ERROR_NONE) {
+    GRPC_CHTTP2_UNREF_TRANSPORT(t, "bdp_ping");
+    return;
+  }
+  schedule_bdp_ping_locked(t);
+}
+
+void grpc_chttp2_config_default_keepalive_args(grpc_channel_args* args,
+                                               bool is_client) {
+  size_t i;
+  if (args) {
+    for (i = 0; i < args->num_args; i++) {
+      if (0 == strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) {
+        const int value = grpc_channel_arg_get_integer(
+            &args->args[i], {is_client ? g_default_client_keepalive_time_ms
+                                       : g_default_server_keepalive_time_ms,
+                             1, INT_MAX});
+        if (is_client) {
+          g_default_client_keepalive_time_ms = value;
+        } else {
+          g_default_server_keepalive_time_ms = value;
+        }
+      } else if (0 ==
+                 strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) {
+        const int value = grpc_channel_arg_get_integer(
+            &args->args[i], {is_client ? g_default_client_keepalive_timeout_ms
+                                       : g_default_server_keepalive_timeout_ms,
+                             0, INT_MAX});
+        if (is_client) {
+          g_default_client_keepalive_timeout_ms = value;
+        } else {
+          g_default_server_keepalive_timeout_ms = value;
+        }
+      } else if (0 == strcmp(args->args[i].key,
+                             GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) {
+        const bool value = static_cast<uint32_t>(grpc_channel_arg_get_integer(
+            &args->args[i],
+            {is_client ? g_default_client_keepalive_permit_without_calls
+                       : g_default_server_keepalive_timeout_ms,
+             0, 1}));
+        if (is_client) {
+          g_default_client_keepalive_permit_without_calls = value;
+        } else {
+          g_default_server_keepalive_permit_without_calls = value;
+        }
+      } else if (0 ==
+                 strcmp(args->args[i].key, GRPC_ARG_HTTP2_MAX_PING_STRIKES)) {
+        g_default_max_ping_strikes = grpc_channel_arg_get_integer(
+            &args->args[i], {g_default_max_ping_strikes, 0, INT_MAX});
+      } else if (0 == strcmp(args->args[i].key,
+                             GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) {
+        g_default_max_pings_without_data = grpc_channel_arg_get_integer(
+            &args->args[i], {g_default_max_pings_without_data, 0, INT_MAX});
+      } else if (0 ==
+                 strcmp(
+                     args->args[i].key,
+                     GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) {
+        g_default_min_sent_ping_interval_without_data_ms =
+            grpc_channel_arg_get_integer(
+                &args->args[i],
+                {g_default_min_sent_ping_interval_without_data_ms, 0, INT_MAX});
+      } else if (0 ==
+                 strcmp(
+                     args->args[i].key,
+                     GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) {
+        g_default_min_recv_ping_interval_without_data_ms =
+            grpc_channel_arg_get_integer(
+                &args->args[i],
+                {g_default_min_recv_ping_interval_without_data_ms, 0, INT_MAX});
+      }
+    }
+  }
+}
+
+static void init_keepalive_ping_locked(void* arg, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(arg);
+  GPR_ASSERT(t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING);
+  if (t->destroying || t->closed_with_error != GRPC_ERROR_NONE) {
+    t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING;
+  } else if (error == GRPC_ERROR_NONE) {
+    if (t->keepalive_permit_without_calls ||
+        grpc_chttp2_stream_map_size(&t->stream_map) > 0) {
+      t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_PINGING;
+      GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive ping end");
+      grpc_timer_init_unset(&t->keepalive_watchdog_timer);
+      send_keepalive_ping_locked(t);
+      grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING);
+    } else {
+      GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
+      grpc_timer_init(&t->keepalive_ping_timer,
+                      grpc_core::ExecCtx::Get()->Now() + t->keepalive_time,
+                      &t->init_keepalive_ping_locked);
+    }
+  } else if (error == GRPC_ERROR_CANCELLED) {
+    /* The keepalive ping timer may be cancelled by bdp */
+    GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
+    grpc_timer_init(&t->keepalive_ping_timer,
+                    grpc_core::ExecCtx::Get()->Now() + t->keepalive_time,
+                    &t->init_keepalive_ping_locked);
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "init keepalive ping");
+}
+
+static void start_keepalive_ping_locked(void* arg, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(arg);
+  if (error != GRPC_ERROR_NONE) {
+    return;
+  }
+  if (t->channelz_socket != nullptr) {
+    t->channelz_socket->RecordKeepaliveSent();
+  }
+  GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog");
+  grpc_timer_init(&t->keepalive_watchdog_timer,
+                  grpc_core::ExecCtx::Get()->Now() + t->keepalive_timeout,
+                  &t->keepalive_watchdog_fired_locked);
+}
+
+static void finish_keepalive_ping_locked(void* arg, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(arg);
+  if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) {
+    if (error == GRPC_ERROR_NONE) {
+      t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
+      grpc_timer_cancel(&t->keepalive_watchdog_timer);
+      GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
+      grpc_timer_init(&t->keepalive_ping_timer,
+                      grpc_core::ExecCtx::Get()->Now() + t->keepalive_time,
+                      &t->init_keepalive_ping_locked);
+    }
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "keepalive ping end");
+}
+
+static void keepalive_watchdog_fired_locked(void* arg, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(arg);
+  if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) {
+    if (error == GRPC_ERROR_NONE) {
+      t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING;
+      close_transport_locked(
+          t, grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                    "keepalive watchdog timeout"),
+                                GRPC_ERROR_INT_GRPC_STATUS,
+                                GRPC_STATUS_UNAVAILABLE));
+    }
+  } else {
+    /* The watchdog timer should have been cancelled by
+     * finish_keepalive_ping_locked. */
+    if (GPR_UNLIKELY(error != GRPC_ERROR_CANCELLED)) {
+      gpr_log(GPR_ERROR, "keepalive_ping_end state error: %d (expect: %d)",
+              t->keepalive_state, GRPC_CHTTP2_KEEPALIVE_STATE_PINGING);
+    }
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "keepalive watchdog");
+}
+
+/*******************************************************************************
+ * CALLBACK LOOP
+ */
+
+static void connectivity_state_set(grpc_chttp2_transport* t,
+                                   grpc_connectivity_state state,
+                                   grpc_error* error, const char* reason) {
+  GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "set connectivity_state=%d", state));
+  grpc_connectivity_state_set(&t->channel_callback.state_tracker, state, error,
+                              reason);
+}
+
+/*******************************************************************************
+ * POLLSET STUFF
+ */
+
+static void set_pollset(grpc_transport* gt, grpc_stream* gs,
+                        grpc_pollset* pollset) {
+  grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
+  grpc_endpoint_add_to_pollset(t->ep, pollset);
+}
+
+static void set_pollset_set(grpc_transport* gt, grpc_stream* gs,
+                            grpc_pollset_set* pollset_set) {
+  grpc_chttp2_transport* t = reinterpret_cast<grpc_chttp2_transport*>(gt);
+  grpc_endpoint_add_to_pollset_set(t->ep, pollset_set);
+}
+
+/*******************************************************************************
+ * BYTE STREAM
+ */
+
+static void reset_byte_stream(void* arg, grpc_error* error) {
+  grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(arg);
+  s->pending_byte_stream = false;
+  if (error == GRPC_ERROR_NONE) {
+    grpc_chttp2_maybe_complete_recv_message(s->t, s);
+    grpc_chttp2_maybe_complete_recv_trailing_metadata(s->t, s);
+  } else {
+    GPR_ASSERT(error != GRPC_ERROR_NONE);
+    GRPC_CLOSURE_SCHED(s->on_next, GRPC_ERROR_REF(error));
+    s->on_next = nullptr;
+    GRPC_ERROR_UNREF(s->byte_stream_error);
+    s->byte_stream_error = GRPC_ERROR_NONE;
+    grpc_chttp2_cancel_stream(s->t, s, GRPC_ERROR_REF(error));
+    s->byte_stream_error = GRPC_ERROR_REF(error);
+  }
+}
+
+namespace grpc_core {
+
+Chttp2IncomingByteStream::Chttp2IncomingByteStream(
+    grpc_chttp2_transport* transport, grpc_chttp2_stream* stream,
+    uint32_t frame_size, uint32_t flags)
+    : ByteStream(frame_size, flags),
+      transport_(transport),
+      stream_(stream),
+      refs_(2),
+      remaining_bytes_(frame_size) {
+  GRPC_ERROR_UNREF(stream->byte_stream_error);
+  stream->byte_stream_error = GRPC_ERROR_NONE;
+}
+
+void Chttp2IncomingByteStream::OrphanLocked(void* arg,
+                                            grpc_error* error_ignored) {
+  Chttp2IncomingByteStream* bs = static_cast<Chttp2IncomingByteStream*>(arg);
+  grpc_chttp2_stream* s = bs->stream_;
+  grpc_chttp2_transport* t = s->t;
+  bs->Unref();
+  s->pending_byte_stream = false;
+  grpc_chttp2_maybe_complete_recv_message(t, s);
+  grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s);
+}
+
+void Chttp2IncomingByteStream::Orphan() {
+  GPR_TIMER_SCOPE("incoming_byte_stream_destroy", 0);
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_INIT(&destroy_action_,
+                        &Chttp2IncomingByteStream::OrphanLocked, this,
+                        grpc_combiner_scheduler(transport_->combiner)),
+      GRPC_ERROR_NONE);
+}
+
+void Chttp2IncomingByteStream::NextLocked(void* arg,
+                                          grpc_error* error_ignored) {
+  Chttp2IncomingByteStream* bs = static_cast<Chttp2IncomingByteStream*>(arg);
+  grpc_chttp2_transport* t = bs->transport_;
+  grpc_chttp2_stream* s = bs->stream_;
+  size_t cur_length = s->frame_storage.length;
+  if (!s->read_closed) {
+    s->flow_control->IncomingByteStreamUpdate(bs->next_action_.max_size_hint,
+                                              cur_length);
+    grpc_chttp2_act_on_flowctl_action(s->flow_control->MakeAction(), t, s);
+  }
+  GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0);
+  if (s->frame_storage.length > 0) {
+    grpc_slice_buffer_swap(&s->frame_storage,
+                           &s->unprocessed_incoming_frames_buffer);
+    s->unprocessed_incoming_frames_decompressed = false;
+    GRPC_CLOSURE_SCHED(bs->next_action_.on_complete, GRPC_ERROR_NONE);
+  } else if (s->byte_stream_error != GRPC_ERROR_NONE) {
+    GRPC_CLOSURE_SCHED(bs->next_action_.on_complete,
+                       GRPC_ERROR_REF(s->byte_stream_error));
+    if (s->data_parser.parsing_frame != nullptr) {
+      s->data_parser.parsing_frame->Unref();
+      s->data_parser.parsing_frame = nullptr;
+    }
+  } else if (s->read_closed) {
+    if (bs->remaining_bytes_ != 0) {
+      s->byte_stream_error =
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message");
+      GRPC_CLOSURE_SCHED(bs->next_action_.on_complete,
+                         GRPC_ERROR_REF(s->byte_stream_error));
+      if (s->data_parser.parsing_frame != nullptr) {
+        s->data_parser.parsing_frame->Unref();
+        s->data_parser.parsing_frame = nullptr;
+      }
+    } else {
+      /* Should never reach here. */
+      GPR_ASSERT(false);
+    }
+  } else {
+    s->on_next = bs->next_action_.on_complete;
+  }
+  bs->Unref();
+}
+
+bool Chttp2IncomingByteStream::Next(size_t max_size_hint,
+                                    grpc_closure* on_complete) {
+  GPR_TIMER_SCOPE("incoming_byte_stream_next", 0);
+  if (stream_->unprocessed_incoming_frames_buffer.length > 0) {
+    return true;
+  } else {
+    Ref();
+    next_action_.max_size_hint = max_size_hint;
+    next_action_.on_complete = on_complete;
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_INIT(&next_action_.closure,
+                          &Chttp2IncomingByteStream::NextLocked, this,
+                          grpc_combiner_scheduler(transport_->combiner)),
+        GRPC_ERROR_NONE);
+    return false;
+  }
+}
+
+void Chttp2IncomingByteStream::MaybeCreateStreamDecompressionCtx() {
+  if (!stream_->stream_decompression_ctx) {
+    stream_->stream_decompression_ctx = grpc_stream_compression_context_create(
+        stream_->stream_decompression_method);
+  }
+}
+
+grpc_error* Chttp2IncomingByteStream::Pull(grpc_slice* slice) {
+  GPR_TIMER_SCOPE("incoming_byte_stream_pull", 0);
+  grpc_error* error;
+  if (stream_->unprocessed_incoming_frames_buffer.length > 0) {
+    if (!stream_->unprocessed_incoming_frames_decompressed) {
+      bool end_of_context;
+      MaybeCreateStreamDecompressionCtx();
+      if (!grpc_stream_decompress(stream_->stream_decompression_ctx,
+                                  &stream_->unprocessed_incoming_frames_buffer,
+                                  &stream_->decompressed_data_buffer, nullptr,
+                                  MAX_SIZE_T, &end_of_context)) {
+        error =
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream decompression error.");
+        return error;
+      }
+      GPR_ASSERT(stream_->unprocessed_incoming_frames_buffer.length == 0);
+      grpc_slice_buffer_swap(&stream_->unprocessed_incoming_frames_buffer,
+                             &stream_->decompressed_data_buffer);
+      stream_->unprocessed_incoming_frames_decompressed = true;
+      if (end_of_context) {
+        grpc_stream_compression_context_destroy(
+            stream_->stream_decompression_ctx);
+        stream_->stream_decompression_ctx = nullptr;
+      }
+      if (stream_->unprocessed_incoming_frames_buffer.length == 0) {
+        *slice = grpc_empty_slice();
+      }
+    }
+    error = grpc_deframe_unprocessed_incoming_frames(
+        &stream_->data_parser, stream_,
+        &stream_->unprocessed_incoming_frames_buffer, slice, nullptr);
+    if (error != GRPC_ERROR_NONE) {
+      return error;
+    }
+  } else {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message");
+    GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error));
+    return error;
+  }
+  return GRPC_ERROR_NONE;
+}
+
+void Chttp2IncomingByteStream::PublishError(grpc_error* error) {
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  GRPC_CLOSURE_SCHED(stream_->on_next, GRPC_ERROR_REF(error));
+  stream_->on_next = nullptr;
+  GRPC_ERROR_UNREF(stream_->byte_stream_error);
+  stream_->byte_stream_error = GRPC_ERROR_REF(error);
+  grpc_chttp2_cancel_stream(transport_, stream_, GRPC_ERROR_REF(error));
+}
+
+grpc_error* Chttp2IncomingByteStream::Push(grpc_slice slice,
+                                           grpc_slice* slice_out) {
+  if (remaining_bytes_ < GRPC_SLICE_LENGTH(slice)) {
+    grpc_error* error =
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many bytes in stream");
+    GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error));
+    grpc_slice_unref_internal(slice);
+    return error;
+  } else {
+    remaining_bytes_ -= static_cast<uint32_t> GRPC_SLICE_LENGTH(slice);
+    if (slice_out != nullptr) {
+      *slice_out = slice;
+    }
+    return GRPC_ERROR_NONE;
+  }
+}
+
+grpc_error* Chttp2IncomingByteStream::Finished(grpc_error* error,
+                                               bool reset_on_error) {
+  if (error == GRPC_ERROR_NONE) {
+    if (remaining_bytes_ != 0) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message");
+    }
+  }
+  if (error != GRPC_ERROR_NONE && reset_on_error) {
+    GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error));
+  }
+  Unref();
+  return error;
+}
+
+void Chttp2IncomingByteStream::Shutdown(grpc_error* error) {
+  GRPC_ERROR_UNREF(Finished(error, true /* reset_on_error */));
+}
+
+}  // namespace grpc_core
+
+/*******************************************************************************
+ * RESOURCE QUOTAS
+ */
+
+static void post_benign_reclaimer(grpc_chttp2_transport* t) {
+  if (!t->benign_reclaimer_registered) {
+    t->benign_reclaimer_registered = true;
+    GRPC_CHTTP2_REF_TRANSPORT(t, "benign_reclaimer");
+    grpc_resource_user_post_reclaimer(grpc_endpoint_get_resource_user(t->ep),
+                                      false, &t->benign_reclaimer_locked);
+  }
+}
+
+static void post_destructive_reclaimer(grpc_chttp2_transport* t) {
+  if (!t->destructive_reclaimer_registered) {
+    t->destructive_reclaimer_registered = true;
+    GRPC_CHTTP2_REF_TRANSPORT(t, "destructive_reclaimer");
+    grpc_resource_user_post_reclaimer(grpc_endpoint_get_resource_user(t->ep),
+                                      true, &t->destructive_reclaimer_locked);
+  }
+}
+
+static void benign_reclaimer_locked(void* arg, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(arg);
+  if (error == GRPC_ERROR_NONE &&
+      grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
+    /* Channel with no active streams: send a goaway to try and make it
+     * disconnect cleanly */
+    if (grpc_resource_quota_trace.enabled()) {
+      gpr_log(GPR_INFO, "HTTP2: %s - send goaway to free memory",
+              t->peer_string);
+    }
+    send_goaway(t,
+                grpc_error_set_int(
+                    GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"),
+                    GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM));
+  } else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR
+            " streams",
+            t->peer_string, grpc_chttp2_stream_map_size(&t->stream_map));
+  }
+  t->benign_reclaimer_registered = false;
+  if (error != GRPC_ERROR_CANCELLED) {
+    grpc_resource_user_finish_reclamation(
+        grpc_endpoint_get_resource_user(t->ep));
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "benign_reclaimer");
+}
+
+static void destructive_reclaimer_locked(void* arg, grpc_error* error) {
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(arg);
+  size_t n = grpc_chttp2_stream_map_size(&t->stream_map);
+  t->destructive_reclaimer_registered = false;
+  if (error == GRPC_ERROR_NONE && n > 0) {
+    grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(
+        grpc_chttp2_stream_map_rand(&t->stream_map));
+    if (grpc_resource_quota_trace.enabled()) {
+      gpr_log(GPR_INFO, "HTTP2: %s - abandon stream id %d", t->peer_string,
+              s->id);
+    }
+    grpc_chttp2_cancel_stream(
+        t, s,
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"),
+                           GRPC_ERROR_INT_HTTP2_ERROR,
+                           GRPC_HTTP2_ENHANCE_YOUR_CALM));
+    if (n > 1) {
+      /* Since we cancel one stream per destructive reclamation, if
+         there are more streams left, we can immediately post a new
+         reclaimer in case the resource quota needs to free more
+         memory */
+      post_destructive_reclaimer(t);
+    }
+  }
+  if (error != GRPC_ERROR_CANCELLED) {
+    grpc_resource_user_finish_reclamation(
+        grpc_endpoint_get_resource_user(t->ep));
+  }
+  GRPC_CHTTP2_UNREF_TRANSPORT(t, "destructive_reclaimer");
+}
+
+/*******************************************************************************
+ * MONITORING
+ */
+
+const char* grpc_chttp2_initiate_write_reason_string(
+    grpc_chttp2_initiate_write_reason reason) {
+  switch (reason) {
+    case GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE:
+      return "INITIAL_WRITE";
+    case GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM:
+      return "START_NEW_STREAM";
+    case GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE:
+      return "SEND_MESSAGE";
+    case GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA:
+      return "SEND_INITIAL_METADATA";
+    case GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA:
+      return "SEND_TRAILING_METADATA";
+    case GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING:
+      return "RETRY_SEND_PING";
+    case GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS:
+      return "CONTINUE_PINGS";
+    case GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT:
+      return "GOAWAY_SENT";
+    case GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM:
+      return "RST_STREAM";
+    case GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API:
+      return "CLOSE_FROM_API";
+    case GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL:
+      return "STREAM_FLOW_CONTROL";
+    case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL:
+      return "TRANSPORT_FLOW_CONTROL";
+    case GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS:
+      return "SEND_SETTINGS";
+    case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING:
+      return "FLOW_CONTROL_UNSTALLED_BY_SETTING";
+    case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE:
+      return "FLOW_CONTROL_UNSTALLED_BY_UPDATE";
+    case GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING:
+      return "APPLICATION_PING";
+    case GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING:
+      return "KEEPALIVE_PING";
+    case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED:
+      return "TRANSPORT_FLOW_CONTROL_UNSTALLED";
+    case GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE:
+      return "PING_RESPONSE";
+    case GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM:
+      return "FORCE_RST_STREAM";
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+static grpc_endpoint* chttp2_get_endpoint(grpc_transport* t) {
+  return (reinterpret_cast<grpc_chttp2_transport*>(t))->ep;
+}
+
+static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream),
+                                             "chttp2",
+                                             init_stream,
+                                             set_pollset,
+                                             set_pollset_set,
+                                             perform_stream_op,
+                                             perform_transport_op,
+                                             destroy_stream,
+                                             destroy_transport,
+                                             chttp2_get_endpoint};
+
+static const grpc_transport_vtable* get_vtable(void) { return &vtable; }
+
+grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode>
+grpc_chttp2_transport_get_socket_node(grpc_transport* transport) {
+  grpc_chttp2_transport* t =
+      reinterpret_cast<grpc_chttp2_transport*>(transport);
+  return t->channelz_socket;
+}
+
+grpc_transport* grpc_create_chttp2_transport(
+    const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client,
+    grpc_resource_user* resource_user) {
+  auto t = grpc_core::New<grpc_chttp2_transport>(channel_args, ep, is_client,
+                                                 resource_user);
+  return &t->base;
+}
+
+void grpc_chttp2_transport_start_reading(
+    grpc_transport* transport, grpc_slice_buffer* read_buffer,
+    grpc_closure* notify_on_receive_settings) {
+  grpc_chttp2_transport* t =
+      reinterpret_cast<grpc_chttp2_transport*>(transport);
+  GRPC_CHTTP2_REF_TRANSPORT(
+      t, "reading_action"); /* matches unref inside reading_action */
+  if (read_buffer != nullptr) {
+    grpc_slice_buffer_move_into(read_buffer, &t->read_buffer);
+    gpr_free(read_buffer);
+  }
+  t->notify_on_receive_settings = notify_on_receive_settings;
+  GRPC_CLOSURE_SCHED(&t->read_action_locked, GRPC_ERROR_NONE);
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.h
new file mode 100644
index 0000000..c22cfb0
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channelz.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/transport/transport.h"
+
+extern grpc_core::TraceFlag grpc_http_trace;
+extern grpc_core::TraceFlag grpc_trace_http2_stream_state;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount;
+
+extern bool g_flow_control_enabled;
+
+grpc_transport* grpc_create_chttp2_transport(
+    const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client,
+    grpc_resource_user* resource_user = nullptr);
+
+grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode>
+grpc_chttp2_transport_get_socket_node(grpc_transport* transport);
+
+/// Takes ownership of \a read_buffer, which (if non-NULL) contains
+/// leftover bytes previously read from the endpoint (e.g., by handshakers).
+/// If non-null, \a notify_on_receive_settings will be scheduled when
+/// HTTP/2 settings are received from the peer.
+void grpc_chttp2_transport_start_reading(
+    grpc_transport* transport, grpc_slice_buffer* read_buffer,
+    grpc_closure* notify_on_receive_settings);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.cc
new file mode 100644
index 0000000..df09809
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.cc
@@ -0,0 +1,67 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/context_list.h"
+
+namespace {
+void (*write_timestamps_callback_g)(void*, grpc_core::Timestamps*,
+                                    grpc_error* error) = nullptr;
+void* (*get_copied_context_fn_g)(void*) = nullptr;
+}  // namespace
+
+namespace grpc_core {
+void ContextList::Append(ContextList** head, grpc_chttp2_stream* s) {
+  if (get_copied_context_fn_g == nullptr ||
+      write_timestamps_callback_g == nullptr) {
+    return;
+  }
+  /* Create a new element in the list and add it at the front */
+  ContextList* elem = grpc_core::New<ContextList>();
+  elem->trace_context_ = get_copied_context_fn_g(s->context);
+  elem->byte_offset_ = s->byte_counter;
+  elem->next_ = *head;
+  *head = elem;
+}
+
+void ContextList::Execute(void* arg, grpc_core::Timestamps* ts,
+                          grpc_error* error) {
+  ContextList* head = static_cast<ContextList*>(arg);
+  ContextList* to_be_freed;
+  while (head != nullptr) {
+    if (write_timestamps_callback_g) {
+      ts->byte_offset = static_cast<uint32_t>(head->byte_offset_);
+      write_timestamps_callback_g(head->trace_context_, ts, error);
+    }
+    to_be_freed = head;
+    head = head->next_;
+    grpc_core::Delete(to_be_freed);
+  }
+}
+
+void grpc_http2_set_write_timestamps_callback(void (*fn)(void*,
+                                                         grpc_core::Timestamps*,
+                                                         grpc_error* error)) {
+  write_timestamps_callback_g = fn;
+}
+
+void grpc_http2_set_fn_get_copied_context(void* (*fn)(void*)) {
+  get_copied_context_fn_g = fn;
+}
+} /* namespace grpc_core */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.h
new file mode 100644
index 0000000..5b9d2ab
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CONTEXT_LIST_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CONTEXT_LIST_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/buffer_list.h"
+
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+namespace grpc_core {
+/** A list of RPC Contexts */
+class ContextList {
+ public:
+  /* Creates a new element with \a context as the value and appends it to the
+   * list. */
+  static void Append(ContextList** head, grpc_chttp2_stream* s);
+
+  /* Executes a function \a fn with each context in the list and \a ts. It also
+   * frees up the entire list after this operation. It is intended as a callback
+   * and hence does not take a ref on \a error */
+  static void Execute(void* arg, grpc_core::Timestamps* ts, grpc_error* error);
+
+ private:
+  void* trace_context_ = nullptr;
+  ContextList* next_ = nullptr;
+  size_t byte_offset_ = 0;
+};
+
+void grpc_http2_set_write_timestamps_callback(void (*fn)(void*,
+                                                         grpc_core::Timestamps*,
+                                                         grpc_error* error));
+void grpc_http2_set_fn_get_copied_context(void* (*fn)(void*));
+} /* namespace grpc_core */
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CONTEXT_LIST_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.cc
new file mode 100644
index 0000000..53932bc
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.cc
@@ -0,0 +1,408 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/flow_control.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+#include "src/core/lib/gpr/string.h"
+
+grpc_core::TraceFlag grpc_flowctl_trace(false, "flowctl");
+
+namespace grpc_core {
+namespace chttp2 {
+
+namespace {
+
+static constexpr const int kTracePadding = 30;
+static constexpr const uint32_t kMaxWindowUpdateSize = (1u << 31) - 1;
+
+static char* fmt_int64_diff_str(int64_t old_val, int64_t new_val) {
+  char* str;
+  if (old_val != new_val) {
+    gpr_asprintf(&str, "%" PRId64 " -> %" PRId64 "", old_val, new_val);
+  } else {
+    gpr_asprintf(&str, "%" PRId64 "", old_val);
+  }
+  char* str_lp = gpr_leftpad(str, ' ', kTracePadding);
+  gpr_free(str);
+  return str_lp;
+}
+
+static char* fmt_uint32_diff_str(uint32_t old_val, uint32_t new_val) {
+  char* str;
+  if (old_val != new_val) {
+    gpr_asprintf(&str, "%" PRIu32 " -> %" PRIu32 "", old_val, new_val);
+  } else {
+    gpr_asprintf(&str, "%" PRIu32 "", old_val);
+  }
+  char* str_lp = gpr_leftpad(str, ' ', kTracePadding);
+  gpr_free(str);
+  return str_lp;
+}
+}  // namespace
+
+void FlowControlTrace::Init(const char* reason, TransportFlowControl* tfc,
+                            StreamFlowControl* sfc) {
+  tfc_ = tfc;
+  sfc_ = sfc;
+  reason_ = reason;
+  remote_window_ = tfc->remote_window();
+  target_window_ = tfc->target_window();
+  announced_window_ = tfc->announced_window();
+  if (sfc != nullptr) {
+    remote_window_delta_ = sfc->remote_window_delta();
+    local_window_delta_ = sfc->local_window_delta();
+    announced_window_delta_ = sfc->announced_window_delta();
+  }
+}
+
+void FlowControlTrace::Finish() {
+  uint32_t acked_local_window =
+      tfc_->transport()->settings[GRPC_SENT_SETTINGS]
+                                 [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
+  uint32_t remote_window =
+      tfc_->transport()->settings[GRPC_PEER_SETTINGS]
+                                 [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
+  char* trw_str = fmt_int64_diff_str(remote_window_, tfc_->remote_window());
+  char* tlw_str = fmt_int64_diff_str(target_window_, tfc_->target_window());
+  char* taw_str =
+      fmt_int64_diff_str(announced_window_, tfc_->announced_window());
+  char* srw_str;
+  char* slw_str;
+  char* saw_str;
+  if (sfc_ != nullptr) {
+    srw_str = fmt_int64_diff_str(remote_window_delta_ + remote_window,
+                                 sfc_->remote_window_delta() + remote_window);
+    slw_str =
+        fmt_int64_diff_str(local_window_delta_ + acked_local_window,
+                           sfc_->local_window_delta() + acked_local_window);
+    saw_str =
+        fmt_int64_diff_str(announced_window_delta_ + acked_local_window,
+                           sfc_->announced_window_delta() + acked_local_window);
+  } else {
+    srw_str = gpr_leftpad("", ' ', kTracePadding);
+    slw_str = gpr_leftpad("", ' ', kTracePadding);
+    saw_str = gpr_leftpad("", ' ', kTracePadding);
+  }
+  gpr_log(GPR_DEBUG,
+          "%p[%u][%s] | %s | trw:%s, ttw:%s, taw:%s, srw:%s, slw:%s, saw:%s",
+          tfc_, sfc_ != nullptr ? sfc_->stream()->id : 0,
+          tfc_->transport()->is_client ? "cli" : "svr", reason_, trw_str,
+          tlw_str, taw_str, srw_str, slw_str, saw_str);
+  gpr_free(trw_str);
+  gpr_free(tlw_str);
+  gpr_free(taw_str);
+  gpr_free(srw_str);
+  gpr_free(slw_str);
+  gpr_free(saw_str);
+}
+
+const char* FlowControlAction::UrgencyString(Urgency u) {
+  switch (u) {
+    case Urgency::NO_ACTION_NEEDED:
+      return "no action";
+    case Urgency::UPDATE_IMMEDIATELY:
+      return "update immediately";
+    case Urgency::QUEUE_UPDATE:
+      return "queue update";
+    default:
+      GPR_UNREACHABLE_CODE(return "unknown");
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+void FlowControlAction::Trace(grpc_chttp2_transport* t) const {
+  char* iw_str = fmt_uint32_diff_str(
+      t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+      initial_window_size_);
+  char* mf_str = fmt_uint32_diff_str(
+      t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],
+      max_frame_size_);
+  gpr_log(GPR_DEBUG, "t[%s],  s[%s], iw:%s:%s mf:%s:%s",
+          UrgencyString(send_transport_update_),
+          UrgencyString(send_stream_update_),
+          UrgencyString(send_initial_window_update_), iw_str,
+          UrgencyString(send_max_frame_size_update_), mf_str);
+  gpr_free(iw_str);
+  gpr_free(mf_str);
+}
+
+TransportFlowControlDisabled::TransportFlowControlDisabled(
+    grpc_chttp2_transport* t) {
+  remote_window_ = kMaxWindow;
+  target_initial_window_size_ = kMaxWindow;
+  announced_window_ = kMaxWindow;
+  t->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE] =
+      kFrameSize;
+  t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE] =
+      kFrameSize;
+  t->settings[GRPC_ACKED_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE] =
+      kFrameSize;
+  t->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] =
+      kMaxWindow;
+  t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] =
+      kMaxWindow;
+  t->settings[GRPC_ACKED_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] =
+      kMaxWindow;
+}
+
+TransportFlowControl::TransportFlowControl(const grpc_chttp2_transport* t,
+                                           bool enable_bdp_probe)
+    : t_(t),
+      enable_bdp_probe_(enable_bdp_probe),
+      bdp_estimator_(t->peer_string),
+      pid_controller_(grpc_core::PidController::Args()
+                          .set_gain_p(4)
+                          .set_gain_i(8)
+                          .set_gain_d(0)
+                          .set_initial_control_value(TargetLogBdp())
+                          .set_min_control_value(-1)
+                          .set_max_control_value(25)
+                          .set_integral_range(10)),
+      last_pid_update_(grpc_core::ExecCtx::Get()->Now()) {}
+
+uint32_t TransportFlowControl::MaybeSendUpdate(bool writing_anyway) {
+  FlowControlTrace trace("t updt sent", this, nullptr);
+  const uint32_t target_announced_window =
+      static_cast<const uint32_t>(target_window());
+  if ((writing_anyway || announced_window_ <= target_announced_window / 2) &&
+      announced_window_ != target_announced_window) {
+    const uint32_t announce = static_cast<uint32_t> GPR_CLAMP(
+        target_announced_window - announced_window_, 0, kMaxWindowUpdateSize);
+    announced_window_ += announce;
+    return announce;
+  }
+  return 0;
+}
+
+grpc_error* TransportFlowControl::ValidateRecvData(
+    int64_t incoming_frame_size) {
+  if (incoming_frame_size > announced_window_) {
+    char* msg;
+    gpr_asprintf(&msg,
+                 "frame of size %" PRId64 " overflows local window of %" PRId64,
+                 incoming_frame_size, announced_window_);
+    grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return err;
+  }
+  return GRPC_ERROR_NONE;
+}
+
+StreamFlowControl::StreamFlowControl(TransportFlowControl* tfc,
+                                     const grpc_chttp2_stream* s)
+    : tfc_(tfc), s_(s) {}
+
+grpc_error* StreamFlowControl::RecvData(int64_t incoming_frame_size) {
+  FlowControlTrace trace("  data recv", tfc_, this);
+
+  grpc_error* error = GRPC_ERROR_NONE;
+  error = tfc_->ValidateRecvData(incoming_frame_size);
+  if (error != GRPC_ERROR_NONE) return error;
+
+  uint32_t sent_init_window =
+      tfc_->transport()->settings[GRPC_SENT_SETTINGS]
+                                 [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
+  uint32_t acked_init_window =
+      tfc_->transport()->settings[GRPC_ACKED_SETTINGS]
+                                 [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
+
+  int64_t acked_stream_window = announced_window_delta_ + acked_init_window;
+  int64_t sent_stream_window = announced_window_delta_ + sent_init_window;
+  if (incoming_frame_size > acked_stream_window) {
+    if (incoming_frame_size <= sent_stream_window) {
+      gpr_log(GPR_ERROR,
+              "Incoming frame of size %" PRId64
+              " exceeds local window size of %" PRId64
+              ".\n"
+              "The (un-acked, future) window size would be %" PRId64
+              " which is not exceeded.\n"
+              "This would usually cause a disconnection, but allowing it due to"
+              "broken HTTP2 implementations in the wild.\n"
+              "See (for example) https://github.com/netty/netty/issues/6520.",
+              incoming_frame_size, acked_stream_window, sent_stream_window);
+    } else {
+      char* msg;
+      gpr_asprintf(
+          &msg, "frame of size %" PRId64 " overflows local window of %" PRId64,
+          incoming_frame_size, acked_stream_window);
+      grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      gpr_free(msg);
+      return err;
+    }
+  }
+
+  UpdateAnnouncedWindowDelta(tfc_, -incoming_frame_size);
+  local_window_delta_ -= incoming_frame_size;
+  tfc_->CommitRecvData(incoming_frame_size);
+  return GRPC_ERROR_NONE;
+}
+
+uint32_t StreamFlowControl::MaybeSendUpdate() {
+  FlowControlTrace trace("s updt sent", tfc_, this);
+  if (local_window_delta_ > announced_window_delta_) {
+    uint32_t announce = static_cast<uint32_t> GPR_CLAMP(
+        local_window_delta_ - announced_window_delta_, 0, kMaxWindowUpdateSize);
+    UpdateAnnouncedWindowDelta(tfc_, announce);
+    return announce;
+  }
+  return 0;
+}
+
+void StreamFlowControl::IncomingByteStreamUpdate(size_t max_size_hint,
+                                                 size_t have_already) {
+  FlowControlTrace trace("app st recv", tfc_, this);
+  uint32_t max_recv_bytes;
+  uint32_t sent_init_window =
+      tfc_->transport()->settings[GRPC_SENT_SETTINGS]
+                                 [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
+
+  /* clamp max recv hint to an allowable size */
+  if (max_size_hint >= UINT32_MAX - sent_init_window) {
+    max_recv_bytes = UINT32_MAX - sent_init_window;
+  } else {
+    max_recv_bytes = static_cast<uint32_t>(max_size_hint);
+  }
+
+  /* account for bytes already received but unknown to higher layers */
+  if (max_recv_bytes >= have_already) {
+    max_recv_bytes -= static_cast<uint32_t>(have_already);
+  } else {
+    max_recv_bytes = 0;
+  }
+
+  /* add some small lookahead to keep pipelines flowing */
+  GPR_ASSERT(max_recv_bytes <= UINT32_MAX - sent_init_window);
+  if (local_window_delta_ < max_recv_bytes) {
+    uint32_t add_max_recv_bytes =
+        static_cast<uint32_t>(max_recv_bytes - local_window_delta_);
+    local_window_delta_ += add_max_recv_bytes;
+  }
+}
+
+// Take in a target and modifies it based on the memory pressure of the system
+static double AdjustForMemoryPressure(grpc_resource_quota* quota,
+                                      double target) {
+  // do not increase window under heavy memory pressure.
+  double memory_pressure = grpc_resource_quota_get_memory_pressure(quota);
+  static const double kLowMemPressure = 0.1;
+  static const double kZeroTarget = 22;
+  static const double kHighMemPressure = 0.8;
+  static const double kMaxMemPressure = 0.9;
+  if (memory_pressure < kLowMemPressure && target < kZeroTarget) {
+    target = (target - kZeroTarget) * memory_pressure / kLowMemPressure +
+             kZeroTarget;
+  } else if (memory_pressure > kHighMemPressure) {
+    target *= 1 - GPR_MIN(1, (memory_pressure - kHighMemPressure) /
+                                 (kMaxMemPressure - kHighMemPressure));
+  }
+  return target;
+}
+
+double TransportFlowControl::TargetLogBdp() {
+  return AdjustForMemoryPressure(
+      grpc_resource_user_quota(grpc_endpoint_get_resource_user(t_->ep)),
+      1 + log2(bdp_estimator_.EstimateBdp()));
+}
+
+double TransportFlowControl::SmoothLogBdp(double value) {
+  grpc_millis now = grpc_core::ExecCtx::Get()->Now();
+  double bdp_error = value - pid_controller_.last_control_value();
+  const double dt = static_cast<double>(now - last_pid_update_) * 1e-3;
+  last_pid_update_ = now;
+  // Limit dt to 100ms
+  const double kMaxDt = 0.1;
+  return pid_controller_.Update(bdp_error, dt > kMaxDt ? kMaxDt : dt);
+}
+
+FlowControlAction::Urgency TransportFlowControl::DeltaUrgency(
+    int64_t value, grpc_chttp2_setting_id setting_id) {
+  int64_t delta = value - static_cast<int64_t>(
+                              t_->settings[GRPC_LOCAL_SETTINGS][setting_id]);
+  // TODO(ncteisen): tune this
+  if (delta != 0 && (delta <= -value / 5 || delta >= value / 5)) {
+    return FlowControlAction::Urgency::QUEUE_UPDATE;
+  } else {
+    return FlowControlAction::Urgency::NO_ACTION_NEEDED;
+  }
+}
+
+FlowControlAction TransportFlowControl::PeriodicUpdate() {
+  FlowControlAction action;
+  if (enable_bdp_probe_) {
+    // get bdp estimate and update initial_window accordingly.
+    // target might change based on how much memory pressure we are under
+    // TODO(ncteisen): experiment with setting target to be huge under low
+    // memory pressure.
+    const double target = pow(2, SmoothLogBdp(TargetLogBdp()));
+
+    // Though initial window 'could' drop to 0, we keep the floor at 128
+    target_initial_window_size_ =
+        static_cast<int32_t> GPR_CLAMP(target, 128, INT32_MAX);
+
+    action.set_send_initial_window_update(
+        DeltaUrgency(target_initial_window_size_,
+                     GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE),
+        static_cast<uint32_t>(target_initial_window_size_));
+
+    // get bandwidth estimate and update max_frame accordingly.
+    double bw_dbl = bdp_estimator_.EstimateBandwidth();
+    // we target the max of BDP or bandwidth in microseconds.
+    int32_t frame_size = static_cast<int32_t> GPR_CLAMP(
+        GPR_MAX((int32_t)GPR_CLAMP(bw_dbl, 0, INT_MAX) / 1000,
+                target_initial_window_size_),
+        16384, 16777215);
+    action.set_send_max_frame_size_update(
+        DeltaUrgency(static_cast<int64_t>(frame_size),
+                     GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE),
+        frame_size);
+  }
+  return UpdateAction(action);
+}
+
+FlowControlAction StreamFlowControl::UpdateAction(FlowControlAction action) {
+  // TODO(ncteisen): tune this
+  if (!s_->read_closed) {
+    uint32_t sent_init_window =
+        tfc_->transport()->settings[GRPC_SENT_SETTINGS]
+                                   [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
+    if (local_window_delta_ > announced_window_delta_ &&
+        announced_window_delta_ + sent_init_window <= sent_init_window / 2) {
+      action.set_send_stream_update(
+          FlowControlAction::Urgency::UPDATE_IMMEDIATELY);
+    } else if (local_window_delta_ > announced_window_delta_) {
+      action.set_send_stream_update(FlowControlAction::Urgency::QUEUE_UPDATE);
+    }
+  }
+
+  return action;
+}
+
+}  // namespace chttp2
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.h
new file mode 100644
index 0000000..120fefc
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.h
@@ -0,0 +1,482 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FLOW_CONTROL_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FLOW_CONTROL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdint.h>
+
+#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/transport/bdp_estimator.h"
+#include "src/core/lib/transport/pid_controller.h"
+
+struct grpc_chttp2_transport;
+struct grpc_chttp2_stream;
+
+extern grpc_core::TraceFlag grpc_flowctl_trace;
+
+namespace grpc {
+namespace testing {
+class TrickledCHTTP2;  // to make this a friend
+}  // namespace testing
+}  // namespace grpc
+
+namespace grpc_core {
+namespace chttp2 {
+
+static constexpr uint32_t kDefaultWindow = 65535;
+static constexpr int64_t kMaxWindow = static_cast<int64_t>((1u << 31) - 1);
+// TODO(ncteisen): Tune this
+static constexpr uint32_t kFrameSize = 1024 * 1024;
+
+class TransportFlowControl;
+class StreamFlowControl;
+
+// Encapsulates a collections of actions the transport needs to take with
+// regard to flow control. Each action comes with urgencies that tell the
+// transport how quickly the action must take place.
+class FlowControlAction {
+ public:
+  enum class Urgency : uint8_t {
+    // Nothing to be done.
+    NO_ACTION_NEEDED = 0,
+    // Initiate a write to update the initial window immediately.
+    UPDATE_IMMEDIATELY,
+    // Push the flow control update into a send buffer, to be sent
+    // out the next time a write is initiated.
+    QUEUE_UPDATE,
+  };
+
+  Urgency send_stream_update() const { return send_stream_update_; }
+  Urgency send_transport_update() const { return send_transport_update_; }
+  Urgency send_initial_window_update() const {
+    return send_initial_window_update_;
+  }
+  Urgency send_max_frame_size_update() const {
+    return send_max_frame_size_update_;
+  }
+  uint32_t initial_window_size() const { return initial_window_size_; }
+  uint32_t max_frame_size() const { return max_frame_size_; }
+
+  FlowControlAction& set_send_stream_update(Urgency u) {
+    send_stream_update_ = u;
+    return *this;
+  }
+  FlowControlAction& set_send_transport_update(Urgency u) {
+    send_transport_update_ = u;
+    return *this;
+  }
+  FlowControlAction& set_send_initial_window_update(Urgency u,
+                                                    uint32_t update) {
+    send_initial_window_update_ = u;
+    initial_window_size_ = update;
+    return *this;
+  }
+  FlowControlAction& set_send_max_frame_size_update(Urgency u,
+                                                    uint32_t update) {
+    send_max_frame_size_update_ = u;
+    max_frame_size_ = update;
+    return *this;
+  }
+
+  static const char* UrgencyString(Urgency u);
+  void Trace(grpc_chttp2_transport* t) const;
+
+ private:
+  Urgency send_stream_update_ = Urgency::NO_ACTION_NEEDED;
+  Urgency send_transport_update_ = Urgency::NO_ACTION_NEEDED;
+  Urgency send_initial_window_update_ = Urgency::NO_ACTION_NEEDED;
+  Urgency send_max_frame_size_update_ = Urgency::NO_ACTION_NEEDED;
+  uint32_t initial_window_size_ = 0;
+  uint32_t max_frame_size_ = 0;
+};
+
+class FlowControlTrace {
+ public:
+  FlowControlTrace(const char* reason, TransportFlowControl* tfc,
+                   StreamFlowControl* sfc) {
+    if (enabled_) Init(reason, tfc, sfc);
+  }
+
+  ~FlowControlTrace() {
+    if (enabled_) Finish();
+  }
+
+ private:
+  void Init(const char* reason, TransportFlowControl* tfc,
+            StreamFlowControl* sfc);
+  void Finish();
+
+  const bool enabled_ = grpc_flowctl_trace.enabled();
+
+  TransportFlowControl* tfc_;
+  StreamFlowControl* sfc_;
+  const char* reason_;
+  int64_t remote_window_;
+  int64_t target_window_;
+  int64_t announced_window_;
+  int64_t remote_window_delta_;
+  int64_t local_window_delta_;
+  int64_t announced_window_delta_;
+};
+
+// Fat interface with all methods a flow control implementation needs to
+// support. gRPC C Core does not support pure virtual functions, so instead
+// we abort in any methods which require implementation in the base class.
+class TransportFlowControlBase {
+ public:
+  TransportFlowControlBase() {}
+  virtual ~TransportFlowControlBase() {}
+
+  // Is flow control enabled? This is needed in other codepaths like the checks
+  // in parsing and in writing.
+  virtual bool flow_control_enabled() const { abort(); }
+
+  // Called to check if the transport needs to send a WINDOW_UPDATE frame
+  virtual uint32_t MaybeSendUpdate(bool writing_anyway) { abort(); }
+
+  // Using the protected members, returns and Action to be taken by the
+  // tranport.
+  virtual FlowControlAction MakeAction() { abort(); }
+
+  // Using the protected members, returns and Action to be taken by the
+  // tranport. Also checks for updates to our BDP estimate and acts
+  // accordingly.
+  virtual FlowControlAction PeriodicUpdate() { abort(); }
+
+  // Called to do bookkeeping when a stream owned by this transport sends
+  // data on the wire
+  virtual void StreamSentData(int64_t size) { abort(); }
+
+  // Called to do bookkeeping when a stream owned by this transport receives
+  // data from the wire. Also does error checking for frame size.
+  virtual grpc_error* RecvData(int64_t incoming_frame_size) { abort(); }
+
+  // Called to do bookkeeping when we receive a WINDOW_UPDATE frame.
+  virtual void RecvUpdate(uint32_t size) { abort(); }
+
+  // Returns the BdpEstimator held by this object. Caller is responsible for
+  // checking for nullptr. TODO(ncteisen): consider fully encapsulating all
+  // bdp estimator actions inside TransportFlowControl
+  virtual BdpEstimator* bdp_estimator() { return nullptr; }
+
+  // Getters
+  int64_t remote_window() const { return remote_window_; }
+  virtual int64_t target_window() const { return target_initial_window_size_; }
+  int64_t announced_window() const { return announced_window_; }
+
+  // Used in certain benchmarks in which we don't want FlowControl to be a
+  // factor
+  virtual void TestOnlyForceHugeWindow() {}
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  friend class ::grpc::testing::TrickledCHTTP2;
+  int64_t remote_window_ = kDefaultWindow;
+  int64_t target_initial_window_size_ = kDefaultWindow;
+  int64_t announced_window_ = kDefaultWindow;
+};
+
+// Implementation of flow control that does NOTHING. Always returns maximum
+// values, never initiates writes, and assumes that the remote peer is doing
+// the same. To be used to narrow down on flow control as the cause of negative
+// performance.
+class TransportFlowControlDisabled final : public TransportFlowControlBase {
+ public:
+  // Maxes out all values
+  TransportFlowControlDisabled(grpc_chttp2_transport* t);
+
+  bool flow_control_enabled() const override { return false; }
+
+  // Never do anything.
+  uint32_t MaybeSendUpdate(bool writing_anyway) override { return 0; }
+  FlowControlAction MakeAction() override { return FlowControlAction(); }
+  FlowControlAction PeriodicUpdate() override { return FlowControlAction(); }
+  void StreamSentData(int64_t size) override {}
+  grpc_error* RecvData(int64_t incoming_frame_size) override {
+    return GRPC_ERROR_NONE;
+  }
+  void RecvUpdate(uint32_t size) override {}
+};
+
+// Implementation of flow control that abides to HTTP/2 spec and attempts
+// to be as performant as possible.
+class TransportFlowControl final : public TransportFlowControlBase {
+ public:
+  TransportFlowControl(const grpc_chttp2_transport* t, bool enable_bdp_probe);
+  ~TransportFlowControl() {}
+
+  bool flow_control_enabled() const override { return true; }
+
+  bool bdp_probe() const { return enable_bdp_probe_; }
+
+  // returns an announce if we should send a transport update to our peer,
+  // else returns zero; writing_anyway indicates if a write would happen
+  // regardless of the send - if it is false and this function returns non-zero,
+  // this announce will cause a write to occur
+  uint32_t MaybeSendUpdate(bool writing_anyway) override;
+
+  // Reads the flow control data and returns and actionable struct that will
+  // tell chttp2 exactly what it needs to do
+  FlowControlAction MakeAction() override {
+    return UpdateAction(FlowControlAction());
+  }
+
+  // Call periodically (at a low-ish rate, 100ms - 10s makes sense)
+  // to perform more complex flow control calculations and return an action
+  // to let chttp2 change its parameters
+  FlowControlAction PeriodicUpdate() override;
+
+  void StreamSentData(int64_t size) override { remote_window_ -= size; }
+
+  grpc_error* ValidateRecvData(int64_t incoming_frame_size);
+  void CommitRecvData(int64_t incoming_frame_size) {
+    announced_window_ -= incoming_frame_size;
+  }
+
+  grpc_error* RecvData(int64_t incoming_frame_size) override {
+    FlowControlTrace trace("  data recv", this, nullptr);
+    grpc_error* error = ValidateRecvData(incoming_frame_size);
+    if (error != GRPC_ERROR_NONE) return error;
+    CommitRecvData(incoming_frame_size);
+    return GRPC_ERROR_NONE;
+  }
+
+  // we have received a WINDOW_UPDATE frame for a transport
+  void RecvUpdate(uint32_t size) override {
+    FlowControlTrace trace("t updt recv", this, nullptr);
+    remote_window_ += size;
+  }
+
+  // See comment above announced_stream_total_over_incoming_window_ for the
+  // logic behind this decision.
+  int64_t target_window() const override {
+    return static_cast<uint32_t> GPR_MIN(
+        (int64_t)((1u << 31) - 1),
+        announced_stream_total_over_incoming_window_ +
+            target_initial_window_size_);
+  }
+
+  const grpc_chttp2_transport* transport() const { return t_; }
+
+  void PreUpdateAnnouncedWindowOverIncomingWindow(int64_t delta) {
+    if (delta > 0) {
+      announced_stream_total_over_incoming_window_ -= delta;
+    } else {
+      announced_stream_total_under_incoming_window_ += -delta;
+    }
+  }
+
+  void PostUpdateAnnouncedWindowOverIncomingWindow(int64_t delta) {
+    if (delta > 0) {
+      announced_stream_total_over_incoming_window_ += delta;
+    } else {
+      announced_stream_total_under_incoming_window_ -= -delta;
+    }
+  }
+
+  BdpEstimator* bdp_estimator() override { return &bdp_estimator_; }
+
+  void TestOnlyForceHugeWindow() override {
+    announced_window_ = 1024 * 1024 * 1024;
+    remote_window_ = 1024 * 1024 * 1024;
+  }
+
+ private:
+  double TargetLogBdp();
+  double SmoothLogBdp(double value);
+  FlowControlAction::Urgency DeltaUrgency(int64_t value,
+                                          grpc_chttp2_setting_id setting_id);
+
+  FlowControlAction UpdateAction(FlowControlAction action) {
+    if (announced_window_ < target_window() / 2) {
+      action.set_send_transport_update(
+          FlowControlAction::Urgency::UPDATE_IMMEDIATELY);
+    }
+    return action;
+  }
+
+  const grpc_chttp2_transport* const t_;
+
+  /** calculating what we should give for local window:
+      we track the total amount of flow control over initial window size
+      across all streams: this is data that we want to receive right now (it
+      has an outstanding read)
+      and the total amount of flow control under initial window size across all
+      streams: this is data we've read early
+      we want to adjust incoming_window such that:
+      incoming_window = total_over - max(bdp - total_under, 0) */
+  int64_t announced_stream_total_over_incoming_window_ = 0;
+  int64_t announced_stream_total_under_incoming_window_ = 0;
+
+  /** should we probe bdp? */
+  const bool enable_bdp_probe_;
+
+  /* bdp estimation */
+  grpc_core::BdpEstimator bdp_estimator_;
+
+  /* pid controller */
+  grpc_core::PidController pid_controller_;
+  grpc_millis last_pid_update_ = 0;
+};
+
+// Fat interface with all methods a stream flow control implementation needs
+// to support. gRPC C Core does not support pure virtual functions, so instead
+// we abort in any methods which require implementation in the base class.
+class StreamFlowControlBase {
+ public:
+  StreamFlowControlBase() {}
+  virtual ~StreamFlowControlBase() {}
+
+  // Updates an action using the protected members.
+  virtual FlowControlAction UpdateAction(FlowControlAction action) { abort(); }
+
+  // Using the protected members, returns an Action for this stream to be
+  // taken by the tranport.
+  virtual FlowControlAction MakeAction() { abort(); }
+
+  // Bookkeeping for when data is sent on this stream.
+  virtual void SentData(int64_t outgoing_frame_size) { abort(); }
+
+  // Bookkeeping and error checking for when data is received by this stream.
+  virtual grpc_error* RecvData(int64_t incoming_frame_size) { abort(); }
+
+  // Called to check if this stream needs to send a WINDOW_UPDATE frame.
+  virtual uint32_t MaybeSendUpdate() { abort(); }
+
+  // Bookkeeping for receiving a WINDOW_UPDATE from for this stream.
+  virtual void RecvUpdate(uint32_t size) { abort(); }
+
+  // Bookkeeping for when a call pulls bytes out of the transport. At this
+  // point we consider the data 'used' and can thus let out peer know we are
+  // ready for more data.
+  virtual void IncomingByteStreamUpdate(size_t max_size_hint,
+                                        size_t have_already) {
+    abort();
+  }
+
+  // Used in certain benchmarks in which we don't want FlowControl to be a
+  // factor
+  virtual void TestOnlyForceHugeWindow() {}
+
+  // Getters
+  int64_t remote_window_delta() { return remote_window_delta_; }
+  int64_t local_window_delta() { return local_window_delta_; }
+  int64_t announced_window_delta() { return announced_window_delta_; }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  friend class ::grpc::testing::TrickledCHTTP2;
+  int64_t remote_window_delta_ = 0;
+  int64_t local_window_delta_ = 0;
+  int64_t announced_window_delta_ = 0;
+};
+
+// Implementation of flow control that does NOTHING. Always returns maximum
+// values, never initiates writes, and assumes that the remote peer is doing
+// the same. To be used to narrow down on flow control as the cause of negative
+// performance.
+class StreamFlowControlDisabled : public StreamFlowControlBase {
+ public:
+  FlowControlAction UpdateAction(FlowControlAction action) override {
+    return action;
+  }
+  FlowControlAction MakeAction() override { return FlowControlAction(); }
+  void SentData(int64_t outgoing_frame_size) override {}
+  grpc_error* RecvData(int64_t incoming_frame_size) override {
+    return GRPC_ERROR_NONE;
+  }
+  uint32_t MaybeSendUpdate() override { return 0; }
+  void RecvUpdate(uint32_t size) override {}
+  void IncomingByteStreamUpdate(size_t max_size_hint,
+                                size_t have_already) override {}
+};
+
+// Implementation of flow control that abides to HTTP/2 spec and attempts
+// to be as performant as possible.
+class StreamFlowControl final : public StreamFlowControlBase {
+ public:
+  StreamFlowControl(TransportFlowControl* tfc, const grpc_chttp2_stream* s);
+  ~StreamFlowControl() {
+    tfc_->PreUpdateAnnouncedWindowOverIncomingWindow(announced_window_delta_);
+  }
+
+  FlowControlAction UpdateAction(FlowControlAction action) override;
+  FlowControlAction MakeAction() override {
+    return UpdateAction(tfc_->MakeAction());
+  }
+
+  // we have sent data on the wire, we must track this in our bookkeeping for
+  // the remote peer's flow control.
+  void SentData(int64_t outgoing_frame_size) override {
+    FlowControlTrace tracer("  data sent", tfc_, this);
+    tfc_->StreamSentData(outgoing_frame_size);
+    remote_window_delta_ -= outgoing_frame_size;
+  }
+
+  // we have received data from the wire
+  grpc_error* RecvData(int64_t incoming_frame_size) override;
+
+  // returns an announce if we should send a stream update to our peer, else
+  // returns zero
+  uint32_t MaybeSendUpdate() override;
+
+  // we have received a WINDOW_UPDATE frame for a stream
+  void RecvUpdate(uint32_t size) override {
+    FlowControlTrace trace("s updt recv", tfc_, this);
+    remote_window_delta_ += size;
+  }
+
+  // the application is asking for a certain amount of bytes
+  void IncomingByteStreamUpdate(size_t max_size_hint,
+                                size_t have_already) override;
+
+  int64_t remote_window_delta() const { return remote_window_delta_; }
+  int64_t local_window_delta() const { return local_window_delta_; }
+  int64_t announced_window_delta() const { return announced_window_delta_; }
+
+  const grpc_chttp2_stream* stream() const { return s_; }
+
+  void TestOnlyForceHugeWindow() override {
+    announced_window_delta_ = 1024 * 1024 * 1024;
+    local_window_delta_ = 1024 * 1024 * 1024;
+    remote_window_delta_ = 1024 * 1024 * 1024;
+  }
+
+ private:
+  TransportFlowControl* const tfc_;
+  const grpc_chttp2_stream* const s_;
+
+  void UpdateAnnouncedWindowDelta(TransportFlowControl* tfc, int64_t change) {
+    tfc->PreUpdateAnnouncedWindowOverIncomingWindow(announced_window_delta_);
+    announced_window_delta_ += change;
+    tfc->PostUpdateAnnouncedWindowOverIncomingWindow(announced_window_delta_);
+  }
+};
+
+}  // namespace chttp2
+}  // namespace grpc_core
+
+#endif
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame.h
new file mode 100644
index 0000000..083c007
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+
+#include "src/core/lib/iomgr/error.h"
+
+/* defined in internal.h */
+typedef struct grpc_chttp2_stream grpc_chttp2_stream;
+typedef struct grpc_chttp2_transport grpc_chttp2_transport;
+
+#define GRPC_CHTTP2_FRAME_DATA 0
+#define GRPC_CHTTP2_FRAME_HEADER 1
+#define GRPC_CHTTP2_FRAME_CONTINUATION 9
+#define GRPC_CHTTP2_FRAME_RST_STREAM 3
+#define GRPC_CHTTP2_FRAME_SETTINGS 4
+#define GRPC_CHTTP2_FRAME_PING 6
+#define GRPC_CHTTP2_FRAME_GOAWAY 7
+#define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8
+
+#define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1
+#define GRPC_CHTTP2_FLAG_ACK 1
+#define GRPC_CHTTP2_DATA_FLAG_END_HEADERS 4
+#define GRPC_CHTTP2_DATA_FLAG_PADDED 8
+#define GRPC_CHTTP2_FLAG_HAS_PRIORITY 0x20
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.cc
new file mode 100644
index 0000000..1de0073
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.cc
@@ -0,0 +1,312 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/frame_data.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/transport.h"
+
+grpc_chttp2_data_parser::~grpc_chttp2_data_parser() {
+  if (parsing_frame != nullptr) {
+    GRPC_ERROR_UNREF(parsing_frame->Finished(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Parser destroyed"), false));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_error* grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser* parser,
+                                                uint8_t flags,
+                                                uint32_t stream_id,
+                                                grpc_chttp2_stream* s) {
+  if (flags & ~GRPC_CHTTP2_DATA_FLAG_END_STREAM) {
+    char* msg;
+    gpr_asprintf(&msg, "unsupported data flags: 0x%02x", flags);
+    grpc_error* err = grpc_error_set_int(
+        GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg), GRPC_ERROR_INT_STREAM_ID,
+        static_cast<intptr_t>(stream_id));
+    gpr_free(msg);
+    return err;
+  }
+
+  if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) {
+    s->received_last_frame = true;
+    s->eos_received = true;
+  } else {
+    s->received_last_frame = false;
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+void grpc_chttp2_encode_data(uint32_t id, grpc_slice_buffer* inbuf,
+                             uint32_t write_bytes, int is_eof,
+                             grpc_transport_one_way_stats* stats,
+                             grpc_slice_buffer* outbuf) {
+  grpc_slice hdr;
+  uint8_t* p;
+  static const size_t header_size = 9;
+
+  hdr = GRPC_SLICE_MALLOC(header_size);
+  p = GRPC_SLICE_START_PTR(hdr);
+  GPR_ASSERT(write_bytes < (1 << 24));
+  *p++ = static_cast<uint8_t>(write_bytes >> 16);
+  *p++ = static_cast<uint8_t>(write_bytes >> 8);
+  *p++ = static_cast<uint8_t>(write_bytes);
+  *p++ = GRPC_CHTTP2_FRAME_DATA;
+  *p++ = is_eof ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0;
+  *p++ = static_cast<uint8_t>(id >> 24);
+  *p++ = static_cast<uint8_t>(id >> 16);
+  *p++ = static_cast<uint8_t>(id >> 8);
+  *p++ = static_cast<uint8_t>(id);
+  grpc_slice_buffer_add(outbuf, hdr);
+
+  grpc_slice_buffer_move_first_no_ref(inbuf, write_bytes, outbuf);
+
+  stats->framing_bytes += header_size;
+  stats->data_bytes += write_bytes;
+}
+
+grpc_error* grpc_deframe_unprocessed_incoming_frames(
+    grpc_chttp2_data_parser* p, grpc_chttp2_stream* s,
+    grpc_slice_buffer* slices, grpc_slice* slice_out,
+    grpc_core::OrphanablePtr<grpc_core::ByteStream>* stream_out) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  grpc_chttp2_transport* t = s->t;
+
+  while (slices->count > 0) {
+    uint8_t* beg = nullptr;
+    uint8_t* end = nullptr;
+    uint8_t* cur = nullptr;
+
+    grpc_slice slice = grpc_slice_buffer_take_first(slices);
+
+    beg = GRPC_SLICE_START_PTR(slice);
+    end = GRPC_SLICE_END_PTR(slice);
+    cur = beg;
+    uint32_t message_flags;
+    char* msg;
+
+    if (cur == end) {
+      grpc_slice_unref_internal(slice);
+      continue;
+    }
+
+    switch (p->state) {
+      case GRPC_CHTTP2_DATA_ERROR:
+        p->state = GRPC_CHTTP2_DATA_ERROR;
+        grpc_slice_unref_internal(slice);
+        return GRPC_ERROR_REF(p->error);
+      case GRPC_CHTTP2_DATA_FH_0:
+        s->stats.incoming.framing_bytes++;
+        p->frame_type = *cur;
+        switch (p->frame_type) {
+          case 0:
+            p->is_frame_compressed = false; /* GPR_FALSE */
+            break;
+          case 1:
+            p->is_frame_compressed = true; /* GPR_TRUE */
+            break;
+          default:
+            gpr_asprintf(&msg, "Bad GRPC frame type 0x%02x", p->frame_type);
+            p->error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+            p->error = grpc_error_set_int(p->error, GRPC_ERROR_INT_STREAM_ID,
+                                          static_cast<intptr_t>(s->id));
+            gpr_free(msg);
+            msg = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+            p->error = grpc_error_set_str(p->error, GRPC_ERROR_STR_RAW_BYTES,
+                                          grpc_slice_from_copied_string(msg));
+            gpr_free(msg);
+            p->error =
+                grpc_error_set_int(p->error, GRPC_ERROR_INT_OFFSET, cur - beg);
+            p->state = GRPC_CHTTP2_DATA_ERROR;
+            grpc_slice_unref_internal(slice);
+            return GRPC_ERROR_REF(p->error);
+        }
+        if (++cur == end) {
+          p->state = GRPC_CHTTP2_DATA_FH_1;
+          grpc_slice_unref_internal(slice);
+          continue;
+        }
+      /* fallthrough */
+      case GRPC_CHTTP2_DATA_FH_1:
+        s->stats.incoming.framing_bytes++;
+        p->frame_size = (static_cast<uint32_t>(*cur)) << 24;
+        if (++cur == end) {
+          p->state = GRPC_CHTTP2_DATA_FH_2;
+          grpc_slice_unref_internal(slice);
+          continue;
+        }
+      /* fallthrough */
+      case GRPC_CHTTP2_DATA_FH_2:
+        s->stats.incoming.framing_bytes++;
+        p->frame_size |= (static_cast<uint32_t>(*cur)) << 16;
+        if (++cur == end) {
+          p->state = GRPC_CHTTP2_DATA_FH_3;
+          grpc_slice_unref_internal(slice);
+          continue;
+        }
+      /* fallthrough */
+      case GRPC_CHTTP2_DATA_FH_3:
+        s->stats.incoming.framing_bytes++;
+        p->frame_size |= (static_cast<uint32_t>(*cur)) << 8;
+        if (++cur == end) {
+          p->state = GRPC_CHTTP2_DATA_FH_4;
+          grpc_slice_unref_internal(slice);
+          continue;
+        }
+      /* fallthrough */
+      case GRPC_CHTTP2_DATA_FH_4:
+        s->stats.incoming.framing_bytes++;
+        GPR_ASSERT(stream_out != nullptr);
+        GPR_ASSERT(p->parsing_frame == nullptr);
+        p->frame_size |= (static_cast<uint32_t>(*cur));
+        if (t->channelz_socket != nullptr) {
+          t->channelz_socket->RecordMessageReceived();
+        }
+        p->state = GRPC_CHTTP2_DATA_FRAME;
+        ++cur;
+        message_flags = 0;
+        if (p->is_frame_compressed) {
+          message_flags |= GRPC_WRITE_INTERNAL_COMPRESS;
+        }
+        p->parsing_frame = grpc_core::New<grpc_core::Chttp2IncomingByteStream>(
+            t, s, p->frame_size, message_flags);
+        stream_out->reset(p->parsing_frame);
+        if (p->parsing_frame->remaining_bytes() == 0) {
+          GRPC_ERROR_UNREF(p->parsing_frame->Finished(GRPC_ERROR_NONE, true));
+          p->parsing_frame = nullptr;
+          p->state = GRPC_CHTTP2_DATA_FH_0;
+        }
+        s->pending_byte_stream = true;
+
+        if (cur != end) {
+          grpc_slice_buffer_undo_take_first(
+              slices, grpc_slice_sub(slice, static_cast<size_t>(cur - beg),
+                                     static_cast<size_t>(end - beg)));
+        }
+        grpc_slice_unref_internal(slice);
+        return GRPC_ERROR_NONE;
+      case GRPC_CHTTP2_DATA_FRAME: {
+        GPR_ASSERT(p->parsing_frame != nullptr);
+        GPR_ASSERT(slice_out != nullptr);
+        if (cur == end) {
+          grpc_slice_unref_internal(slice);
+          continue;
+        }
+        uint32_t remaining = static_cast<uint32_t>(end - cur);
+        if (remaining == p->frame_size) {
+          s->stats.incoming.data_bytes += remaining;
+          if (GRPC_ERROR_NONE !=
+              (error = p->parsing_frame->Push(
+                   grpc_slice_sub(slice, static_cast<size_t>(cur - beg),
+                                  static_cast<size_t>(end - beg)),
+                   slice_out))) {
+            grpc_slice_unref_internal(slice);
+            return error;
+          }
+          if (GRPC_ERROR_NONE !=
+              (error = p->parsing_frame->Finished(GRPC_ERROR_NONE, true))) {
+            grpc_slice_unref_internal(slice);
+            return error;
+          }
+          p->parsing_frame = nullptr;
+          p->state = GRPC_CHTTP2_DATA_FH_0;
+          grpc_slice_unref_internal(slice);
+          return GRPC_ERROR_NONE;
+        } else if (remaining < p->frame_size) {
+          s->stats.incoming.data_bytes += remaining;
+          if (GRPC_ERROR_NONE !=
+              (error = p->parsing_frame->Push(
+                   grpc_slice_sub(slice, static_cast<size_t>(cur - beg),
+                                  static_cast<size_t>(end - beg)),
+                   slice_out))) {
+            return error;
+          }
+          p->frame_size -= remaining;
+          grpc_slice_unref_internal(slice);
+          return GRPC_ERROR_NONE;
+        } else {
+          GPR_ASSERT(remaining > p->frame_size);
+          s->stats.incoming.data_bytes += p->frame_size;
+          if (GRPC_ERROR_NONE !=
+              p->parsing_frame->Push(
+                  grpc_slice_sub(
+                      slice, static_cast<size_t>(cur - beg),
+                      static_cast<size_t>(cur + p->frame_size - beg)),
+                  slice_out)) {
+            grpc_slice_unref_internal(slice);
+            return error;
+          }
+          if (GRPC_ERROR_NONE !=
+              (error = p->parsing_frame->Finished(GRPC_ERROR_NONE, true))) {
+            grpc_slice_unref_internal(slice);
+            return error;
+          }
+          p->parsing_frame = nullptr;
+          p->state = GRPC_CHTTP2_DATA_FH_0;
+          cur += p->frame_size;
+          grpc_slice_buffer_undo_take_first(
+              slices, grpc_slice_sub(slice, static_cast<size_t>(cur - beg),
+                                     static_cast<size_t>(end - beg)));
+          grpc_slice_unref_internal(slice);
+          return GRPC_ERROR_NONE;
+        }
+      }
+    }
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_chttp2_data_parser_parse(void* parser,
+                                          grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream* s,
+                                          grpc_slice slice, int is_last) {
+  if (!s->pending_byte_stream) {
+    grpc_slice_ref_internal(slice);
+    grpc_slice_buffer_add(&s->frame_storage, slice);
+    grpc_chttp2_maybe_complete_recv_message(t, s);
+  } else if (s->on_next) {
+    GPR_ASSERT(s->frame_storage.length == 0);
+    grpc_slice_ref_internal(slice);
+    grpc_slice_buffer_add(&s->unprocessed_incoming_frames_buffer, slice);
+    GRPC_CLOSURE_SCHED(s->on_next, GRPC_ERROR_NONE);
+    s->on_next = nullptr;
+    s->unprocessed_incoming_frames_decompressed = false;
+  } else {
+    grpc_slice_ref_internal(slice);
+    grpc_slice_buffer_add(&s->frame_storage, slice);
+  }
+
+  if (is_last && s->received_last_frame) {
+    grpc_chttp2_mark_stream_closed(t, s, true, false, GRPC_ERROR_NONE);
+  }
+
+  return GRPC_ERROR_NONE;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.h
new file mode 100644
index 0000000..2c5da99
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.h
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H
+
+/* Parser for GRPC streams embedded in DATA frames */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/lib/transport/byte_stream.h"
+#include "src/core/lib/transport/transport.h"
+
+typedef enum {
+  GRPC_CHTTP2_DATA_FH_0,
+  GRPC_CHTTP2_DATA_FH_1,
+  GRPC_CHTTP2_DATA_FH_2,
+  GRPC_CHTTP2_DATA_FH_3,
+  GRPC_CHTTP2_DATA_FH_4,
+  GRPC_CHTTP2_DATA_FRAME,
+  GRPC_CHTTP2_DATA_ERROR
+} grpc_chttp2_stream_state;
+
+namespace grpc_core {
+class Chttp2IncomingByteStream;
+}  // namespace grpc_core
+
+struct grpc_chttp2_data_parser {
+  grpc_chttp2_data_parser() = default;
+  ~grpc_chttp2_data_parser();
+
+  grpc_chttp2_stream_state state = GRPC_CHTTP2_DATA_FH_0;
+  uint8_t frame_type = 0;
+  uint32_t frame_size = 0;
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  bool is_frame_compressed = false;
+  grpc_core::Chttp2IncomingByteStream* parsing_frame = nullptr;
+};
+
+/* start processing a new data frame */
+grpc_error* grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser* parser,
+                                                uint8_t flags,
+                                                uint32_t stream_id,
+                                                grpc_chttp2_stream* s);
+
+/* handle a slice of a data frame - is_last indicates the last slice of a
+   frame */
+grpc_error* grpc_chttp2_data_parser_parse(void* parser,
+                                          grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream* s,
+                                          grpc_slice slice, int is_last);
+
+void grpc_chttp2_encode_data(uint32_t id, grpc_slice_buffer* inbuf,
+                             uint32_t write_bytes, int is_eof,
+                             grpc_transport_one_way_stats* stats,
+                             grpc_slice_buffer* outbuf);
+
+grpc_error* grpc_deframe_unprocessed_incoming_frames(
+    grpc_chttp2_data_parser* p, grpc_chttp2_stream* s,
+    grpc_slice_buffer* slices, grpc_slice* slice_out,
+    grpc_core::OrphanablePtr<grpc_core::ByteStream>* stream_out);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.cc
new file mode 100644
index 0000000..2a1dd3c
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.cc
@@ -0,0 +1,186 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser* p) {
+  p->debug_data = nullptr;
+}
+
+void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser* p) {
+  gpr_free(p->debug_data);
+}
+
+grpc_error* grpc_chttp2_goaway_parser_begin_frame(grpc_chttp2_goaway_parser* p,
+                                                  uint32_t length,
+                                                  uint8_t flags) {
+  if (length < 8) {
+    char* msg;
+    gpr_asprintf(&msg, "goaway frame too short (%d bytes)", length);
+    grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return err;
+  }
+
+  gpr_free(p->debug_data);
+  p->debug_length = length - 8;
+  p->debug_data = static_cast<char*>(gpr_malloc(p->debug_length));
+  p->debug_pos = 0;
+  p->state = GRPC_CHTTP2_GOAWAY_LSI0;
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_chttp2_goaway_parser_parse(void* parser,
+                                            grpc_chttp2_transport* t,
+                                            grpc_chttp2_stream* s,
+                                            grpc_slice slice, int is_last) {
+  uint8_t* const beg = GRPC_SLICE_START_PTR(slice);
+  uint8_t* const end = GRPC_SLICE_END_PTR(slice);
+  uint8_t* cur = beg;
+  grpc_chttp2_goaway_parser* p =
+      static_cast<grpc_chttp2_goaway_parser*>(parser);
+
+  switch (p->state) {
+    case GRPC_CHTTP2_GOAWAY_LSI0:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_LSI0;
+        return GRPC_ERROR_NONE;
+      }
+      p->last_stream_id = (static_cast<uint32_t>(*cur)) << 24;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_LSI1:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_LSI1;
+        return GRPC_ERROR_NONE;
+      }
+      p->last_stream_id |= (static_cast<uint32_t>(*cur)) << 16;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_LSI2:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_LSI2;
+        return GRPC_ERROR_NONE;
+      }
+      p->last_stream_id |= (static_cast<uint32_t>(*cur)) << 8;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_LSI3:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_LSI3;
+        return GRPC_ERROR_NONE;
+      }
+      p->last_stream_id |= (static_cast<uint32_t>(*cur));
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_ERR0:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_ERR0;
+        return GRPC_ERROR_NONE;
+      }
+      p->error_code = (static_cast<uint32_t>(*cur)) << 24;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_ERR1:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_ERR1;
+        return GRPC_ERROR_NONE;
+      }
+      p->error_code |= (static_cast<uint32_t>(*cur)) << 16;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_ERR2:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_ERR2;
+        return GRPC_ERROR_NONE;
+      }
+      p->error_code |= (static_cast<uint32_t>(*cur)) << 8;
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_ERR3:
+      if (cur == end) {
+        p->state = GRPC_CHTTP2_GOAWAY_ERR3;
+        return GRPC_ERROR_NONE;
+      }
+      p->error_code |= (static_cast<uint32_t>(*cur));
+      ++cur;
+    /* fallthrough */
+    case GRPC_CHTTP2_GOAWAY_DEBUG:
+      if (end != cur)
+        memcpy(p->debug_data + p->debug_pos, cur,
+               static_cast<size_t>(end - cur));
+      GPR_ASSERT((size_t)(end - cur) < UINT32_MAX - p->debug_pos);
+      p->debug_pos += static_cast<uint32_t>(end - cur);
+      p->state = GRPC_CHTTP2_GOAWAY_DEBUG;
+      if (is_last) {
+        grpc_chttp2_add_incoming_goaway(
+            t, p->error_code,
+            grpc_slice_new(p->debug_data, p->debug_length, gpr_free));
+        p->debug_data = nullptr;
+      }
+      return GRPC_ERROR_NONE;
+  }
+  GPR_UNREACHABLE_CODE(
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here"));
+}
+
+void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code,
+                               grpc_slice debug_data,
+                               grpc_slice_buffer* slice_buffer) {
+  grpc_slice header = GRPC_SLICE_MALLOC(9 + 4 + 4);
+  uint8_t* p = GRPC_SLICE_START_PTR(header);
+  uint32_t frame_length;
+  GPR_ASSERT(GRPC_SLICE_LENGTH(debug_data) < UINT32_MAX - 4 - 4);
+  frame_length = 4 + 4 + static_cast<uint32_t> GRPC_SLICE_LENGTH(debug_data);
+
+  /* frame header: length */
+  *p++ = static_cast<uint8_t>(frame_length >> 16);
+  *p++ = static_cast<uint8_t>(frame_length >> 8);
+  *p++ = static_cast<uint8_t>(frame_length);
+  /* frame header: type */
+  *p++ = GRPC_CHTTP2_FRAME_GOAWAY;
+  /* frame header: flags */
+  *p++ = 0;
+  /* frame header: stream id */
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  /* payload: last stream id */
+  *p++ = static_cast<uint8_t>(last_stream_id >> 24);
+  *p++ = static_cast<uint8_t>(last_stream_id >> 16);
+  *p++ = static_cast<uint8_t>(last_stream_id >> 8);
+  *p++ = static_cast<uint8_t>(last_stream_id);
+  /* payload: error code */
+  *p++ = static_cast<uint8_t>(error_code >> 24);
+  *p++ = static_cast<uint8_t>(error_code >> 16);
+  *p++ = static_cast<uint8_t>(error_code >> 8);
+  *p++ = static_cast<uint8_t>(error_code);
+  GPR_ASSERT(p == GRPC_SLICE_END_PTR(header));
+  grpc_slice_buffer_add(slice_buffer, header);
+  grpc_slice_buffer_add(slice_buffer, debug_data);
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.h
new file mode 100644
index 0000000..66c7a68
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+
+typedef enum {
+  GRPC_CHTTP2_GOAWAY_LSI0,
+  GRPC_CHTTP2_GOAWAY_LSI1,
+  GRPC_CHTTP2_GOAWAY_LSI2,
+  GRPC_CHTTP2_GOAWAY_LSI3,
+  GRPC_CHTTP2_GOAWAY_ERR0,
+  GRPC_CHTTP2_GOAWAY_ERR1,
+  GRPC_CHTTP2_GOAWAY_ERR2,
+  GRPC_CHTTP2_GOAWAY_ERR3,
+  GRPC_CHTTP2_GOAWAY_DEBUG
+} grpc_chttp2_goaway_parse_state;
+
+typedef struct {
+  grpc_chttp2_goaway_parse_state state;
+  uint32_t last_stream_id;
+  uint32_t error_code;
+  char* debug_data;
+  uint32_t debug_length;
+  uint32_t debug_pos;
+} grpc_chttp2_goaway_parser;
+
+void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser* p);
+void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser* p);
+grpc_error* grpc_chttp2_goaway_parser_begin_frame(
+    grpc_chttp2_goaway_parser* parser, uint32_t length, uint8_t flags);
+grpc_error* grpc_chttp2_goaway_parser_parse(void* parser,
+                                            grpc_chttp2_transport* t,
+                                            grpc_chttp2_stream* s,
+                                            grpc_slice slice, int is_last);
+
+void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code,
+                               grpc_slice debug_data,
+                               grpc_slice_buffer* slice_buffer);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.cc
new file mode 100644
index 0000000..205826b
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.cc
@@ -0,0 +1,131 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/frame_ping.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+static bool g_disable_ping_ack = false;
+
+grpc_slice grpc_chttp2_ping_create(uint8_t ack, uint64_t opaque_8bytes) {
+  grpc_slice slice = GRPC_SLICE_MALLOC(9 + 8);
+  uint8_t* p = GRPC_SLICE_START_PTR(slice);
+
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 8;
+  *p++ = GRPC_CHTTP2_FRAME_PING;
+  *p++ = ack ? 1 : 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = static_cast<uint8_t>(opaque_8bytes >> 56);
+  *p++ = static_cast<uint8_t>(opaque_8bytes >> 48);
+  *p++ = static_cast<uint8_t>(opaque_8bytes >> 40);
+  *p++ = static_cast<uint8_t>(opaque_8bytes >> 32);
+  *p++ = static_cast<uint8_t>(opaque_8bytes >> 24);
+  *p++ = static_cast<uint8_t>(opaque_8bytes >> 16);
+  *p++ = static_cast<uint8_t>(opaque_8bytes >> 8);
+  *p++ = static_cast<uint8_t>(opaque_8bytes);
+
+  return slice;
+}
+
+grpc_error* grpc_chttp2_ping_parser_begin_frame(grpc_chttp2_ping_parser* parser,
+                                                uint32_t length,
+                                                uint8_t flags) {
+  if (flags & 0xfe || length != 8) {
+    char* msg;
+    gpr_asprintf(&msg, "invalid ping: length=%d, flags=%02x", length, flags);
+    grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return error;
+  }
+  parser->byte = 0;
+  parser->is_ack = flags;
+  parser->opaque_8bytes = 0;
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_chttp2_ping_parser_parse(void* parser,
+                                          grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream* s,
+                                          grpc_slice slice, int is_last) {
+  uint8_t* const beg = GRPC_SLICE_START_PTR(slice);
+  uint8_t* const end = GRPC_SLICE_END_PTR(slice);
+  uint8_t* cur = beg;
+  grpc_chttp2_ping_parser* p = static_cast<grpc_chttp2_ping_parser*>(parser);
+
+  while (p->byte != 8 && cur != end) {
+    p->opaque_8bytes |= ((static_cast<uint64_t>(*cur)) << (56 - 8 * p->byte));
+    cur++;
+    p->byte++;
+  }
+
+  if (p->byte == 8) {
+    GPR_ASSERT(is_last);
+    if (p->is_ack) {
+      grpc_chttp2_ack_ping(t, p->opaque_8bytes);
+    } else {
+      if (!t->is_client) {
+        grpc_millis now = grpc_core::ExecCtx::Get()->Now();
+        grpc_millis next_allowed_ping =
+            t->ping_recv_state.last_ping_recv_time +
+            t->ping_policy.min_recv_ping_interval_without_data;
+
+        if (t->keepalive_permit_without_calls == 0 &&
+            grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
+          /* According to RFC1122, the interval of TCP Keep-Alive is default to
+             no less than two hours. When there is no outstanding streams, we
+             restrict the number of PINGS equivalent to TCP Keep-Alive. */
+          next_allowed_ping =
+              t->ping_recv_state.last_ping_recv_time + 7200 * GPR_MS_PER_SEC;
+        }
+
+        if (next_allowed_ping > now) {
+          grpc_chttp2_add_ping_strike(t);
+        }
+
+        t->ping_recv_state.last_ping_recv_time = now;
+      }
+      if (!g_disable_ping_ack) {
+        if (t->ping_ack_count == t->ping_ack_capacity) {
+          t->ping_ack_capacity = GPR_MAX(t->ping_ack_capacity * 3 / 2, 3);
+          t->ping_acks = static_cast<uint64_t*>(gpr_realloc(
+              t->ping_acks, t->ping_ack_capacity * sizeof(*t->ping_acks)));
+        }
+        t->ping_acks[t->ping_ack_count++] = p->opaque_8bytes;
+        grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE);
+      }
+    }
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+void grpc_set_disable_ping_ack(bool disable_ping_ack) {
+  g_disable_ping_ack = disable_ping_ack;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.h
new file mode 100644
index 0000000..55a4499
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+
+typedef struct {
+  uint8_t byte;
+  uint8_t is_ack;
+  uint64_t opaque_8bytes;
+} grpc_chttp2_ping_parser;
+
+grpc_slice grpc_chttp2_ping_create(uint8_t ack, uint64_t opaque_8bytes);
+
+grpc_error* grpc_chttp2_ping_parser_begin_frame(grpc_chttp2_ping_parser* parser,
+                                                uint32_t length, uint8_t flags);
+grpc_error* grpc_chttp2_ping_parser_parse(void* parser,
+                                          grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream* s,
+                                          grpc_slice slice, int is_last);
+
+/* Test-only function for disabling ping ack */
+void grpc_set_disable_ping_ack(bool disable_ping_ack);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
new file mode 100644
index 0000000..a0a7534
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+grpc_slice grpc_chttp2_rst_stream_create(uint32_t id, uint32_t code,
+                                         grpc_transport_one_way_stats* stats) {
+  static const size_t frame_size = 13;
+  grpc_slice slice = GRPC_SLICE_MALLOC(frame_size);
+  if (stats != nullptr) stats->framing_bytes += frame_size;
+  uint8_t* p = GRPC_SLICE_START_PTR(slice);
+
+  // Frame size.
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 4;
+  // Frame type.
+  *p++ = GRPC_CHTTP2_FRAME_RST_STREAM;
+  // Flags.
+  *p++ = 0;
+  // Stream ID.
+  *p++ = static_cast<uint8_t>(id >> 24);
+  *p++ = static_cast<uint8_t>(id >> 16);
+  *p++ = static_cast<uint8_t>(id >> 8);
+  *p++ = static_cast<uint8_t>(id);
+  // Error code.
+  *p++ = static_cast<uint8_t>(code >> 24);
+  *p++ = static_cast<uint8_t>(code >> 16);
+  *p++ = static_cast<uint8_t>(code >> 8);
+  *p++ = static_cast<uint8_t>(code);
+
+  return slice;
+}
+
+grpc_error* grpc_chttp2_rst_stream_parser_begin_frame(
+    grpc_chttp2_rst_stream_parser* parser, uint32_t length, uint8_t flags) {
+  if (length != 4) {
+    char* msg;
+    gpr_asprintf(&msg, "invalid rst_stream: length=%d, flags=%02x", length,
+                 flags);
+    grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return err;
+  }
+  parser->byte = 0;
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_chttp2_rst_stream_parser_parse(void* parser,
+                                                grpc_chttp2_transport* t,
+                                                grpc_chttp2_stream* s,
+                                                grpc_slice slice, int is_last) {
+  uint8_t* const beg = GRPC_SLICE_START_PTR(slice);
+  uint8_t* const end = GRPC_SLICE_END_PTR(slice);
+  uint8_t* cur = beg;
+  grpc_chttp2_rst_stream_parser* p =
+      static_cast<grpc_chttp2_rst_stream_parser*>(parser);
+
+  while (p->byte != 4 && cur != end) {
+    p->reason_bytes[p->byte] = *cur;
+    cur++;
+    p->byte++;
+  }
+  s->stats.incoming.framing_bytes += static_cast<uint64_t>(end - cur);
+
+  if (p->byte == 4) {
+    GPR_ASSERT(is_last);
+    uint32_t reason = ((static_cast<uint32_t>(p->reason_bytes[0])) << 24) |
+                      ((static_cast<uint32_t>(p->reason_bytes[1])) << 16) |
+                      ((static_cast<uint32_t>(p->reason_bytes[2])) << 8) |
+                      ((static_cast<uint32_t>(p->reason_bytes[3])));
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (reason != GRPC_HTTP2_NO_ERROR || s->metadata_buffer[1].size == 0) {
+      char* message;
+      gpr_asprintf(&message, "Received RST_STREAM with error code %d", reason);
+      error = grpc_error_set_int(
+          grpc_error_set_str(GRPC_ERROR_CREATE_FROM_STATIC_STRING("RST_STREAM"),
+                             GRPC_ERROR_STR_GRPC_MESSAGE,
+                             grpc_slice_from_copied_string(message)),
+          GRPC_ERROR_INT_HTTP2_ERROR, static_cast<intptr_t>(reason));
+      gpr_free(message);
+    }
+    grpc_chttp2_mark_stream_closed(t, s, true, true, error);
+  }
+
+  return GRPC_ERROR_NONE;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
new file mode 100644
index 0000000..6bcf9c4
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/lib/transport/transport.h"
+
+typedef struct {
+  uint8_t byte;
+  uint8_t reason_bytes[4];
+} grpc_chttp2_rst_stream_parser;
+
+grpc_slice grpc_chttp2_rst_stream_create(uint32_t stream_id, uint32_t code,
+                                         grpc_transport_one_way_stats* stats);
+
+grpc_error* grpc_chttp2_rst_stream_parser_begin_frame(
+    grpc_chttp2_rst_stream_parser* parser, uint32_t length, uint8_t flags);
+grpc_error* grpc_chttp2_rst_stream_parser_parse(void* parser,
+                                                grpc_chttp2_transport* t,
+                                                grpc_chttp2_stream* s,
+                                                grpc_slice slice, int is_last);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.cc
new file mode 100644
index 0000000..987ac0e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.cc
@@ -0,0 +1,238 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/frame_settings.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+static uint8_t* fill_header(uint8_t* out, uint32_t length, uint8_t flags) {
+  *out++ = static_cast<uint8_t>(length >> 16);
+  *out++ = static_cast<uint8_t>(length >> 8);
+  *out++ = static_cast<uint8_t>(length);
+  *out++ = GRPC_CHTTP2_FRAME_SETTINGS;
+  *out++ = flags;
+  *out++ = 0;
+  *out++ = 0;
+  *out++ = 0;
+  *out++ = 0;
+  return out;
+}
+
+grpc_slice grpc_chttp2_settings_create(uint32_t* old_settings,
+                                       const uint32_t* new_settings,
+                                       uint32_t force_mask, size_t count) {
+  size_t i;
+  uint32_t n = 0;
+  grpc_slice output;
+  uint8_t* p;
+
+  for (i = 0; i < count; i++) {
+    n += (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0);
+  }
+
+  output = GRPC_SLICE_MALLOC(9 + 6 * n);
+  p = fill_header(GRPC_SLICE_START_PTR(output), 6 * n, 0);
+
+  for (i = 0; i < count; i++) {
+    if (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0) {
+      *p++ = static_cast<uint8_t>(grpc_setting_id_to_wire_id[i] >> 8);
+      *p++ = static_cast<uint8_t>(grpc_setting_id_to_wire_id[i]);
+      *p++ = static_cast<uint8_t>(new_settings[i] >> 24);
+      *p++ = static_cast<uint8_t>(new_settings[i] >> 16);
+      *p++ = static_cast<uint8_t>(new_settings[i] >> 8);
+      *p++ = static_cast<uint8_t>(new_settings[i]);
+      old_settings[i] = new_settings[i];
+    }
+  }
+
+  GPR_ASSERT(p == GRPC_SLICE_END_PTR(output));
+
+  return output;
+}
+
+grpc_slice grpc_chttp2_settings_ack_create(void) {
+  grpc_slice output = GRPC_SLICE_MALLOC(9);
+  fill_header(GRPC_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK);
+  return output;
+}
+
+grpc_error* grpc_chttp2_settings_parser_begin_frame(
+    grpc_chttp2_settings_parser* parser, uint32_t length, uint8_t flags,
+    uint32_t* settings) {
+  parser->target_settings = settings;
+  memcpy(parser->incoming_settings, settings,
+         GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
+  parser->is_ack = 0;
+  parser->state = GRPC_CHTTP2_SPS_ID0;
+  if (flags == GRPC_CHTTP2_FLAG_ACK) {
+    parser->is_ack = 1;
+    if (length != 0) {
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "non-empty settings ack frame received");
+    }
+    return GRPC_ERROR_NONE;
+  } else if (flags != 0) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "invalid flags on settings frame");
+  } else if (length % 6 != 0) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "settings frames must be a multiple of six bytes");
+  } else {
+    return GRPC_ERROR_NONE;
+  }
+}
+
+grpc_error* grpc_chttp2_settings_parser_parse(void* p, grpc_chttp2_transport* t,
+                                              grpc_chttp2_stream* s,
+                                              grpc_slice slice, int is_last) {
+  grpc_chttp2_settings_parser* parser =
+      static_cast<grpc_chttp2_settings_parser*>(p);
+  const uint8_t* cur = GRPC_SLICE_START_PTR(slice);
+  const uint8_t* end = GRPC_SLICE_END_PTR(slice);
+  char* msg;
+  grpc_chttp2_setting_id id;
+
+  if (parser->is_ack) {
+    return GRPC_ERROR_NONE;
+  }
+
+  for (;;) {
+    switch (parser->state) {
+      case GRPC_CHTTP2_SPS_ID0:
+        if (cur == end) {
+          parser->state = GRPC_CHTTP2_SPS_ID0;
+          if (is_last) {
+            memcpy(parser->target_settings, parser->incoming_settings,
+                   GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
+            grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_settings_ack_create());
+            if (t->notify_on_receive_settings != nullptr) {
+              GRPC_CLOSURE_SCHED(t->notify_on_receive_settings,
+                                 GRPC_ERROR_NONE);
+              t->notify_on_receive_settings = nullptr;
+            }
+          }
+          return GRPC_ERROR_NONE;
+        }
+        parser->id = static_cast<uint16_t>((static_cast<uint16_t>(*cur)) << 8);
+        cur++;
+      /* fallthrough */
+      case GRPC_CHTTP2_SPS_ID1:
+        if (cur == end) {
+          parser->state = GRPC_CHTTP2_SPS_ID1;
+          return GRPC_ERROR_NONE;
+        }
+        parser->id = static_cast<uint16_t>(parser->id | (*cur));
+        cur++;
+      /* fallthrough */
+      case GRPC_CHTTP2_SPS_VAL0:
+        if (cur == end) {
+          parser->state = GRPC_CHTTP2_SPS_VAL0;
+          return GRPC_ERROR_NONE;
+        }
+        parser->value = (static_cast<uint32_t>(*cur)) << 24;
+        cur++;
+      /* fallthrough */
+      case GRPC_CHTTP2_SPS_VAL1:
+        if (cur == end) {
+          parser->state = GRPC_CHTTP2_SPS_VAL1;
+          return GRPC_ERROR_NONE;
+        }
+        parser->value |= (static_cast<uint32_t>(*cur)) << 16;
+        cur++;
+      /* fallthrough */
+      case GRPC_CHTTP2_SPS_VAL2:
+        if (cur == end) {
+          parser->state = GRPC_CHTTP2_SPS_VAL2;
+          return GRPC_ERROR_NONE;
+        }
+        parser->value |= (static_cast<uint32_t>(*cur)) << 8;
+        cur++;
+      /* fallthrough */
+      case GRPC_CHTTP2_SPS_VAL3:
+        if (cur == end) {
+          parser->state = GRPC_CHTTP2_SPS_VAL3;
+          return GRPC_ERROR_NONE;
+        } else {
+          parser->state = GRPC_CHTTP2_SPS_ID0;
+        }
+        parser->value |= *cur;
+        cur++;
+
+        if (grpc_wire_id_to_setting_id(parser->id, &id)) {
+          const grpc_chttp2_setting_parameters* sp =
+              &grpc_chttp2_settings_parameters[id];
+          // If flow control is disabled we skip these.
+          if (!t->flow_control->flow_control_enabled() &&
+              (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE ||
+               id == GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE)) {
+            continue;
+          }
+          if (parser->value < sp->min_value || parser->value > sp->max_value) {
+            switch (sp->invalid_value_behavior) {
+              case GRPC_CHTTP2_CLAMP_INVALID_VALUE:
+                parser->value =
+                    GPR_CLAMP(parser->value, sp->min_value, sp->max_value);
+                break;
+              case GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE:
+                grpc_chttp2_goaway_append(
+                    t->last_new_stream_id, sp->error_value,
+                    grpc_slice_from_static_string("HTTP2 settings error"),
+                    &t->qbuf);
+                gpr_asprintf(&msg, "invalid value %u passed for %s",
+                             parser->value, sp->name);
+                grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+                gpr_free(msg);
+                return err;
+            }
+          }
+          if (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE &&
+              parser->incoming_settings[id] != parser->value) {
+            t->initial_window_update += static_cast<int64_t>(parser->value) -
+                                        parser->incoming_settings[id];
+            if (grpc_http_trace.enabled() || grpc_flowctl_trace.enabled()) {
+              gpr_log(GPR_INFO, "%p[%s] adding %d for initial_window change", t,
+                      t->is_client ? "cli" : "svr",
+                      static_cast<int>(t->initial_window_update));
+            }
+          }
+          parser->incoming_settings[id] = parser->value;
+          if (grpc_http_trace.enabled()) {
+            gpr_log(GPR_INFO, "CHTTP2:%s:%s: got setting %s = %d",
+                    t->is_client ? "CLI" : "SVR", t->peer_string, sp->name,
+                    parser->value);
+          }
+        } else if (grpc_http_trace.enabled()) {
+          gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)",
+                  parser->id, parser->value);
+        }
+        break;
+    }
+  }
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.h
new file mode 100644
index 0000000..8d8d9b1
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
+
+typedef enum {
+  GRPC_CHTTP2_SPS_ID0,
+  GRPC_CHTTP2_SPS_ID1,
+  GRPC_CHTTP2_SPS_VAL0,
+  GRPC_CHTTP2_SPS_VAL1,
+  GRPC_CHTTP2_SPS_VAL2,
+  GRPC_CHTTP2_SPS_VAL3
+} grpc_chttp2_settings_parse_state;
+
+typedef struct {
+  grpc_chttp2_settings_parse_state state;
+  uint32_t* target_settings;
+  uint8_t is_ack;
+  uint16_t id;
+  uint32_t value;
+  uint32_t incoming_settings[GRPC_CHTTP2_NUM_SETTINGS];
+} grpc_chttp2_settings_parser;
+
+/* Create a settings frame by diffing old & new, and updating old to be new */
+grpc_slice grpc_chttp2_settings_create(uint32_t* old, const uint32_t* newval,
+                                       uint32_t force_mask, size_t count);
+/* Create an ack settings frame */
+grpc_slice grpc_chttp2_settings_ack_create(void);
+
+grpc_error* grpc_chttp2_settings_parser_begin_frame(
+    grpc_chttp2_settings_parser* parser, uint32_t length, uint8_t flags,
+    uint32_t* settings);
+grpc_error* grpc_chttp2_settings_parser_parse(void* parser,
+                                              grpc_chttp2_transport* t,
+                                              grpc_chttp2_stream* s,
+                                              grpc_slice slice, int is_last);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.cc
new file mode 100644
index 0000000..4b586dc
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.cc
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/frame_window_update.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+grpc_slice grpc_chttp2_window_update_create(
+    uint32_t id, uint32_t window_update, grpc_transport_one_way_stats* stats) {
+  static const size_t frame_size = 13;
+  grpc_slice slice = GRPC_SLICE_MALLOC(frame_size);
+  stats->header_bytes += frame_size;
+  uint8_t* p = GRPC_SLICE_START_PTR(slice);
+
+  GPR_ASSERT(window_update);
+
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 4;
+  *p++ = GRPC_CHTTP2_FRAME_WINDOW_UPDATE;
+  *p++ = 0;
+  *p++ = static_cast<uint8_t>(id >> 24);
+  *p++ = static_cast<uint8_t>(id >> 16);
+  *p++ = static_cast<uint8_t>(id >> 8);
+  *p++ = static_cast<uint8_t>(id);
+  *p++ = static_cast<uint8_t>(window_update >> 24);
+  *p++ = static_cast<uint8_t>(window_update >> 16);
+  *p++ = static_cast<uint8_t>(window_update >> 8);
+  *p++ = static_cast<uint8_t>(window_update);
+
+  return slice;
+}
+
+grpc_error* grpc_chttp2_window_update_parser_begin_frame(
+    grpc_chttp2_window_update_parser* parser, uint32_t length, uint8_t flags) {
+  if (flags || length != 4) {
+    char* msg;
+    gpr_asprintf(&msg, "invalid window update: length=%d, flags=%02x", length,
+                 flags);
+    grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return err;
+  }
+  parser->byte = 0;
+  parser->amount = 0;
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_chttp2_window_update_parser_parse(void* parser,
+                                                   grpc_chttp2_transport* t,
+                                                   grpc_chttp2_stream* s,
+                                                   grpc_slice slice,
+                                                   int is_last) {
+  uint8_t* const beg = GRPC_SLICE_START_PTR(slice);
+  uint8_t* const end = GRPC_SLICE_END_PTR(slice);
+  uint8_t* cur = beg;
+  grpc_chttp2_window_update_parser* p =
+      static_cast<grpc_chttp2_window_update_parser*>(parser);
+
+  while (p->byte != 4 && cur != end) {
+    p->amount |= (static_cast<uint32_t>(*cur)) << (8 * (3 - p->byte));
+    cur++;
+    p->byte++;
+  }
+
+  if (s != nullptr) {
+    s->stats.incoming.framing_bytes += static_cast<uint32_t>(end - cur);
+  }
+
+  if (p->byte == 4) {
+    uint32_t received_update = p->amount;
+    if (received_update == 0 || (received_update & 0x80000000u)) {
+      char* msg;
+      gpr_asprintf(&msg, "invalid window update bytes: %d", p->amount);
+      grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      gpr_free(msg);
+      return err;
+    }
+    GPR_ASSERT(is_last);
+
+    if (t->incoming_stream_id != 0) {
+      if (s != nullptr) {
+        s->flow_control->RecvUpdate(received_update);
+        if (grpc_chttp2_list_remove_stalled_by_stream(t, s)) {
+          grpc_chttp2_mark_stream_writable(t, s);
+          grpc_chttp2_initiate_write(
+              t, GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE);
+        }
+      }
+    } else {
+      bool was_zero = t->flow_control->remote_window() <= 0;
+      t->flow_control->RecvUpdate(received_update);
+      bool is_zero = t->flow_control->remote_window() <= 0;
+      if (was_zero && !is_zero) {
+        grpc_chttp2_initiate_write(
+            t, GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED);
+      }
+    }
+  }
+
+  return GRPC_ERROR_NONE;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.h
new file mode 100644
index 0000000..3d2391f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/lib/transport/transport.h"
+
+typedef struct {
+  uint8_t byte;
+  uint8_t is_connection_update;
+  uint32_t amount;
+} grpc_chttp2_window_update_parser;
+
+grpc_slice grpc_chttp2_window_update_create(
+    uint32_t id, uint32_t window_delta, grpc_transport_one_way_stats* stats);
+
+grpc_error* grpc_chttp2_window_update_parser_begin_frame(
+    grpc_chttp2_window_update_parser* parser, uint32_t length, uint8_t flags);
+grpc_error* grpc_chttp2_window_update_parser_parse(void* parser,
+                                                   grpc_chttp2_transport* t,
+                                                   grpc_chttp2_stream* s,
+                                                   grpc_slice slice,
+                                                   int is_last);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
new file mode 100644
index 0000000..dbe9df6
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
@@ -0,0 +1,717 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
+
+#include <assert.h>
+#include <string.h>
+
+/* This is here for grpc_is_binary_header
+ * TODO(murgatroid99): Remove this
+ */
+#include <grpc/grpc.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#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/varint.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.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_MASK (GRPC_CHTTP2_HPACKC_NUM_VALUES - 1)
+#define HASH_FRAGMENT_1(x) ((x)&HASH_FRAGMENT_MASK)
+#define HASH_FRAGMENT_2(x) \
+  (((x) >> GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS) & HASH_FRAGMENT_MASK)
+#define HASH_FRAGMENT_3(x) \
+  (((x) >> (GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS * 2)) & HASH_FRAGMENT_MASK)
+#define HASH_FRAGMENT_4(x) \
+  (((x) >> (GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS * 3)) & HASH_FRAGMENT_MASK)
+
+/* if the probability of this item being seen again is < 1/x then don't add
+   it to the table */
+#define ONE_ON_ADD_PROBABILITY (GRPC_CHTTP2_HPACKC_NUM_VALUES >> 1)
+/* don't consider adding anything bigger than this to the hpack table */
+#define MAX_DECODER_SPACE_USAGE 512
+
+static grpc_slice_refcount terminal_slice_refcount = {nullptr, nullptr};
+static const grpc_slice terminal_slice = {
+    &terminal_slice_refcount, /* refcount */
+    {{nullptr, 0}}            /* data.refcounted */
+};
+
+typedef struct {
+  int is_first_frame;
+  /* number of bytes in 'output' when we started the frame - used to calculate
+     frame length */
+  size_t output_length_at_start_of_frame;
+  /* index (in output) of the header for the current frame */
+  size_t header_idx;
+  /* have we seen a regular (non-colon-prefixed) header yet? */
+  uint8_t seen_regular_header;
+  /* output stream id */
+  uint32_t stream_id;
+  grpc_slice_buffer* output;
+  grpc_transport_one_way_stats* stats;
+  /* maximum size of a frame */
+  size_t max_frame_size;
+  bool use_true_binary_metadata;
+} framer_state;
+
+/* fills p (which is expected to be 9 bytes long) with a data frame header */
+static void fill_header(uint8_t* p, uint8_t type, uint32_t id, size_t len,
+                        uint8_t flags) {
+  GPR_ASSERT(len < 16777316);
+  *p++ = static_cast<uint8_t>(len >> 16);
+  *p++ = static_cast<uint8_t>(len >> 8);
+  *p++ = static_cast<uint8_t>(len);
+  *p++ = type;
+  *p++ = flags;
+  *p++ = static_cast<uint8_t>(id >> 24);
+  *p++ = static_cast<uint8_t>(id >> 16);
+  *p++ = static_cast<uint8_t>(id >> 8);
+  *p++ = static_cast<uint8_t>(id);
+}
+
+/* finish a frame - fill in the previously reserved header */
+static void finish_frame(framer_state* st, int is_header_boundary,
+                         int is_last_in_stream) {
+  uint8_t type = 0xff;
+  type = st->is_first_frame ? GRPC_CHTTP2_FRAME_HEADER
+                            : GRPC_CHTTP2_FRAME_CONTINUATION;
+  fill_header(
+      GRPC_SLICE_START_PTR(st->output->slices[st->header_idx]), type,
+      st->stream_id, st->output->length - st->output_length_at_start_of_frame,
+      static_cast<uint8_t>(
+          (is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) |
+          (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0)));
+  st->stats->framing_bytes += 9;
+  st->is_first_frame = 0;
+}
+
+/* begin a new frame: reserve off header space, remember how many bytes we'd
+   output before beginning */
+static void begin_frame(framer_state* st) {
+  st->header_idx =
+      grpc_slice_buffer_add_indexed(st->output, GRPC_SLICE_MALLOC(9));
+  st->output_length_at_start_of_frame = st->output->length;
+}
+
+/* make sure that the current frame is of the type desired, and has sufficient
+   space to add at least about_to_add bytes -- finishes the current frame if
+   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 <=
+      st->max_frame_size) {
+    return;
+  }
+  finish_frame(st, 0, 0);
+  begin_frame(st);
+}
+
+/* increment a filter count, halve all counts if one element reaches max */
+static void inc_filter(uint8_t idx, uint32_t* sum, uint8_t* elems) {
+  elems[idx]++;
+  if (elems[idx] < 255) {
+    (*sum)++;
+  } else {
+    int i;
+    *sum = 0;
+    for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) {
+      elems[i] /= 2;
+      (*sum) += elems[i];
+    }
+  }
+}
+
+static void add_header_data(framer_state* st, grpc_slice slice) {
+  size_t len = GRPC_SLICE_LENGTH(slice);
+  size_t remaining;
+  if (len == 0) return;
+  remaining = st->max_frame_size + st->output_length_at_start_of_frame -
+              st->output->length;
+  if (len <= remaining) {
+    st->stats->header_bytes += len;
+    grpc_slice_buffer_add(st->output, slice);
+  } else {
+    st->stats->header_bytes += remaining;
+    grpc_slice_buffer_add(st->output, grpc_slice_split_head(&slice, remaining));
+    finish_frame(st, 0, 0);
+    begin_frame(st);
+    add_header_data(st, slice);
+  }
+}
+
+static uint8_t* add_tiny_header_data(framer_state* st, size_t len) {
+  ensure_space(st, len);
+  st->stats->header_bytes += len;
+  return grpc_slice_buffer_tiny_add(st->output, len);
+}
+
+static void evict_entry(grpc_chttp2_hpack_compressor* c) {
+  c->tail_remote_index++;
+  GPR_ASSERT(c->tail_remote_index > 0);
+  GPR_ASSERT(c->table_size >=
+             c->table_elem_size[c->tail_remote_index % c->cap_table_elems]);
+  GPR_ASSERT(c->table_elems > 0);
+  c->table_size = static_cast<uint16_t>(
+      c->table_size -
+      c->table_elem_size[c->tail_remote_index % c->cap_table_elems]);
+  c->table_elems--;
+}
+
+// Reserve space in table for the new element, evict entries if needed.
+// Return the new index of the element. Return 0 to indicate not adding to
+// table.
+static uint32_t prepare_space_for_new_elem(grpc_chttp2_hpack_compressor* c,
+                                           size_t elem_size) {
+  uint32_t new_index = c->tail_remote_index + c->table_elems + 1;
+  GPR_ASSERT(elem_size < 65536);
+
+  if (elem_size > c->max_table_size) {
+    while (c->table_size > 0) {
+      evict_entry(c);
+    }
+    return 0;
+  }
+
+  /* Reserve space for this element in the remote table: if this overflows
+     the current table, drop elements until it fits, matching the decompressor
+     algorithm */
+  while (c->table_size + elem_size > c->max_table_size) {
+    evict_entry(c);
+  }
+  GPR_ASSERT(c->table_elems < c->max_table_size);
+  c->table_elem_size[new_index % c->cap_table_elems] =
+      static_cast<uint16_t>(elem_size);
+  c->table_size = static_cast<uint16_t>(c->table_size + elem_size);
+  c->table_elems++;
+
+  return new_index;
+}
+
+// Add a key to the dynamic table. Both key and value will be added to table at
+// the decoder.
+static void add_key_with_index(grpc_chttp2_hpack_compressor* c,
+                               grpc_mdelem elem, uint32_t new_index) {
+  if (new_index == 0) {
+    return;
+  }
+
+  uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem));
+
+  /* Store the key into {entries,indices}_keys */
+  if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_2(key_hash)],
+                    GRPC_MDKEY(elem))) {
+    c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index;
+  } else if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_3(key_hash)],
+                           GRPC_MDKEY(elem))) {
+    c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
+  } else if (c->entries_keys[HASH_FRAGMENT_2(key_hash)].refcount ==
+             &terminal_slice_refcount) {
+    c->entries_keys[HASH_FRAGMENT_2(key_hash)] =
+        grpc_slice_ref_internal(GRPC_MDKEY(elem));
+    c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index;
+  } else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)].refcount ==
+             &terminal_slice_refcount) {
+    c->entries_keys[HASH_FRAGMENT_3(key_hash)] =
+        grpc_slice_ref_internal(GRPC_MDKEY(elem));
+    c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
+  } else if (c->indices_keys[HASH_FRAGMENT_2(key_hash)] <
+             c->indices_keys[HASH_FRAGMENT_3(key_hash)]) {
+    grpc_slice_unref_internal(c->entries_keys[HASH_FRAGMENT_2(key_hash)]);
+    c->entries_keys[HASH_FRAGMENT_2(key_hash)] =
+        grpc_slice_ref_internal(GRPC_MDKEY(elem));
+    c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index;
+  } else {
+    grpc_slice_unref_internal(c->entries_keys[HASH_FRAGMENT_3(key_hash)]);
+    c->entries_keys[HASH_FRAGMENT_3(key_hash)] =
+        grpc_slice_ref_internal(GRPC_MDKEY(elem));
+    c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index;
+  }
+}
+
+/* add an element to the decoder table */
+static void add_elem_with_index(grpc_chttp2_hpack_compressor* c,
+                                grpc_mdelem elem, uint32_t new_index) {
+  if (new_index == 0) {
+    return;
+  }
+  GPR_ASSERT(GRPC_MDELEM_IS_INTERNED(elem));
+
+  uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem));
+  uint32_t value_hash = grpc_slice_hash(GRPC_MDVALUE(elem));
+  uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash);
+
+  /* Store this element into {entries,indices}_elem */
+  if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_2(elem_hash)], elem)) {
+    /* already there: update with new index */
+    c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index;
+  } else if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_3(elem_hash)],
+                            elem)) {
+    /* already there (cuckoo): update with new index */
+    c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index;
+  } else if (GRPC_MDISNULL(c->entries_elems[HASH_FRAGMENT_2(elem_hash)])) {
+    /* not there, but a free element: add */
+    c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem);
+    c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index;
+  } else if (GRPC_MDISNULL(c->entries_elems[HASH_FRAGMENT_3(elem_hash)])) {
+    /* not there (cuckoo), but a free element: add */
+    c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem);
+    c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index;
+  } else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] <
+             c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) {
+    /* not there: replace oldest */
+    GRPC_MDELEM_UNREF(c->entries_elems[HASH_FRAGMENT_2(elem_hash)]);
+    c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem);
+    c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index;
+  } else {
+    /* not there: replace oldest */
+    GRPC_MDELEM_UNREF(c->entries_elems[HASH_FRAGMENT_3(elem_hash)]);
+    c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem);
+    c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index;
+  }
+
+  add_key_with_index(c, elem, new_index);
+}
+
+static void add_elem(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
+                     size_t elem_size) {
+  uint32_t new_index = prepare_space_for_new_elem(c, elem_size);
+  add_elem_with_index(c, elem, new_index);
+}
+
+static void add_key(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
+                    size_t elem_size) {
+  uint32_t new_index = prepare_space_for_new_elem(c, elem_size);
+  add_key_with_index(c, elem, new_index);
+}
+
+static void emit_indexed(grpc_chttp2_hpack_compressor* c, uint32_t elem_index,
+                         framer_state* st) {
+  GRPC_STATS_INC_HPACK_SEND_INDEXED();
+  uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(elem_index, 1);
+  GRPC_CHTTP2_WRITE_VARINT(elem_index, 1, 0x80, add_tiny_header_data(st, len),
+                           len);
+}
+
+typedef struct {
+  grpc_slice data;
+  uint8_t huffman_prefix;
+  bool insert_null_before_wire_value;
+} wire_value;
+
+static wire_value get_wire_value(grpc_mdelem elem, bool true_binary_enabled) {
+  wire_value wire_val;
+  if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
+    if (true_binary_enabled) {
+      GRPC_STATS_INC_HPACK_SEND_BINARY();
+      wire_val.huffman_prefix = 0x00;
+      wire_val.insert_null_before_wire_value = true;
+      wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem));
+
+    } else {
+      GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64();
+      wire_val.huffman_prefix = 0x80;
+      wire_val.insert_null_before_wire_value = false;
+      wire_val.data =
+          grpc_chttp2_base64_encode_and_huffman_compress(GRPC_MDVALUE(elem));
+    }
+  } else {
+    /* TODO(ctiller): opportunistically compress non-binary headers */
+    GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
+    wire_val.huffman_prefix = 0x00;
+    wire_val.insert_null_before_wire_value = false;
+    wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem));
+  }
+  return wire_val;
+}
+
+static size_t wire_value_length(wire_value v) {
+  return GPR_SLICE_LENGTH(v.data) + v.insert_null_before_wire_value;
+}
+
+static void add_wire_value(framer_state* st, wire_value v) {
+  if (v.insert_null_before_wire_value) *add_tiny_header_data(st, 1) = 0;
+  add_header_data(st, v.data);
+}
+
+static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor* c,
+                               uint32_t key_index, grpc_mdelem elem,
+                               framer_state* st) {
+  GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX();
+  uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2);
+  wire_value value = get_wire_value(elem, st->use_true_binary_metadata);
+  size_t len_val = wire_value_length(value);
+  uint32_t len_val_len;
+  GPR_ASSERT(len_val <= UINT32_MAX);
+  len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1);
+  GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40,
+                           add_tiny_header_data(st, len_pfx), len_pfx);
+  GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix,
+                           add_tiny_header_data(st, len_val_len), len_val_len);
+  add_wire_value(st, value);
+}
+
+static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor* c,
+                              uint32_t key_index, grpc_mdelem elem,
+                              framer_state* st) {
+  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX();
+  uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4);
+  wire_value value = get_wire_value(elem, st->use_true_binary_metadata);
+  size_t len_val = wire_value_length(value);
+  uint32_t len_val_len;
+  GPR_ASSERT(len_val <= UINT32_MAX);
+  len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1);
+  GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00,
+                           add_tiny_header_data(st, len_pfx), len_pfx);
+  GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix,
+                           add_tiny_header_data(st, len_val_len), len_val_len);
+  add_wire_value(st, value);
+}
+
+static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor* c,
+                                 uint32_t unused_index, grpc_mdelem elem,
+                                 framer_state* st) {
+  GPR_ASSERT(unused_index == 0);
+  GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V();
+  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
+  uint32_t len_key = static_cast<uint32_t> GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
+  wire_value value = get_wire_value(elem, st->use_true_binary_metadata);
+  uint32_t len_val = static_cast<uint32_t>(wire_value_length(value));
+  uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1);
+  uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1);
+  GPR_ASSERT(len_key <= UINT32_MAX);
+  GPR_ASSERT(wire_value_length(value) <= UINT32_MAX);
+  *add_tiny_header_data(st, 1) = 0x40;
+  GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00,
+                           add_tiny_header_data(st, len_key_len), len_key_len);
+  add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem)));
+  GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix,
+                           add_tiny_header_data(st, len_val_len), len_val_len);
+  add_wire_value(st, value);
+}
+
+static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor* c,
+                                uint32_t unused_index, grpc_mdelem elem,
+                                framer_state* st) {
+  GPR_ASSERT(unused_index == 0);
+  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V();
+  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
+  uint32_t len_key = static_cast<uint32_t> GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
+  wire_value value = get_wire_value(elem, st->use_true_binary_metadata);
+  uint32_t len_val = static_cast<uint32_t>(wire_value_length(value));
+  uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1);
+  uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1);
+  GPR_ASSERT(len_key <= UINT32_MAX);
+  GPR_ASSERT(wire_value_length(value) <= UINT32_MAX);
+  *add_tiny_header_data(st, 1) = 0x00;
+  GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00,
+                           add_tiny_header_data(st, len_key_len), len_key_len);
+  add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem)));
+  GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix,
+                           add_tiny_header_data(st, len_val_len), len_val_len);
+  add_wire_value(st, value);
+}
+
+static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor* c,
+                                             framer_state* st) {
+  uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(c->max_table_size, 3);
+  GRPC_CHTTP2_WRITE_VARINT(c->max_table_size, 3, 0x20,
+                           add_tiny_header_data(st, len), len);
+  c->advertise_table_size_change = 0;
+}
+
+static uint32_t dynidx(grpc_chttp2_hpack_compressor* c, uint32_t elem_index) {
+  return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index +
+         c->table_elems - elem_index;
+}
+
+/* encode an mdelem */
+static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
+                      framer_state* st) {
+  GPR_ASSERT(GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)) > 0);
+  if (GRPC_SLICE_START_PTR(GRPC_MDKEY(elem))[0] != ':') { /* regular header */
+    st->seen_regular_header = 1;
+  } else {
+    GPR_ASSERT(
+        st->seen_regular_header == 0 &&
+        "Reserved header (colon-prefixed) happening after regular ones.");
+  }
+
+  if (grpc_http_trace.enabled()) {
+    char* k = grpc_slice_to_c_string(GRPC_MDKEY(elem));
+    char* v = nullptr;
+    if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
+      v = grpc_dump_slice(GRPC_MDVALUE(elem), GPR_DUMP_HEX);
+    } else {
+      v = grpc_slice_to_c_string(GRPC_MDVALUE(elem));
+    }
+    gpr_log(
+        GPR_INFO,
+        "Encode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d",
+        k, v, GRPC_MDELEM_IS_INTERNED(elem), GRPC_MDELEM_STORAGE(elem),
+        grpc_slice_is_interned(GRPC_MDKEY(elem)),
+        grpc_slice_is_interned(GRPC_MDVALUE(elem)));
+    gpr_free(k);
+    gpr_free(v);
+  }
+
+  bool elem_interned = GRPC_MDELEM_IS_INTERNED(elem);
+  bool key_interned = elem_interned || grpc_slice_is_interned(GRPC_MDKEY(elem));
+
+  // Key is not interned, emit literals.
+  if (!key_interned) {
+    emit_lithdr_noidx_v(c, 0, elem, st);
+    return;
+  }
+
+  uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem));
+  uint32_t elem_hash = 0;
+
+  if (elem_interned) {
+    uint32_t value_hash = grpc_slice_hash(GRPC_MDVALUE(elem));
+    elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash);
+
+    inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum,
+               c->filter_elems);
+
+    /* is this elem currently in the decoders table? */
+
+    if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_2(elem_hash)], elem) &&
+        c->indices_elems[HASH_FRAGMENT_2(elem_hash)] > c->tail_remote_index) {
+      /* HIT: complete element (first cuckoo hash) */
+      emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]),
+                   st);
+      return;
+    }
+
+    if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_3(elem_hash)], elem) &&
+        c->indices_elems[HASH_FRAGMENT_3(elem_hash)] > c->tail_remote_index) {
+      /* HIT: complete element (second cuckoo hash) */
+      emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]),
+                   st);
+      return;
+    }
+  }
+
+  uint32_t indices_key;
+
+  /* should this elem be in the table? */
+  const size_t decoder_space_usage =
+      grpc_chttp2_get_size_in_hpack_table(elem, st->use_true_binary_metadata);
+  const bool should_add_elem = elem_interned &&
+                               decoder_space_usage < MAX_DECODER_SPACE_USAGE &&
+                               c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >=
+                                   c->filter_elems_sum / ONE_ON_ADD_PROBABILITY;
+
+  auto emit_maybe_add = [&should_add_elem, &elem, &st, &c, &indices_key,
+                         &decoder_space_usage] {
+    if (should_add_elem) {
+      emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
+      add_elem(c, elem, decoder_space_usage);
+    } else {
+      emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
+    }
+  };
+
+  /* no hits for the elem... maybe there's a key? */
+  indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)];
+  if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_2(key_hash)],
+                    GRPC_MDKEY(elem)) &&
+      indices_key > c->tail_remote_index) {
+    /* HIT: key (first cuckoo hash) */
+    emit_maybe_add();
+    return;
+  }
+
+  indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)];
+  if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_3(key_hash)],
+                    GRPC_MDKEY(elem)) &&
+      indices_key > c->tail_remote_index) {
+    /* HIT: key (first cuckoo hash) */
+    emit_maybe_add();
+    return;
+  }
+
+  /* no elem, key in the table... fall back to literal emission */
+  const bool should_add_key =
+      !elem_interned && decoder_space_usage < MAX_DECODER_SPACE_USAGE;
+  if (should_add_elem || should_add_key) {
+    emit_lithdr_incidx_v(c, 0, elem, st);
+  } else {
+    emit_lithdr_noidx_v(c, 0, elem, st);
+  }
+  if (should_add_elem) {
+    add_elem(c, elem, decoder_space_usage);
+  } else if (should_add_key) {
+    add_key(c, elem, decoder_space_usage);
+  }
+}
+
+#define STRLEN_LIT(x) (sizeof(x) - 1)
+#define TIMEOUT_KEY "grpc-timeout"
+
+static void deadline_enc(grpc_chttp2_hpack_compressor* c, grpc_millis deadline,
+                         framer_state* st) {
+  char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
+  grpc_mdelem mdelem;
+  grpc_http2_encode_timeout(deadline - grpc_core::ExecCtx::Get()->Now(),
+                            timeout_str);
+  mdelem = grpc_mdelem_from_slices(GRPC_MDSTR_GRPC_TIMEOUT,
+                                   grpc_slice_from_copied_string(timeout_str));
+  hpack_enc(c, mdelem, st);
+  GRPC_MDELEM_UNREF(mdelem);
+}
+
+static uint32_t elems_for_bytes(uint32_t bytes) { return (bytes + 31) / 32; }
+
+void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor* c) {
+  memset(c, 0, sizeof(*c));
+  c->max_table_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE;
+  c->cap_table_elems = elems_for_bytes(c->max_table_size);
+  c->max_table_elems = c->cap_table_elems;
+  c->max_usable_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE;
+  c->table_elem_size = static_cast<uint16_t*>(
+      gpr_malloc(sizeof(*c->table_elem_size) * c->cap_table_elems));
+  memset(c->table_elem_size, 0,
+         sizeof(*c->table_elem_size) * c->cap_table_elems);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(c->entries_keys); i++) {
+    c->entries_keys[i] = terminal_slice;
+  }
+}
+
+void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor* c) {
+  int i;
+  for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) {
+    if (c->entries_keys[i].refcount != &terminal_slice_refcount) {
+      grpc_slice_unref_internal(c->entries_keys[i]);
+    }
+    GRPC_MDELEM_UNREF(c->entries_elems[i]);
+  }
+  gpr_free(c->table_elem_size);
+}
+
+void grpc_chttp2_hpack_compressor_set_max_usable_size(
+    grpc_chttp2_hpack_compressor* c, uint32_t max_table_size) {
+  c->max_usable_size = max_table_size;
+  grpc_chttp2_hpack_compressor_set_max_table_size(
+      c, GPR_MIN(c->max_table_size, max_table_size));
+}
+
+static void rebuild_elems(grpc_chttp2_hpack_compressor* c, uint32_t new_cap) {
+  uint16_t* table_elem_size =
+      static_cast<uint16_t*>(gpr_malloc(sizeof(*table_elem_size) * new_cap));
+  uint32_t i;
+
+  memset(table_elem_size, 0, sizeof(*table_elem_size) * new_cap);
+  GPR_ASSERT(c->table_elems <= new_cap);
+
+  for (i = 0; i < c->table_elems; i++) {
+    uint32_t ofs = c->tail_remote_index + i + 1;
+    table_elem_size[ofs % new_cap] =
+        c->table_elem_size[ofs % c->cap_table_elems];
+  }
+
+  c->cap_table_elems = new_cap;
+  gpr_free(c->table_elem_size);
+  c->table_elem_size = table_elem_size;
+}
+
+void grpc_chttp2_hpack_compressor_set_max_table_size(
+    grpc_chttp2_hpack_compressor* c, uint32_t max_table_size) {
+  max_table_size = GPR_MIN(max_table_size, c->max_usable_size);
+  if (max_table_size == c->max_table_size) {
+    return;
+  }
+  while (c->table_size > 0 && c->table_size > max_table_size) {
+    evict_entry(c);
+  }
+  c->max_table_size = max_table_size;
+  c->max_table_elems = elems_for_bytes(max_table_size);
+  if (c->max_table_elems > c->cap_table_elems) {
+    rebuild_elems(c, GPR_MAX(c->max_table_elems, 2 * c->cap_table_elems));
+  } else if (c->max_table_elems < c->cap_table_elems / 3) {
+    uint32_t new_cap = GPR_MAX(c->max_table_elems, 16);
+    if (new_cap != c->cap_table_elems) {
+      rebuild_elems(c, new_cap);
+    }
+  }
+  c->advertise_table_size_change = 1;
+  if (grpc_http_trace.enabled()) {
+    gpr_log(GPR_INFO, "set max table size from encoder to %d", max_table_size);
+  }
+}
+
+void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor* c,
+                               grpc_mdelem** extra_headers,
+                               size_t extra_headers_size,
+                               grpc_metadata_batch* metadata,
+                               const grpc_encode_header_options* options,
+                               grpc_slice_buffer* outbuf) {
+  GPR_ASSERT(options->stream_id != 0);
+
+  framer_state st;
+  st.seen_regular_header = 0;
+  st.stream_id = options->stream_id;
+  st.output = outbuf;
+  st.is_first_frame = 1;
+  st.stats = options->stats;
+  st.max_frame_size = options->max_frame_size;
+  st.use_true_binary_metadata = options->use_true_binary_metadata;
+
+  /* Encode a metadata batch; store the returned values, representing
+     a metadata element that needs to be unreffed back into the metadata
+     slot. THIS MAY NOT BE THE SAME ELEMENT (if a decoder table slot got
+     updated). After this loop, we'll do a batch unref of elements. */
+  begin_frame(&st);
+  if (c->advertise_table_size_change != 0) {
+    emit_advertise_table_size_change(c, &st);
+  }
+  for (size_t i = 0; i < extra_headers_size; ++i) {
+    grpc_mdelem md = *extra_headers[i];
+    uint8_t static_index = grpc_chttp2_get_static_hpack_table_index(md);
+    if (static_index) {
+      emit_indexed(c, static_index, &st);
+    } else {
+      hpack_enc(c, md, &st);
+    }
+  }
+  grpc_metadata_batch_assert_ok(metadata);
+  for (grpc_linked_mdelem* l = metadata->list.head; l; l = l->next) {
+    uint8_t static_index = grpc_chttp2_get_static_hpack_table_index(l->md);
+    if (static_index) {
+      emit_indexed(c, static_index, &st);
+    } else {
+      hpack_enc(c, l->md, &st);
+    }
+  }
+  grpc_millis deadline = metadata->deadline;
+  if (deadline != GRPC_MILLIS_INF_FUTURE) {
+    deadline_enc(c, deadline, &st);
+  }
+
+  finish_frame(&st, 1, options->is_eof);
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.h
new file mode 100644
index 0000000..e31a739
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.h
@@ -0,0 +1,96 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/metadata_batch.h"
+#include "src/core/lib/transport/transport.h"
+
+// This should be <= 8. We use 6 to save space.
+#define GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS 6
+#define GRPC_CHTTP2_HPACKC_NUM_VALUES (1 << GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS)
+/* initial table size, per spec */
+#define GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE 4096
+/* maximum table size we'll actually use */
+#define GRPC_CHTTP2_HPACKC_MAX_TABLE_SIZE (1024 * 1024)
+
+extern grpc_core::TraceFlag grpc_http_trace;
+
+typedef struct {
+  uint32_t filter_elems_sum;
+  uint32_t max_table_size;
+  uint32_t max_table_elems;
+  uint32_t cap_table_elems;
+  /** if non-zero, advertise to the decoder that we'll start using a table
+      of this size */
+  uint8_t advertise_table_size_change;
+  /** maximum number of bytes we'll use for the decode table (to guard against
+      peers ooming us by setting decode table size high) */
+  uint32_t max_usable_size;
+  /* one before the lowest usable table index */
+  uint32_t tail_remote_index;
+  uint32_t table_size;
+  uint32_t table_elems;
+
+  /* filter tables for elems: this tables provides an approximate
+     popularity count for particular hashes, and are used to determine whether
+     a new literal should be added to the compression table or not.
+     They track a single integer that counts how often a particular value has
+     been seen. When that count reaches max (255), all values are halved. */
+  uint8_t filter_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES];
+
+  /* entry tables for keys & elems: these tables track values that have been
+     seen and *may* be in the decompressor table */
+  grpc_slice entries_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES];
+  grpc_mdelem entries_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES];
+  uint32_t indices_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES];
+  uint32_t indices_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES];
+
+  uint16_t* table_elem_size;
+} grpc_chttp2_hpack_compressor;
+
+void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor* c);
+void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor* c);
+void grpc_chttp2_hpack_compressor_set_max_table_size(
+    grpc_chttp2_hpack_compressor* c, uint32_t max_table_size);
+void grpc_chttp2_hpack_compressor_set_max_usable_size(
+    grpc_chttp2_hpack_compressor* c, uint32_t max_table_size);
+
+typedef struct {
+  uint32_t stream_id;
+  bool is_eof;
+  bool use_true_binary_metadata;
+  size_t max_frame_size;
+  grpc_transport_one_way_stats* stats;
+} grpc_encode_header_options;
+
+void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor* c,
+                               grpc_mdelem** extra_headers,
+                               size_t extra_headers_size,
+                               grpc_metadata_batch* metadata,
+                               const grpc_encode_header_options* options,
+                               grpc_slice_buffer* outbuf);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.cc
new file mode 100644
index 0000000..ccf2256
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.cc
@@ -0,0 +1,1680 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+typedef enum {
+  NOT_BINARY,
+  BINARY_BEGIN,
+  B64_BYTE0,
+  B64_BYTE1,
+  B64_BYTE2,
+  B64_BYTE3
+} binary_state;
+
+/* How parsing works:
+
+   The parser object keeps track of a function pointer which represents the
+   current parse state.
+
+   Each time new bytes are presented, we call into the current state, which
+   recursively parses until all bytes in the given chunk are exhausted.
+
+   The parse state that terminates then saves its function pointer to be the
+   current state so that it can resume when more bytes are available.
+
+   It's expected that most optimizing compilers will turn this code into
+   a set of indirect jumps, and so not waste stack space. */
+
+/* forward declarations for parsing states */
+static grpc_error* parse_begin(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                               const uint8_t* end);
+static grpc_error* parse_error(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                               const uint8_t* end, grpc_error* error);
+static grpc_error* still_parse_error(grpc_chttp2_hpack_parser* p,
+                                     const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_illegal_op(grpc_chttp2_hpack_parser* p,
+                                    const uint8_t* cur, const uint8_t* end);
+
+static grpc_error* parse_string_prefix(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_key_string(grpc_chttp2_hpack_parser* p,
+                                    const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_value_string_with_indexed_key(
+    grpc_chttp2_hpack_parser* p, const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_value_string_with_literal_key(
+    grpc_chttp2_hpack_parser* p, const uint8_t* cur, const uint8_t* end);
+
+static grpc_error* parse_value0(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end);
+static grpc_error* parse_value1(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end);
+static grpc_error* parse_value2(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end);
+static grpc_error* parse_value3(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end);
+static grpc_error* parse_value4(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end);
+static grpc_error* parse_value5up(grpc_chttp2_hpack_parser* p,
+                                  const uint8_t* cur, const uint8_t* end);
+
+static grpc_error* parse_indexed_field(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_indexed_field_x(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end);
+static grpc_error* parse_lithdr_incidx(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_lithdr_incidx_x(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end);
+static grpc_error* parse_lithdr_incidx_v(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end);
+static grpc_error* parse_lithdr_notidx(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_lithdr_notidx_x(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end);
+static grpc_error* parse_lithdr_notidx_v(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end);
+static grpc_error* parse_lithdr_nvridx(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end);
+static grpc_error* parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end);
+static grpc_error* parse_max_tbl_size(grpc_chttp2_hpack_parser* p,
+                                      const uint8_t* cur, const uint8_t* end);
+static grpc_error* parse_max_tbl_size_x(grpc_chttp2_hpack_parser* p,
+                                        const uint8_t* cur, const uint8_t* end);
+
+/* we translate the first byte of a hpack field into one of these decoding
+   cases, then use a lookup table to jump directly to the appropriate parser.
+
+   _X => the integer index is all ones, meaning we need to do varint decoding
+   _V => the integer index is all zeros, meaning we need to decode an additional
+         string value */
+typedef enum {
+  INDEXED_FIELD,
+  INDEXED_FIELD_X,
+  LITHDR_INCIDX,
+  LITHDR_INCIDX_X,
+  LITHDR_INCIDX_V,
+  LITHDR_NOTIDX,
+  LITHDR_NOTIDX_X,
+  LITHDR_NOTIDX_V,
+  LITHDR_NVRIDX,
+  LITHDR_NVRIDX_X,
+  LITHDR_NVRIDX_V,
+  MAX_TBL_SIZE,
+  MAX_TBL_SIZE_X,
+  ILLEGAL
+} first_byte_type;
+
+/* jump table of parse state functions -- order must match first_byte_type
+   above */
+static const grpc_chttp2_hpack_parser_state first_byte_action[] = {
+    parse_indexed_field,   parse_indexed_field_x, parse_lithdr_incidx,
+    parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx,
+    parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx,
+    parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size,
+    parse_max_tbl_size_x,  parse_illegal_op};
+
+/* indexes the first byte to a parse state function - generated by
+   gen_hpack_tables.c */
+static const uint8_t first_byte_lut[256] = {
+    LITHDR_NOTIDX_V, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX,
+    LITHDR_NOTIDX,   LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX,
+    LITHDR_NOTIDX,   LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX,
+    LITHDR_NOTIDX,   LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX_X,
+    LITHDR_NVRIDX_V, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
+    LITHDR_NVRIDX,   LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
+    LITHDR_NVRIDX,   LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
+    LITHDR_NVRIDX,   LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X,
+    MAX_TBL_SIZE,    MAX_TBL_SIZE,  MAX_TBL_SIZE,  MAX_TBL_SIZE,
+    MAX_TBL_SIZE,    MAX_TBL_SIZE,  MAX_TBL_SIZE,  MAX_TBL_SIZE,
+    MAX_TBL_SIZE,    MAX_TBL_SIZE,  MAX_TBL_SIZE,  MAX_TBL_SIZE,
+    MAX_TBL_SIZE,    MAX_TBL_SIZE,  MAX_TBL_SIZE,  MAX_TBL_SIZE,
+    MAX_TBL_SIZE,    MAX_TBL_SIZE,  MAX_TBL_SIZE,  MAX_TBL_SIZE,
+    MAX_TBL_SIZE,    MAX_TBL_SIZE,  MAX_TBL_SIZE,  MAX_TBL_SIZE,
+    MAX_TBL_SIZE,    MAX_TBL_SIZE,  MAX_TBL_SIZE,  MAX_TBL_SIZE,
+    MAX_TBL_SIZE,    MAX_TBL_SIZE,  MAX_TBL_SIZE,  MAX_TBL_SIZE_X,
+    LITHDR_INCIDX_V, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+    LITHDR_INCIDX,   LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX_X,
+    ILLEGAL,         INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+    INDEXED_FIELD,   INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD_X,
+};
+
+/* state table for huffman decoding: given a state, gives an index/16 into
+   next_sub_tbl. Taking that index and adding the value of the nibble being
+   considered returns the next state.
+
+   generated by gen_hpack_tables.c */
+static const uint8_t next_tbl[256] = {
+    0,  1,  2,  3,  4,  1,  2, 5,  6,  1, 7,  8,  1,  3,  3,  9,  10, 11, 1,  1,
+    1,  12, 1,  2,  13, 1,  1, 1,  1,  1, 1,  1,  1,  1,  1,  1,  1,  1,  1,  2,
+    14, 1,  15, 16, 1,  17, 1, 15, 2,  7, 3,  18, 19, 1,  1,  1,  1,  20, 1,  1,
+    1,  1,  1,  1,  1,  1,  1, 1,  15, 2, 2,  7,  21, 1,  22, 1,  1,  1,  1,  1,
+    1,  1,  1,  15, 2,  2,  2, 2,  2,  2, 23, 24, 25, 1,  1,  1,  1,  2,  2,  2,
+    26, 3,  3,  27, 10, 28, 1, 1,  1,  1, 1,  1,  2,  3,  29, 10, 30, 1,  1,  1,
+    1,  1,  1,  1,  1,  1,  1, 1,  1,  1, 1,  31, 1,  1,  1,  1,  1,  1,  1,  2,
+    2,  2,  2,  2,  2,  2,  2, 32, 1,  1, 15, 33, 1,  34, 35, 9,  36, 1,  1,  1,
+    1,  1,  1,  1,  37, 1,  1, 1,  1,  1, 1,  2,  2,  2,  2,  2,  2,  2,  26, 9,
+    38, 1,  1,  1,  1,  1,  1, 1,  15, 2, 2,  2,  2,  26, 3,  3,  39, 1,  1,  1,
+    1,  1,  1,  1,  1,  1,  1, 1,  2,  2, 2,  2,  2,  2,  7,  3,  3,  3,  40, 2,
+    41, 1,  1,  1,  42, 43, 1, 1,  44, 1, 1,  1,  1,  15, 2,  2,  2,  2,  2,  2,
+    3,  3,  3,  45, 46, 1,  1, 2,  2,  2, 35, 3,  3,  18, 47, 2,
+};
+
+/* next state, based upon current state and the current nibble: see above.
+   generated by gen_hpack_tables.c */
+static const int16_t next_sub_tbl[48 * 16] = {
+    1,   204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+    218, 2,   6,   10,  13,  14,  15,  16,  17,  2,   6,   10,  13,  14,  15,
+    16,  17,  3,   7,   11,  24,  3,   7,   11,  24,  3,   7,   11,  24,  3,
+    7,   11,  24,  4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   4,   8,
+    4,   8,   4,   8,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,
+    199, 200, 201, 202, 203, 4,   8,   4,   8,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   9,   133, 134, 135, 136, 137, 138, 139, 140,
+    141, 142, 143, 144, 145, 146, 147, 3,   7,   11,  24,  3,   7,   11,  24,
+    4,   8,   4,   8,   4,   8,   4,   8,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   12,  132, 4,   8,   4,   8,   4,   8,
+    4,   8,   4,   8,   4,   8,   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,   18,  19,  20,  21,  4,   8,   4,
+    8,   4,   8,   4,   8,   4,   8,   0,   0,   0,   22,  23,  91,  25,  26,
+    27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  3,
+    7,   11,  24,  3,   7,   11,  24,  0,   0,   0,   0,   0,   41,  42,  43,
+    2,   6,   10,  13,  14,  15,  16,  17,  3,   7,   11,  24,  3,   7,   11,
+    24,  4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   0,   0,
+    44,  45,  2,   6,   10,  13,  14,  15,  16,  17,  46,  47,  48,  49,  50,
+    51,  52,  57,  4,   8,   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,   53,  54,  55,  56,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,
+    68,  69,  70,  71,  72,  74,  0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   73,  75,  76,  77,  78,  79,  80,  81,  82,
+    83,  84,  85,  86,  87,  88,  89,  90,  3,   7,   11,  24,  3,   7,   11,
+    24,  3,   7,   11,  24,  0,   0,   0,   0,   3,   7,   11,  24,  3,   7,
+    11,  24,  4,   8,   4,   8,   0,   0,   0,   92,  0,   0,   0,   93,  94,
+    95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 3,   7,   11,  24,
+    4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   4,
+    8,   4,   8,   4,   8,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 4,
+    8,   4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   0,   0,
+    0,   117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+    131, 2,   6,   10,  13,  14,  15,  16,  17,  4,   8,   4,   8,   4,   8,
+    4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   148,
+    149, 150, 151, 3,   7,   11,  24,  4,   8,   4,   8,   0,   0,   0,   0,
+    0,   0,   152, 153, 3,   7,   11,  24,  3,   7,   11,  24,  3,   7,   11,
+    24,  154, 155, 156, 164, 3,   7,   11,  24,  3,   7,   11,  24,  3,   7,
+    11,  24,  4,   8,   4,   8,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    157, 158, 159, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172,
+    173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+    188, 189, 190, 191, 192, 193, 194, 195, 196, 4,   8,   4,   8,   4,   8,
+    4,   8,   4,   8,   4,   8,   4,   8,   197, 198, 4,   8,   4,   8,   4,
+    8,   4,   8,   0,   0,   0,   0,   0,   0,   219, 220, 3,   7,   11,  24,
+    4,   8,   4,   8,   4,   8,   0,   0,   221, 222, 223, 224, 3,   7,   11,
+    24,  3,   7,   11,  24,  4,   8,   4,   8,   4,   8,   225, 228, 4,   8,
+    4,   8,   4,   8,   0,   0,   0,   0,   0,   0,   0,   0,   226, 227, 229,
+    230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+    4,   8,   4,   8,   4,   8,   4,   8,   4,   8,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   245, 246, 247, 248, 249, 250, 251, 252,
+    253, 254, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   255,
+};
+
+/* emission table: indexed like next_tbl, ultimately gives the byte to be
+   emitted, or -1 for no byte, or 256 for end of stream
+
+   generated by gen_hpack_tables.c */
+static const uint16_t emit_tbl[256] = {
+    0,   1,   2,   3,   4,   5,   6,   7,   0,   8,   9,   10,  11,  12,  13,
+    14,  15,  16,  17,  18,  19,  20,  21,  22,  0,   23,  24,  25,  26,  27,
+    28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,
+    43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  0,   55,  56,
+    57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  0,
+    71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,
+    86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100,
+    101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
+    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+    131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
+    146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 0,
+    160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+    0,   175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
+    189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
+    204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
+    219, 220, 221, 0,   222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+    248,
+};
+
+/* generated by gen_hpack_tables.c */
+static const int16_t emit_sub_tbl[249 * 16] = {
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+    -1,  48,  48,  48,  48,  48,  48,  48,  48,  49,  49,  49,  49,  49,  49,
+    49,  49,  48,  48,  48,  48,  49,  49,  49,  49,  50,  50,  50,  50,  97,
+    97,  97,  97,  48,  48,  49,  49,  50,  50,  97,  97,  99,  99,  101, 101,
+    105, 105, 111, 111, 48,  49,  50,  97,  99,  101, 105, 111, 115, 116, -1,
+    -1,  -1,  -1,  -1,  -1,  32,  32,  32,  32,  32,  32,  32,  32,  37,  37,
+    37,  37,  37,  37,  37,  37,  99,  99,  99,  99,  101, 101, 101, 101, 105,
+    105, 105, 105, 111, 111, 111, 111, 115, 115, 116, 116, 32,  37,  45,  46,
+    47,  51,  52,  53,  54,  55,  56,  57,  61,  61,  61,  61,  61,  61,  61,
+    61,  65,  65,  65,  65,  65,  65,  65,  65,  115, 115, 115, 115, 116, 116,
+    116, 116, 32,  32,  37,  37,  45,  45,  46,  46,  61,  65,  95,  98,  100,
+    102, 103, 104, 108, 109, 110, 112, 114, 117, -1,  -1,  58,  58,  58,  58,
+    58,  58,  58,  58,  66,  66,  66,  66,  66,  66,  66,  66,  47,  47,  51,
+    51,  52,  52,  53,  53,  54,  54,  55,  55,  56,  56,  57,  57,  61,  61,
+    65,  65,  95,  95,  98,  98,  100, 100, 102, 102, 103, 103, 104, 104, 108,
+    108, 109, 109, 110, 110, 112, 112, 114, 114, 117, 117, 58,  66,  67,  68,
+    69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,
+    84,  85,  86,  87,  89,  106, 107, 113, 118, 119, 120, 121, 122, -1,  -1,
+    -1,  -1,  38,  38,  38,  38,  38,  38,  38,  38,  42,  42,  42,  42,  42,
+    42,  42,  42,  44,  44,  44,  44,  44,  44,  44,  44,  59,  59,  59,  59,
+    59,  59,  59,  59,  88,  88,  88,  88,  88,  88,  88,  88,  90,  90,  90,
+    90,  90,  90,  90,  90,  33,  33,  34,  34,  40,  40,  41,  41,  63,  63,
+    39,  43,  124, -1,  -1,  -1,  35,  35,  35,  35,  35,  35,  35,  35,  62,
+    62,  62,  62,  62,  62,  62,  62,  0,   0,   0,   0,   36,  36,  36,  36,
+    64,  64,  64,  64,  91,  91,  91,  91,  69,  69,  69,  69,  69,  69,  69,
+    69,  70,  70,  70,  70,  70,  70,  70,  70,  71,  71,  71,  71,  71,  71,
+    71,  71,  72,  72,  72,  72,  72,  72,  72,  72,  73,  73,  73,  73,  73,
+    73,  73,  73,  74,  74,  74,  74,  74,  74,  74,  74,  75,  75,  75,  75,
+    75,  75,  75,  75,  76,  76,  76,  76,  76,  76,  76,  76,  77,  77,  77,
+    77,  77,  77,  77,  77,  78,  78,  78,  78,  78,  78,  78,  78,  79,  79,
+    79,  79,  79,  79,  79,  79,  80,  80,  80,  80,  80,  80,  80,  80,  81,
+    81,  81,  81,  81,  81,  81,  81,  82,  82,  82,  82,  82,  82,  82,  82,
+    83,  83,  83,  83,  83,  83,  83,  83,  84,  84,  84,  84,  84,  84,  84,
+    84,  85,  85,  85,  85,  85,  85,  85,  85,  86,  86,  86,  86,  86,  86,
+    86,  86,  87,  87,  87,  87,  87,  87,  87,  87,  89,  89,  89,  89,  89,
+    89,  89,  89,  106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107,
+    107, 107, 107, 107, 113, 113, 113, 113, 113, 113, 113, 113, 118, 118, 118,
+    118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120,
+    120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 122,
+    122, 122, 122, 122, 122, 122, 122, 38,  38,  38,  38,  42,  42,  42,  42,
+    44,  44,  44,  44,  59,  59,  59,  59,  88,  88,  88,  88,  90,  90,  90,
+    90,  33,  34,  40,  41,  63,  -1,  -1,  -1,  39,  39,  39,  39,  39,  39,
+    39,  39,  43,  43,  43,  43,  43,  43,  43,  43,  124, 124, 124, 124, 124,
+    124, 124, 124, 35,  35,  35,  35,  62,  62,  62,  62,  0,   0,   36,  36,
+    64,  64,  91,  91,  93,  93,  126, 126, 94,  125, -1,  -1,  60,  60,  60,
+    60,  60,  60,  60,  60,  96,  96,  96,  96,  96,  96,  96,  96,  123, 123,
+    123, 123, 123, 123, 123, 123, -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  92,
+    92,  92,  92,  92,  92,  92,  92,  195, 195, 195, 195, 195, 195, 195, 195,
+    208, 208, 208, 208, 208, 208, 208, 208, 128, 128, 128, 128, 130, 130, 130,
+    130, 131, 131, 131, 131, 162, 162, 162, 162, 184, 184, 184, 184, 194, 194,
+    194, 194, 224, 224, 224, 224, 226, 226, 226, 226, 153, 153, 161, 161, 167,
+    167, 172, 172, 176, 176, 177, 177, 179, 179, 209, 209, 216, 216, 217, 217,
+    227, 227, 229, 229, 230, 230, 129, 132, 133, 134, 136, 146, 154, 156, 160,
+    163, 164, 169, 170, 173, 178, 181, 185, 186, 187, 189, 190, 196, 198, 228,
+    232, 233, -1,  -1,  -1,  -1,  1,   1,   1,   1,   1,   1,   1,   1,   135,
+    135, 135, 135, 135, 135, 135, 135, 137, 137, 137, 137, 137, 137, 137, 137,
+    138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139,
+    139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141,
+    141, 141, 143, 143, 143, 143, 143, 143, 143, 143, 147, 147, 147, 147, 147,
+    147, 147, 147, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150,
+    150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152,
+    152, 152, 152, 152, 152, 155, 155, 155, 155, 155, 155, 155, 155, 157, 157,
+    157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 165,
+    165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166,
+    168, 168, 168, 168, 168, 168, 168, 168, 174, 174, 174, 174, 174, 174, 174,
+    174, 175, 175, 175, 175, 175, 175, 175, 175, 180, 180, 180, 180, 180, 180,
+    180, 180, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183,
+    183, 183, 183, 188, 188, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191,
+    191, 191, 191, 191, 197, 197, 197, 197, 197, 197, 197, 197, 231, 231, 231,
+    231, 231, 231, 231, 231, 239, 239, 239, 239, 239, 239, 239, 239, 9,   9,
+    9,   9,   142, 142, 142, 142, 144, 144, 144, 144, 145, 145, 145, 145, 148,
+    148, 148, 148, 159, 159, 159, 159, 171, 171, 171, 171, 206, 206, 206, 206,
+    215, 215, 215, 215, 225, 225, 225, 225, 236, 236, 236, 236, 237, 237, 237,
+    237, 199, 199, 207, 207, 234, 234, 235, 235, 192, 193, 200, 201, 202, 205,
+    210, 213, 218, 219, 238, 240, 242, 243, 255, -1,  203, 203, 203, 203, 203,
+    203, 203, 203, 204, 204, 204, 204, 204, 204, 204, 204, 211, 211, 211, 211,
+    211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 214, 214, 214,
+    214, 214, 214, 214, 214, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222,
+    222, 222, 222, 222, 222, 222, 223, 223, 223, 223, 223, 223, 223, 223, 241,
+    241, 241, 241, 241, 241, 241, 241, 244, 244, 244, 244, 244, 244, 244, 244,
+    245, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246,
+    246, 247, 247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 248, 248, 248,
+    248, 248, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251,
+    251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253,
+    253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 2,   2,   2,
+    2,   3,   3,   3,   3,   4,   4,   4,   4,   5,   5,   5,   5,   6,   6,
+    6,   6,   7,   7,   7,   7,   8,   8,   8,   8,   11,  11,  11,  11,  12,
+    12,  12,  12,  14,  14,  14,  14,  15,  15,  15,  15,  16,  16,  16,  16,
+    17,  17,  17,  17,  18,  18,  18,  18,  19,  19,  19,  19,  20,  20,  20,
+    20,  21,  21,  21,  21,  23,  23,  23,  23,  24,  24,  24,  24,  25,  25,
+    25,  25,  26,  26,  26,  26,  27,  27,  27,  27,  28,  28,  28,  28,  29,
+    29,  29,  29,  30,  30,  30,  30,  31,  31,  31,  31,  127, 127, 127, 127,
+    220, 220, 220, 220, 249, 249, 249, 249, 10,  13,  22,  256, 93,  93,  93,
+    93,  126, 126, 126, 126, 94,  94,  125, 125, 60,  96,  123, -1,  92,  195,
+    208, -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  128,
+    128, 128, 128, 128, 128, 128, 128, 130, 130, 130, 130, 130, 130, 130, 130,
+    131, 131, 131, 131, 131, 131, 131, 131, 162, 162, 162, 162, 162, 162, 162,
+    162, 184, 184, 184, 184, 184, 184, 184, 184, 194, 194, 194, 194, 194, 194,
+    194, 194, 224, 224, 224, 224, 224, 224, 224, 224, 226, 226, 226, 226, 226,
+    226, 226, 226, 153, 153, 153, 153, 161, 161, 161, 161, 167, 167, 167, 167,
+    172, 172, 172, 172, 176, 176, 176, 176, 177, 177, 177, 177, 179, 179, 179,
+    179, 209, 209, 209, 209, 216, 216, 216, 216, 217, 217, 217, 217, 227, 227,
+    227, 227, 229, 229, 229, 229, 230, 230, 230, 230, 129, 129, 132, 132, 133,
+    133, 134, 134, 136, 136, 146, 146, 154, 154, 156, 156, 160, 160, 163, 163,
+    164, 164, 169, 169, 170, 170, 173, 173, 178, 178, 181, 181, 185, 185, 186,
+    186, 187, 187, 189, 189, 190, 190, 196, 196, 198, 198, 228, 228, 232, 232,
+    233, 233, 1,   135, 137, 138, 139, 140, 141, 143, 147, 149, 150, 151, 152,
+    155, 157, 158, 165, 166, 168, 174, 175, 180, 182, 183, 188, 191, 197, 231,
+    239, -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  9,   9,   9,
+    9,   9,   9,   9,   9,   142, 142, 142, 142, 142, 142, 142, 142, 144, 144,
+    144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 148,
+    148, 148, 148, 148, 148, 148, 148, 159, 159, 159, 159, 159, 159, 159, 159,
+    171, 171, 171, 171, 171, 171, 171, 171, 206, 206, 206, 206, 206, 206, 206,
+    206, 215, 215, 215, 215, 215, 215, 215, 215, 225, 225, 225, 225, 225, 225,
+    225, 225, 236, 236, 236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237,
+    237, 237, 237, 199, 199, 199, 199, 207, 207, 207, 207, 234, 234, 234, 234,
+    235, 235, 235, 235, 192, 192, 193, 193, 200, 200, 201, 201, 202, 202, 205,
+    205, 210, 210, 213, 213, 218, 218, 219, 219, 238, 238, 240, 240, 242, 242,
+    243, 243, 255, 255, 203, 204, 211, 212, 214, 221, 222, 223, 241, 244, 245,
+    246, 247, 248, 250, 251, 252, 253, 254, -1,  -1,  -1,  -1,  -1,  -1,  -1,
+    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  2,   2,   2,   2,   2,   2,   2,
+    2,   3,   3,   3,   3,   3,   3,   3,   3,   4,   4,   4,   4,   4,   4,
+    4,   4,   5,   5,   5,   5,   5,   5,   5,   5,   6,   6,   6,   6,   6,
+    6,   6,   6,   7,   7,   7,   7,   7,   7,   7,   7,   8,   8,   8,   8,
+    8,   8,   8,   8,   11,  11,  11,  11,  11,  11,  11,  11,  12,  12,  12,
+    12,  12,  12,  12,  12,  14,  14,  14,  14,  14,  14,  14,  14,  15,  15,
+    15,  15,  15,  15,  15,  15,  16,  16,  16,  16,  16,  16,  16,  16,  17,
+    17,  17,  17,  17,  17,  17,  17,  18,  18,  18,  18,  18,  18,  18,  18,
+    19,  19,  19,  19,  19,  19,  19,  19,  20,  20,  20,  20,  20,  20,  20,
+    20,  21,  21,  21,  21,  21,  21,  21,  21,  23,  23,  23,  23,  23,  23,
+    23,  23,  24,  24,  24,  24,  24,  24,  24,  24,  25,  25,  25,  25,  25,
+    25,  25,  25,  26,  26,  26,  26,  26,  26,  26,  26,  27,  27,  27,  27,
+    27,  27,  27,  27,  28,  28,  28,  28,  28,  28,  28,  28,  29,  29,  29,
+    29,  29,  29,  29,  29,  30,  30,  30,  30,  30,  30,  30,  30,  31,  31,
+    31,  31,  31,  31,  31,  31,  127, 127, 127, 127, 127, 127, 127, 127, 220,
+    220, 220, 220, 220, 220, 220, 220, 249, 249, 249, 249, 249, 249, 249, 249,
+    10,  10,  13,  13,  22,  22,  256, 256, 67,  67,  67,  67,  67,  67,  67,
+    67,  68,  68,  68,  68,  68,  68,  68,  68,  95,  95,  95,  95,  95,  95,
+    95,  95,  98,  98,  98,  98,  98,  98,  98,  98,  100, 100, 100, 100, 100,
+    100, 100, 100, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103,
+    103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 108, 108, 108,
+    108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110,
+    110, 110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 112, 112, 114,
+    114, 114, 114, 114, 114, 114, 114, 117, 117, 117, 117, 117, 117, 117, 117,
+    58,  58,  58,  58,  66,  66,  66,  66,  67,  67,  67,  67,  68,  68,  68,
+    68,  69,  69,  69,  69,  70,  70,  70,  70,  71,  71,  71,  71,  72,  72,
+    72,  72,  73,  73,  73,  73,  74,  74,  74,  74,  75,  75,  75,  75,  76,
+    76,  76,  76,  77,  77,  77,  77,  78,  78,  78,  78,  79,  79,  79,  79,
+    80,  80,  80,  80,  81,  81,  81,  81,  82,  82,  82,  82,  83,  83,  83,
+    83,  84,  84,  84,  84,  85,  85,  85,  85,  86,  86,  86,  86,  87,  87,
+    87,  87,  89,  89,  89,  89,  106, 106, 106, 106, 107, 107, 107, 107, 113,
+    113, 113, 113, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 120,
+    121, 121, 121, 121, 122, 122, 122, 122, 38,  38,  42,  42,  44,  44,  59,
+    59,  88,  88,  90,  90,  -1,  -1,  -1,  -1,  33,  33,  33,  33,  33,  33,
+    33,  33,  34,  34,  34,  34,  34,  34,  34,  34,  40,  40,  40,  40,  40,
+    40,  40,  40,  41,  41,  41,  41,  41,  41,  41,  41,  63,  63,  63,  63,
+    63,  63,  63,  63,  39,  39,  39,  39,  43,  43,  43,  43,  124, 124, 124,
+    124, 35,  35,  62,  62,  0,   36,  64,  91,  93,  126, -1,  -1,  94,  94,
+    94,  94,  94,  94,  94,  94,  125, 125, 125, 125, 125, 125, 125, 125, 60,
+    60,  60,  60,  96,  96,  96,  96,  123, 123, 123, 123, -1,  -1,  -1,  -1,
+    92,  92,  92,  92,  195, 195, 195, 195, 208, 208, 208, 208, 128, 128, 130,
+    130, 131, 131, 162, 162, 184, 184, 194, 194, 224, 224, 226, 226, 153, 161,
+    167, 172, 176, 177, 179, 209, 216, 217, 227, 229, 230, -1,  -1,  -1,  -1,
+    -1,  -1,  -1,  129, 129, 129, 129, 129, 129, 129, 129, 132, 132, 132, 132,
+    132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134,
+    134, 134, 134, 134, 134, 136, 136, 136, 136, 136, 136, 136, 136, 146, 146,
+    146, 146, 146, 146, 146, 146, 154, 154, 154, 154, 154, 154, 154, 154, 156,
+    156, 156, 156, 156, 156, 156, 156, 160, 160, 160, 160, 160, 160, 160, 160,
+    163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164,
+    164, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170,
+    170, 170, 173, 173, 173, 173, 173, 173, 173, 173, 178, 178, 178, 178, 178,
+    178, 178, 178, 181, 181, 181, 181, 181, 181, 181, 181, 185, 185, 185, 185,
+    185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187,
+    187, 187, 187, 187, 187, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190,
+    190, 190, 190, 190, 190, 190, 196, 196, 196, 196, 196, 196, 196, 196, 198,
+    198, 198, 198, 198, 198, 198, 198, 228, 228, 228, 228, 228, 228, 228, 228,
+    232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, 233,
+    233, 1,   1,   1,   1,   135, 135, 135, 135, 137, 137, 137, 137, 138, 138,
+    138, 138, 139, 139, 139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 143,
+    143, 143, 143, 147, 147, 147, 147, 149, 149, 149, 149, 150, 150, 150, 150,
+    151, 151, 151, 151, 152, 152, 152, 152, 155, 155, 155, 155, 157, 157, 157,
+    157, 158, 158, 158, 158, 165, 165, 165, 165, 166, 166, 166, 166, 168, 168,
+    168, 168, 174, 174, 174, 174, 175, 175, 175, 175, 180, 180, 180, 180, 182,
+    182, 182, 182, 183, 183, 183, 183, 188, 188, 188, 188, 191, 191, 191, 191,
+    197, 197, 197, 197, 231, 231, 231, 231, 239, 239, 239, 239, 9,   9,   142,
+    142, 144, 144, 145, 145, 148, 148, 159, 159, 171, 171, 206, 206, 215, 215,
+    225, 225, 236, 236, 237, 237, 199, 207, 234, 235, 192, 192, 192, 192, 192,
+    192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 200, 200, 200, 200,
+    200, 200, 200, 200, 201, 201, 201, 201, 201, 201, 201, 201, 202, 202, 202,
+    202, 202, 202, 202, 202, 205, 205, 205, 205, 205, 205, 205, 205, 210, 210,
+    210, 210, 210, 210, 210, 210, 213, 213, 213, 213, 213, 213, 213, 213, 218,
+    218, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219,
+    238, 238, 238, 238, 238, 238, 238, 238, 240, 240, 240, 240, 240, 240, 240,
+    240, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243,
+    243, 243, 255, 255, 255, 255, 255, 255, 255, 255, 203, 203, 203, 203, 204,
+    204, 204, 204, 211, 211, 211, 211, 212, 212, 212, 212, 214, 214, 214, 214,
+    221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 241, 241, 241,
+    241, 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247,
+    247, 247, 248, 248, 248, 248, 250, 250, 250, 250, 251, 251, 251, 251, 252,
+    252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 2,   2,   3,   3,
+    4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   11,  11,  12,  12,  14,
+    14,  15,  15,  16,  16,  17,  17,  18,  18,  19,  19,  20,  20,  21,  21,
+    23,  23,  24,  24,  25,  25,  26,  26,  27,  27,  28,  28,  29,  29,  30,
+    30,  31,  31,  127, 127, 220, 220, 249, 249, -1,  -1,  10,  10,  10,  10,
+    10,  10,  10,  10,  13,  13,  13,  13,  13,  13,  13,  13,  22,  22,  22,
+    22,  22,  22,  22,  22,  256, 256, 256, 256, 256, 256, 256, 256, 45,  45,
+    45,  45,  45,  45,  45,  45,  46,  46,  46,  46,  46,  46,  46,  46,  47,
+    47,  47,  47,  47,  47,  47,  47,  51,  51,  51,  51,  51,  51,  51,  51,
+    52,  52,  52,  52,  52,  52,  52,  52,  53,  53,  53,  53,  53,  53,  53,
+    53,  54,  54,  54,  54,  54,  54,  54,  54,  55,  55,  55,  55,  55,  55,
+    55,  55,  56,  56,  56,  56,  56,  56,  56,  56,  57,  57,  57,  57,  57,
+    57,  57,  57,  50,  50,  50,  50,  50,  50,  50,  50,  97,  97,  97,  97,
+    97,  97,  97,  97,  99,  99,  99,  99,  99,  99,  99,  99,  101, 101, 101,
+    101, 101, 101, 101, 101, 105, 105, 105, 105, 105, 105, 105, 105, 111, 111,
+    111, 111, 111, 111, 111, 111, 115, 115, 115, 115, 115, 115, 115, 115, 116,
+    116, 116, 116, 116, 116, 116, 116, 32,  32,  32,  32,  37,  37,  37,  37,
+    45,  45,  45,  45,  46,  46,  46,  46,  47,  47,  47,  47,  51,  51,  51,
+    51,  52,  52,  52,  52,  53,  53,  53,  53,  54,  54,  54,  54,  55,  55,
+    55,  55,  56,  56,  56,  56,  57,  57,  57,  57,  61,  61,  61,  61,  65,
+    65,  65,  65,  95,  95,  95,  95,  98,  98,  98,  98,  100, 100, 100, 100,
+    102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 108, 108, 108,
+    108, 109, 109, 109, 109, 110, 110, 110, 110, 112, 112, 112, 112, 114, 114,
+    114, 114, 117, 117, 117, 117, 58,  58,  66,  66,  67,  67,  68,  68,  69,
+    69,  70,  70,  71,  71,  72,  72,  73,  73,  74,  74,  75,  75,  76,  76,
+    77,  77,  78,  78,  79,  79,  80,  80,  81,  81,  82,  82,  83,  83,  84,
+    84,  85,  85,  86,  86,  87,  87,  89,  89,  106, 106, 107, 107, 113, 113,
+    118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 38,  42,  44,  59,  88,
+    90,  -1,  -1,  33,  33,  33,  33,  34,  34,  34,  34,  40,  40,  40,  40,
+    41,  41,  41,  41,  63,  63,  63,  63,  39,  39,  43,  43,  124, 124, 35,
+    62,  -1,  -1,  -1,  -1,  0,   0,   0,   0,   0,   0,   0,   0,   36,  36,
+    36,  36,  36,  36,  36,  36,  64,  64,  64,  64,  64,  64,  64,  64,  91,
+    91,  91,  91,  91,  91,  91,  91,  93,  93,  93,  93,  93,  93,  93,  93,
+    126, 126, 126, 126, 126, 126, 126, 126, 94,  94,  94,  94,  125, 125, 125,
+    125, 60,  60,  96,  96,  123, 123, -1,  -1,  92,  92,  195, 195, 208, 208,
+    128, 130, 131, 162, 184, 194, 224, 226, -1,  -1,  153, 153, 153, 153, 153,
+    153, 153, 153, 161, 161, 161, 161, 161, 161, 161, 161, 167, 167, 167, 167,
+    167, 167, 167, 167, 172, 172, 172, 172, 172, 172, 172, 172, 176, 176, 176,
+    176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 179, 179,
+    179, 179, 179, 179, 179, 179, 209, 209, 209, 209, 209, 209, 209, 209, 216,
+    216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, 217, 217,
+    227, 227, 227, 227, 227, 227, 227, 227, 229, 229, 229, 229, 229, 229, 229,
+    229, 230, 230, 230, 230, 230, 230, 230, 230, 129, 129, 129, 129, 132, 132,
+    132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 136, 136, 136, 136, 146,
+    146, 146, 146, 154, 154, 154, 154, 156, 156, 156, 156, 160, 160, 160, 160,
+    163, 163, 163, 163, 164, 164, 164, 164, 169, 169, 169, 169, 170, 170, 170,
+    170, 173, 173, 173, 173, 178, 178, 178, 178, 181, 181, 181, 181, 185, 185,
+    185, 185, 186, 186, 186, 186, 187, 187, 187, 187, 189, 189, 189, 189, 190,
+    190, 190, 190, 196, 196, 196, 196, 198, 198, 198, 198, 228, 228, 228, 228,
+    232, 232, 232, 232, 233, 233, 233, 233, 1,   1,   135, 135, 137, 137, 138,
+    138, 139, 139, 140, 140, 141, 141, 143, 143, 147, 147, 149, 149, 150, 150,
+    151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 165, 165, 166, 166, 168,
+    168, 174, 174, 175, 175, 180, 180, 182, 182, 183, 183, 188, 188, 191, 191,
+    197, 197, 231, 231, 239, 239, 9,   142, 144, 145, 148, 159, 171, 206, 215,
+    225, 236, 237, -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  199, 199,
+    199, 199, 199, 199, 199, 199, 207, 207, 207, 207, 207, 207, 207, 207, 234,
+    234, 234, 234, 234, 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, 235,
+    192, 192, 192, 192, 193, 193, 193, 193, 200, 200, 200, 200, 201, 201, 201,
+    201, 202, 202, 202, 202, 205, 205, 205, 205, 210, 210, 210, 210, 213, 213,
+    213, 213, 218, 218, 218, 218, 219, 219, 219, 219, 238, 238, 238, 238, 240,
+    240, 240, 240, 242, 242, 242, 242, 243, 243, 243, 243, 255, 255, 255, 255,
+    203, 203, 204, 204, 211, 211, 212, 212, 214, 214, 221, 221, 222, 222, 223,
+    223, 241, 241, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 250, 250,
+    251, 251, 252, 252, 253, 253, 254, 254, 2,   3,   4,   5,   6,   7,   8,
+    11,  12,  14,  15,  16,  17,  18,  19,  20,  21,  23,  24,  25,  26,  27,
+    28,  29,  30,  31,  127, 220, 249, -1,  10,  10,  10,  10,  13,  13,  13,
+    13,  22,  22,  22,  22,  256, 256, 256, 256,
+};
+
+static const uint8_t inverse_base64[256] = {
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,  255,
+    255, 255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255,
+    255, 64,  255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
+    25,  255, 255, 255, 255, 255, 255, 26,  27,  28,  29,  30,  31,  32,  33,
+    34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
+    49,  50,  51,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255,
+};
+
+/* emission helpers */
+static grpc_error* on_hdr(grpc_chttp2_hpack_parser* p, grpc_mdelem md,
+                          int add_to_table) {
+  if (grpc_http_trace.enabled()) {
+    char* k = grpc_slice_to_c_string(GRPC_MDKEY(md));
+    char* v = nullptr;
+    if (grpc_is_binary_header(GRPC_MDKEY(md))) {
+      v = grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX);
+    } else {
+      v = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+    }
+    gpr_log(
+        GPR_INFO,
+        "Decode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d",
+        k, v, GRPC_MDELEM_IS_INTERNED(md), GRPC_MDELEM_STORAGE(md),
+        grpc_slice_is_interned(GRPC_MDKEY(md)),
+        grpc_slice_is_interned(GRPC_MDVALUE(md)));
+    gpr_free(k);
+    gpr_free(v);
+  }
+  if (add_to_table) {
+    GPR_ASSERT(GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_INTERNED ||
+               GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC);
+    grpc_error* err = grpc_chttp2_hptbl_add(&p->table, md);
+    if (err != GRPC_ERROR_NONE) return err;
+  }
+  if (p->on_header == nullptr) {
+    GRPC_MDELEM_UNREF(md);
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("on_header callback not set");
+  }
+  p->on_header(p->on_header_user_data, md);
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_slice take_string(grpc_chttp2_hpack_parser* p,
+                              grpc_chttp2_hpack_parser_string* str,
+                              bool intern) {
+  grpc_slice s;
+  if (!str->copied) {
+    if (intern) {
+      s = grpc_slice_intern(str->data.referenced);
+      grpc_slice_unref_internal(str->data.referenced);
+    } else {
+      s = str->data.referenced;
+    }
+    str->copied = true;
+    str->data.referenced = grpc_empty_slice();
+  } else if (intern) {
+    s = grpc_slice_intern(grpc_slice_from_static_buffer(
+        str->data.copied.str, str->data.copied.length));
+  } else {
+    s = grpc_slice_from_copied_buffer(str->data.copied.str,
+                                      str->data.copied.length);
+  }
+  str->data.copied.length = 0;
+  return s;
+}
+
+/* jump to the next state */
+static grpc_error* parse_next(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                              const uint8_t* end) {
+  p->state = *p->next_state++;
+  return p->state(p, cur, end);
+}
+
+/* begin parsing a header: all functionality is encoded into lookup tables
+   above */
+static grpc_error* parse_begin(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                               const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_begin;
+    return GRPC_ERROR_NONE;
+  }
+
+  return first_byte_action[first_byte_lut[*cur]](p, cur, end);
+}
+
+/* stream dependency and prioritization data: we just skip it */
+static grpc_error* parse_stream_weight(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_stream_weight;
+    return GRPC_ERROR_NONE;
+  }
+
+  return p->after_prioritization(p, cur + 1, end);
+}
+
+static grpc_error* parse_stream_dep3(grpc_chttp2_hpack_parser* p,
+                                     const uint8_t* cur, const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_stream_dep3;
+    return GRPC_ERROR_NONE;
+  }
+
+  return parse_stream_weight(p, cur + 1, end);
+}
+
+static grpc_error* parse_stream_dep2(grpc_chttp2_hpack_parser* p,
+                                     const uint8_t* cur, const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_stream_dep2;
+    return GRPC_ERROR_NONE;
+  }
+
+  return parse_stream_dep3(p, cur + 1, end);
+}
+
+static grpc_error* parse_stream_dep1(grpc_chttp2_hpack_parser* p,
+                                     const uint8_t* cur, const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_stream_dep1;
+    return GRPC_ERROR_NONE;
+  }
+
+  return parse_stream_dep2(p, cur + 1, end);
+}
+
+static grpc_error* parse_stream_dep0(grpc_chttp2_hpack_parser* p,
+                                     const uint8_t* cur, const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_stream_dep0;
+    return GRPC_ERROR_NONE;
+  }
+
+  return parse_stream_dep1(p, cur + 1, end);
+}
+
+/* emit an indexed field; jumps to begin the next field on completion */
+static grpc_error* finish_indexed_field(grpc_chttp2_hpack_parser* p,
+                                        const uint8_t* cur,
+                                        const uint8_t* end) {
+  grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+  if (GRPC_MDISNULL(md)) {
+    return grpc_error_set_int(
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "Invalid HPACK index received"),
+                           GRPC_ERROR_INT_INDEX,
+                           static_cast<intptr_t>(p->index)),
+        GRPC_ERROR_INT_SIZE, static_cast<intptr_t>(p->table.num_ents));
+  }
+  GRPC_MDELEM_REF(md);
+  GRPC_STATS_INC_HPACK_RECV_INDEXED();
+  grpc_error* err = on_hdr(p, md, 0);
+  if (err != GRPC_ERROR_NONE) return err;
+  return parse_begin(p, cur, end);
+}
+
+/* parse an indexed field with index < 127 */
+static grpc_error* parse_indexed_field(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end) {
+  p->dynamic_table_update_allowed = 0;
+  p->index = (*cur) & 0x7f;
+  return finish_indexed_field(p, cur + 1, end);
+}
+
+/* parse an indexed field with index >= 127 */
+static grpc_error* parse_indexed_field_x(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      finish_indexed_field};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  p->index = 0x7f;
+  p->parsing.value = &p->index;
+  return parse_value0(p, cur + 1, end);
+}
+
+/* finish a literal header with incremental indexing */
+static grpc_error* finish_lithdr_incidx(grpc_chttp2_hpack_parser* p,
+                                        const uint8_t* cur,
+                                        const uint8_t* end) {
+  grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+  GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX();
+  grpc_error* err =
+      on_hdr(p,
+             grpc_mdelem_from_slices(grpc_slice_ref_internal(GRPC_MDKEY(md)),
+                                     take_string(p, &p->value, true)),
+             1);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
+}
+
+/* finish a literal header with incremental indexing with no index */
+static grpc_error* finish_lithdr_incidx_v(grpc_chttp2_hpack_parser* p,
+                                          const uint8_t* cur,
+                                          const uint8_t* end) {
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V();
+  grpc_error* err =
+      on_hdr(p,
+             grpc_mdelem_from_slices(take_string(p, &p->key, true),
+                                     take_string(p, &p->value, true)),
+             1);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
+}
+
+/* parse a literal header with incremental indexing; index < 63 */
+static grpc_error* parse_lithdr_incidx(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_value_string_with_indexed_key, finish_lithdr_incidx};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  p->index = (*cur) & 0x3f;
+  return parse_string_prefix(p, cur + 1, end);
+}
+
+/* parse a literal header with incremental indexing; index >= 63 */
+static grpc_error* parse_lithdr_incidx_x(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_string_prefix, parse_value_string_with_indexed_key,
+      finish_lithdr_incidx};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  p->index = 0x3f;
+  p->parsing.value = &p->index;
+  return parse_value0(p, cur + 1, end);
+}
+
+/* parse a literal header with incremental indexing; index = 0 */
+static grpc_error* parse_lithdr_incidx_v(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_key_string, parse_string_prefix,
+      parse_value_string_with_literal_key, finish_lithdr_incidx_v};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  return parse_string_prefix(p, cur + 1, end);
+}
+
+/* finish a literal header without incremental indexing */
+static grpc_error* finish_lithdr_notidx(grpc_chttp2_hpack_parser* p,
+                                        const uint8_t* cur,
+                                        const uint8_t* end) {
+  grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+  GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX();
+  grpc_error* err =
+      on_hdr(p,
+             grpc_mdelem_from_slices(grpc_slice_ref_internal(GRPC_MDKEY(md)),
+                                     take_string(p, &p->value, false)),
+             0);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
+}
+
+/* finish a literal header without incremental indexing with index = 0 */
+static grpc_error* finish_lithdr_notidx_v(grpc_chttp2_hpack_parser* p,
+                                          const uint8_t* cur,
+                                          const uint8_t* end) {
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V();
+  grpc_error* err =
+      on_hdr(p,
+             grpc_mdelem_from_slices(take_string(p, &p->key, true),
+                                     take_string(p, &p->value, false)),
+             0);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
+}
+
+/* parse a literal header without incremental indexing; index < 15 */
+static grpc_error* parse_lithdr_notidx(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_value_string_with_indexed_key, finish_lithdr_notidx};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  p->index = (*cur) & 0xf;
+  return parse_string_prefix(p, cur + 1, end);
+}
+
+/* parse a literal header without incremental indexing; index >= 15 */
+static grpc_error* parse_lithdr_notidx_x(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_string_prefix, parse_value_string_with_indexed_key,
+      finish_lithdr_notidx};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  p->index = 0xf;
+  p->parsing.value = &p->index;
+  return parse_value0(p, cur + 1, end);
+}
+
+/* parse a literal header without incremental indexing; index == 0 */
+static grpc_error* parse_lithdr_notidx_v(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_key_string, parse_string_prefix,
+      parse_value_string_with_literal_key, finish_lithdr_notidx_v};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  return parse_string_prefix(p, cur + 1, end);
+}
+
+/* finish a literal header that is never indexed */
+static grpc_error* finish_lithdr_nvridx(grpc_chttp2_hpack_parser* p,
+                                        const uint8_t* cur,
+                                        const uint8_t* end) {
+  grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+  GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX();
+  grpc_error* err =
+      on_hdr(p,
+             grpc_mdelem_from_slices(grpc_slice_ref_internal(GRPC_MDKEY(md)),
+                                     take_string(p, &p->value, false)),
+             0);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
+}
+
+/* finish a literal header that is never indexed with an extra value */
+static grpc_error* finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser* p,
+                                          const uint8_t* cur,
+                                          const uint8_t* end) {
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V();
+  grpc_error* err =
+      on_hdr(p,
+             grpc_mdelem_from_slices(take_string(p, &p->key, true),
+                                     take_string(p, &p->value, false)),
+             0);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
+}
+
+/* parse a literal header that is never indexed; index < 15 */
+static grpc_error* parse_lithdr_nvridx(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_value_string_with_indexed_key, finish_lithdr_nvridx};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  p->index = (*cur) & 0xf;
+  return parse_string_prefix(p, cur + 1, end);
+}
+
+/* parse a literal header that is never indexed; index >= 15 */
+static grpc_error* parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_string_prefix, parse_value_string_with_indexed_key,
+      finish_lithdr_nvridx};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  p->index = 0xf;
+  p->parsing.value = &p->index;
+  return parse_value0(p, cur + 1, end);
+}
+
+/* parse a literal header that is never indexed; index == 0 */
+static grpc_error* parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser* p,
+                                         const uint8_t* cur,
+                                         const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      parse_key_string, parse_string_prefix,
+      parse_value_string_with_literal_key, finish_lithdr_nvridx_v};
+  p->dynamic_table_update_allowed = 0;
+  p->next_state = and_then;
+  return parse_string_prefix(p, cur + 1, end);
+}
+
+/* finish parsing a max table size change */
+static grpc_error* finish_max_tbl_size(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end) {
+  if (grpc_http_trace.enabled()) {
+    gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
+  }
+  grpc_error* err =
+      grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
+}
+
+/* parse a max table size change, max size < 15 */
+static grpc_error* parse_max_tbl_size(grpc_chttp2_hpack_parser* p,
+                                      const uint8_t* cur, const uint8_t* end) {
+  if (p->dynamic_table_update_allowed == 0) {
+    return parse_error(
+        p, cur, end,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "More than two max table size changes in a single frame"));
+  }
+  p->dynamic_table_update_allowed--;
+  p->index = (*cur) & 0x1f;
+  return finish_max_tbl_size(p, cur + 1, end);
+}
+
+/* parse a max table size change, max size >= 15 */
+static grpc_error* parse_max_tbl_size_x(grpc_chttp2_hpack_parser* p,
+                                        const uint8_t* cur,
+                                        const uint8_t* end) {
+  static const grpc_chttp2_hpack_parser_state and_then[] = {
+      finish_max_tbl_size};
+  if (p->dynamic_table_update_allowed == 0) {
+    return parse_error(
+        p, cur, end,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "More than two max table size changes in a single frame"));
+  }
+  p->dynamic_table_update_allowed--;
+  p->next_state = and_then;
+  p->index = 0x1f;
+  p->parsing.value = &p->index;
+  return parse_value0(p, cur + 1, end);
+}
+
+/* a parse error: jam the parse state into parse_error, and return error */
+static grpc_error* parse_error(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                               const uint8_t* end, grpc_error* err) {
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
+  if (p->last_error == GRPC_ERROR_NONE) {
+    p->last_error = GRPC_ERROR_REF(err);
+  }
+  p->state = still_parse_error;
+  return err;
+}
+
+static grpc_error* still_parse_error(grpc_chttp2_hpack_parser* p,
+                                     const uint8_t* cur, const uint8_t* end) {
+  return GRPC_ERROR_REF(p->last_error);
+}
+
+static grpc_error* parse_illegal_op(grpc_chttp2_hpack_parser* p,
+                                    const uint8_t* cur, const uint8_t* end) {
+  GPR_ASSERT(cur != end);
+  char* msg;
+  gpr_asprintf(&msg, "Illegal hpack op code %d", *cur);
+  grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+  gpr_free(msg);
+  return parse_error(p, cur, end, err);
+}
+
+/* parse the 1st byte of a varint into p->parsing.value
+   no overflow is possible */
+static grpc_error* parse_value0(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_value0;
+    return GRPC_ERROR_NONE;
+  }
+
+  *p->parsing.value += (*cur) & 0x7f;
+
+  if ((*cur) & 0x80) {
+    return parse_value1(p, cur + 1, end);
+  } else {
+    return parse_next(p, cur + 1, end);
+  }
+}
+
+/* parse the 2nd byte of a varint into p->parsing.value
+   no overflow is possible */
+static grpc_error* parse_value1(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_value1;
+    return GRPC_ERROR_NONE;
+  }
+
+  *p->parsing.value += ((static_cast<uint32_t>(*cur)) & 0x7f) << 7;
+
+  if ((*cur) & 0x80) {
+    return parse_value2(p, cur + 1, end);
+  } else {
+    return parse_next(p, cur + 1, end);
+  }
+}
+
+/* parse the 3rd byte of a varint into p->parsing.value
+   no overflow is possible */
+static grpc_error* parse_value2(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_value2;
+    return GRPC_ERROR_NONE;
+  }
+
+  *p->parsing.value += ((static_cast<uint32_t>(*cur)) & 0x7f) << 14;
+
+  if ((*cur) & 0x80) {
+    return parse_value3(p, cur + 1, end);
+  } else {
+    return parse_next(p, cur + 1, end);
+  }
+}
+
+/* parse the 4th byte of a varint into p->parsing.value
+   no overflow is possible */
+static grpc_error* parse_value3(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_value3;
+    return GRPC_ERROR_NONE;
+  }
+
+  *p->parsing.value += ((static_cast<uint32_t>(*cur)) & 0x7f) << 21;
+
+  if ((*cur) & 0x80) {
+    return parse_value4(p, cur + 1, end);
+  } else {
+    return parse_next(p, cur + 1, end);
+  }
+}
+
+/* parse the 5th byte of a varint into p->parsing.value
+   depending on the byte, we may overflow, and care must be taken */
+static grpc_error* parse_value4(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end) {
+  uint8_t c;
+  uint32_t cur_value;
+  uint32_t add_value;
+  char* msg;
+
+  if (cur == end) {
+    p->state = parse_value4;
+    return GRPC_ERROR_NONE;
+  }
+
+  c = (*cur) & 0x7f;
+  if (c > 0xf) {
+    goto error;
+  }
+
+  cur_value = *p->parsing.value;
+  add_value = (static_cast<uint32_t>(c)) << 28;
+  if (add_value > 0xffffffffu - cur_value) {
+    goto error;
+  }
+
+  *p->parsing.value = cur_value + add_value;
+
+  if ((*cur) & 0x80) {
+    return parse_value5up(p, cur + 1, end);
+  } else {
+    return parse_next(p, cur + 1, end);
+  }
+
+error:
+  gpr_asprintf(&msg,
+               "integer overflow in hpack integer decoding: have 0x%08x, "
+               "got byte 0x%02x on byte 5",
+               *p->parsing.value, *cur);
+  grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+  gpr_free(msg);
+  return parse_error(p, cur, end, err);
+}
+
+/* parse any trailing bytes in a varint: it's possible to append an arbitrary
+   number of 0x80's and not affect the value - a zero will terminate - and
+   anything else will overflow */
+static grpc_error* parse_value5up(grpc_chttp2_hpack_parser* p,
+                                  const uint8_t* cur, const uint8_t* end) {
+  while (cur != end && *cur == 0x80) {
+    ++cur;
+  }
+
+  if (cur == end) {
+    p->state = parse_value5up;
+    return GRPC_ERROR_NONE;
+  }
+
+  if (*cur == 0) {
+    return parse_next(p, cur + 1, end);
+  }
+
+  char* msg;
+  gpr_asprintf(&msg,
+               "integer overflow in hpack integer decoding: have 0x%08x, "
+               "got byte 0x%02x sometime after byte 5",
+               *p->parsing.value, *cur);
+  grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+  gpr_free(msg);
+  return parse_error(p, cur, end, err);
+}
+
+/* parse a string prefix */
+static grpc_error* parse_string_prefix(grpc_chttp2_hpack_parser* p,
+                                       const uint8_t* cur, const uint8_t* end) {
+  if (cur == end) {
+    p->state = parse_string_prefix;
+    return GRPC_ERROR_NONE;
+  }
+
+  p->strlen = (*cur) & 0x7f;
+  p->huff = (*cur) >> 7;
+  if (p->strlen == 0x7f) {
+    p->parsing.value = &p->strlen;
+    return parse_value0(p, cur + 1, end);
+  } else {
+    return parse_next(p, cur + 1, end);
+  }
+}
+
+/* append some bytes to a string */
+static void append_bytes(grpc_chttp2_hpack_parser_string* str,
+                         const uint8_t* data, size_t length) {
+  if (length == 0) return;
+  if (length + str->data.copied.length > str->data.copied.capacity) {
+    GPR_ASSERT(str->data.copied.length + length <= UINT32_MAX);
+    str->data.copied.capacity =
+        static_cast<uint32_t>(str->data.copied.length + length);
+    str->data.copied.str = static_cast<char*>(
+        gpr_realloc(str->data.copied.str, str->data.copied.capacity));
+  }
+  memcpy(str->data.copied.str + str->data.copied.length, data, length);
+  GPR_ASSERT(length <= UINT32_MAX - str->data.copied.length);
+  str->data.copied.length += static_cast<uint32_t>(length);
+}
+
+static grpc_error* append_string(grpc_chttp2_hpack_parser* p,
+                                 const uint8_t* cur, const uint8_t* end) {
+  grpc_chttp2_hpack_parser_string* str = p->parsing.str;
+  uint32_t bits;
+  uint8_t decoded[3];
+  switch (static_cast<binary_state>(p->binary)) {
+    case NOT_BINARY:
+      append_bytes(str, cur, static_cast<size_t>(end - cur));
+      return GRPC_ERROR_NONE;
+    case BINARY_BEGIN:
+      if (cur == end) {
+        p->binary = BINARY_BEGIN;
+        return GRPC_ERROR_NONE;
+      }
+      if (*cur == 0) {
+        /* 'true-binary' case */
+        ++cur;
+        p->binary = NOT_BINARY;
+        GRPC_STATS_INC_HPACK_RECV_BINARY();
+        append_bytes(str, cur, static_cast<size_t>(end - cur));
+        return GRPC_ERROR_NONE;
+      }
+      GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64();
+    /* fallthrough */
+    b64_byte0:
+    case B64_BYTE0:
+      if (cur == end) {
+        p->binary = B64_BYTE0;
+        return GRPC_ERROR_NONE;
+      }
+      bits = inverse_base64[*cur];
+      ++cur;
+      if (bits == 255)
+        return parse_error(
+            p, cur, end,
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character"));
+      else if (bits == 64)
+        goto b64_byte0;
+      p->base64_buffer = bits << 18;
+    /* fallthrough */
+    b64_byte1:
+    case B64_BYTE1:
+      if (cur == end) {
+        p->binary = B64_BYTE1;
+        return GRPC_ERROR_NONE;
+      }
+      bits = inverse_base64[*cur];
+      ++cur;
+      if (bits == 255)
+        return parse_error(
+            p, cur, end,
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character"));
+      else if (bits == 64)
+        goto b64_byte1;
+      p->base64_buffer |= bits << 12;
+    /* fallthrough */
+    b64_byte2:
+    case B64_BYTE2:
+      if (cur == end) {
+        p->binary = B64_BYTE2;
+        return GRPC_ERROR_NONE;
+      }
+      bits = inverse_base64[*cur];
+      ++cur;
+      if (bits == 255)
+        return parse_error(
+            p, cur, end,
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character"));
+      else if (bits == 64)
+        goto b64_byte2;
+      p->base64_buffer |= bits << 6;
+    /* fallthrough */
+    b64_byte3:
+    case B64_BYTE3:
+      if (cur == end) {
+        p->binary = B64_BYTE3;
+        return GRPC_ERROR_NONE;
+      }
+      bits = inverse_base64[*cur];
+      ++cur;
+      if (bits == 255)
+        return parse_error(
+            p, cur, end,
+            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character"));
+      else if (bits == 64)
+        goto b64_byte3;
+      p->base64_buffer |= bits;
+      bits = p->base64_buffer;
+      decoded[0] = static_cast<uint8_t>(bits >> 16);
+      decoded[1] = static_cast<uint8_t>(bits >> 8);
+      decoded[2] = static_cast<uint8_t>(bits);
+      append_bytes(str, decoded, 3);
+      goto b64_byte0;
+  }
+  GPR_UNREACHABLE_CODE(return parse_error(
+      p, cur, end,
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")));
+}
+
+static grpc_error* finish_str(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                              const uint8_t* end) {
+  uint8_t decoded[2];
+  uint32_t bits;
+  grpc_chttp2_hpack_parser_string* str = p->parsing.str;
+  switch (static_cast<binary_state>(p->binary)) {
+    case NOT_BINARY:
+      break;
+    case BINARY_BEGIN:
+      break;
+    case B64_BYTE0:
+      break;
+    case B64_BYTE1:
+      return parse_error(p, cur, end,
+                         GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                             "illegal base64 encoding")); /* illegal encoding */
+    case B64_BYTE2:
+      bits = p->base64_buffer;
+      if (bits & 0xffff) {
+        char* msg;
+        gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%04x",
+                     bits & 0xffff);
+        grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+        gpr_free(msg);
+        return parse_error(p, cur, end, err);
+      }
+      decoded[0] = static_cast<uint8_t>(bits >> 16);
+      append_bytes(str, decoded, 1);
+      break;
+    case B64_BYTE3:
+      bits = p->base64_buffer;
+      if (bits & 0xff) {
+        char* msg;
+        gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%02x",
+                     bits & 0xff);
+        grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+        gpr_free(msg);
+        return parse_error(p, cur, end, err);
+      }
+      decoded[0] = static_cast<uint8_t>(bits >> 16);
+      decoded[1] = static_cast<uint8_t>(bits >> 8);
+      append_bytes(str, decoded, 2);
+      break;
+  }
+  return GRPC_ERROR_NONE;
+}
+
+/* decode a nibble from a huffman encoded stream */
+static grpc_error* huff_nibble(grpc_chttp2_hpack_parser* p, uint8_t nibble) {
+  int16_t emit = emit_sub_tbl[16 * emit_tbl[p->huff_state] + nibble];
+  int16_t next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble];
+  if (emit != -1) {
+    if (emit >= 0 && emit < 256) {
+      uint8_t c = static_cast<uint8_t>(emit);
+      grpc_error* err = append_string(p, &c, (&c) + 1);
+      if (err != GRPC_ERROR_NONE) return err;
+    } else {
+      assert(emit == 256);
+    }
+  }
+  p->huff_state = next;
+  return GRPC_ERROR_NONE;
+}
+
+/* decode full bytes from a huffman encoded stream */
+static grpc_error* add_huff_bytes(grpc_chttp2_hpack_parser* p,
+                                  const uint8_t* cur, const uint8_t* end) {
+  for (; cur != end; ++cur) {
+    grpc_error* err = huff_nibble(p, *cur >> 4);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+    err = huff_nibble(p, *cur & 0xf);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  }
+  return GRPC_ERROR_NONE;
+}
+
+/* decode some string bytes based on the current decoding mode
+   (huffman or not) */
+static grpc_error* add_str_bytes(grpc_chttp2_hpack_parser* p,
+                                 const uint8_t* cur, const uint8_t* end) {
+  if (p->huff) {
+    return add_huff_bytes(p, cur, end);
+  } else {
+    return append_string(p, cur, end);
+  }
+}
+
+/* parse a string - tries to do large chunks at a time */
+static grpc_error* parse_string(grpc_chttp2_hpack_parser* p, const uint8_t* cur,
+                                const uint8_t* end) {
+  size_t remaining = p->strlen - p->strgot;
+  size_t given = static_cast<size_t>(end - cur);
+  if (remaining <= given) {
+    grpc_error* err = add_str_bytes(p, cur, cur + remaining);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+    err = finish_str(p, cur + remaining, end);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+    return parse_next(p, cur + remaining, end);
+  } else {
+    grpc_error* err = add_str_bytes(p, cur, cur + given);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+    GPR_ASSERT(given <= UINT32_MAX - p->strgot);
+    p->strgot += static_cast<uint32_t>(given);
+    p->state = parse_string;
+    return GRPC_ERROR_NONE;
+  }
+}
+
+/* begin parsing a string - performs setup, calls parse_string */
+static grpc_error* begin_parse_string(grpc_chttp2_hpack_parser* p,
+                                      const uint8_t* cur, const uint8_t* end,
+                                      uint8_t binary,
+                                      grpc_chttp2_hpack_parser_string* str) {
+  if (!p->huff && binary == NOT_BINARY &&
+      (end - cur) >= static_cast<intptr_t>(p->strlen) &&
+      p->current_slice_refcount != nullptr) {
+    GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED();
+    str->copied = false;
+    str->data.referenced.refcount = p->current_slice_refcount;
+    str->data.referenced.data.refcounted.bytes = const_cast<uint8_t*>(cur);
+    str->data.referenced.data.refcounted.length = p->strlen;
+    grpc_slice_ref_internal(str->data.referenced);
+    return parse_next(p, cur + p->strlen, end);
+  }
+  p->strgot = 0;
+  str->copied = true;
+  str->data.copied.length = 0;
+  p->parsing.str = str;
+  p->huff_state = 0;
+  p->binary = binary;
+  switch (p->binary) {
+    case NOT_BINARY:
+      if (p->huff) {
+        GRPC_STATS_INC_HPACK_RECV_HUFFMAN();
+      } else {
+        GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED();
+      }
+      break;
+    case BINARY_BEGIN:
+      /* stats incremented later: don't know true binary or not */
+      break;
+    default:
+      abort();
+  }
+  return parse_string(p, cur, end);
+}
+
+/* parse the key string */
+static grpc_error* parse_key_string(grpc_chttp2_hpack_parser* p,
+                                    const uint8_t* cur, const uint8_t* end) {
+  return begin_parse_string(p, cur, end, NOT_BINARY, &p->key);
+}
+
+/* check if a key represents a binary header or not */
+
+static bool is_binary_literal_header(grpc_chttp2_hpack_parser* p) {
+  return grpc_is_binary_header(
+      p->key.copied ? grpc_slice_from_static_buffer(p->key.data.copied.str,
+                                                    p->key.data.copied.length)
+                    : p->key.data.referenced);
+}
+
+static grpc_error* is_binary_indexed_header(grpc_chttp2_hpack_parser* p,
+                                            bool* is) {
+  grpc_mdelem elem = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+  if (GRPC_MDISNULL(elem)) {
+    return grpc_error_set_int(
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "Invalid HPACK index received"),
+                           GRPC_ERROR_INT_INDEX,
+                           static_cast<intptr_t>(p->index)),
+        GRPC_ERROR_INT_SIZE, static_cast<intptr_t>(p->table.num_ents));
+  }
+  *is = grpc_is_binary_header(GRPC_MDKEY(elem));
+  return GRPC_ERROR_NONE;
+}
+
+/* parse the value string */
+static grpc_error* parse_value_string(grpc_chttp2_hpack_parser* p,
+                                      const uint8_t* cur, const uint8_t* end,
+                                      bool is_binary) {
+  return begin_parse_string(p, cur, end, is_binary ? BINARY_BEGIN : NOT_BINARY,
+                            &p->value);
+}
+
+static grpc_error* parse_value_string_with_indexed_key(
+    grpc_chttp2_hpack_parser* p, const uint8_t* cur, const uint8_t* end) {
+  bool is_binary = false;
+  grpc_error* err = is_binary_indexed_header(p, &is_binary);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_value_string(p, cur, end, is_binary);
+}
+
+static grpc_error* parse_value_string_with_literal_key(
+    grpc_chttp2_hpack_parser* p, const uint8_t* cur, const uint8_t* end) {
+  return parse_value_string(p, cur, end, is_binary_literal_header(p));
+}
+
+/* PUBLIC INTERFACE */
+
+void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser* p) {
+  p->on_header = nullptr;
+  p->on_header_user_data = nullptr;
+  p->state = parse_begin;
+  p->key.data.referenced = grpc_empty_slice();
+  p->key.data.copied.str = nullptr;
+  p->key.data.copied.capacity = 0;
+  p->key.data.copied.length = 0;
+  p->value.data.referenced = grpc_empty_slice();
+  p->value.data.copied.str = nullptr;
+  p->value.data.copied.capacity = 0;
+  p->value.data.copied.length = 0;
+  p->dynamic_table_update_allowed = 2;
+  p->last_error = GRPC_ERROR_NONE;
+  grpc_chttp2_hptbl_init(&p->table);
+}
+
+void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser* p) {
+  p->after_prioritization = p->state;
+  p->state = parse_stream_dep0;
+}
+
+void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser* p) {
+  grpc_chttp2_hptbl_destroy(&p->table);
+  GRPC_ERROR_UNREF(p->last_error);
+  grpc_slice_unref_internal(p->key.data.referenced);
+  grpc_slice_unref_internal(p->value.data.referenced);
+  gpr_free(p->key.data.copied.str);
+  gpr_free(p->value.data.copied.str);
+}
+
+grpc_error* grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser* p,
+                                           grpc_slice slice) {
+/* max number of bytes to parse at a time... limits call stack depth on
+ * compilers without TCO */
+#define MAX_PARSE_LENGTH 1024
+  p->current_slice_refcount = slice.refcount;
+  uint8_t* start = GRPC_SLICE_START_PTR(slice);
+  uint8_t* end = GRPC_SLICE_END_PTR(slice);
+  grpc_error* error = GRPC_ERROR_NONE;
+  while (start != end && error == GRPC_ERROR_NONE) {
+    uint8_t* target = start + GPR_MIN(MAX_PARSE_LENGTH, end - start);
+    error = p->state(p, start, target);
+    start = target;
+  }
+  p->current_slice_refcount = nullptr;
+  return error;
+}
+
+typedef void (*maybe_complete_func_type)(grpc_chttp2_transport* t,
+                                         grpc_chttp2_stream* s);
+static const maybe_complete_func_type maybe_complete_funcs[] = {
+    grpc_chttp2_maybe_complete_recv_initial_metadata,
+    grpc_chttp2_maybe_complete_recv_trailing_metadata};
+
+static void force_client_rst_stream(void* sp, grpc_error* error) {
+  grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(sp);
+  grpc_chttp2_transport* t = s->t;
+  if (!s->write_closed) {
+    grpc_slice_buffer_add(
+        &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR,
+                                                &s->stats.outgoing));
+    grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM);
+    grpc_chttp2_mark_stream_closed(t, s, true, true, GRPC_ERROR_NONE);
+  }
+  GRPC_CHTTP2_STREAM_UNREF(s, "final_rst");
+}
+
+static void parse_stream_compression_md(grpc_chttp2_transport* t,
+                                        grpc_chttp2_stream* s,
+                                        grpc_metadata_batch* initial_metadata) {
+  if (initial_metadata->idx.named.content_encoding == nullptr ||
+      grpc_stream_compression_method_parse(
+          GRPC_MDVALUE(initial_metadata->idx.named.content_encoding->md), false,
+          &s->stream_decompression_method) == 0) {
+    s->stream_decompression_method =
+        GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
+  }
+}
+
+grpc_error* grpc_chttp2_header_parser_parse(void* hpack_parser,
+                                            grpc_chttp2_transport* t,
+                                            grpc_chttp2_stream* s,
+                                            grpc_slice slice, int is_last) {
+  GPR_TIMER_SCOPE("grpc_chttp2_header_parser_parse", 0);
+  grpc_chttp2_hpack_parser* parser =
+      static_cast<grpc_chttp2_hpack_parser*>(hpack_parser);
+  if (s != nullptr) {
+    s->stats.incoming.header_bytes += GRPC_SLICE_LENGTH(slice);
+  }
+  grpc_error* error = grpc_chttp2_hpack_parser_parse(parser, slice);
+  if (error != GRPC_ERROR_NONE) {
+    return error;
+  }
+  if (is_last) {
+    if (parser->is_boundary && parser->state != parse_begin) {
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "end of header frame not aligned with a hpack record boundary");
+    }
+    /* need to check for null stream: this can occur if we receive an invalid
+       stream id on a header */
+    if (s != nullptr) {
+      if (parser->is_boundary) {
+        if (s->header_frames_received == GPR_ARRAY_SIZE(s->metadata_buffer)) {
+          return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+              "Too many trailer frames");
+        }
+        /* Process stream compression md element if it exists */
+        if (s->header_frames_received ==
+            0) { /* Only acts on initial metadata */
+          parse_stream_compression_md(t, s, &s->metadata_buffer[0].batch);
+        }
+        s->published_metadata[s->header_frames_received] =
+            GRPC_METADATA_PUBLISHED_FROM_WIRE;
+        maybe_complete_funcs[s->header_frames_received](t, s);
+        s->header_frames_received++;
+      }
+      if (parser->is_eof) {
+        if (t->is_client && !s->write_closed) {
+          /* server eof ==> complete closure; we may need to forcefully close
+             the stream. Wait until the combiner lock is ready to be released
+             however -- it might be that we receive a RST_STREAM following this
+             and can avoid the extra write */
+          GRPC_CHTTP2_STREAM_REF(s, "final_rst");
+          GRPC_CLOSURE_SCHED(
+              GRPC_CLOSURE_CREATE(force_client_rst_stream, s,
+                                  grpc_combiner_finally_scheduler(t->combiner)),
+              GRPC_ERROR_NONE);
+        }
+        grpc_chttp2_mark_stream_closed(t, s, true, false, GRPC_ERROR_NONE);
+      }
+    }
+    parser->on_header = nullptr;
+    parser->on_header_user_data = nullptr;
+    parser->is_boundary = 0xde;
+    parser->is_eof = 0xde;
+    parser->dynamic_table_update_allowed = 2;
+  }
+  return GRPC_ERROR_NONE;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.h
new file mode 100644
index 0000000..3e05de4
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.h
@@ -0,0 +1,109 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/ext/transport/chttp2/transport/hpack_table.h"
+#include "src/core/lib/transport/metadata.h"
+
+typedef struct grpc_chttp2_hpack_parser grpc_chttp2_hpack_parser;
+
+typedef grpc_error* (*grpc_chttp2_hpack_parser_state)(
+    grpc_chttp2_hpack_parser* p, const uint8_t* beg, const uint8_t* end);
+
+typedef struct {
+  bool copied;
+  struct {
+    grpc_slice referenced;
+    struct {
+      char* str;
+      uint32_t length;
+      uint32_t capacity;
+    } copied;
+  } data;
+} grpc_chttp2_hpack_parser_string;
+
+struct grpc_chttp2_hpack_parser {
+  /* user specified callback for each header output */
+  void (*on_header)(void* user_data, grpc_mdelem md);
+  void* on_header_user_data;
+
+  grpc_error* last_error;
+
+  /* current parse state - or a function that implements it */
+  grpc_chttp2_hpack_parser_state state;
+  /* future states dependent on the opening op code */
+  const grpc_chttp2_hpack_parser_state* next_state;
+  /* what to do after skipping prioritization data */
+  grpc_chttp2_hpack_parser_state after_prioritization;
+  /* the refcount of the slice that we're currently parsing */
+  grpc_slice_refcount* current_slice_refcount;
+  /* the value we're currently parsing */
+  union {
+    uint32_t* value;
+    grpc_chttp2_hpack_parser_string* str;
+  } parsing;
+  /* string parameters for each chunk */
+  grpc_chttp2_hpack_parser_string key;
+  grpc_chttp2_hpack_parser_string value;
+  /* parsed index */
+  uint32_t index;
+  /* length of source bytes for the currently parsing string */
+  uint32_t strlen;
+  /* number of source bytes read for the currently parsing string */
+  uint32_t strgot;
+  /* huffman decoding state */
+  int16_t huff_state;
+  /* is the string being decoded binary? */
+  uint8_t binary;
+  /* is the current string huffman encoded? */
+  uint8_t huff;
+  /* is a dynamic table update allowed? */
+  uint8_t dynamic_table_update_allowed;
+  /* set by higher layers, used by grpc_chttp2_header_parser_parse to signal
+     it should append a metadata boundary at the end of frame */
+  uint8_t is_boundary;
+  uint8_t is_eof;
+  uint32_t base64_buffer;
+
+  /* hpack table */
+  grpc_chttp2_hptbl table;
+};
+
+void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser* p);
+void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser* p);
+
+void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser* p);
+
+grpc_error* grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser* p,
+                                           grpc_slice slice);
+
+/* wraps grpc_chttp2_hpack_parser_parse to provide a frame level parser for
+   the transport */
+grpc_error* grpc_chttp2_header_parser_parse(void* hpack_parser,
+                                            grpc_chttp2_transport* t,
+                                            grpc_chttp2_stream* s,
+                                            grpc_slice slice, int is_last);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.cc
new file mode 100644
index 0000000..fcfb018
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.cc
@@ -0,0 +1,397 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/hpack_table.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/murmur_hash.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+extern grpc_core::TraceFlag grpc_http_trace;
+
+static struct {
+  const char* key;
+  const char* value;
+} static_table[] = {
+    /* 0: */
+    {nullptr, nullptr},
+    /* 1: */
+    {":authority", ""},
+    /* 2: */
+    {":method", "GET"},
+    /* 3: */
+    {":method", "POST"},
+    /* 4: */
+    {":path", "/"},
+    /* 5: */
+    {":path", "/index.html"},
+    /* 6: */
+    {":scheme", "http"},
+    /* 7: */
+    {":scheme", "https"},
+    /* 8: */
+    {":status", "200"},
+    /* 9: */
+    {":status", "204"},
+    /* 10: */
+    {":status", "206"},
+    /* 11: */
+    {":status", "304"},
+    /* 12: */
+    {":status", "400"},
+    /* 13: */
+    {":status", "404"},
+    /* 14: */
+    {":status", "500"},
+    /* 15: */
+    {"accept-charset", ""},
+    /* 16: */
+    {"accept-encoding", "gzip, deflate"},
+    /* 17: */
+    {"accept-language", ""},
+    /* 18: */
+    {"accept-ranges", ""},
+    /* 19: */
+    {"accept", ""},
+    /* 20: */
+    {"access-control-allow-origin", ""},
+    /* 21: */
+    {"age", ""},
+    /* 22: */
+    {"allow", ""},
+    /* 23: */
+    {"authorization", ""},
+    /* 24: */
+    {"cache-control", ""},
+    /* 25: */
+    {"content-disposition", ""},
+    /* 26: */
+    {"content-encoding", ""},
+    /* 27: */
+    {"content-language", ""},
+    /* 28: */
+    {"content-length", ""},
+    /* 29: */
+    {"content-location", ""},
+    /* 30: */
+    {"content-range", ""},
+    /* 31: */
+    {"content-type", ""},
+    /* 32: */
+    {"cookie", ""},
+    /* 33: */
+    {"date", ""},
+    /* 34: */
+    {"etag", ""},
+    /* 35: */
+    {"expect", ""},
+    /* 36: */
+    {"expires", ""},
+    /* 37: */
+    {"from", ""},
+    /* 38: */
+    {"host", ""},
+    /* 39: */
+    {"if-match", ""},
+    /* 40: */
+    {"if-modified-since", ""},
+    /* 41: */
+    {"if-none-match", ""},
+    /* 42: */
+    {"if-range", ""},
+    /* 43: */
+    {"if-unmodified-since", ""},
+    /* 44: */
+    {"last-modified", ""},
+    /* 45: */
+    {"link", ""},
+    /* 46: */
+    {"location", ""},
+    /* 47: */
+    {"max-forwards", ""},
+    /* 48: */
+    {"proxy-authenticate", ""},
+    /* 49: */
+    {"proxy-authorization", ""},
+    /* 50: */
+    {"range", ""},
+    /* 51: */
+    {"referer", ""},
+    /* 52: */
+    {"refresh", ""},
+    /* 53: */
+    {"retry-after", ""},
+    /* 54: */
+    {"server", ""},
+    /* 55: */
+    {"set-cookie", ""},
+    /* 56: */
+    {"strict-transport-security", ""},
+    /* 57: */
+    {"transfer-encoding", ""},
+    /* 58: */
+    {"user-agent", ""},
+    /* 59: */
+    {"vary", ""},
+    /* 60: */
+    {"via", ""},
+    /* 61: */
+    {"www-authenticate", ""},
+};
+
+static uint32_t entries_for_bytes(uint32_t bytes) {
+  return (bytes + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) /
+         GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
+}
+
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl* tbl) {
+  size_t i;
+
+  memset(tbl, 0, sizeof(*tbl));
+  tbl->current_table_bytes = tbl->max_bytes =
+      GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE;
+  tbl->max_entries = tbl->cap_entries =
+      entries_for_bytes(tbl->current_table_bytes);
+  tbl->ents = static_cast<grpc_mdelem*>(
+      gpr_malloc(sizeof(*tbl->ents) * tbl->cap_entries));
+  memset(tbl->ents, 0, sizeof(*tbl->ents) * tbl->cap_entries);
+  for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
+    tbl->static_ents[i - 1] = grpc_mdelem_from_slices(
+        grpc_slice_intern(grpc_slice_from_static_string(static_table[i].key)),
+        grpc_slice_intern(
+            grpc_slice_from_static_string(static_table[i].value)));
+  }
+}
+
+void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl* tbl) {
+  size_t i;
+  for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
+    GRPC_MDELEM_UNREF(tbl->static_ents[i]);
+  }
+  for (i = 0; i < tbl->num_ents; i++) {
+    GRPC_MDELEM_UNREF(tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]);
+  }
+  gpr_free(tbl->ents);
+}
+
+grpc_mdelem grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl* tbl,
+                                     uint32_t tbl_index) {
+  /* Static table comes first, just return an entry from it */
+  if (tbl_index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) {
+    return tbl->static_ents[tbl_index - 1];
+  }
+  /* Otherwise, find the value in the list of valid entries */
+  tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1);
+  if (tbl_index < tbl->num_ents) {
+    uint32_t offset =
+        (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % tbl->cap_entries;
+    return tbl->ents[offset];
+  }
+  /* Invalid entry: return error */
+  return GRPC_MDNULL;
+}
+
+/* Evict one element from the table */
+static void evict1(grpc_chttp2_hptbl* tbl) {
+  grpc_mdelem first_ent = tbl->ents[tbl->first_ent];
+  size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(first_ent)) +
+                      GRPC_SLICE_LENGTH(GRPC_MDVALUE(first_ent)) +
+                      GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
+  GPR_ASSERT(elem_bytes <= tbl->mem_used);
+  tbl->mem_used -= static_cast<uint32_t>(elem_bytes);
+  tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries);
+  tbl->num_ents--;
+  GRPC_MDELEM_UNREF(first_ent);
+}
+
+static void rebuild_ents(grpc_chttp2_hptbl* tbl, uint32_t new_cap) {
+  grpc_mdelem* ents =
+      static_cast<grpc_mdelem*>(gpr_malloc(sizeof(*ents) * new_cap));
+  uint32_t i;
+
+  for (i = 0; i < tbl->num_ents; i++) {
+    ents[i] = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
+  }
+  gpr_free(tbl->ents);
+  tbl->ents = ents;
+  tbl->cap_entries = new_cap;
+  tbl->first_ent = 0;
+}
+
+void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl* tbl,
+                                     uint32_t max_bytes) {
+  if (tbl->max_bytes == max_bytes) {
+    return;
+  }
+  if (grpc_http_trace.enabled()) {
+    gpr_log(GPR_INFO, "Update hpack parser max size to %d", max_bytes);
+  }
+  while (tbl->mem_used > max_bytes) {
+    evict1(tbl);
+  }
+  tbl->max_bytes = max_bytes;
+}
+
+grpc_error* grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl* tbl,
+                                                     uint32_t bytes) {
+  if (tbl->current_table_bytes == bytes) {
+    return GRPC_ERROR_NONE;
+  }
+  if (bytes > tbl->max_bytes) {
+    char* msg;
+    gpr_asprintf(&msg,
+                 "Attempt to make hpack table %d bytes when max is %d bytes",
+                 bytes, tbl->max_bytes);
+    grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return err;
+  }
+  if (grpc_http_trace.enabled()) {
+    gpr_log(GPR_INFO, "Update hpack parser table size to %d", bytes);
+  }
+  while (tbl->mem_used > bytes) {
+    evict1(tbl);
+  }
+  tbl->current_table_bytes = bytes;
+  tbl->max_entries = entries_for_bytes(bytes);
+  if (tbl->max_entries > tbl->cap_entries) {
+    rebuild_ents(tbl, GPR_MAX(tbl->max_entries, 2 * tbl->cap_entries));
+  } else if (tbl->max_entries < tbl->cap_entries / 3) {
+    uint32_t new_cap = GPR_MAX(tbl->max_entries, 16u);
+    if (new_cap != tbl->cap_entries) {
+      rebuild_ents(tbl, new_cap);
+    }
+  }
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_chttp2_hptbl_add(grpc_chttp2_hptbl* tbl, grpc_mdelem md) {
+  /* determine how many bytes of buffer this entry represents */
+  size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(md)) +
+                      GRPC_SLICE_LENGTH(GRPC_MDVALUE(md)) +
+                      GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
+
+  if (tbl->current_table_bytes > tbl->max_bytes) {
+    char* msg;
+    gpr_asprintf(
+        &msg,
+        "HPACK max table size reduced to %d but not reflected by hpack "
+        "stream (still at %d)",
+        tbl->max_bytes, tbl->current_table_bytes);
+    grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return err;
+  }
+
+  /* we can't add elements bigger than the max table size */
+  if (elem_bytes > tbl->current_table_bytes) {
+    /* HPACK draft 10 section 4.4 states:
+     * If the size of the new entry is less than or equal to the maximum
+     * size, that entry is added to the table.  It is not an error to
+     * attempt to add an entry that is larger than the maximum size; an
+     * attempt to add an entry larger than the entire table causes
+     * the table
+     * to be emptied of all existing entries, and results in an
+     * empty table.
+     */
+    while (tbl->num_ents) {
+      evict1(tbl);
+    }
+    return GRPC_ERROR_NONE;
+  }
+
+  /* evict entries to ensure no overflow */
+  while (elem_bytes >
+         static_cast<size_t>(tbl->current_table_bytes) - tbl->mem_used) {
+    evict1(tbl);
+  }
+
+  /* copy the finalized entry in */
+  tbl->ents[(tbl->first_ent + tbl->num_ents) % tbl->cap_entries] =
+      GRPC_MDELEM_REF(md);
+
+  /* update accounting values */
+  tbl->num_ents++;
+  tbl->mem_used += static_cast<uint32_t>(elem_bytes);
+  return GRPC_ERROR_NONE;
+}
+
+grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
+    const grpc_chttp2_hptbl* tbl, grpc_mdelem md) {
+  grpc_chttp2_hptbl_find_result r = {0, 0};
+  uint32_t i;
+
+  /* See if the string is in the static table */
+  for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
+    grpc_mdelem ent = tbl->static_ents[i];
+    if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue;
+    r.index = i + 1u;
+    r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent));
+    if (r.has_value) return r;
+  }
+
+  /* Scan the dynamic table */
+  for (i = 0; i < tbl->num_ents; i++) {
+    uint32_t idx = static_cast<uint32_t>(tbl->num_ents - i +
+                                         GRPC_CHTTP2_LAST_STATIC_ENTRY);
+    grpc_mdelem ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
+    if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue;
+    r.index = idx;
+    r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent));
+    if (r.has_value) return r;
+  }
+
+  return r;
+}
+
+static size_t get_base64_encoded_size(size_t raw_length) {
+  static const uint8_t tail_xtra[3] = {0, 2, 3};
+  return raw_length / 3 * 4 + tail_xtra[raw_length % 3];
+}
+
+size_t grpc_chttp2_get_size_in_hpack_table(grpc_mdelem elem,
+                                           bool use_true_binary_metadata) {
+  size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
+  size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem));
+  if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
+    return overhead_and_key + (use_true_binary_metadata
+                                   ? value_len + 1
+                                   : get_base64_encoded_size(value_len));
+  } else {
+    return overhead_and_key + value_len;
+  }
+}
+
+uint8_t grpc_chttp2_get_static_hpack_table_index(grpc_mdelem md) {
+  if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) {
+    uint8_t index = GRPC_MDELEM_DATA(md) - grpc_static_mdelem_table;
+    if (index < GRPC_CHTTP2_LAST_STATIC_ENTRY) {
+      return index + 1;  // Hpack static metadata element indices start at 1
+    }
+  }
+  return 0;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.h
new file mode 100644
index 0000000..a0ffc6f
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.h
@@ -0,0 +1,104 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/transport/metadata.h"
+
+/* HPACK header table */
+
+/* last index in the static table */
+#define GRPC_CHTTP2_LAST_STATIC_ENTRY 61
+
+/* Initial table size as per the spec */
+#define GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE 4096
+/* Maximum table size that we'll use */
+#define GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE
+/* Per entry overhead bytes as per the spec */
+#define GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD 32
+#if 0
+/* Maximum number of entries we could possibly fit in the table, given defined
+   overheads */
+#define GRPC_CHTTP2_MAX_TABLE_COUNT                                            \
+  ((GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / \
+   GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD)
+#endif
+
+/* hpack decoder table */
+typedef struct {
+  /* the first used entry in ents */
+  uint32_t first_ent;
+  /* how many entries are in the table */
+  uint32_t num_ents;
+  /* the amount of memory used by the table, according to the hpack algorithm */
+  uint32_t mem_used;
+  /* the max memory allowed to be used by the table, according to the hpack
+     algorithm */
+  uint32_t max_bytes;
+  /* the currently agreed size of the table, according to the hpack algorithm */
+  uint32_t current_table_bytes;
+  /* Maximum number of entries we could possibly fit in the table, given defined
+     overheads */
+  uint32_t max_entries;
+  /* Number of entries allocated in ents */
+  uint32_t cap_entries;
+  /* a circular buffer of headers - this is stored in the opposite order to
+     what hpack specifies, in order to simplify table management a little...
+     meaning lookups need to SUBTRACT from the end position */
+  grpc_mdelem* ents;
+  grpc_mdelem static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY];
+} grpc_chttp2_hptbl;
+
+/* initialize a hpack table */
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl* tbl);
+void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl* tbl);
+void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl* tbl,
+                                     uint32_t max_bytes);
+grpc_error* grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl* tbl,
+                                                     uint32_t bytes);
+
+/* lookup a table entry based on its hpack index */
+grpc_mdelem grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl* tbl,
+                                     uint32_t index);
+/* add a table entry to the index */
+grpc_error* grpc_chttp2_hptbl_add(grpc_chttp2_hptbl* tbl,
+                                  grpc_mdelem md) GRPC_MUST_USE_RESULT;
+
+size_t grpc_chttp2_get_size_in_hpack_table(grpc_mdelem elem,
+                                           bool use_true_binary_metadata);
+
+/* Returns the static hpack table index that corresponds to /a elem. Returns 0
+  if /a elem is not statically stored or if it is not in the static hpack
+  table */
+uint8_t grpc_chttp2_get_static_hpack_table_index(grpc_mdelem md);
+
+/* Find a key/value pair in the table... returns the index in the table of the
+   most similar entry, or 0 if the value was not found */
+typedef struct {
+  uint32_t index;
+  int has_value;
+} grpc_chttp2_hptbl_find_result;
+grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
+    const grpc_chttp2_hptbl* tbl, grpc_mdelem md);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_tables.txt b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_tables.txt
new file mode 100644
index 0000000..08842a0
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_tables.txt
@@ -0,0 +1,66 @@
+Static table, from the spec:
+          +-------+-----------------------------+---------------+
+          | Index | Header Name                 | Header Value  |
+          +-------+-----------------------------+---------------+
+          | 1     | :authority                  |               |
+          | 2     | :method                     | GET           |
+          | 3     | :method                     | POST          |
+          | 4     | :path                       | /             |
+          | 5     | :path                       | /index.html   |
+          | 6     | :scheme                     | http          |
+          | 7     | :scheme                     | https         |
+          | 8     | :status                     | 200           |
+          | 9     | :status                     | 204           |
+          | 10    | :status                     | 206           |
+          | 11    | :status                     | 304           |
+          | 12    | :status                     | 400           |
+          | 13    | :status                     | 404           |
+          | 14    | :status                     | 500           |
+          | 15    | accept-charset              |               |
+          | 16    | accept-encoding             | gzip, deflate |
+          | 17    | accept-language             |               |
+          | 18    | accept-ranges               |               |
+          | 19    | accept                      |               |
+          | 20    | access-control-allow-origin |               |
+          | 21    | age                         |               |
+          | 22    | allow                       |               |
+          | 23    | authorization               |               |
+          | 24    | cache-control               |               |
+          | 25    | content-disposition         |               |
+          | 26    | content-encoding            |               |
+          | 27    | content-language            |               |
+          | 28    | content-length              |               |
+          | 29    | content-location            |               |
+          | 30    | content-range               |               |
+          | 31    | content-type                |               |
+          | 32    | cookie                      |               |
+          | 33    | date                        |               |
+          | 34    | etag                        |               |
+          | 35    | expect                      |               |
+          | 36    | expires                     |               |
+          | 37    | from                        |               |
+          | 38    | host                        |               |
+          | 39    | if-match                    |               |
+          | 40    | if-modified-since           |               |
+          | 41    | if-none-match               |               |
+          | 42    | if-range                    |               |
+          | 43    | if-unmodified-since         |               |
+          | 44    | last-modified               |               |
+          | 45    | link                        |               |
+          | 46    | location                    |               |
+          | 47    | max-forwards                |               |
+          | 48    | proxy-authenticate          |               |
+          | 49    | proxy-authorization         |               |
+          | 50    | range                       |               |
+          | 51    | referer                     |               |
+          | 52    | refresh                     |               |
+          | 53    | retry-after                 |               |
+          | 54    | server                      |               |
+          | 55    | set-cookie                  |               |
+          | 56    | strict-transport-security   |               |
+          | 57    | transfer-encoding           |               |
+          | 58    | user-agent                  |               |
+          | 59    | vary                        |               |
+          | 60    | via                         |               |
+          | 61    | www-authenticate            |               |
+          +-------+-----------------------------+---------------+
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.cc
new file mode 100644
index 0000000..294ee8e
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ */
+
+/*
+ * Automatically generated by tools/codegen/core/gen_settings_ids.py
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/http2_settings.h"
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+const uint16_t grpc_setting_id_to_wire_id[] = {1, 2, 3, 4, 5, 6, 65027};
+
+bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id* out) {
+  uint32_t i = wire_id - 1;
+  uint32_t x = i % 256;
+  uint32_t y = i / 256;
+  uint32_t h = x;
+  switch (y) {
+    case 254:
+      h += 4;
+      break;
+  }
+  *out = static_cast<grpc_chttp2_setting_id>(h);
+  return h < GPR_ARRAY_SIZE(grpc_setting_id_to_wire_id) &&
+         grpc_setting_id_to_wire_id[h] == wire_id;
+}
+
+const grpc_chttp2_setting_parameters
+    grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = {
+        {"HEADER_TABLE_SIZE", 4096u, 0u, 4294967295u,
+         GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
+        {"ENABLE_PUSH", 1u, 0u, 1u, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
+         GRPC_HTTP2_PROTOCOL_ERROR},
+        {"MAX_CONCURRENT_STREAMS", 4294967295u, 0u, 4294967295u,
+         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
+        {"INITIAL_WINDOW_SIZE", 65535u, 0u, 2147483647u,
+         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
+         GRPC_HTTP2_FLOW_CONTROL_ERROR},
+        {"MAX_FRAME_SIZE", 16384u, 16384u, 16777215u,
+         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
+        {"MAX_HEADER_LIST_SIZE", 16777216u, 0u, 16777216u,
+         GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
+        {"GRPC_ALLOW_TRUE_BINARY_METADATA", 0u, 0u, 1u,
+         GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
+};
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.h
new file mode 100644
index 0000000..07ce062
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ */
+
+/*
+ * Automatically generated by tools/codegen/core/gen_settings_ids.py
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef enum {
+  GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0,               /* wire id 1 */
+  GRPC_CHTTP2_SETTINGS_ENABLE_PUSH = 1,                     /* wire id 2 */
+  GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 2,          /* wire id 3 */
+  GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 3,             /* wire id 4 */
+  GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE = 4,                  /* wire id 5 */
+  GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 5,            /* wire id 6 */
+  GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA = 6, /* wire id 65027 */
+} grpc_chttp2_setting_id;
+
+#define GRPC_CHTTP2_NUM_SETTINGS 7
+
+extern const uint16_t grpc_setting_id_to_wire_id[];
+
+bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id* out);
+
+typedef enum {
+  GRPC_CHTTP2_CLAMP_INVALID_VALUE,
+  GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE
+} grpc_chttp2_invalid_value_behavior;
+
+typedef struct {
+  const char* name;
+  uint32_t default_value;
+  uint32_t min_value;
+  uint32_t max_value;
+  grpc_chttp2_invalid_value_behavior invalid_value_behavior;
+  uint32_t error_value;
+} grpc_chttp2_setting_parameters;
+
+extern const grpc_chttp2_setting_parameters
+    grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS];
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.cc
new file mode 100644
index 0000000..813e4c9
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.cc
@@ -0,0 +1,92 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/huffsyms.h"
+
+/* Constants pulled from the HPACK spec, and converted to C using the vim
+   command:
+   :%s/.*   \([0-9a-f]\+\)  \[ *\([0-9]\+\)\]/{0x\1, \2},/g */
+const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS] = {
+    {0x1ff8, 13},     {0x7fffd8, 23},   {0xfffffe2, 28},  {0xfffffe3, 28},
+    {0xfffffe4, 28},  {0xfffffe5, 28},  {0xfffffe6, 28},  {0xfffffe7, 28},
+    {0xfffffe8, 28},  {0xffffea, 24},   {0x3ffffffc, 30}, {0xfffffe9, 28},
+    {0xfffffea, 28},  {0x3ffffffd, 30}, {0xfffffeb, 28},  {0xfffffec, 28},
+    {0xfffffed, 28},  {0xfffffee, 28},  {0xfffffef, 28},  {0xffffff0, 28},
+    {0xffffff1, 28},  {0xffffff2, 28},  {0x3ffffffe, 30}, {0xffffff3, 28},
+    {0xffffff4, 28},  {0xffffff5, 28},  {0xffffff6, 28},  {0xffffff7, 28},
+    {0xffffff8, 28},  {0xffffff9, 28},  {0xffffffa, 28},  {0xffffffb, 28},
+    {0x14, 6},        {0x3f8, 10},      {0x3f9, 10},      {0xffa, 12},
+    {0x1ff9, 13},     {0x15, 6},        {0xf8, 8},        {0x7fa, 11},
+    {0x3fa, 10},      {0x3fb, 10},      {0xf9, 8},        {0x7fb, 11},
+    {0xfa, 8},        {0x16, 6},        {0x17, 6},        {0x18, 6},
+    {0x0, 5},         {0x1, 5},         {0x2, 5},         {0x19, 6},
+    {0x1a, 6},        {0x1b, 6},        {0x1c, 6},        {0x1d, 6},
+    {0x1e, 6},        {0x1f, 6},        {0x5c, 7},        {0xfb, 8},
+    {0x7ffc, 15},     {0x20, 6},        {0xffb, 12},      {0x3fc, 10},
+    {0x1ffa, 13},     {0x21, 6},        {0x5d, 7},        {0x5e, 7},
+    {0x5f, 7},        {0x60, 7},        {0x61, 7},        {0x62, 7},
+    {0x63, 7},        {0x64, 7},        {0x65, 7},        {0x66, 7},
+    {0x67, 7},        {0x68, 7},        {0x69, 7},        {0x6a, 7},
+    {0x6b, 7},        {0x6c, 7},        {0x6d, 7},        {0x6e, 7},
+    {0x6f, 7},        {0x70, 7},        {0x71, 7},        {0x72, 7},
+    {0xfc, 8},        {0x73, 7},        {0xfd, 8},        {0x1ffb, 13},
+    {0x7fff0, 19},    {0x1ffc, 13},     {0x3ffc, 14},     {0x22, 6},
+    {0x7ffd, 15},     {0x3, 5},         {0x23, 6},        {0x4, 5},
+    {0x24, 6},        {0x5, 5},         {0x25, 6},        {0x26, 6},
+    {0x27, 6},        {0x6, 5},         {0x74, 7},        {0x75, 7},
+    {0x28, 6},        {0x29, 6},        {0x2a, 6},        {0x7, 5},
+    {0x2b, 6},        {0x76, 7},        {0x2c, 6},        {0x8, 5},
+    {0x9, 5},         {0x2d, 6},        {0x77, 7},        {0x78, 7},
+    {0x79, 7},        {0x7a, 7},        {0x7b, 7},        {0x7ffe, 15},
+    {0x7fc, 11},      {0x3ffd, 14},     {0x1ffd, 13},     {0xffffffc, 28},
+    {0xfffe6, 20},    {0x3fffd2, 22},   {0xfffe7, 20},    {0xfffe8, 20},
+    {0x3fffd3, 22},   {0x3fffd4, 22},   {0x3fffd5, 22},   {0x7fffd9, 23},
+    {0x3fffd6, 22},   {0x7fffda, 23},   {0x7fffdb, 23},   {0x7fffdc, 23},
+    {0x7fffdd, 23},   {0x7fffde, 23},   {0xffffeb, 24},   {0x7fffdf, 23},
+    {0xffffec, 24},   {0xffffed, 24},   {0x3fffd7, 22},   {0x7fffe0, 23},
+    {0xffffee, 24},   {0x7fffe1, 23},   {0x7fffe2, 23},   {0x7fffe3, 23},
+    {0x7fffe4, 23},   {0x1fffdc, 21},   {0x3fffd8, 22},   {0x7fffe5, 23},
+    {0x3fffd9, 22},   {0x7fffe6, 23},   {0x7fffe7, 23},   {0xffffef, 24},
+    {0x3fffda, 22},   {0x1fffdd, 21},   {0xfffe9, 20},    {0x3fffdb, 22},
+    {0x3fffdc, 22},   {0x7fffe8, 23},   {0x7fffe9, 23},   {0x1fffde, 21},
+    {0x7fffea, 23},   {0x3fffdd, 22},   {0x3fffde, 22},   {0xfffff0, 24},
+    {0x1fffdf, 21},   {0x3fffdf, 22},   {0x7fffeb, 23},   {0x7fffec, 23},
+    {0x1fffe0, 21},   {0x1fffe1, 21},   {0x3fffe0, 22},   {0x1fffe2, 21},
+    {0x7fffed, 23},   {0x3fffe1, 22},   {0x7fffee, 23},   {0x7fffef, 23},
+    {0xfffea, 20},    {0x3fffe2, 22},   {0x3fffe3, 22},   {0x3fffe4, 22},
+    {0x7ffff0, 23},   {0x3fffe5, 22},   {0x3fffe6, 22},   {0x7ffff1, 23},
+    {0x3ffffe0, 26},  {0x3ffffe1, 26},  {0xfffeb, 20},    {0x7fff1, 19},
+    {0x3fffe7, 22},   {0x7ffff2, 23},   {0x3fffe8, 22},   {0x1ffffec, 25},
+    {0x3ffffe2, 26},  {0x3ffffe3, 26},  {0x3ffffe4, 26},  {0x7ffffde, 27},
+    {0x7ffffdf, 27},  {0x3ffffe5, 26},  {0xfffff1, 24},   {0x1ffffed, 25},
+    {0x7fff2, 19},    {0x1fffe3, 21},   {0x3ffffe6, 26},  {0x7ffffe0, 27},
+    {0x7ffffe1, 27},  {0x3ffffe7, 26},  {0x7ffffe2, 27},  {0xfffff2, 24},
+    {0x1fffe4, 21},   {0x1fffe5, 21},   {0x3ffffe8, 26},  {0x3ffffe9, 26},
+    {0xffffffd, 28},  {0x7ffffe3, 27},  {0x7ffffe4, 27},  {0x7ffffe5, 27},
+    {0xfffec, 20},    {0xfffff3, 24},   {0xfffed, 20},    {0x1fffe6, 21},
+    {0x3fffe9, 22},   {0x1fffe7, 21},   {0x1fffe8, 21},   {0x7ffff3, 23},
+    {0x3fffea, 22},   {0x3fffeb, 22},   {0x1ffffee, 25},  {0x1ffffef, 25},
+    {0xfffff4, 24},   {0xfffff5, 24},   {0x3ffffea, 26},  {0x7ffff4, 23},
+    {0x3ffffeb, 26},  {0x7ffffe6, 27},  {0x3ffffec, 26},  {0x3ffffed, 26},
+    {0x7ffffe7, 27},  {0x7ffffe8, 27},  {0x7ffffe9, 27},  {0x7ffffea, 27},
+    {0x7ffffeb, 27},  {0xffffffe, 28},  {0x7ffffec, 27},  {0x7ffffed, 27},
+    {0x7ffffee, 27},  {0x7ffffef, 27},  {0x7fffff0, 27},  {0x3ffffee, 26},
+    {0x3fffffff, 30},
+};
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.h
new file mode 100644
index 0000000..2e2a5da
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HUFFSYMS_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HUFFSYMS_H
+
+/* HPACK static huffman table */
+
+#define GRPC_CHTTP2_NUM_HUFFSYMS 257
+
+typedef struct {
+  unsigned bits;
+  unsigned length;
+} grpc_chttp2_huffsym;
+
+extern const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS];
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HUFFSYMS_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.cc
new file mode 100644
index 0000000..dca15e7
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.cc
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h"
+
+#include <string.h>
+
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+grpc_error* grpc_chttp2_incoming_metadata_buffer_add(
+    grpc_chttp2_incoming_metadata_buffer* buffer, grpc_mdelem elem) {
+  buffer->size += GRPC_MDELEM_LENGTH(elem);
+  return grpc_metadata_batch_add_tail(
+      &buffer->batch,
+      static_cast<grpc_linked_mdelem*>(
+          gpr_arena_alloc(buffer->arena, sizeof(grpc_linked_mdelem))),
+      elem);
+}
+
+grpc_error* grpc_chttp2_incoming_metadata_buffer_replace_or_add(
+    grpc_chttp2_incoming_metadata_buffer* buffer, grpc_mdelem elem) {
+  for (grpc_linked_mdelem* l = buffer->batch.list.head; l != nullptr;
+       l = l->next) {
+    if (grpc_slice_eq(GRPC_MDKEY(l->md), GRPC_MDKEY(elem))) {
+      GRPC_MDELEM_UNREF(l->md);
+      l->md = elem;
+      return GRPC_ERROR_NONE;
+    }
+  }
+  return grpc_chttp2_incoming_metadata_buffer_add(buffer, elem);
+}
+
+void grpc_chttp2_incoming_metadata_buffer_set_deadline(
+    grpc_chttp2_incoming_metadata_buffer* buffer, grpc_millis deadline) {
+  buffer->batch.deadline = deadline;
+}
+
+void grpc_chttp2_incoming_metadata_buffer_publish(
+    grpc_chttp2_incoming_metadata_buffer* buffer, grpc_metadata_batch* batch) {
+  grpc_metadata_batch_move(&buffer->batch, batch);
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.h
new file mode 100644
index 0000000..c551b3c
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/transport.h"
+
+struct grpc_chttp2_incoming_metadata_buffer {
+  grpc_chttp2_incoming_metadata_buffer(gpr_arena* arena) : arena(arena) {
+    grpc_metadata_batch_init(&batch);
+    batch.deadline = GRPC_MILLIS_INF_FUTURE;
+  }
+  ~grpc_chttp2_incoming_metadata_buffer() {
+    grpc_metadata_batch_destroy(&batch);
+  }
+
+  gpr_arena* arena;
+  grpc_metadata_batch batch;
+  size_t size = 0;  // total size of metadata
+};
+
+void grpc_chttp2_incoming_metadata_buffer_publish(
+    grpc_chttp2_incoming_metadata_buffer* buffer, grpc_metadata_batch* batch);
+
+grpc_error* grpc_chttp2_incoming_metadata_buffer_add(
+    grpc_chttp2_incoming_metadata_buffer* buffer,
+    grpc_mdelem elem) GRPC_MUST_USE_RESULT;
+grpc_error* grpc_chttp2_incoming_metadata_buffer_replace_or_add(
+    grpc_chttp2_incoming_metadata_buffer* buffer,
+    grpc_mdelem elem) GRPC_MUST_USE_RESULT;
+void grpc_chttp2_incoming_metadata_buffer_set_deadline(
+    grpc_chttp2_incoming_metadata_buffer* buffer, grpc_millis deadline);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/internal.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/internal.h
new file mode 100644
index 0000000..341f5b3
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/internal.h
@@ -0,0 +1,851 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <assert.h>
+#include <stdbool.h>
+
+#include "src/core/ext/transport/chttp2/transport/flow_control.h"
+#include "src/core/ext/transport/chttp2/transport/frame.h"
+#include "src/core/ext/transport/chttp2/transport/frame_data.h"
+#include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
+#include "src/core/ext/transport/chttp2/transport/frame_ping.h"
+#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
+#include "src/core/ext/transport/chttp2/transport/frame_settings.h"
+#include "src/core/ext/transport/chttp2/transport/frame_window_update.h"
+#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
+#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/channel/channelz.h"
+#include "src/core/lib/compression/stream_compression.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/transport_impl.h"
+
+namespace grpc_core {
+class ContextList;
+}
+
+/* streams are kept in various linked lists depending on what things need to
+   happen to them... this enum labels each list */
+typedef enum {
+  GRPC_CHTTP2_LIST_WRITABLE,
+  GRPC_CHTTP2_LIST_WRITING,
+  GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT,
+  GRPC_CHTTP2_LIST_STALLED_BY_STREAM,
+  /** streams that are waiting to start because there are too many concurrent
+      streams on the connection */
+  GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY,
+  STREAM_LIST_COUNT /* must be last */
+} grpc_chttp2_stream_list_id;
+
+typedef enum {
+  GRPC_CHTTP2_WRITE_STATE_IDLE,
+  GRPC_CHTTP2_WRITE_STATE_WRITING,
+  GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE,
+} grpc_chttp2_write_state;
+
+typedef enum {
+  GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY,
+  GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT,
+} grpc_chttp2_optimization_target;
+
+typedef enum {
+  GRPC_CHTTP2_PCL_INITIATE = 0,
+  GRPC_CHTTP2_PCL_NEXT,
+  GRPC_CHTTP2_PCL_INFLIGHT,
+  GRPC_CHTTP2_PCL_COUNT /* must be last */
+} grpc_chttp2_ping_closure_list;
+
+typedef enum {
+  GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE,
+  GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM,
+  GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE,
+  GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA,
+  GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA,
+  GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING,
+  GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS,
+  GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT,
+  GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM,
+  GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API,
+  GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL,
+  GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL,
+  GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS,
+  GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING,
+  GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE,
+  GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING,
+  GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING,
+  GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED,
+  GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE,
+  GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM,
+} grpc_chttp2_initiate_write_reason;
+
+const char* grpc_chttp2_initiate_write_reason_string(
+    grpc_chttp2_initiate_write_reason reason);
+
+typedef struct {
+  grpc_closure_list lists[GRPC_CHTTP2_PCL_COUNT] = {};
+  uint64_t inflight_id = 0;
+} grpc_chttp2_ping_queue;
+
+typedef struct {
+  int max_pings_without_data;
+  int max_ping_strikes;
+  grpc_millis min_sent_ping_interval_without_data;
+  grpc_millis min_recv_ping_interval_without_data;
+} grpc_chttp2_repeated_ping_policy;
+
+typedef struct {
+  grpc_millis last_ping_sent_time;
+  int pings_before_data_required;
+  grpc_timer delayed_ping_timer;
+  bool is_delayed_ping_timer_set;
+} grpc_chttp2_repeated_ping_state;
+
+typedef struct {
+  grpc_millis last_ping_recv_time;
+  int ping_strikes;
+} grpc_chttp2_server_ping_recv_state;
+
+/* deframer state for the overall http2 stream of bytes */
+typedef enum {
+  /* prefix: one entry per http2 connection prefix byte */
+  GRPC_DTS_CLIENT_PREFIX_0 = 0,
+  GRPC_DTS_CLIENT_PREFIX_1,
+  GRPC_DTS_CLIENT_PREFIX_2,
+  GRPC_DTS_CLIENT_PREFIX_3,
+  GRPC_DTS_CLIENT_PREFIX_4,
+  GRPC_DTS_CLIENT_PREFIX_5,
+  GRPC_DTS_CLIENT_PREFIX_6,
+  GRPC_DTS_CLIENT_PREFIX_7,
+  GRPC_DTS_CLIENT_PREFIX_8,
+  GRPC_DTS_CLIENT_PREFIX_9,
+  GRPC_DTS_CLIENT_PREFIX_10,
+  GRPC_DTS_CLIENT_PREFIX_11,
+  GRPC_DTS_CLIENT_PREFIX_12,
+  GRPC_DTS_CLIENT_PREFIX_13,
+  GRPC_DTS_CLIENT_PREFIX_14,
+  GRPC_DTS_CLIENT_PREFIX_15,
+  GRPC_DTS_CLIENT_PREFIX_16,
+  GRPC_DTS_CLIENT_PREFIX_17,
+  GRPC_DTS_CLIENT_PREFIX_18,
+  GRPC_DTS_CLIENT_PREFIX_19,
+  GRPC_DTS_CLIENT_PREFIX_20,
+  GRPC_DTS_CLIENT_PREFIX_21,
+  GRPC_DTS_CLIENT_PREFIX_22,
+  GRPC_DTS_CLIENT_PREFIX_23,
+  /* frame header byte 0... */
+  /* must follow from the prefix states */
+  GRPC_DTS_FH_0,
+  GRPC_DTS_FH_1,
+  GRPC_DTS_FH_2,
+  GRPC_DTS_FH_3,
+  GRPC_DTS_FH_4,
+  GRPC_DTS_FH_5,
+  GRPC_DTS_FH_6,
+  GRPC_DTS_FH_7,
+  /* ... frame header byte 8 */
+  GRPC_DTS_FH_8,
+  /* inside a http2 frame */
+  GRPC_DTS_FRAME
+} grpc_chttp2_deframe_transport_state;
+
+typedef struct {
+  grpc_chttp2_stream* head;
+  grpc_chttp2_stream* tail;
+} grpc_chttp2_stream_list;
+
+typedef struct {
+  grpc_chttp2_stream* next;
+  grpc_chttp2_stream* prev;
+} grpc_chttp2_stream_link;
+
+/* We keep several sets of connection wide parameters */
+typedef enum {
+  /* The settings our peer has asked for (and we have acked) */
+  GRPC_PEER_SETTINGS = 0,
+  /* The settings we'd like to have */
+  GRPC_LOCAL_SETTINGS,
+  /* The settings we've published to our peer */
+  GRPC_SENT_SETTINGS,
+  /* The settings the peer has acked */
+  GRPC_ACKED_SETTINGS,
+  GRPC_NUM_SETTING_SETS
+} grpc_chttp2_setting_set;
+
+typedef enum {
+  GRPC_CHTTP2_NO_GOAWAY_SEND,
+  GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED,
+  GRPC_CHTTP2_GOAWAY_SENT,
+} grpc_chttp2_sent_goaway_state;
+
+typedef struct grpc_chttp2_write_cb {
+  int64_t call_at_byte;
+  grpc_closure* closure;
+  struct grpc_chttp2_write_cb* next;
+} grpc_chttp2_write_cb;
+
+namespace grpc_core {
+
+class Chttp2IncomingByteStream : public ByteStream {
+ public:
+  Chttp2IncomingByteStream(grpc_chttp2_transport* transport,
+                           grpc_chttp2_stream* stream, uint32_t frame_size,
+                           uint32_t flags);
+
+  void Orphan() override;
+
+  bool Next(size_t max_size_hint, grpc_closure* on_complete) override;
+  grpc_error* Pull(grpc_slice* slice) override;
+  void Shutdown(grpc_error* error) override;
+
+  // TODO(roth): When I converted this class to C++, I wanted to make it
+  // inherit from RefCounted or InternallyRefCounted instead of continuing
+  // to use its own custom ref-counting code.  However, that would require
+  // using multiple inheritence, which sucks in general.  And to make matters
+  // worse, it causes problems with our New<> and Delete<> wrappers.
+  // Specifically, unless RefCounted is first in the list of parent classes,
+  // it will see a different value of the address of the object than the one
+  // we actually allocated, in which case gpr_free() will be called on a
+  // different address than the one we got from gpr_malloc(), thus causing a
+  // crash.  Given the fragility of depending on that, as well as a desire to
+  // avoid multiple inheritence in general, I've decided to leave this
+  // alone for now.  We can revisit this once we're able to link against
+  // libc++, at which point we can eliminate New<> and Delete<> and
+  // switch to std::shared_ptr<>.
+  void Ref() { refs_.Ref(); }
+  void Unref() {
+    if (refs_.Unref()) {
+      grpc_core::Delete(this);
+    }
+  }
+
+  void PublishError(grpc_error* error);
+
+  grpc_error* Push(grpc_slice slice, grpc_slice* slice_out);
+
+  grpc_error* Finished(grpc_error* error, bool reset_on_error);
+
+  uint32_t remaining_bytes() const { return remaining_bytes_; }
+
+ private:
+  static void NextLocked(void* arg, grpc_error* error_ignored);
+  static void OrphanLocked(void* arg, grpc_error* error_ignored);
+
+  void MaybeCreateStreamDecompressionCtx();
+
+  grpc_chttp2_transport* transport_;  // Immutable.
+  grpc_chttp2_stream* stream_;        // Immutable.
+
+  grpc_core::RefCount refs_;
+
+  /* Accessed only by transport thread when stream->pending_byte_stream == false
+   * Accessed only by application thread when stream->pending_byte_stream ==
+   * true */
+  uint32_t remaining_bytes_;
+
+  /* Accessed only by transport thread when stream->pending_byte_stream == false
+   * Accessed only by application thread when stream->pending_byte_stream ==
+   * true */
+  struct {
+    grpc_closure closure;
+    size_t max_size_hint;
+    grpc_closure* on_complete;
+  } next_action_;
+  grpc_closure destroy_action_;
+};
+
+}  // namespace grpc_core
+
+typedef enum {
+  GRPC_CHTTP2_KEEPALIVE_STATE_WAITING,
+  GRPC_CHTTP2_KEEPALIVE_STATE_PINGING,
+  GRPC_CHTTP2_KEEPALIVE_STATE_DYING,
+  GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED,
+} grpc_chttp2_keepalive_state;
+
+struct grpc_chttp2_transport {
+  grpc_chttp2_transport(const grpc_channel_args* channel_args,
+                        grpc_endpoint* ep, bool is_client,
+                        grpc_resource_user* resource_user);
+  ~grpc_chttp2_transport();
+
+  grpc_transport base; /* must be first */
+  grpc_core::RefCount refs;
+  grpc_endpoint* ep;
+  char* peer_string;
+
+  grpc_resource_user* resource_user;
+
+  grpc_combiner* combiner;
+
+  grpc_closure* notify_on_receive_settings = nullptr;
+
+  /** write execution state of the transport */
+  grpc_chttp2_write_state write_state = GRPC_CHTTP2_WRITE_STATE_IDLE;
+  /** is this the first write in a series of writes?
+      set when we initiate writing from idle, cleared when we
+      initiate writing from writing+more */
+  bool is_first_write_in_batch = false;
+
+  /** is the transport destroying itself? */
+  uint8_t destroying = false;
+  /** has the upper layer closed the transport? */
+  grpc_error* closed_with_error = GRPC_ERROR_NONE;
+
+  /** is there a read request to the endpoint outstanding? */
+  uint8_t endpoint_reading = 1;
+
+  grpc_chttp2_optimization_target opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY;
+
+  /** various lists of streams */
+  grpc_chttp2_stream_list lists[STREAM_LIST_COUNT] = {};
+
+  /** maps stream id to grpc_chttp2_stream objects */
+  grpc_chttp2_stream_map stream_map;
+
+  grpc_closure write_action_begin_locked;
+  grpc_closure write_action;
+  grpc_closure write_action_end_locked;
+
+  grpc_closure read_action_locked;
+
+  /** incoming read bytes */
+  grpc_slice_buffer read_buffer;
+
+  /** address to place a newly accepted stream - set and unset by
+      grpc_chttp2_parsing_accept_stream; used by init_stream to
+      publish the accepted server stream */
+  grpc_chttp2_stream** accepting_stream = nullptr;
+
+  struct {
+    /* accept stream callback */
+    void (*accept_stream)(void* user_data, grpc_transport* transport,
+                          const void* server_data);
+    void* accept_stream_user_data;
+
+    /** connectivity tracking */
+    grpc_connectivity_state_tracker state_tracker;
+  } channel_callback;
+
+  /** data to write now */
+  grpc_slice_buffer outbuf;
+  /** hpack encoding */
+  grpc_chttp2_hpack_compressor hpack_compressor;
+  /** is this a client? */
+  bool is_client;
+
+  /** data to write next write */
+  grpc_slice_buffer qbuf;
+
+  /** how much data are we willing to buffer when the WRITE_BUFFER_HINT is set?
+   */
+  uint32_t write_buffer_size = grpc_core::chttp2::kDefaultWindow;
+
+  /** Set to a grpc_error object if a goaway frame is received. By default, set
+   * to GRPC_ERROR_NONE */
+  grpc_error* goaway_error = GRPC_ERROR_NONE;
+
+  grpc_chttp2_sent_goaway_state sent_goaway_state = GRPC_CHTTP2_NO_GOAWAY_SEND;
+
+  /** are the local settings dirty and need to be sent? */
+  bool dirtied_local_settings = true;
+  /** have local settings been sent? */
+  bool sent_local_settings = false;
+  /** bitmask of setting indexes to send out
+      Hack: it's common for implementations to assume 65536 bytes initial send
+      window -- this should by rights be 0 */
+  uint32_t force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
+  /** settings values */
+  uint32_t settings[GRPC_NUM_SETTING_SETS][GRPC_CHTTP2_NUM_SETTINGS];
+
+  /** what is the next stream id to be allocated by this peer?
+      copied to next_stream_id in parsing when parsing commences */
+  uint32_t next_stream_id = 0;
+
+  /** last new stream id */
+  uint32_t last_new_stream_id = 0;
+
+  /** ping queues for various ping insertion points */
+  grpc_chttp2_ping_queue ping_queue = grpc_chttp2_ping_queue();
+  grpc_chttp2_repeated_ping_policy ping_policy;
+  grpc_chttp2_repeated_ping_state ping_state;
+  uint64_t ping_ctr = 0; /* unique id for pings */
+  grpc_closure retry_initiate_ping_locked;
+
+  /** ping acks */
+  size_t ping_ack_count = 0;
+  size_t ping_ack_capacity = 0;
+  uint64_t* ping_acks = nullptr;
+  grpc_chttp2_server_ping_recv_state ping_recv_state;
+
+  /** parser for headers */
+  grpc_chttp2_hpack_parser hpack_parser;
+  /** simple one shot parsers */
+  union {
+    grpc_chttp2_window_update_parser window_update;
+    grpc_chttp2_settings_parser settings;
+    grpc_chttp2_ping_parser ping;
+    grpc_chttp2_rst_stream_parser rst_stream;
+  } simple;
+  /** parser for goaway frames */
+  grpc_chttp2_goaway_parser goaway_parser;
+
+  grpc_core::PolymorphicManualConstructor<
+      grpc_core::chttp2::TransportFlowControlBase,
+      grpc_core::chttp2::TransportFlowControl,
+      grpc_core::chttp2::TransportFlowControlDisabled>
+      flow_control;
+  /** initial window change. This is tracked as we parse settings frames from
+   * the remote peer. If there is a positive delta, then we will make all
+   * streams readable since they may have become unstalled */
+  int64_t initial_window_update = 0;
+
+  /* deframing */
+  grpc_chttp2_deframe_transport_state deframe_state = GRPC_DTS_CLIENT_PREFIX_0;
+  uint8_t incoming_frame_type = 0;
+  uint8_t incoming_frame_flags = 0;
+  uint8_t header_eof = 0;
+  bool is_first_frame = true;
+  uint32_t expect_continuation_stream_id = 0;
+  uint32_t incoming_frame_size = 0;
+  uint32_t incoming_stream_id = 0;
+
+  /* active parser */
+  void* parser_data = nullptr;
+  grpc_chttp2_stream* incoming_stream = nullptr;
+  grpc_error* (*parser)(void* parser_user_data, grpc_chttp2_transport* t,
+                        grpc_chttp2_stream* s, grpc_slice slice, int is_last);
+
+  grpc_chttp2_write_cb* write_cb_pool = nullptr;
+
+  /* bdp estimator */
+  grpc_closure next_bdp_ping_timer_expired_locked;
+  grpc_closure start_bdp_ping_locked;
+  grpc_closure finish_bdp_ping_locked;
+
+  /* if non-NULL, close the transport with this error when writes are finished
+   */
+  grpc_error* close_transport_on_writes_finished = GRPC_ERROR_NONE;
+
+  /* a list of closures to run after writes are finished */
+  grpc_closure_list run_after_write = GRPC_CLOSURE_LIST_INIT;
+
+  /* buffer pool state */
+  /** have we scheduled a benign cleanup? */
+  bool benign_reclaimer_registered = false;
+  /** have we scheduled a destructive cleanup? */
+  bool destructive_reclaimer_registered = false;
+  /** benign cleanup closure */
+  grpc_closure benign_reclaimer_locked;
+  /** destructive cleanup closure */
+  grpc_closure destructive_reclaimer_locked;
+
+  /* next bdp ping timer */
+  bool have_next_bdp_ping_timer = false;
+  grpc_timer next_bdp_ping_timer;
+
+  /* keep-alive ping support */
+  /** Closure to initialize a keepalive ping */
+  grpc_closure init_keepalive_ping_locked;
+  /** Closure to run when the keepalive ping is sent */
+  grpc_closure start_keepalive_ping_locked;
+  /** Cousure to run when the keepalive ping ack is received */
+  grpc_closure finish_keepalive_ping_locked;
+  /** Closrue to run when the keepalive ping timeouts */
+  grpc_closure keepalive_watchdog_fired_locked;
+  /** timer to initiate ping events */
+  grpc_timer keepalive_ping_timer;
+  /** watchdog to kill the transport when waiting for the keepalive ping */
+  grpc_timer keepalive_watchdog_timer;
+  /** time duration in between pings */
+  grpc_millis keepalive_time;
+  /** grace period for a ping to complete before watchdog kicks in */
+  grpc_millis keepalive_timeout;
+  /** if keepalive pings are allowed when there's no outstanding streams */
+  bool keepalive_permit_without_calls = false;
+  /** keep-alive state machine state */
+  grpc_chttp2_keepalive_state keepalive_state;
+  grpc_core::ContextList* cl = nullptr;
+  grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode> channelz_socket;
+  uint32_t num_messages_in_next_write = 0;
+};
+
+typedef enum {
+  GRPC_METADATA_NOT_PUBLISHED,
+  GRPC_METADATA_SYNTHESIZED_FROM_FAKE,
+  GRPC_METADATA_PUBLISHED_FROM_WIRE,
+  GPRC_METADATA_PUBLISHED_AT_CLOSE
+} grpc_published_metadata_method;
+
+struct grpc_chttp2_stream {
+  grpc_chttp2_stream(grpc_chttp2_transport* t, grpc_stream_refcount* refcount,
+                     const void* server_data, gpr_arena* arena);
+  ~grpc_chttp2_stream();
+
+  void* context;
+  grpc_chttp2_transport* t;
+  grpc_stream_refcount* refcount;
+
+  grpc_closure destroy_stream;
+  grpc_closure* destroy_stream_arg;
+
+  grpc_chttp2_stream_link links[STREAM_LIST_COUNT];
+  uint8_t included[STREAM_LIST_COUNT] = {};
+
+  /** HTTP2 stream id for this stream, or zero if one has not been assigned */
+  uint32_t id = 0;
+
+  /** things the upper layers would like to send */
+  grpc_metadata_batch* send_initial_metadata = nullptr;
+  grpc_closure* send_initial_metadata_finished = nullptr;
+  grpc_metadata_batch* send_trailing_metadata = nullptr;
+  grpc_closure* send_trailing_metadata_finished = nullptr;
+
+  grpc_core::OrphanablePtr<grpc_core::ByteStream> fetching_send_message;
+  uint32_t fetched_send_message_length = 0;
+  grpc_slice fetching_slice = grpc_empty_slice();
+  int64_t next_message_end_offset;
+  int64_t flow_controlled_bytes_written = 0;
+  int64_t flow_controlled_bytes_flowed = 0;
+  grpc_closure complete_fetch_locked;
+  grpc_closure* fetching_send_message_finished = nullptr;
+
+  grpc_metadata_batch* recv_initial_metadata;
+  grpc_closure* recv_initial_metadata_ready = nullptr;
+  bool* trailing_metadata_available = nullptr;
+  grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message;
+  grpc_closure* recv_message_ready = nullptr;
+  grpc_metadata_batch* recv_trailing_metadata;
+  grpc_closure* recv_trailing_metadata_finished = nullptr;
+
+  grpc_transport_stream_stats* collecting_stats = nullptr;
+  grpc_transport_stream_stats stats = grpc_transport_stream_stats();
+
+  /** Is this stream closed for writing. */
+  bool write_closed = false;
+  /** Is this stream reading half-closed. */
+  bool read_closed = false;
+  /** Are all published incoming byte streams closed. */
+  bool all_incoming_byte_streams_finished = false;
+  /** Has this stream seen an error.
+      If true, then pending incoming frames can be thrown away. */
+  bool seen_error = false;
+  /** Are we buffering writes on this stream? If yes, we won't become writable
+      until there's enough queued up in the flow_controlled_buffer */
+  bool write_buffering = false;
+  /** Has trailing metadata been received. */
+  bool received_trailing_metadata = false;
+
+  /* have we sent or received the EOS bit? */
+  bool eos_received = false;
+  bool eos_sent = false;
+
+  /** the error that resulted in this stream being read-closed */
+  grpc_error* read_closed_error = GRPC_ERROR_NONE;
+  /** the error that resulted in this stream being write-closed */
+  grpc_error* write_closed_error = GRPC_ERROR_NONE;
+
+  grpc_published_metadata_method published_metadata[2] = {};
+  bool final_metadata_requested = false;
+
+  grpc_chttp2_incoming_metadata_buffer metadata_buffer[2];
+
+  grpc_slice_buffer frame_storage; /* protected by t combiner */
+
+  /* Accessed only by transport thread when stream->pending_byte_stream == false
+   * Accessed only by application thread when stream->pending_byte_stream ==
+   * true */
+  grpc_slice_buffer unprocessed_incoming_frames_buffer;
+  grpc_closure* on_next = nullptr;  /* protected by t combiner */
+  bool pending_byte_stream = false; /* protected by t combiner */
+  // cached length of buffer to be used by the transport thread in cases where
+  // stream->pending_byte_stream == true. The value is saved before
+  // application threads are allowed to modify
+  // unprocessed_incoming_frames_buffer
+  size_t unprocessed_incoming_frames_buffer_cached_length = 0;
+  grpc_closure reset_byte_stream;
+  grpc_error* byte_stream_error = GRPC_ERROR_NONE; /* protected by t combiner */
+  bool received_last_frame = false;                /* protected by t combiner */
+
+  grpc_millis deadline = GRPC_MILLIS_INF_FUTURE;
+
+  /** saw some stream level error */
+  grpc_error* forced_close_error = GRPC_ERROR_NONE;
+  /** how many header frames have we received? */
+  uint8_t header_frames_received = 0;
+  /** parsing state for data frames */
+  /* Accessed only by transport thread when stream->pending_byte_stream == false
+   * Accessed only by application thread when stream->pending_byte_stream ==
+   * true */
+  grpc_chttp2_data_parser data_parser;
+  /** number of bytes received - reset at end of parse thread execution */
+  int64_t received_bytes = 0;
+
+  bool sent_initial_metadata = false;
+  bool sent_trailing_metadata = false;
+
+  grpc_core::PolymorphicManualConstructor<
+      grpc_core::chttp2::StreamFlowControlBase,
+      grpc_core::chttp2::StreamFlowControl,
+      grpc_core::chttp2::StreamFlowControlDisabled>
+      flow_control;
+
+  grpc_slice_buffer flow_controlled_buffer;
+
+  grpc_chttp2_write_cb* on_flow_controlled_cbs = nullptr;
+  grpc_chttp2_write_cb* on_write_finished_cbs = nullptr;
+  grpc_chttp2_write_cb* finish_after_write = nullptr;
+  size_t sending_bytes = 0;
+
+  /* Stream compression method to be used. */
+  grpc_stream_compression_method stream_compression_method =
+      GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
+  /* Stream decompression method to be used. */
+  grpc_stream_compression_method stream_decompression_method =
+      GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
+  /** Stream compression decompress context */
+  grpc_stream_compression_context* stream_decompression_ctx = nullptr;
+  /** Stream compression compress context */
+  grpc_stream_compression_context* stream_compression_ctx = nullptr;
+
+  /** Buffer storing data that is compressed but not sent */
+  grpc_slice_buffer compressed_data_buffer;
+  /** Amount of uncompressed bytes sent out when compressed_data_buffer is
+   * emptied */
+  size_t uncompressed_data_size = 0;
+  /** Temporary buffer storing decompressed data */
+  grpc_slice_buffer decompressed_data_buffer;
+  /** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed
+   */
+  bool unprocessed_incoming_frames_decompressed = false;
+  /** Whether the bytes needs to be traced using Fathom */
+  bool traced = false;
+  /** gRPC header bytes that are already decompressed */
+  size_t decompressed_header_bytes = 0;
+  /** Byte counter for number of bytes written */
+  size_t byte_counter = 0;
+};
+
+/** Transport writing call flow:
+    grpc_chttp2_initiate_write() is called anywhere that we know bytes need to
+    go out on the wire.
+    If no other write has been started, a task is enqueued onto our workqueue.
+    When that task executes, it obtains the global lock, and gathers the data
+    to write.
+    The global lock is dropped and we do the syscall to write.
+    After writing, a follow-up check is made to see if another round of writing
+    should be performed.
+
+    The actual call chain is documented in the implementation of this function.
+    */
+void grpc_chttp2_initiate_write(grpc_chttp2_transport* t,
+                                grpc_chttp2_initiate_write_reason reason);
+
+typedef struct {
+  /** are we writing? */
+  bool writing;
+  /** if writing: was it a complete flush (false) or a partial flush (true) */
+  bool partial;
+  /** did we queue any completions as part of beginning the write */
+  bool early_results_scheduled;
+} grpc_chttp2_begin_write_result;
+
+grpc_chttp2_begin_write_result grpc_chttp2_begin_write(
+    grpc_chttp2_transport* t);
+void grpc_chttp2_end_write(grpc_chttp2_transport* t, grpc_error* error);
+
+/** Process one slice of incoming data; return 1 if the connection is still
+    viable after reading, or 0 if the connection should be torn down */
+grpc_error* grpc_chttp2_perform_read(grpc_chttp2_transport* t,
+                                     grpc_slice slice);
+
+bool grpc_chttp2_list_add_writable_stream(grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream* s);
+/** Get a writable stream
+    returns non-zero if there was a stream available */
+bool grpc_chttp2_list_pop_writable_stream(grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream** s);
+bool grpc_chttp2_list_remove_writable_stream(grpc_chttp2_transport* t,
+                                             grpc_chttp2_stream* s);
+
+bool grpc_chttp2_list_add_writing_stream(grpc_chttp2_transport* t,
+                                         grpc_chttp2_stream* s);
+bool grpc_chttp2_list_have_writing_streams(grpc_chttp2_transport* t);
+bool grpc_chttp2_list_pop_writing_stream(grpc_chttp2_transport* t,
+                                         grpc_chttp2_stream** s);
+
+void grpc_chttp2_list_add_written_stream(grpc_chttp2_transport* t,
+                                         grpc_chttp2_stream* s);
+bool grpc_chttp2_list_pop_written_stream(grpc_chttp2_transport* t,
+                                         grpc_chttp2_stream** s);
+
+void grpc_chttp2_list_add_waiting_for_concurrency(grpc_chttp2_transport* t,
+                                                  grpc_chttp2_stream* s);
+bool grpc_chttp2_list_pop_waiting_for_concurrency(grpc_chttp2_transport* t,
+                                                  grpc_chttp2_stream** s);
+void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport* t,
+                                                     grpc_chttp2_stream* s);
+
+void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport* t,
+                                               grpc_chttp2_stream* s);
+bool grpc_chttp2_list_pop_stalled_by_transport(grpc_chttp2_transport* t,
+                                               grpc_chttp2_stream** s);
+void grpc_chttp2_list_remove_stalled_by_transport(grpc_chttp2_transport* t,
+                                                  grpc_chttp2_stream* s);
+
+void grpc_chttp2_list_add_stalled_by_stream(grpc_chttp2_transport* t,
+                                            grpc_chttp2_stream* s);
+bool grpc_chttp2_list_pop_stalled_by_stream(grpc_chttp2_transport* t,
+                                            grpc_chttp2_stream** s);
+bool grpc_chttp2_list_remove_stalled_by_stream(grpc_chttp2_transport* t,
+                                               grpc_chttp2_stream* s);
+
+/********* Flow Control ***************/
+
+// Takes in a flow control action and performs all the needed operations.
+void grpc_chttp2_act_on_flowctl_action(
+    const grpc_core::chttp2::FlowControlAction& action,
+    grpc_chttp2_transport* t, grpc_chttp2_stream* s);
+
+/********* End of Flow Control ***************/
+
+grpc_chttp2_stream* grpc_chttp2_parsing_lookup_stream(grpc_chttp2_transport* t,
+                                                      uint32_t id);
+grpc_chttp2_stream* grpc_chttp2_parsing_accept_stream(grpc_chttp2_transport* t,
+                                                      uint32_t id);
+
+void grpc_chttp2_add_incoming_goaway(grpc_chttp2_transport* t,
+                                     uint32_t goaway_error,
+                                     grpc_slice goaway_text);
+
+void grpc_chttp2_parsing_become_skip_parser(grpc_chttp2_transport* t);
+
+void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t,
+                                       grpc_chttp2_stream* s,
+                                       grpc_closure** pclosure,
+                                       grpc_error* error, const char* desc);
+
+#define GRPC_HEADER_SIZE_IN_BYTES 5
+#define MAX_SIZE_T (~(size_t)0)
+
+#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)
+
+// extern grpc_core::TraceFlag grpc_http_trace;
+// extern grpc_core::TraceFlag grpc_flowctl_trace;
+
+#define GRPC_CHTTP2_IF_TRACING(stmt) \
+  if (!(grpc_http_trace.enabled()))  \
+    ;                                \
+  else                               \
+    stmt
+
+void grpc_chttp2_fake_status(grpc_chttp2_transport* t,
+                             grpc_chttp2_stream* stream, grpc_error* error);
+void grpc_chttp2_mark_stream_closed(grpc_chttp2_transport* t,
+                                    grpc_chttp2_stream* s, int close_reads,
+                                    int close_writes, grpc_error* error);
+void grpc_chttp2_start_writing(grpc_chttp2_transport* t);
+
+#ifndef NDEBUG
+#define GRPC_CHTTP2_STREAM_REF(stream, reason) \
+  grpc_chttp2_stream_ref(stream, reason)
+#define GRPC_CHTTP2_STREAM_UNREF(stream, reason) \
+  grpc_chttp2_stream_unref(stream, reason)
+void grpc_chttp2_stream_ref(grpc_chttp2_stream* s, const char* reason);
+void grpc_chttp2_stream_unref(grpc_chttp2_stream* s, const char* reason);
+#else
+#define GRPC_CHTTP2_STREAM_REF(stream, reason) grpc_chttp2_stream_ref(stream)
+#define GRPC_CHTTP2_STREAM_UNREF(stream, reason) \
+  grpc_chttp2_stream_unref(stream)
+void grpc_chttp2_stream_ref(grpc_chttp2_stream* s);
+void grpc_chttp2_stream_unref(grpc_chttp2_stream* s);
+#endif
+
+#ifndef NDEBUG
+#define GRPC_CHTTP2_REF_TRANSPORT(t, r) \
+  grpc_chttp2_ref_transport(t, r, __FILE__, __LINE__)
+#define GRPC_CHTTP2_UNREF_TRANSPORT(t, r) \
+  grpc_chttp2_unref_transport(t, r, __FILE__, __LINE__)
+inline void grpc_chttp2_unref_transport(grpc_chttp2_transport* t,
+                                        const char* reason, const char* file,
+                                        int line) {
+  if (t->refs.Unref(grpc_core::DebugLocation(file, line), reason)) {
+    grpc_core::Delete(t);
+  }
+}
+inline void grpc_chttp2_ref_transport(grpc_chttp2_transport* t,
+                                      const char* reason, const char* file,
+                                      int line) {
+  t->refs.Ref(grpc_core::DebugLocation(file, line), reason);
+}
+#else
+#define GRPC_CHTTP2_REF_TRANSPORT(t, r) grpc_chttp2_ref_transport(t)
+#define GRPC_CHTTP2_UNREF_TRANSPORT(t, r) grpc_chttp2_unref_transport(t)
+inline void grpc_chttp2_unref_transport(grpc_chttp2_transport* t) {
+  if (t->refs.Unref()) {
+    grpc_core::Delete(t);
+  }
+}
+inline void grpc_chttp2_ref_transport(grpc_chttp2_transport* t) {
+  t->refs.Ref();
+}
+#endif
+
+void grpc_chttp2_ack_ping(grpc_chttp2_transport* t, uint64_t id);
+
+/** Add a new ping strike to ping_recv_state.ping_strikes. If
+    ping_recv_state.ping_strikes > ping_policy.max_ping_strikes, it sends GOAWAY
+    with error code ENHANCE_YOUR_CALM and additional debug data resembling
+    "too_many_pings" followed by immediately closing the connection. */
+void grpc_chttp2_add_ping_strike(grpc_chttp2_transport* t);
+
+/** add a ref to the stream and add it to the writable list;
+    ref will be dropped in writing.c */
+void grpc_chttp2_mark_stream_writable(grpc_chttp2_transport* t,
+                                      grpc_chttp2_stream* s);
+
+void grpc_chttp2_cancel_stream(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                               grpc_error* due_to_error);
+
+void grpc_chttp2_maybe_complete_recv_initial_metadata(grpc_chttp2_transport* t,
+                                                      grpc_chttp2_stream* s);
+void grpc_chttp2_maybe_complete_recv_message(grpc_chttp2_transport* t,
+                                             grpc_chttp2_stream* s);
+void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t,
+                                                       grpc_chttp2_stream* s);
+
+void grpc_chttp2_fail_pending_writes(grpc_chttp2_transport* t,
+                                     grpc_chttp2_stream* s, grpc_error* error);
+
+/** Set the default keepalive configurations, must only be called at
+    initialization */
+void grpc_chttp2_config_default_keepalive_args(grpc_channel_args* args,
+                                               bool is_client);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/parsing.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/parsing.cc
new file mode 100644
index 0000000..1ff96d3
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/parsing.cc
@@ -0,0 +1,780 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/http2_errors.h"
+#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/status_conversion.h"
+#include "src/core/lib/transport/timeout_encoding.h"
+
+static grpc_error* init_frame_parser(grpc_chttp2_transport* t);
+static grpc_error* init_header_frame_parser(grpc_chttp2_transport* t,
+                                            int is_continuation);
+static grpc_error* init_data_frame_parser(grpc_chttp2_transport* t);
+static grpc_error* init_rst_stream_parser(grpc_chttp2_transport* t);
+static grpc_error* init_settings_frame_parser(grpc_chttp2_transport* t);
+static grpc_error* init_window_update_frame_parser(grpc_chttp2_transport* t);
+static grpc_error* init_ping_parser(grpc_chttp2_transport* t);
+static grpc_error* init_goaway_parser(grpc_chttp2_transport* t);
+static grpc_error* init_skip_frame_parser(grpc_chttp2_transport* t,
+                                          int is_header);
+
+static grpc_error* parse_frame_slice(grpc_chttp2_transport* t, grpc_slice slice,
+                                     int is_last);
+
+grpc_error* grpc_chttp2_perform_read(grpc_chttp2_transport* t,
+                                     grpc_slice slice) {
+  uint8_t* beg = GRPC_SLICE_START_PTR(slice);
+  uint8_t* end = GRPC_SLICE_END_PTR(slice);
+  uint8_t* cur = beg;
+  grpc_error* err;
+
+  if (cur == end) return GRPC_ERROR_NONE;
+
+  switch (t->deframe_state) {
+    case GRPC_DTS_CLIENT_PREFIX_0:
+    case GRPC_DTS_CLIENT_PREFIX_1:
+    case GRPC_DTS_CLIENT_PREFIX_2:
+    case GRPC_DTS_CLIENT_PREFIX_3:
+    case GRPC_DTS_CLIENT_PREFIX_4:
+    case GRPC_DTS_CLIENT_PREFIX_5:
+    case GRPC_DTS_CLIENT_PREFIX_6:
+    case GRPC_DTS_CLIENT_PREFIX_7:
+    case GRPC_DTS_CLIENT_PREFIX_8:
+    case GRPC_DTS_CLIENT_PREFIX_9:
+    case GRPC_DTS_CLIENT_PREFIX_10:
+    case GRPC_DTS_CLIENT_PREFIX_11:
+    case GRPC_DTS_CLIENT_PREFIX_12:
+    case GRPC_DTS_CLIENT_PREFIX_13:
+    case GRPC_DTS_CLIENT_PREFIX_14:
+    case GRPC_DTS_CLIENT_PREFIX_15:
+    case GRPC_DTS_CLIENT_PREFIX_16:
+    case GRPC_DTS_CLIENT_PREFIX_17:
+    case GRPC_DTS_CLIENT_PREFIX_18:
+    case GRPC_DTS_CLIENT_PREFIX_19:
+    case GRPC_DTS_CLIENT_PREFIX_20:
+    case GRPC_DTS_CLIENT_PREFIX_21:
+    case GRPC_DTS_CLIENT_PREFIX_22:
+    case GRPC_DTS_CLIENT_PREFIX_23:
+      while (cur != end && t->deframe_state != GRPC_DTS_FH_0) {
+        if (*cur != GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state]) {
+          char* msg;
+          gpr_asprintf(
+              &msg,
+              "Connect string mismatch: expected '%c' (%d) got '%c' (%d) "
+              "at byte %d",
+              GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state],
+              static_cast<int>(static_cast<uint8_t>(
+                  GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state])),
+              *cur, static_cast<int>(*cur), t->deframe_state);
+          err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+          gpr_free(msg);
+          return err;
+        }
+        ++cur;
+        t->deframe_state = static_cast<grpc_chttp2_deframe_transport_state>(
+            1 + static_cast<int>(t->deframe_state));
+      }
+      if (cur == end) {
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    dts_fh_0:
+    case GRPC_DTS_FH_0:
+      GPR_ASSERT(cur < end);
+      t->incoming_frame_size = (static_cast<uint32_t>(*cur)) << 16;
+      if (++cur == end) {
+        t->deframe_state = GRPC_DTS_FH_1;
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FH_1:
+      GPR_ASSERT(cur < end);
+      t->incoming_frame_size |= (static_cast<uint32_t>(*cur)) << 8;
+      if (++cur == end) {
+        t->deframe_state = GRPC_DTS_FH_2;
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FH_2:
+      GPR_ASSERT(cur < end);
+      t->incoming_frame_size |= *cur;
+      if (++cur == end) {
+        t->deframe_state = GRPC_DTS_FH_3;
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FH_3:
+      GPR_ASSERT(cur < end);
+      t->incoming_frame_type = *cur;
+      if (++cur == end) {
+        t->deframe_state = GRPC_DTS_FH_4;
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FH_4:
+      GPR_ASSERT(cur < end);
+      t->incoming_frame_flags = *cur;
+      if (++cur == end) {
+        t->deframe_state = GRPC_DTS_FH_5;
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FH_5:
+      GPR_ASSERT(cur < end);
+      t->incoming_stream_id = ((static_cast<uint32_t>(*cur)) & 0x7f) << 24;
+      if (++cur == end) {
+        t->deframe_state = GRPC_DTS_FH_6;
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FH_6:
+      GPR_ASSERT(cur < end);
+      t->incoming_stream_id |= (static_cast<uint32_t>(*cur)) << 16;
+      if (++cur == end) {
+        t->deframe_state = GRPC_DTS_FH_7;
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FH_7:
+      GPR_ASSERT(cur < end);
+      t->incoming_stream_id |= (static_cast<uint32_t>(*cur)) << 8;
+      if (++cur == end) {
+        t->deframe_state = GRPC_DTS_FH_8;
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FH_8:
+      GPR_ASSERT(cur < end);
+      t->incoming_stream_id |= (static_cast<uint32_t>(*cur));
+      t->deframe_state = GRPC_DTS_FRAME;
+      err = init_frame_parser(t);
+      if (err != GRPC_ERROR_NONE) {
+        return err;
+      }
+      if (t->incoming_frame_size == 0) {
+        err = parse_frame_slice(t, grpc_empty_slice(), 1);
+        if (err != GRPC_ERROR_NONE) {
+          return err;
+        }
+        t->incoming_stream = nullptr;
+        if (++cur == end) {
+          t->deframe_state = GRPC_DTS_FH_0;
+          return GRPC_ERROR_NONE;
+        }
+        goto dts_fh_0; /* loop */
+      } else if (t->flow_control->flow_control_enabled() &&
+                 t->incoming_frame_size >
+                     t->settings[GRPC_ACKED_SETTINGS]
+                                [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]) {
+        char* msg;
+        gpr_asprintf(&msg, "Frame size %d is larger than max frame size %d",
+                     t->incoming_frame_size,
+                     t->settings[GRPC_ACKED_SETTINGS]
+                                [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]);
+        err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+        gpr_free(msg);
+        return err;
+      }
+      if (++cur == end) {
+        return GRPC_ERROR_NONE;
+      }
+    /* fallthrough */
+    case GRPC_DTS_FRAME:
+      GPR_ASSERT(cur < end);
+      if (static_cast<uint32_t>(end - cur) == t->incoming_frame_size) {
+        err = parse_frame_slice(
+            t,
+            grpc_slice_sub_no_ref(slice, static_cast<size_t>(cur - beg),
+                                  static_cast<size_t>(end - beg)),
+            1);
+        if (err != GRPC_ERROR_NONE) {
+          return err;
+        }
+        t->deframe_state = GRPC_DTS_FH_0;
+        t->incoming_stream = nullptr;
+        return GRPC_ERROR_NONE;
+      } else if (static_cast<uint32_t>(end - cur) > t->incoming_frame_size) {
+        size_t cur_offset = static_cast<size_t>(cur - beg);
+        err = parse_frame_slice(
+            t,
+            grpc_slice_sub_no_ref(slice, cur_offset,
+                                  cur_offset + t->incoming_frame_size),
+            1);
+        if (err != GRPC_ERROR_NONE) {
+          return err;
+        }
+        cur += t->incoming_frame_size;
+        t->incoming_stream = nullptr;
+        goto dts_fh_0; /* loop */
+      } else {
+        err = parse_frame_slice(
+            t,
+            grpc_slice_sub_no_ref(slice, static_cast<size_t>(cur - beg),
+                                  static_cast<size_t>(end - beg)),
+            0);
+        if (err != GRPC_ERROR_NONE) {
+          return err;
+        }
+        t->incoming_frame_size -= static_cast<uint32_t>(end - cur);
+        return GRPC_ERROR_NONE;
+      }
+      GPR_UNREACHABLE_CODE(return nullptr);
+  }
+
+  GPR_UNREACHABLE_CODE(return nullptr);
+}
+
+static grpc_error* init_frame_parser(grpc_chttp2_transport* t) {
+  if (t->is_first_frame &&
+      t->incoming_frame_type != GRPC_CHTTP2_FRAME_SETTINGS) {
+    char* msg;
+    gpr_asprintf(
+        &msg, "Expected SETTINGS frame as the first frame, got frame type %d",
+        t->incoming_frame_type);
+    grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return err;
+  }
+  t->is_first_frame = false;
+  if (t->expect_continuation_stream_id != 0) {
+    if (t->incoming_frame_type != GRPC_CHTTP2_FRAME_CONTINUATION) {
+      char* msg;
+      gpr_asprintf(&msg, "Expected CONTINUATION frame, got frame type %02x",
+                   t->incoming_frame_type);
+      grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      gpr_free(msg);
+      return err;
+    }
+    if (t->expect_continuation_stream_id != t->incoming_stream_id) {
+      char* msg;
+      gpr_asprintf(
+          &msg,
+          "Expected CONTINUATION frame for grpc_chttp2_stream %08x, got "
+          "grpc_chttp2_stream %08x",
+          t->expect_continuation_stream_id, t->incoming_stream_id);
+      grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      gpr_free(msg);
+      return err;
+    }
+    return init_header_frame_parser(t, 1);
+  }
+  switch (t->incoming_frame_type) {
+    case GRPC_CHTTP2_FRAME_DATA:
+      return init_data_frame_parser(t);
+    case GRPC_CHTTP2_FRAME_HEADER:
+      return init_header_frame_parser(t, 0);
+    case GRPC_CHTTP2_FRAME_CONTINUATION:
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "Unexpected CONTINUATION frame");
+    case GRPC_CHTTP2_FRAME_RST_STREAM:
+      return init_rst_stream_parser(t);
+    case GRPC_CHTTP2_FRAME_SETTINGS:
+      return init_settings_frame_parser(t);
+    case GRPC_CHTTP2_FRAME_WINDOW_UPDATE:
+      return init_window_update_frame_parser(t);
+    case GRPC_CHTTP2_FRAME_PING:
+      return init_ping_parser(t);
+    case GRPC_CHTTP2_FRAME_GOAWAY:
+      return init_goaway_parser(t);
+    default:
+      if (grpc_http_trace.enabled()) {
+        gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type);
+      }
+      return init_skip_frame_parser(t, 0);
+  }
+}
+
+static grpc_error* skip_parser(void* parser, grpc_chttp2_transport* t,
+                               grpc_chttp2_stream* s, grpc_slice slice,
+                               int is_last) {
+  return GRPC_ERROR_NONE;
+}
+
+static void skip_header(void* tp, grpc_mdelem md) { GRPC_MDELEM_UNREF(md); }
+
+static grpc_error* init_skip_frame_parser(grpc_chttp2_transport* t,
+                                          int is_header) {
+  if (is_header) {
+    uint8_t is_eoh = t->expect_continuation_stream_id != 0;
+    t->parser = grpc_chttp2_header_parser_parse;
+    t->parser_data = &t->hpack_parser;
+    t->hpack_parser.on_header = skip_header;
+    t->hpack_parser.on_header_user_data = nullptr;
+    t->hpack_parser.is_boundary = is_eoh;
+    t->hpack_parser.is_eof = static_cast<uint8_t>(is_eoh ? t->header_eof : 0);
+  } else {
+    t->parser = skip_parser;
+  }
+  return GRPC_ERROR_NONE;
+}
+
+void grpc_chttp2_parsing_become_skip_parser(grpc_chttp2_transport* t) {
+  init_skip_frame_parser(t, t->parser == grpc_chttp2_header_parser_parse);
+}
+
+static grpc_error* init_data_frame_parser(grpc_chttp2_transport* t) {
+  grpc_chttp2_stream* s =
+      grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id);
+  grpc_error* err = GRPC_ERROR_NONE;
+  grpc_core::chttp2::FlowControlAction action;
+  if (s == nullptr) {
+    err = t->flow_control->RecvData(t->incoming_frame_size);
+    action = t->flow_control->MakeAction();
+  } else {
+    err = s->flow_control->RecvData(t->incoming_frame_size);
+    action = s->flow_control->MakeAction();
+  }
+  grpc_chttp2_act_on_flowctl_action(action, t, s);
+  if (err != GRPC_ERROR_NONE) {
+    goto error_handler;
+  }
+  if (s == nullptr) {
+    return init_skip_frame_parser(t, 0);
+  }
+  s->received_bytes += t->incoming_frame_size;
+  s->stats.incoming.framing_bytes += 9;
+  if (err == GRPC_ERROR_NONE && s->read_closed) {
+    return init_skip_frame_parser(t, 0);
+  }
+  if (err == GRPC_ERROR_NONE) {
+    err = grpc_chttp2_data_parser_begin_frame(
+        &s->data_parser, t->incoming_frame_flags, s->id, s);
+  }
+error_handler:
+  intptr_t unused;
+  if (err == GRPC_ERROR_NONE) {
+    t->incoming_stream = s;
+    /* t->parser = grpc_chttp2_data_parser_parse;*/
+    t->parser = grpc_chttp2_data_parser_parse;
+    t->parser_data = &s->data_parser;
+    t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST;
+    return GRPC_ERROR_NONE;
+  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, &unused)) {
+    /* handle stream errors by closing the stream */
+    if (s != nullptr) {
+      grpc_chttp2_mark_stream_closed(t, s, true, false, err);
+    }
+    grpc_slice_buffer_add(
+        &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id,
+                                                GRPC_HTTP2_PROTOCOL_ERROR,
+                                                &s->stats.outgoing));
+    return init_skip_frame_parser(t, 0);
+  } else {
+    return err;
+  }
+}
+
+static void free_timeout(void* p) { gpr_free(p); }
+
+static void on_initial_header(void* tp, grpc_mdelem md) {
+  GPR_TIMER_SCOPE("on_initial_header", 0);
+
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+  grpc_chttp2_stream* s = t->incoming_stream;
+  GPR_ASSERT(s != nullptr);
+
+  if (grpc_http_trace.enabled()) {
+    char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
+    char* value =
+        grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
+    gpr_log(GPR_INFO, "HTTP:%d:HDR:%s: %s: %s", s->id,
+            t->is_client ? "CLI" : "SVR", key, value);
+    gpr_free(key);
+    gpr_free(value);
+  }
+
+  if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) {
+    // We don't use grpc_mdelem_eq here to avoid executing additional
+    // instructions. The reasoning is if the payload is not equal, we already
+    // know that the metadata elements are not equal because the md is
+    // confirmed to be static. If we had used grpc_mdelem_eq here, then if the
+    // payloads are not equal, grpc_mdelem_eq executes more instructions to
+    // determine if they're equal or not.
+    if (md.payload == GRPC_MDELEM_GRPC_STATUS_1.payload ||
+        md.payload == GRPC_MDELEM_GRPC_STATUS_2.payload) {
+      s->seen_error = true;
+    }
+  } else {
+    if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) &&
+        !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) {
+      /* TODO(ctiller): check for a status like " 0" */
+      s->seen_error = true;
+    }
+
+    if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) {
+      grpc_millis* cached_timeout = static_cast<grpc_millis*>(
+          grpc_mdelem_get_user_data(md, free_timeout));
+      grpc_millis timeout;
+      if (cached_timeout != nullptr) {
+        timeout = *cached_timeout;
+      } else {
+        if (GPR_UNLIKELY(
+                !grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout))) {
+          char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+          gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val);
+          gpr_free(val);
+          timeout = GRPC_MILLIS_INF_FUTURE;
+        }
+        if (GRPC_MDELEM_IS_INTERNED(md)) {
+          /* store the result */
+          cached_timeout =
+              static_cast<grpc_millis*>(gpr_malloc(sizeof(grpc_millis)));
+          *cached_timeout = timeout;
+          grpc_mdelem_set_user_data(md, free_timeout, cached_timeout);
+        }
+      }
+      if (timeout != GRPC_MILLIS_INF_FUTURE) {
+        grpc_chttp2_incoming_metadata_buffer_set_deadline(
+            &s->metadata_buffer[0], grpc_core::ExecCtx::Get()->Now() + timeout);
+      }
+      GRPC_MDELEM_UNREF(md);
+      return;
+    }
+  }
+
+  const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md);
+  const size_t metadata_size_limit =
+      t->settings[GRPC_ACKED_SETTINGS]
+                 [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
+  if (new_size > metadata_size_limit) {
+    gpr_log(GPR_DEBUG,
+            "received initial metadata size exceeds limit (%" PRIuPTR
+            " vs. %" PRIuPTR ")",
+            new_size, metadata_size_limit);
+    grpc_chttp2_cancel_stream(
+        t, s,
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "received initial metadata size exceeds limit"),
+                           GRPC_ERROR_INT_GRPC_STATUS,
+                           GRPC_STATUS_RESOURCE_EXHAUSTED));
+    grpc_chttp2_parsing_become_skip_parser(t);
+    s->seen_error = true;
+    GRPC_MDELEM_UNREF(md);
+  } else {
+    grpc_error* error =
+        grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[0], md);
+    if (error != GRPC_ERROR_NONE) {
+      grpc_chttp2_cancel_stream(t, s, error);
+      grpc_chttp2_parsing_become_skip_parser(t);
+      s->seen_error = true;
+      GRPC_MDELEM_UNREF(md);
+    }
+  }
+}
+
+static void on_trailing_header(void* tp, grpc_mdelem md) {
+  GPR_TIMER_SCOPE("on_trailing_header", 0);
+
+  grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp);
+  grpc_chttp2_stream* s = t->incoming_stream;
+  GPR_ASSERT(s != nullptr);
+
+  if (grpc_http_trace.enabled()) {
+    char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
+    char* value =
+        grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
+    gpr_log(GPR_INFO, "HTTP:%d:TRL:%s: %s: %s", s->id,
+            t->is_client ? "CLI" : "SVR", key, value);
+    gpr_free(key);
+    gpr_free(value);
+  }
+
+  if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) {
+    // We don't use grpc_mdelem_eq here to avoid executing additional
+    // instructions. The reasoning is if the payload is not equal, we already
+    // know that the metadata elements are not equal because the md is
+    // confirmed to be static. If we had used grpc_mdelem_eq here, then if the
+    // payloads are not equal, grpc_mdelem_eq executes more instructions to
+    // determine if they're equal or not.
+    if (md.payload == GRPC_MDELEM_GRPC_STATUS_1.payload ||
+        md.payload == GRPC_MDELEM_GRPC_STATUS_2.payload) {
+      s->seen_error = true;
+    }
+  } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) &&
+             !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) {
+    /* TODO(ctiller): check for a status like " 0" */
+    s->seen_error = true;
+  }
+
+  const size_t new_size = s->metadata_buffer[1].size + GRPC_MDELEM_LENGTH(md);
+  const size_t metadata_size_limit =
+      t->settings[GRPC_ACKED_SETTINGS]
+                 [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
+  if (new_size > metadata_size_limit) {
+    gpr_log(GPR_DEBUG,
+            "received trailing metadata size exceeds limit (%" PRIuPTR
+            " vs. %" PRIuPTR ")",
+            new_size, metadata_size_limit);
+    grpc_chttp2_cancel_stream(
+        t, s,
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "received trailing metadata size exceeds limit"),
+                           GRPC_ERROR_INT_GRPC_STATUS,
+                           GRPC_STATUS_RESOURCE_EXHAUSTED));
+    grpc_chttp2_parsing_become_skip_parser(t);
+    s->seen_error = true;
+    GRPC_MDELEM_UNREF(md);
+  } else {
+    grpc_error* error =
+        grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[1], md);
+    if (error != GRPC_ERROR_NONE) {
+      grpc_chttp2_cancel_stream(t, s, error);
+      grpc_chttp2_parsing_become_skip_parser(t);
+      s->seen_error = true;
+      GRPC_MDELEM_UNREF(md);
+    }
+  }
+}
+
+static grpc_error* init_header_frame_parser(grpc_chttp2_transport* t,
+                                            int is_continuation) {
+  uint8_t is_eoh =
+      (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0;
+  grpc_chttp2_stream* s;
+
+  /* TODO(ctiller): when to increment header_frames_received? */
+
+  if (is_eoh) {
+    t->expect_continuation_stream_id = 0;
+  } else {
+    t->expect_continuation_stream_id = t->incoming_stream_id;
+  }
+
+  if (!is_continuation) {
+    t->header_eof =
+        (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) != 0;
+  }
+
+  t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST;
+
+  /* could be a new grpc_chttp2_stream or an existing grpc_chttp2_stream */
+  s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id);
+  if (s == nullptr) {
+    if (GPR_UNLIKELY(is_continuation)) {
+      GRPC_CHTTP2_IF_TRACING(
+          gpr_log(GPR_ERROR,
+                  "grpc_chttp2_stream disbanded before CONTINUATION received"));
+      return init_skip_frame_parser(t, 1);
+    }
+    if (t->is_client) {
+      if (GPR_LIKELY((t->incoming_stream_id & 1) &&
+                     t->incoming_stream_id < t->next_stream_id)) {
+        /* this is an old (probably cancelled) grpc_chttp2_stream */
+      } else {
+        GRPC_CHTTP2_IF_TRACING(gpr_log(
+            GPR_ERROR, "ignoring new grpc_chttp2_stream creation on client"));
+      }
+      grpc_error* err = init_skip_frame_parser(t, 1);
+      if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY) {
+        grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser);
+      }
+      return err;
+    } else if (GPR_UNLIKELY(t->last_new_stream_id >= t->incoming_stream_id)) {
+      GRPC_CHTTP2_IF_TRACING(gpr_log(
+          GPR_ERROR,
+          "ignoring out of order new grpc_chttp2_stream request on server; "
+          "last grpc_chttp2_stream "
+          "id=%d, new grpc_chttp2_stream id=%d",
+          t->last_new_stream_id, t->incoming_stream_id));
+      return init_skip_frame_parser(t, 1);
+    } else if (GPR_UNLIKELY((t->incoming_stream_id & 1) == 0)) {
+      GRPC_CHTTP2_IF_TRACING(gpr_log(
+          GPR_ERROR,
+          "ignoring grpc_chttp2_stream with non-client generated index %d",
+          t->incoming_stream_id));
+      return init_skip_frame_parser(t, 1);
+    } else if (GPR_UNLIKELY(
+                   grpc_chttp2_stream_map_size(&t->stream_map) >=
+                   t->settings[GRPC_ACKED_SETTINGS]
+                              [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS])) {
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Max stream count exceeded");
+    }
+    t->last_new_stream_id = t->incoming_stream_id;
+    s = t->incoming_stream =
+        grpc_chttp2_parsing_accept_stream(t, t->incoming_stream_id);
+    if (GPR_UNLIKELY(s == nullptr)) {
+      GRPC_CHTTP2_IF_TRACING(
+          gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted"));
+      return init_skip_frame_parser(t, 1);
+    }
+    if (t->channelz_socket != nullptr) {
+      t->channelz_socket->RecordStreamStartedFromRemote();
+    }
+  } else {
+    t->incoming_stream = s;
+  }
+  GPR_ASSERT(s != nullptr);
+  s->stats.incoming.framing_bytes += 9;
+  if (GPR_UNLIKELY(s->read_closed)) {
+    GRPC_CHTTP2_IF_TRACING(gpr_log(
+        GPR_ERROR, "skipping already closed grpc_chttp2_stream header"));
+    t->incoming_stream = nullptr;
+    return init_skip_frame_parser(t, 1);
+  }
+  t->parser = grpc_chttp2_header_parser_parse;
+  t->parser_data = &t->hpack_parser;
+  if (t->header_eof) {
+    s->eos_received = true;
+  }
+  switch (s->header_frames_received) {
+    case 0:
+      if (t->is_client && t->header_eof) {
+        GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing Trailers-Only"));
+        if (s->trailing_metadata_available != nullptr) {
+          *s->trailing_metadata_available = true;
+        }
+        t->hpack_parser.on_header = on_trailing_header;
+        s->received_trailing_metadata = true;
+      } else {
+        GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing initial_metadata"));
+        t->hpack_parser.on_header = on_initial_header;
+      }
+      break;
+    case 1:
+      GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing trailing_metadata"));
+      t->hpack_parser.on_header = on_trailing_header;
+      s->received_trailing_metadata = true;
+      break;
+    case 2:
+      gpr_log(GPR_ERROR, "too many header frames received");
+      return init_skip_frame_parser(t, 1);
+  }
+  t->hpack_parser.on_header_user_data = t;
+  t->hpack_parser.is_boundary = is_eoh;
+  t->hpack_parser.is_eof = static_cast<uint8_t>(is_eoh ? t->header_eof : 0);
+  if (!is_continuation &&
+      (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY)) {
+    grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser);
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* init_window_update_frame_parser(grpc_chttp2_transport* t) {
+  grpc_error* err = grpc_chttp2_window_update_parser_begin_frame(
+      &t->simple.window_update, t->incoming_frame_size,
+      t->incoming_frame_flags);
+  if (err != GRPC_ERROR_NONE) return err;
+  if (t->incoming_stream_id != 0) {
+    grpc_chttp2_stream* s = t->incoming_stream =
+        grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id);
+    if (s == nullptr) {
+      return init_skip_frame_parser(t, 0);
+    }
+    s->stats.incoming.framing_bytes += 9;
+  }
+  t->parser = grpc_chttp2_window_update_parser_parse;
+  t->parser_data = &t->simple.window_update;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* init_ping_parser(grpc_chttp2_transport* t) {
+  grpc_error* err = grpc_chttp2_ping_parser_begin_frame(
+      &t->simple.ping, t->incoming_frame_size, t->incoming_frame_flags);
+  if (err != GRPC_ERROR_NONE) return err;
+  t->parser = grpc_chttp2_ping_parser_parse;
+  t->parser_data = &t->simple.ping;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* init_rst_stream_parser(grpc_chttp2_transport* t) {
+  grpc_error* err = grpc_chttp2_rst_stream_parser_begin_frame(
+      &t->simple.rst_stream, t->incoming_frame_size, t->incoming_frame_flags);
+  if (err != GRPC_ERROR_NONE) return err;
+  grpc_chttp2_stream* s = t->incoming_stream =
+      grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id);
+  if (!t->incoming_stream) {
+    return init_skip_frame_parser(t, 0);
+  }
+  s->stats.incoming.framing_bytes += 9;
+  t->parser = grpc_chttp2_rst_stream_parser_parse;
+  t->parser_data = &t->simple.rst_stream;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* init_goaway_parser(grpc_chttp2_transport* t) {
+  grpc_error* err = grpc_chttp2_goaway_parser_begin_frame(
+      &t->goaway_parser, t->incoming_frame_size, t->incoming_frame_flags);
+  if (err != GRPC_ERROR_NONE) return err;
+  t->parser = grpc_chttp2_goaway_parser_parse;
+  t->parser_data = &t->goaway_parser;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* init_settings_frame_parser(grpc_chttp2_transport* t) {
+  if (t->incoming_stream_id != 0) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Settings frame received for grpc_chttp2_stream");
+  }
+
+  grpc_error* err = grpc_chttp2_settings_parser_begin_frame(
+      &t->simple.settings, t->incoming_frame_size, t->incoming_frame_flags,
+      t->settings[GRPC_PEER_SETTINGS]);
+  if (err != GRPC_ERROR_NONE) {
+    return err;
+  }
+  if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) {
+    memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS],
+           GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
+    grpc_chttp2_hptbl_set_max_bytes(
+        &t->hpack_parser.table,
+        t->settings[GRPC_ACKED_SETTINGS]
+                   [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
+    t->sent_local_settings = 0;
+  }
+  t->parser = grpc_chttp2_settings_parser_parse;
+  t->parser_data = &t->simple.settings;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* parse_frame_slice(grpc_chttp2_transport* t, grpc_slice slice,
+                                     int is_last) {
+  grpc_chttp2_stream* s = t->incoming_stream;
+  grpc_error* err = t->parser(t->parser_data, t, s, slice, is_last);
+  intptr_t unused;
+  if (GPR_LIKELY(err == GRPC_ERROR_NONE)) {
+    return err;
+  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, &unused)) {
+    if (grpc_http_trace.enabled()) {
+      const char* msg = grpc_error_string(err);
+      gpr_log(GPR_ERROR, "%s", msg);
+    }
+    grpc_chttp2_parsing_become_skip_parser(t);
+    if (s) {
+      s->forced_close_error = err;
+      grpc_slice_buffer_add(
+          &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id,
+                                                  GRPC_HTTP2_PROTOCOL_ERROR,
+                                                  &s->stats.outgoing));
+    } else {
+      GRPC_ERROR_UNREF(err);
+    }
+  }
+  return err;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_lists.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_lists.cc
new file mode 100644
index 0000000..6626170
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_lists.cc
@@ -0,0 +1,216 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <grpc/support/log.h>
+
+static const char* stream_list_id_string(grpc_chttp2_stream_list_id id) {
+  switch (id) {
+    case GRPC_CHTTP2_LIST_WRITABLE:
+      return "writable";
+    case GRPC_CHTTP2_LIST_WRITING:
+      return "writing";
+    case GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT:
+      return "stalled_by_transport";
+    case GRPC_CHTTP2_LIST_STALLED_BY_STREAM:
+      return "stalled_by_stream";
+    case GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY:
+      return "waiting_for_concurrency";
+    case STREAM_LIST_COUNT:
+      GPR_UNREACHABLE_CODE(return "unknown");
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+grpc_core::TraceFlag grpc_trace_http2_stream_state(false, "http2_stream_state");
+
+/* core list management */
+
+static bool stream_list_empty(grpc_chttp2_transport* t,
+                              grpc_chttp2_stream_list_id id) {
+  return t->lists[id].head == nullptr;
+}
+
+static bool stream_list_pop(grpc_chttp2_transport* t,
+                            grpc_chttp2_stream** stream,
+                            grpc_chttp2_stream_list_id id) {
+  grpc_chttp2_stream* s = t->lists[id].head;
+  if (s) {
+    grpc_chttp2_stream* new_head = s->links[id].next;
+    GPR_ASSERT(s->included[id]);
+    if (new_head) {
+      t->lists[id].head = new_head;
+      new_head->links[id].prev = nullptr;
+    } else {
+      t->lists[id].head = nullptr;
+      t->lists[id].tail = nullptr;
+    }
+    s->included[id] = 0;
+  }
+  *stream = s;
+  if (s && grpc_trace_http2_stream_state.enabled()) {
+    gpr_log(GPR_INFO, "%p[%d][%s]: pop from %s", t, s->id,
+            t->is_client ? "cli" : "svr", stream_list_id_string(id));
+  }
+  return s != nullptr;
+}
+
+static void stream_list_remove(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                               grpc_chttp2_stream_list_id id) {
+  GPR_ASSERT(s->included[id]);
+  s->included[id] = 0;
+  if (s->links[id].prev) {
+    s->links[id].prev->links[id].next = s->links[id].next;
+  } else {
+    GPR_ASSERT(t->lists[id].head == s);
+    t->lists[id].head = s->links[id].next;
+  }
+  if (s->links[id].next) {
+    s->links[id].next->links[id].prev = s->links[id].prev;
+  } else {
+    t->lists[id].tail = s->links[id].prev;
+  }
+  if (grpc_trace_http2_stream_state.enabled()) {
+    gpr_log(GPR_INFO, "%p[%d][%s]: remove from %s", t, s->id,
+            t->is_client ? "cli" : "svr", stream_list_id_string(id));
+  }
+}
+
+static bool stream_list_maybe_remove(grpc_chttp2_transport* t,
+                                     grpc_chttp2_stream* s,
+                                     grpc_chttp2_stream_list_id id) {
+  if (s->included[id]) {
+    stream_list_remove(t, s, id);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+static void stream_list_add_tail(grpc_chttp2_transport* t,
+                                 grpc_chttp2_stream* s,
+                                 grpc_chttp2_stream_list_id id) {
+  grpc_chttp2_stream* old_tail;
+  GPR_ASSERT(!s->included[id]);
+  old_tail = t->lists[id].tail;
+  s->links[id].next = nullptr;
+  s->links[id].prev = old_tail;
+  if (old_tail) {
+    old_tail->links[id].next = s;
+  } else {
+    t->lists[id].head = s;
+  }
+  t->lists[id].tail = s;
+  s->included[id] = 1;
+  if (grpc_trace_http2_stream_state.enabled()) {
+    gpr_log(GPR_INFO, "%p[%d][%s]: add to %s", t, s->id,
+            t->is_client ? "cli" : "svr", stream_list_id_string(id));
+  }
+}
+
+static bool stream_list_add(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                            grpc_chttp2_stream_list_id id) {
+  if (s->included[id]) {
+    return false;
+  }
+  stream_list_add_tail(t, s, id);
+  return true;
+}
+
+/* wrappers for specializations */
+
+bool grpc_chttp2_list_add_writable_stream(grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream* s) {
+  GPR_ASSERT(s->id != 0);
+  return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITABLE);
+}
+
+bool grpc_chttp2_list_pop_writable_stream(grpc_chttp2_transport* t,
+                                          grpc_chttp2_stream** s) {
+  return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WRITABLE);
+}
+
+bool grpc_chttp2_list_remove_writable_stream(grpc_chttp2_transport* t,
+                                             grpc_chttp2_stream* s) {
+  return stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WRITABLE);
+}
+
+bool grpc_chttp2_list_add_writing_stream(grpc_chttp2_transport* t,
+                                         grpc_chttp2_stream* s) {
+  return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITING);
+}
+
+bool grpc_chttp2_list_have_writing_streams(grpc_chttp2_transport* t) {
+  return !stream_list_empty(t, GRPC_CHTTP2_LIST_WRITING);
+}
+
+bool grpc_chttp2_list_pop_writing_stream(grpc_chttp2_transport* t,
+                                         grpc_chttp2_stream** s) {
+  return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WRITING);
+}
+
+void grpc_chttp2_list_add_waiting_for_concurrency(grpc_chttp2_transport* t,
+                                                  grpc_chttp2_stream* s) {
+  stream_list_add(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY);
+}
+
+bool grpc_chttp2_list_pop_waiting_for_concurrency(grpc_chttp2_transport* t,
+                                                  grpc_chttp2_stream** s) {
+  return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY);
+}
+
+void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport* t,
+                                                     grpc_chttp2_stream* s) {
+  stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY);
+}
+
+void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport* t,
+                                               grpc_chttp2_stream* s) {
+  GPR_ASSERT(t->flow_control->flow_control_enabled());
+  stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT);
+}
+
+bool grpc_chttp2_list_pop_stalled_by_transport(grpc_chttp2_transport* t,
+                                               grpc_chttp2_stream** s) {
+  return stream_list_pop(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT);
+}
+
+void grpc_chttp2_list_remove_stalled_by_transport(grpc_chttp2_transport* t,
+                                                  grpc_chttp2_stream* s) {
+  stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT);
+}
+
+void grpc_chttp2_list_add_stalled_by_stream(grpc_chttp2_transport* t,
+                                            grpc_chttp2_stream* s) {
+  GPR_ASSERT(t->flow_control->flow_control_enabled());
+  stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM);
+}
+
+bool grpc_chttp2_list_pop_stalled_by_stream(grpc_chttp2_transport* t,
+                                            grpc_chttp2_stream** s) {
+  return stream_list_pop(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM);
+}
+
+bool grpc_chttp2_list_remove_stalled_by_stream(grpc_chttp2_transport* t,
+                                               grpc_chttp2_stream* s) {
+  return stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM);
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.cc
new file mode 100644
index 0000000..f300e23
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.cc
@@ -0,0 +1,167 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/stream_map.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+void grpc_chttp2_stream_map_init(grpc_chttp2_stream_map* map,
+                                 size_t initial_capacity) {
+  GPR_ASSERT(initial_capacity > 1);
+  map->keys =
+      static_cast<uint32_t*>(gpr_malloc(sizeof(uint32_t) * initial_capacity));
+  map->values =
+      static_cast<void**>(gpr_malloc(sizeof(void*) * initial_capacity));
+  map->count = 0;
+  map->free = 0;
+  map->capacity = initial_capacity;
+}
+
+void grpc_chttp2_stream_map_destroy(grpc_chttp2_stream_map* map) {
+  gpr_free(map->keys);
+  gpr_free(map->values);
+}
+
+static size_t compact(uint32_t* keys, void** values, size_t count) {
+  size_t i, out;
+
+  for (i = 0, out = 0; i < count; i++) {
+    if (values[i]) {
+      keys[out] = keys[i];
+      values[out] = values[i];
+      out++;
+    }
+  }
+
+  return out;
+}
+
+void grpc_chttp2_stream_map_add(grpc_chttp2_stream_map* map, uint32_t key,
+                                void* value) {
+  size_t count = map->count;
+  size_t capacity = map->capacity;
+  uint32_t* keys = map->keys;
+  void** values = map->values;
+
+  GPR_ASSERT(count == 0 || keys[count - 1] < key);
+  GPR_ASSERT(value);
+  GPR_ASSERT(grpc_chttp2_stream_map_find(map, key) == nullptr);
+
+  if (count == capacity) {
+    if (map->free > capacity / 4) {
+      count = compact(keys, values, count);
+      map->free = 0;
+    } else {
+      /* resize when less than 25% of the table is free, because compaction
+         won't help much */
+      map->capacity = capacity = 3 * capacity / 2;
+      map->keys = keys = static_cast<uint32_t*>(
+          gpr_realloc(keys, capacity * sizeof(uint32_t)));
+      map->values = values =
+          static_cast<void**>(gpr_realloc(values, capacity * sizeof(void*)));
+    }
+  }
+
+  keys[count] = key;
+  values[count] = value;
+  map->count = count + 1;
+}
+
+static void** find(grpc_chttp2_stream_map* map, uint32_t key) {
+  size_t min_idx = 0;
+  size_t max_idx = map->count;
+  size_t mid_idx;
+  uint32_t* keys = map->keys;
+  void** values = map->values;
+  uint32_t mid_key;
+
+  if (max_idx == 0) return nullptr;
+
+  while (min_idx < max_idx) {
+    /* find the midpoint, avoiding overflow */
+    mid_idx = min_idx + ((max_idx - min_idx) / 2);
+    mid_key = keys[mid_idx];
+
+    if (mid_key < key) {
+      min_idx = mid_idx + 1;
+    } else if (mid_key > key) {
+      max_idx = mid_idx;
+    } else /* mid_key == key */
+    {
+      return &values[mid_idx];
+    }
+  }
+
+  return nullptr;
+}
+
+void* grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map* map, uint32_t key) {
+  void** pvalue = find(map, key);
+  void* out = nullptr;
+  if (pvalue != nullptr) {
+    out = *pvalue;
+    *pvalue = nullptr;
+    map->free += (out != nullptr);
+    /* recognize complete emptyness and ensure we can skip
+     * defragmentation later */
+    if (map->free == map->count) {
+      map->free = map->count = 0;
+    }
+    GPR_ASSERT(grpc_chttp2_stream_map_find(map, key) == nullptr);
+  }
+  return out;
+}
+
+void* grpc_chttp2_stream_map_find(grpc_chttp2_stream_map* map, uint32_t key) {
+  void** pvalue = find(map, key);
+  return pvalue != nullptr ? *pvalue : nullptr;
+}
+
+size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map* map) {
+  return map->count - map->free;
+}
+
+void* grpc_chttp2_stream_map_rand(grpc_chttp2_stream_map* map) {
+  if (map->count == map->free) {
+    return nullptr;
+  }
+  if (map->free != 0) {
+    map->count = compact(map->keys, map->values, map->count);
+    map->free = 0;
+    GPR_ASSERT(map->count > 0);
+  }
+  return map->values[(static_cast<size_t>(rand())) % map->count];
+}
+
+void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map* map,
+                                     void (*f)(void* user_data, uint32_t key,
+                                               void* value),
+                                     void* user_data) {
+  size_t i;
+
+  for (i = 0; i < map->count; i++) {
+    if (map->values[i]) {
+      f(user_data, map->keys[i], map->values[i]);
+    }
+  }
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.h
new file mode 100644
index 0000000..9fb8826
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STREAM_MAP_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STREAM_MAP_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+/* Data structure to map a uint32_t to a data object (represented by a void*)
+
+   Represented as a sorted array of keys, and a corresponding array of values.
+   Lookups are performed with binary search.
+   Adds are restricted to strictly higher keys than previously seen (this is
+   guaranteed by http2). */
+typedef struct {
+  uint32_t* keys;
+  void** values;
+  size_t count;
+  size_t free;
+  size_t capacity;
+} grpc_chttp2_stream_map;
+
+void grpc_chttp2_stream_map_init(grpc_chttp2_stream_map* map,
+                                 size_t initial_capacity);
+void grpc_chttp2_stream_map_destroy(grpc_chttp2_stream_map* map);
+
+/* Add a new key: given http2 semantics, new keys must always be greater than
+   existing keys - this is asserted */
+void grpc_chttp2_stream_map_add(grpc_chttp2_stream_map* map, uint32_t key,
+                                void* value);
+
+/* Delete an existing key - returns the previous value of the key if it existed,
+   or NULL otherwise */
+void* grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map* map, uint32_t key);
+
+/* Return an existing key, or NULL if it does not exist */
+void* grpc_chttp2_stream_map_find(grpc_chttp2_stream_map* map, uint32_t key);
+
+/* Return a random entry */
+void* grpc_chttp2_stream_map_rand(grpc_chttp2_stream_map* map);
+
+/* How many (populated) entries are in the stream map? */
+size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map* map);
+
+/* Callback on each stream */
+void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map* map,
+                                     void (*f)(void* user_data, uint32_t key,
+                                               void* value),
+                                     void* user_data);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STREAM_MAP_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.cc
new file mode 100644
index 0000000..d4b0178
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.cc
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/varint.h"
+
+uint32_t grpc_chttp2_hpack_varint_length(uint32_t tail_value) {
+  if (tail_value < (1 << 7)) {
+    return 2;
+  } else if (tail_value < (1 << 14)) {
+    return 3;
+  } else if (tail_value < (1 << 21)) {
+    return 4;
+  } else if (tail_value < (1 << 28)) {
+    return 5;
+  } else {
+    return 6;
+  }
+}
+
+void grpc_chttp2_hpack_write_varint_tail(uint32_t tail_value, uint8_t* target,
+                                         uint32_t tail_length) {
+  switch (tail_length) {
+    case 5:
+      target[4] = static_cast<uint8_t>((tail_value >> 28) | 0x80);
+    /* fallthrough */
+    case 4:
+      target[3] = static_cast<uint8_t>((tail_value >> 21) | 0x80);
+    /* fallthrough */
+    case 3:
+      target[2] = static_cast<uint8_t>((tail_value >> 14) | 0x80);
+    /* fallthrough */
+    case 2:
+      target[1] = static_cast<uint8_t>((tail_value >> 7) | 0x80);
+    /* fallthrough */
+    case 1:
+      target[0] = static_cast<uint8_t>((tail_value) | 0x80);
+  }
+  target[tail_length - 1] &= 0x7f;
+}
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.h
new file mode 100644
index 0000000..5a2b670
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_VARINT_H
+#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_VARINT_H
+
+#include <grpc/support/port_platform.h>
+
+/* Helpers for hpack varint encoding */
+
+/* length of a value that needs varint tail encoding (it's bigger than can be
+   bitpacked into the opcode byte) - returned value includes the length of the
+   opcode byte */
+uint32_t grpc_chttp2_hpack_varint_length(uint32_t tail_value);
+
+void grpc_chttp2_hpack_write_varint_tail(uint32_t tail_value, uint8_t* target,
+                                         uint32_t tail_length);
+
+/* maximum value that can be bitpacked with the opcode if the opcode has a
+   prefix
+   of length prefix_bits */
+#define GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) \
+  ((uint32_t)((1 << (8 - (prefix_bits))) - 1))
+
+/* length required to bitpack a value */
+#define GRPC_CHTTP2_VARINT_LENGTH(n, prefix_bits) \
+  ((n) < GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits)   \
+       ? 1u                                       \
+       : grpc_chttp2_hpack_varint_length(         \
+             (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits)))
+
+#define GRPC_CHTTP2_WRITE_VARINT(n, prefix_bits, prefix_or, target, length)   \
+  do {                                                                        \
+    uint8_t* tgt = target;                                                    \
+    if ((length) == 1u) {                                                     \
+      (tgt)[0] = (uint8_t)((prefix_or) | (n));                                \
+    } else {                                                                  \
+      (tgt)[0] =                                                              \
+          (prefix_or) | (uint8_t)GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits);      \
+      grpc_chttp2_hpack_write_varint_tail(                                    \
+          (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits), (tgt) + 1, (length)-1); \
+    }                                                                         \
+  } while (0)
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_VARINT_H */
diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/writing.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/writing.cc
new file mode 100644
index 0000000..265d336
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/writing.cc
@@ -0,0 +1,657 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/chttp2/transport/context_list.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+
+#include <limits.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+static void add_to_write_list(grpc_chttp2_write_cb** list,
+                              grpc_chttp2_write_cb* cb) {
+  cb->next = *list;
+  *list = cb;
+}
+
+static void finish_write_cb(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                            grpc_chttp2_write_cb* cb, grpc_error* error) {
+  grpc_chttp2_complete_closure_step(t, s, &cb->closure, error,
+                                    "finish_write_cb");
+  cb->next = t->write_cb_pool;
+  t->write_cb_pool = cb;
+}
+
+static void maybe_initiate_ping(grpc_chttp2_transport* t) {
+  grpc_chttp2_ping_queue* pq = &t->ping_queue;
+  if (grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) {
+    /* no ping needed: wait */
+    return;
+  }
+  if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) {
+    /* ping already in-flight: wait */
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
+      gpr_log(GPR_INFO, "%s: Ping delayed [%p]: already pinging",
+              t->is_client ? "CLIENT" : "SERVER", t->peer_string);
+    }
+    return;
+  }
+  if (t->ping_state.pings_before_data_required == 0 &&
+      t->ping_policy.max_pings_without_data != 0) {
+    /* need to receive something of substance before sending a ping again */
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
+      gpr_log(GPR_INFO, "%s: Ping delayed [%p]: too many recent pings: %d/%d",
+              t->is_client ? "CLIENT" : "SERVER", t->peer_string,
+              t->ping_state.pings_before_data_required,
+              t->ping_policy.max_pings_without_data);
+    }
+    return;
+  }
+  grpc_millis now = grpc_core::ExecCtx::Get()->Now();
+
+  grpc_millis next_allowed_ping_interval =
+      (t->keepalive_permit_without_calls == 0 &&
+       grpc_chttp2_stream_map_size(&t->stream_map) == 0)
+          ? 7200 * GPR_MS_PER_SEC
+          : t->ping_policy.min_sent_ping_interval_without_data;
+  grpc_millis next_allowed_ping =
+      t->ping_state.last_ping_sent_time + next_allowed_ping_interval;
+
+  if (next_allowed_ping > now) {
+    /* not enough elapsed time between successive pings */
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "%s: Ping delayed [%p]: not enough time elapsed since last ping. "
+              " Last ping %f: Next ping %f: Now %f",
+              t->is_client ? "CLIENT" : "SERVER", t->peer_string,
+              static_cast<double>(t->ping_state.last_ping_sent_time),
+              static_cast<double>(next_allowed_ping), static_cast<double>(now));
+    }
+    if (!t->ping_state.is_delayed_ping_timer_set) {
+      t->ping_state.is_delayed_ping_timer_set = true;
+      GRPC_CHTTP2_REF_TRANSPORT(t, "retry_initiate_ping_locked");
+      grpc_timer_init(&t->ping_state.delayed_ping_timer, next_allowed_ping,
+                      &t->retry_initiate_ping_locked);
+    }
+    return;
+  }
+
+  pq->inflight_id = t->ping_ctr;
+  t->ping_ctr++;
+  GRPC_CLOSURE_LIST_SCHED(&pq->lists[GRPC_CHTTP2_PCL_INITIATE]);
+  grpc_closure_list_move(&pq->lists[GRPC_CHTTP2_PCL_NEXT],
+                         &pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]);
+  grpc_slice_buffer_add(&t->outbuf,
+                        grpc_chttp2_ping_create(false, pq->inflight_id));
+  GRPC_STATS_INC_HTTP2_PINGS_SENT();
+  t->ping_state.last_ping_sent_time = now;
+  if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
+    gpr_log(GPR_INFO, "%s: Ping sent [%p]: %d/%d",
+            t->is_client ? "CLIENT" : "SERVER", t->peer_string,
+            t->ping_state.pings_before_data_required,
+            t->ping_policy.max_pings_without_data);
+  }
+  t->ping_state.pings_before_data_required -=
+      (t->ping_state.pings_before_data_required != 0);
+}
+
+static bool update_list(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                        int64_t send_bytes, grpc_chttp2_write_cb** list,
+                        int64_t* ctr, grpc_error* error) {
+  bool sched_any = false;
+  grpc_chttp2_write_cb* cb = *list;
+  *list = nullptr;
+  *ctr += send_bytes;
+  while (cb) {
+    grpc_chttp2_write_cb* next = cb->next;
+    if (cb->call_at_byte <= *ctr) {
+      sched_any = true;
+      finish_write_cb(t, s, cb, GRPC_ERROR_REF(error));
+    } else {
+      add_to_write_list(list, cb);
+    }
+    cb = next;
+  }
+  GRPC_ERROR_UNREF(error);
+  return sched_any;
+}
+
+static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
+                         const char* staller) {
+  if (grpc_flowctl_trace.enabled()) {
+    gpr_log(
+        GPR_DEBUG,
+        "%s:%p stream %d moved to stalled list by %s. This is FULLY expected "
+        "to happen in a healthy program that is not seeing flow control stalls."
+        " However, if you know that there are unwanted stalls, here is some "
+        "helpful data: [fc:pending=%" PRIdPTR ":pending-compressed=%" PRIdPTR
+        ":flowed=%" PRId64 ":peer_initwin=%d:t_win=%" PRId64
+        ":s_win=%d:s_delta=%" PRId64 "]",
+        t->peer_string, t, s->id, staller, s->flow_controlled_buffer.length,
+        s->compressed_data_buffer.length, s->flow_controlled_bytes_flowed,
+        t->settings[GRPC_ACKED_SETTINGS]
+                   [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
+        t->flow_control->remote_window(),
+        static_cast<uint32_t> GPR_MAX(
+            0,
+            s->flow_control->remote_window_delta() +
+                (int64_t)t->settings[GRPC_PEER_SETTINGS]
+                                    [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]),
+        s->flow_control->remote_window_delta());
+  }
+}
+
+static bool stream_ref_if_not_destroyed(gpr_refcount* r) {
+  gpr_atm count;
+  do {
+    count = gpr_atm_acq_load(&r->count);
+    if (count == 0) return false;
+  } while (!gpr_atm_rel_cas(&r->count, count, count + 1));
+  return true;
+}
+
+/* How many bytes would we like to put on the wire during a single syscall */
+static uint32_t target_write_size(grpc_chttp2_transport* t) {
+  return 1024 * 1024;
+}
+
+// Returns true if initial_metadata contains only default headers.
+static bool is_default_initial_metadata(grpc_metadata_batch* initial_metadata) {
+  return initial_metadata->list.default_count == initial_metadata->list.count;
+}
+
+namespace {
+class StreamWriteContext;
+
+class WriteContext {
+ public:
+  WriteContext(grpc_chttp2_transport* t) : t_(t) {
+    GRPC_STATS_INC_HTTP2_WRITES_BEGUN();
+    GPR_TIMER_SCOPE("grpc_chttp2_begin_write", 0);
+  }
+
+  // TODO(ctiller): make this the destructor
+  void FlushStats() {
+    GRPC_STATS_INC_HTTP2_SEND_INITIAL_METADATA_PER_WRITE(
+        initial_metadata_writes_);
+    GRPC_STATS_INC_HTTP2_SEND_MESSAGE_PER_WRITE(message_writes_);
+    GRPC_STATS_INC_HTTP2_SEND_TRAILING_METADATA_PER_WRITE(
+        trailing_metadata_writes_);
+    GRPC_STATS_INC_HTTP2_SEND_FLOWCTL_PER_WRITE(flow_control_writes_);
+  }
+
+  void FlushSettings() {
+    if (t_->dirtied_local_settings && !t_->sent_local_settings) {
+      grpc_slice_buffer_add(
+          &t_->outbuf, grpc_chttp2_settings_create(
+                           t_->settings[GRPC_SENT_SETTINGS],
+                           t_->settings[GRPC_LOCAL_SETTINGS],
+                           t_->force_send_settings, GRPC_CHTTP2_NUM_SETTINGS));
+      t_->force_send_settings = false;
+      t_->dirtied_local_settings = false;
+      t_->sent_local_settings = true;
+      GRPC_STATS_INC_HTTP2_SETTINGS_WRITES();
+    }
+  }
+
+  void FlushQueuedBuffers() {
+    /* simple writes are queued to qbuf, and flushed here */
+    grpc_slice_buffer_move_into(&t_->qbuf, &t_->outbuf);
+    GPR_ASSERT(t_->qbuf.count == 0);
+  }
+
+  void FlushWindowUpdates() {
+    uint32_t transport_announce =
+        t_->flow_control->MaybeSendUpdate(t_->outbuf.count > 0);
+    if (transport_announce) {
+      grpc_transport_one_way_stats throwaway_stats;
+      grpc_slice_buffer_add(
+          &t_->outbuf, grpc_chttp2_window_update_create(0, transport_announce,
+                                                        &throwaway_stats));
+      ResetPingClock();
+    }
+  }
+
+  void FlushPingAcks() {
+    for (size_t i = 0; i < t_->ping_ack_count; i++) {
+      grpc_slice_buffer_add(&t_->outbuf,
+                            grpc_chttp2_ping_create(true, t_->ping_acks[i]));
+    }
+    t_->ping_ack_count = 0;
+  }
+
+  void EnactHpackSettings() {
+    grpc_chttp2_hpack_compressor_set_max_table_size(
+        &t_->hpack_compressor,
+        t_->settings[GRPC_PEER_SETTINGS]
+                    [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
+  }
+
+  void UpdateStreamsNoLongerStalled() {
+    grpc_chttp2_stream* s;
+    while (grpc_chttp2_list_pop_stalled_by_transport(t_, &s)) {
+      if (t_->closed_with_error == GRPC_ERROR_NONE &&
+          grpc_chttp2_list_add_writable_stream(t_, s)) {
+        if (!stream_ref_if_not_destroyed(&s->refcount->refs)) {
+          grpc_chttp2_list_remove_writable_stream(t_, s);
+        }
+      }
+    }
+  }
+
+  grpc_chttp2_stream* NextStream() {
+    if (t_->outbuf.length > target_write_size(t_)) {
+      result_.partial = true;
+      return nullptr;
+    }
+
+    grpc_chttp2_stream* s;
+    if (!grpc_chttp2_list_pop_writable_stream(t_, &s)) {
+      return nullptr;
+    }
+
+    return s;
+  }
+
+  void ResetPingClock() {
+    if (!t_->is_client) {
+      t_->ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
+      t_->ping_recv_state.ping_strikes = 0;
+    }
+    t_->ping_state.pings_before_data_required =
+        t_->ping_policy.max_pings_without_data;
+  }
+
+  void IncInitialMetadataWrites() { ++initial_metadata_writes_; }
+  void IncWindowUpdateWrites() { ++flow_control_writes_; }
+  void IncMessageWrites() { ++message_writes_; }
+  void IncTrailingMetadataWrites() { ++trailing_metadata_writes_; }
+
+  void NoteScheduledResults() { result_.early_results_scheduled = true; }
+
+  grpc_chttp2_transport* transport() const { return t_; }
+
+  grpc_chttp2_begin_write_result Result() {
+    result_.writing = t_->outbuf.count > 0;
+    return result_;
+  }
+
+ private:
+  grpc_chttp2_transport* const t_;
+
+  /* stats histogram counters: we increment these throughout this function,
+     and at the end publish to the central stats histograms */
+  int flow_control_writes_ = 0;
+  int initial_metadata_writes_ = 0;
+  int trailing_metadata_writes_ = 0;
+  int message_writes_ = 0;
+  grpc_chttp2_begin_write_result result_ = {false, false, false};
+};
+
+class DataSendContext {
+ public:
+  DataSendContext(WriteContext* write_context, grpc_chttp2_transport* t,
+                  grpc_chttp2_stream* s)
+      : write_context_(write_context),
+        t_(t),
+        s_(s),
+        sending_bytes_before_(s_->sending_bytes) {}
+
+  uint32_t stream_remote_window() const {
+    return static_cast<uint32_t> GPR_MAX(
+        0, s_->flow_control->remote_window_delta() +
+               (int64_t)t_->settings[GRPC_PEER_SETTINGS]
+                                    [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]);
+  }
+
+  uint32_t max_outgoing() const {
+    return static_cast<uint32_t> GPR_MIN(
+        t_->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],
+        GPR_MIN(stream_remote_window(), t_->flow_control->remote_window()));
+  }
+
+  bool AnyOutgoing() const { return max_outgoing() > 0; }
+
+  void FlushCompressedBytes() {
+    uint32_t send_bytes = static_cast<uint32_t> GPR_MIN(
+        max_outgoing(), s_->compressed_data_buffer.length);
+    bool is_last_data_frame =
+        (send_bytes == s_->compressed_data_buffer.length &&
+         s_->flow_controlled_buffer.length == 0 &&
+         s_->fetching_send_message == nullptr);
+    if (is_last_data_frame && s_->send_trailing_metadata != nullptr &&
+        s_->stream_compression_ctx != nullptr) {
+      if (GPR_UNLIKELY(!grpc_stream_compress(
+              s_->stream_compression_ctx, &s_->flow_controlled_buffer,
+              &s_->compressed_data_buffer, nullptr, MAX_SIZE_T,
+              GRPC_STREAM_COMPRESSION_FLUSH_FINISH))) {
+        gpr_log(GPR_ERROR, "Stream compression failed.");
+      }
+      grpc_stream_compression_context_destroy(s_->stream_compression_ctx);
+      s_->stream_compression_ctx = nullptr;
+      /* After finish, bytes in s->compressed_data_buffer may be
+       * more than max_outgoing. Start another round of the current
+       * while loop so that send_bytes and is_last_data_frame are
+       * recalculated. */
+      return;
+    }
+    is_last_frame_ = is_last_data_frame &&
+                     s_->send_trailing_metadata != nullptr &&
+                     grpc_metadata_batch_is_empty(s_->send_trailing_metadata);
+    grpc_chttp2_encode_data(s_->id, &s_->compressed_data_buffer, send_bytes,
+                            is_last_frame_, &s_->stats.outgoing, &t_->outbuf);
+    s_->flow_control->SentData(send_bytes);
+    s_->byte_counter += send_bytes;
+    if (s_->compressed_data_buffer.length == 0) {
+      s_->sending_bytes += s_->uncompressed_data_size;
+    }
+  }
+
+  void CompressMoreBytes() {
+    if (s_->stream_compression_ctx == nullptr) {
+      s_->stream_compression_ctx =
+          grpc_stream_compression_context_create(s_->stream_compression_method);
+    }
+    s_->uncompressed_data_size = s_->flow_controlled_buffer.length;
+    if (GPR_UNLIKELY(!grpc_stream_compress(
+            s_->stream_compression_ctx, &s_->flow_controlled_buffer,
+            &s_->compressed_data_buffer, nullptr, MAX_SIZE_T,
+            GRPC_STREAM_COMPRESSION_FLUSH_SYNC))) {
+      gpr_log(GPR_ERROR, "Stream compression failed.");
+    }
+  }
+
+  bool is_last_frame() const { return is_last_frame_; }
+
+  void CallCallbacks() {
+    if (update_list(
+            t_, s_,
+            static_cast<int64_t>(s_->sending_bytes - sending_bytes_before_),
+            &s_->on_flow_controlled_cbs, &s_->flow_controlled_bytes_flowed,
+            GRPC_ERROR_NONE)) {
+      write_context_->NoteScheduledResults();
+    }
+  }
+
+ private:
+  WriteContext* write_context_;
+  grpc_chttp2_transport* t_;
+  grpc_chttp2_stream* s_;
+  const size_t sending_bytes_before_;
+  bool is_last_frame_ = false;
+};
+
+class StreamWriteContext {
+ public:
+  StreamWriteContext(WriteContext* write_context, grpc_chttp2_stream* s)
+      : write_context_(write_context), t_(write_context->transport()), s_(s) {
+    GRPC_CHTTP2_IF_TRACING(
+        gpr_log(GPR_INFO, "W:%p %s[%d] im-(sent,send)=(%d,%d) announce=%d", t_,
+                t_->is_client ? "CLIENT" : "SERVER", s->id,
+                s->sent_initial_metadata, s->send_initial_metadata != nullptr,
+                (int)(s->flow_control->local_window_delta() -
+                      s->flow_control->announced_window_delta())));
+  }
+
+  void FlushInitialMetadata() {
+    /* send initial metadata if it's available */
+    if (s_->sent_initial_metadata) return;
+    if (s_->send_initial_metadata == nullptr) return;
+
+    // We skip this on the server side if there is no custom initial
+    // metadata, there are no messages to send, and we are also sending
+    // trailing metadata.  This results in a Trailers-Only response,
+    // which is required for retries, as per:
+    // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid
+    if (!t_->is_client && s_->fetching_send_message == nullptr &&
+        s_->flow_controlled_buffer.length == 0 &&
+        s_->compressed_data_buffer.length == 0 &&
+        s_->send_trailing_metadata != nullptr &&
+        is_default_initial_metadata(s_->send_initial_metadata)) {
+      ConvertInitialMetadataToTrailingMetadata();
+    } else {
+      grpc_encode_header_options hopt = {
+          s_->id,  // stream_id
+          false,   // is_eof
+          t_->settings[GRPC_PEER_SETTINGS]
+                      [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] !=
+              0,  // use_true_binary_metadata
+          t_->settings[GRPC_PEER_SETTINGS]
+                      [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],  // max_frame_size
+          &s_->stats.outgoing                                 // stats
+      };
+      grpc_chttp2_encode_header(&t_->hpack_compressor, nullptr, 0,
+                                s_->send_initial_metadata, &hopt, &t_->outbuf);
+      write_context_->ResetPingClock();
+      write_context_->IncInitialMetadataWrites();
+    }
+
+    s_->send_initial_metadata = nullptr;
+    s_->sent_initial_metadata = true;
+    write_context_->NoteScheduledResults();
+    grpc_chttp2_complete_closure_step(
+        t_, s_, &s_->send_initial_metadata_finished, GRPC_ERROR_NONE,
+        "send_initial_metadata_finished");
+  }
+
+  void FlushWindowUpdates() {
+    /* send any window updates */
+    const uint32_t stream_announce = s_->flow_control->MaybeSendUpdate();
+    if (stream_announce == 0) return;
+
+    grpc_slice_buffer_add(
+        &t_->outbuf, grpc_chttp2_window_update_create(s_->id, stream_announce,
+                                                      &s_->stats.outgoing));
+    write_context_->ResetPingClock();
+    write_context_->IncWindowUpdateWrites();
+  }
+
+  void FlushData() {
+    if (!s_->sent_initial_metadata) return;
+
+    if (s_->flow_controlled_buffer.length == 0 &&
+        s_->compressed_data_buffer.length == 0) {
+      return;  // early out: nothing to do
+    }
+
+    DataSendContext data_send_context(write_context_, t_, s_);
+
+    if (!data_send_context.AnyOutgoing()) {
+      if (t_->flow_control->remote_window() <= 0) {
+        report_stall(t_, s_, "transport");
+        grpc_chttp2_list_add_stalled_by_transport(t_, s_);
+      } else if (data_send_context.stream_remote_window() <= 0) {
+        report_stall(t_, s_, "stream");
+        grpc_chttp2_list_add_stalled_by_stream(t_, s_);
+      }
+      return;  // early out: nothing to do
+    }
+
+    while ((s_->flow_controlled_buffer.length > 0 ||
+            s_->compressed_data_buffer.length > 0) &&
+           data_send_context.max_outgoing() > 0) {
+      if (s_->compressed_data_buffer.length > 0) {
+        data_send_context.FlushCompressedBytes();
+      } else {
+        data_send_context.CompressMoreBytes();
+      }
+    }
+    if (s_->traced && grpc_endpoint_can_track_err(t_->ep)) {
+      grpc_core::ContextList::Append(&t_->cl, s_);
+    }
+    write_context_->ResetPingClock();
+    if (data_send_context.is_last_frame()) {
+      SentLastFrame();
+    }
+    data_send_context.CallCallbacks();
+    stream_became_writable_ = true;
+    if (s_->flow_controlled_buffer.length > 0 ||
+        s_->compressed_data_buffer.length > 0) {
+      GRPC_CHTTP2_STREAM_REF(s_, "chttp2_writing:fork");
+      grpc_chttp2_list_add_writable_stream(t_, s_);
+    }
+    write_context_->IncMessageWrites();
+  }
+
+  void FlushTrailingMetadata() {
+    if (!s_->sent_initial_metadata) return;
+
+    if (s_->send_trailing_metadata == nullptr) return;
+    if (s_->fetching_send_message != nullptr) return;
+    if (s_->flow_controlled_buffer.length != 0) return;
+    if (s_->compressed_data_buffer.length != 0) return;
+
+    GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata"));
+    if (grpc_metadata_batch_is_empty(s_->send_trailing_metadata)) {
+      grpc_chttp2_encode_data(s_->id, &s_->flow_controlled_buffer, 0, true,
+                              &s_->stats.outgoing, &t_->outbuf);
+    } else {
+      grpc_encode_header_options hopt = {
+          s_->id, true,
+          t_->settings[GRPC_PEER_SETTINGS]
+                      [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] !=
+              0,
+
+          t_->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE],
+          &s_->stats.outgoing};
+      grpc_chttp2_encode_header(&t_->hpack_compressor,
+                                extra_headers_for_trailing_metadata_,
+                                num_extra_headers_for_trailing_metadata_,
+                                s_->send_trailing_metadata, &hopt, &t_->outbuf);
+    }
+    write_context_->IncTrailingMetadataWrites();
+    write_context_->ResetPingClock();
+    SentLastFrame();
+
+    write_context_->NoteScheduledResults();
+    grpc_chttp2_complete_closure_step(
+        t_, s_, &s_->send_trailing_metadata_finished, GRPC_ERROR_NONE,
+        "send_trailing_metadata_finished");
+  }
+
+  bool stream_became_writable() { return stream_became_writable_; }
+
+ private:
+  void ConvertInitialMetadataToTrailingMetadata() {
+    GRPC_CHTTP2_IF_TRACING(
+        gpr_log(GPR_INFO, "not sending initial_metadata (Trailers-Only)"));
+    // When sending Trailers-Only, we need to move the :status and
+    // content-type headers to the trailers.
+    if (s_->send_initial_metadata->idx.named.status != nullptr) {
+      extra_headers_for_trailing_metadata_
+          [num_extra_headers_for_trailing_metadata_++] =
+              &s_->send_initial_metadata->idx.named.status->md;
+    }
+    if (s_->send_initial_metadata->idx.named.content_type != nullptr) {
+      extra_headers_for_trailing_metadata_
+          [num_extra_headers_for_trailing_metadata_++] =
+              &s_->send_initial_metadata->idx.named.content_type->md;
+    }
+  }
+
+  void SentLastFrame() {
+    s_->send_trailing_metadata = nullptr;
+    s_->sent_trailing_metadata = true;
+    s_->eos_sent = true;
+
+    if (!t_->is_client && !s_->read_closed) {
+      grpc_slice_buffer_add(
+          &t_->outbuf, grpc_chttp2_rst_stream_create(
+                           s_->id, GRPC_HTTP2_NO_ERROR, &s_->stats.outgoing));
+    }
+    grpc_chttp2_mark_stream_closed(t_, s_, !t_->is_client, true,
+                                   GRPC_ERROR_NONE);
+  }
+
+  WriteContext* const write_context_;
+  grpc_chttp2_transport* const t_;
+  grpc_chttp2_stream* const s_;
+  bool stream_became_writable_ = false;
+  grpc_mdelem* extra_headers_for_trailing_metadata_[2];
+  size_t num_extra_headers_for_trailing_metadata_ = 0;
+};
+}  // namespace
+
+grpc_chttp2_begin_write_result grpc_chttp2_begin_write(
+    grpc_chttp2_transport* t) {
+  WriteContext ctx(t);
+  ctx.FlushSettings();
+  ctx.FlushPingAcks();
+  ctx.FlushQueuedBuffers();
+  ctx.EnactHpackSettings();
+
+  if (t->flow_control->remote_window() > 0) {
+    ctx.UpdateStreamsNoLongerStalled();
+  }
+
+  /* for each grpc_chttp2_stream that's become writable, frame it's data
+     (according to available window sizes) and add to the output buffer */
+  while (grpc_chttp2_stream* s = ctx.NextStream()) {
+    StreamWriteContext stream_ctx(&ctx, s);
+    stream_ctx.FlushInitialMetadata();
+    stream_ctx.FlushWindowUpdates();
+    stream_ctx.FlushData();
+    stream_ctx.FlushTrailingMetadata();
+
+    if (stream_ctx.stream_became_writable()) {
+      if (!grpc_chttp2_list_add_writing_stream(t, s)) {
+        /* already in writing list: drop ref */
+        GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:already_writing");
+      } else {
+        /* ref will be dropped at end of write */
+      }
+    } else {
+      GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:no_write");
+    }
+  }
+
+  ctx.FlushWindowUpdates();
+
+  maybe_initiate_ping(t);
+
+  return ctx.Result();
+}
+
+void grpc_chttp2_end_write(grpc_chttp2_transport* t, grpc_error* error) {
+  GPR_TIMER_SCOPE("grpc_chttp2_end_write", 0);
+  grpc_chttp2_stream* s;
+
+  if (t->channelz_socket != nullptr) {
+    t->channelz_socket->RecordMessagesSent(t->num_messages_in_next_write);
+  }
+  t->num_messages_in_next_write = 0;
+
+  while (grpc_chttp2_list_pop_writing_stream(t, &s)) {
+    if (s->sending_bytes != 0) {
+      update_list(t, s, static_cast<int64_t>(s->sending_bytes),
+                  &s->on_write_finished_cbs, &s->flow_controlled_bytes_written,
+                  GRPC_ERROR_REF(error));
+      s->sending_bytes = 0;
+    }
+    GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:end");
+  }
+  grpc_slice_buffer_reset_and_unref_internal(&t->outbuf);
+  GRPC_ERROR_UNREF(error);
+}
diff --git a/third_party/grpc/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc b/third_party/grpc/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc
new file mode 100644
index 0000000..40a30e4
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/impl/codegen/port_platform.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/grpc_cronet.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/ext/transport/cronet/transport/cronet_transport.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/transport_impl.h"
+
+// Cronet transport object
+typedef struct cronet_transport {
+  grpc_transport base;  // must be first element in this structure
+  void* engine;
+  char* host;
+} cronet_transport;
+
+extern grpc_transport_vtable grpc_cronet_vtable;
+
+GRPCAPI grpc_channel* grpc_cronet_secure_channel_create(
+    void* engine, const char* target, const grpc_channel_args* args,
+    void* reserved) {
+  gpr_log(GPR_DEBUG,
+          "grpc_create_cronet_transport: stream_engine = %p, target=%s", engine,
+          target);
+
+  grpc_transport* ct =
+      grpc_create_cronet_transport(engine, target, args, reserved);
+
+  grpc_core::ExecCtx exec_ctx;
+  return grpc_channel_create(target, args, GRPC_CLIENT_DIRECT_CHANNEL, ct);
+}
diff --git a/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc
new file mode 100644
index 0000000..1a6bded
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* This file has empty implementation of all the functions exposed by the cronet
+library, so we can build it in all environments */
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include <grpc/support/log.h>
+
+#include "third_party/objective_c/Cronet/bidirectional_stream_c.h"
+
+#ifdef GRPC_COMPILE_WITH_CRONET
+/* link with the real CRONET library in the build system */
+#else
+/* Dummy implementation of cronet API just to test for build-ability */
+bidirectional_stream* bidirectional_stream_create(
+    stream_engine* engine, void* annotation,
+    bidirectional_stream_callback* callback) {
+  GPR_ASSERT(0);
+  return nullptr;
+}
+
+int bidirectional_stream_destroy(bidirectional_stream* stream) {
+  GPR_ASSERT(0);
+  return 0;
+}
+
+int bidirectional_stream_start(bidirectional_stream* stream, const char* url,
+                               int priority, const char* method,
+                               const bidirectional_stream_header_array* headers,
+                               bool end_of_stream) {
+  GPR_ASSERT(0);
+  return 0;
+}
+
+int bidirectional_stream_read(bidirectional_stream* stream, char* buffer,
+                              int capacity) {
+  GPR_ASSERT(0);
+  return 0;
+}
+
+int bidirectional_stream_write(bidirectional_stream* stream, const char* buffer,
+                               int count, bool end_of_stream) {
+  GPR_ASSERT(0);
+  return 0;
+}
+
+void bidirectional_stream_cancel(bidirectional_stream* stream) {
+  GPR_ASSERT(0);
+}
+
+void bidirectional_stream_disable_auto_flush(bidirectional_stream* stream,
+                                             bool disable_auto_flush) {
+  GPR_ASSERT(0);
+}
+
+void bidirectional_stream_delay_request_headers_until_flush(
+    bidirectional_stream* stream, bool delay_headers_until_flush) {
+  GPR_ASSERT(0);
+}
+
+void bidirectional_stream_flush(bidirectional_stream* stream) { GPR_ASSERT(0); }
+
+#endif /* GRPC_COMPILE_WITH_CRONET */
diff --git a/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.cc b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.cc
new file mode 100644
index 0000000..349d868
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.cc
@@ -0,0 +1,1491 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/transport/bin_decoder.h"
+#include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
+#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h"
+#include "src/core/ext/transport/cronet/transport/cronet_transport.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.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/bidirectional_stream_c.h"
+
+#define GRPC_HEADER_SIZE_IN_BYTES 5
+#define GRPC_FLUSH_READ_SIZE 4096
+
+#define CRONET_LOG(...)                          \
+  do {                                           \
+    if (grpc_cronet_trace) gpr_log(__VA_ARGS__); \
+  } while (0)
+
+/* 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_stream_ready(bidirectional_stream*);
+static void on_response_headers_received(
+    bidirectional_stream*, const bidirectional_stream_header_array*,
+    const char*);
+static void on_write_completed(bidirectional_stream*, const char*);
+static void on_read_completed(bidirectional_stream*, char*, int);
+static void on_response_trailers_received(
+    bidirectional_stream*, const bidirectional_stream_header_array*);
+static void on_succeeded(bidirectional_stream*);
+static void on_failed(bidirectional_stream*, int);
+static void on_canceled(bidirectional_stream*);
+static bidirectional_stream_callback cronet_callbacks = {
+    on_stream_ready,
+    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 */
+  stream_engine* engine;
+  char* host;
+  bool use_packet_coalescing;
+};
+typedef struct grpc_cronet_transport grpc_cronet_transport;
+
+/* TODO (makdharma): reorder structure for memory efficiency per
+   http://www.catb.org/esr/structure-packing/#_structure_reordering: */
+struct read_state {
+  read_state(gpr_arena* arena)
+      : trailing_metadata(arena), initial_metadata(arena) {
+    grpc_slice_buffer_init(&read_slice_buffer);
+  }
+
+  /* vars to store data coming from server */
+  char* read_buffer = nullptr;
+  bool length_field_received = false;
+  int received_bytes = 0;
+  int remaining_bytes = 0;
+  int length_field = 0;
+  bool compressed = 0;
+  char grpc_header_bytes[GRPC_HEADER_SIZE_IN_BYTES] = {};
+  char* payload_field = nullptr;
+  bool read_stream_closed = 0;
+
+  /* vars for holding data destined for the application */
+  grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> sbs;
+  grpc_slice_buffer read_slice_buffer;
+
+  /* vars for trailing metadata */
+  grpc_chttp2_incoming_metadata_buffer trailing_metadata;
+  bool trailing_metadata_valid = false;
+
+  /* vars for initial metadata */
+  grpc_chttp2_incoming_metadata_buffer initial_metadata;
+};
+
+struct write_state {
+  char* write_buffer = nullptr;
+};
+
+/* track state of one stream op */
+struct op_state {
+  op_state(gpr_arena* arena) : rs(arena) {}
+
+  bool state_op_done[OP_NUM_OPS] = {};
+  bool state_callback_received[OP_NUM_OPS] = {};
+  /* A non-zero gRPC status code has been seen */
+  bool fail_state = false;
+  /* Transport is discarding all buffered messages */
+  bool flush_read = false;
+  bool flush_cronet_when_ready = false;
+  bool pending_write_for_trailer = false;
+  bool pending_send_message = false;
+  /* User requested RECV_TRAILING_METADATA */
+  bool pending_recv_trailing_metadata = false;
+  /* Cronet has not issued a callback of a bidirectional read */
+  bool pending_read_from_cronet = false;
+  grpc_error* cancel_error = GRPC_ERROR_NONE;
+  /* 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;
+};
+
+struct stream_obj;
+
+struct op_and_state {
+  op_and_state(stream_obj* s, const grpc_transport_stream_op_batch& op);
+
+  grpc_transport_stream_op_batch op;
+  struct op_state state;
+  bool done = false;
+  struct stream_obj* s; /* Pointer back to the stream object */
+  /* next op_and_state in the linked list */
+  struct op_and_state* next;
+};
+
+struct op_storage {
+  int num_pending_ops = 0;
+  struct op_and_state* head = nullptr;
+};
+
+struct stream_obj {
+  stream_obj(grpc_transport* gt, grpc_stream* gs,
+             grpc_stream_refcount* refcount, gpr_arena* arena);
+  ~stream_obj();
+
+  gpr_arena* arena;
+  struct op_and_state* oas = nullptr;
+  grpc_transport_stream_op_batch* curr_op = nullptr;
+  grpc_cronet_transport* curr_ct;
+  grpc_stream* curr_gs;
+  bidirectional_stream* cbs = nullptr;
+  bidirectional_stream_header_array header_array =
+      bidirectional_stream_header_array();  // Zero-initialize the structure.
+
+  /* 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;
+
+  /* Refcount object of the stream */
+  grpc_stream_refcount* refcount;
+};
+
+#ifndef NDEBUG
+#define GRPC_CRONET_STREAM_REF(stream, reason) \
+  grpc_cronet_stream_ref((stream), (reason))
+#define GRPC_CRONET_STREAM_UNREF(stream, reason) \
+  grpc_cronet_stream_unref((stream), (reason))
+void grpc_cronet_stream_ref(stream_obj* s, const char* reason) {
+  grpc_stream_ref(s->refcount, reason);
+}
+void grpc_cronet_stream_unref(stream_obj* s, const char* reason) {
+  grpc_stream_unref(s->refcount, reason);
+}
+#else
+#define GRPC_CRONET_STREAM_REF(stream, reason) grpc_cronet_stream_ref((stream))
+#define GRPC_CRONET_STREAM_UNREF(stream, reason) \
+  grpc_cronet_stream_unref((stream))
+void grpc_cronet_stream_ref(stream_obj* s) { grpc_stream_ref(s->refcount); }
+void grpc_cronet_stream_unref(stream_obj* s) { grpc_stream_unref(s->refcount); }
+#endif
+
+static enum e_op_result execute_stream_op(struct op_and_state* oas);
+
+/*
+  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 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 null_and_maybe_free_read_buffer(stream_obj* s) {
+  if (s->state.rs.read_buffer &&
+      s->state.rs.read_buffer != s->state.rs.grpc_header_bytes) {
+    gpr_free(s->state.rs.read_buffer);
+  }
+  s->state.rs.read_buffer = nullptr;
+}
+
+static void maybe_flush_read(stream_obj* s) {
+  /* To enter flush read state (discarding all the buffered messages in
+   * transport layer), two conditions must be satisfied: 1) non-zero grpc status
+   * has been received, and 2) an op requesting the status code
+   * (RECV_TRAILING_METADATA) is issued by the user. (See
+   * doc/status_ordering.md) */
+  /* Whenever the evaluation of any of the two condition is changed, we check
+   * whether we should enter the flush read state. */
+  if (s->state.pending_recv_trailing_metadata && s->state.fail_state) {
+    if (!s->state.flush_read && !s->state.rs.read_stream_closed) {
+      CRONET_LOG(GPR_DEBUG, "%p: Flush read", s);
+      s->state.flush_read = true;
+      null_and_maybe_free_read_buffer(s);
+      s->state.rs.read_buffer =
+          static_cast<char*>(gpr_malloc(GRPC_FLUSH_READ_SIZE));
+      if (!s->state.pending_read_from_cronet) {
+        CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs);
+        bidirectional_stream_read(s->cbs, s->state.rs.read_buffer,
+                                  GRPC_FLUSH_READ_SIZE);
+        s->state.pending_read_from_cronet = true;
+      }
+    }
+  }
+}
+
+static grpc_error* make_error_with_desc(int error_code, const char* desc) {
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc);
+  error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, error_code);
+  return error;
+}
+
+inline op_and_state::op_and_state(stream_obj* s,
+                                  const grpc_transport_stream_op_batch& op)
+    : op(op), state(s->arena), s(s), next(s->storage.head) {}
+
+/*
+  Add a new stream op to op storage.
+*/
+static void add_to_storage(struct stream_obj* s,
+                           grpc_transport_stream_op_batch* 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 */
+  op_and_state* new_op = grpc_core::New<op_and_state>(s, *op);
+  gpr_mu_lock(&s->mu);
+  storage->head = new_op;
+  storage->num_pending_ops++;
+  if (op->send_message) {
+    s->state.pending_send_message = true;
+  }
+  if (op->recv_trailing_metadata) {
+    s->state.pending_recv_trailing_metadata = true;
+    maybe_flush_read(s);
+  }
+  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 == nullptr || oas == nullptr) {
+    return;
+  }
+  if (s->storage.head == oas) {
+    s->storage.head = oas->next;
+    grpc_core::Delete(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 != nullptr; 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);
+        grpc_core::Delete(oas);
+        break;
+      } else if (GPR_UNLIKELY(curr->next == nullptr)) {
+        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) {
+  gpr_mu_lock(&s->mu);
+  for (struct op_and_state* curr = s->storage.head; curr != nullptr;) {
+    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(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;
+    }
+  }
+  gpr_mu_unlock(&s->mu);
+}
+
+static void convert_cronet_array_to_metadata(
+    const bidirectional_stream_header_array* header_array,
+    grpc_chttp2_incoming_metadata_buffer* mds) {
+  for (size_t i = 0; i < header_array->count; i++) {
+    CRONET_LOG(GPR_DEBUG, "header key=%s, value=%s",
+               header_array->headers[i].key, header_array->headers[i].value);
+    grpc_slice key = grpc_slice_intern(
+        grpc_slice_from_static_string(header_array->headers[i].key));
+    grpc_slice value;
+    if (grpc_is_binary_header(key)) {
+      value = grpc_slice_from_static_string(header_array->headers[i].value);
+      value = grpc_slice_intern(grpc_chttp2_base64_decode_with_length(
+          value, grpc_chttp2_base64_infer_length_after_decode(value)));
+    } else {
+      value = grpc_slice_intern(
+          grpc_slice_from_static_string(header_array->headers[i].value));
+    }
+    GRPC_LOG_IF_ERROR("convert_cronet_array_to_metadata",
+                      grpc_chttp2_incoming_metadata_buffer_add(
+                          mds, grpc_mdelem_from_slices(key, value)));
+  }
+}
+
+/*
+  Cronet callback
+*/
+static void on_failed(bidirectional_stream* stream, int net_error) {
+  gpr_log(GPR_ERROR, "on_failed(%p, %d)", stream, net_error);
+  grpc_core::ExecCtx exec_ctx;
+
+  stream_obj* s = static_cast<stream_obj*>(stream->annotation);
+  gpr_mu_lock(&s->mu);
+  bidirectional_stream_destroy(s->cbs);
+  s->state.state_callback_received[OP_FAILED] = true;
+  s->cbs = nullptr;
+  if (s->header_array.headers) {
+    gpr_free(s->header_array.headers);
+    s->header_array.headers = nullptr;
+  }
+  if (s->state.ws.write_buffer) {
+    gpr_free(s->state.ws.write_buffer);
+    s->state.ws.write_buffer = nullptr;
+  }
+  null_and_maybe_free_read_buffer(s);
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+  GRPC_CRONET_STREAM_UNREF(s, "cronet transport");
+}
+
+/*
+  Cronet callback
+*/
+static void on_canceled(bidirectional_stream* stream) {
+  CRONET_LOG(GPR_DEBUG, "on_canceled(%p)", stream);
+  grpc_core::ExecCtx exec_ctx;
+
+  stream_obj* s = static_cast<stream_obj*>(stream->annotation);
+  gpr_mu_lock(&s->mu);
+  bidirectional_stream_destroy(s->cbs);
+  s->state.state_callback_received[OP_CANCELED] = true;
+  s->cbs = nullptr;
+  if (s->header_array.headers) {
+    gpr_free(s->header_array.headers);
+    s->header_array.headers = nullptr;
+  }
+  if (s->state.ws.write_buffer) {
+    gpr_free(s->state.ws.write_buffer);
+    s->state.ws.write_buffer = nullptr;
+  }
+  null_and_maybe_free_read_buffer(s);
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+  GRPC_CRONET_STREAM_UNREF(s, "cronet transport");
+}
+
+/*
+  Cronet callback
+*/
+static void on_succeeded(bidirectional_stream* stream) {
+  CRONET_LOG(GPR_DEBUG, "on_succeeded(%p)", stream);
+  grpc_core::ExecCtx exec_ctx;
+
+  stream_obj* s = static_cast<stream_obj*>(stream->annotation);
+  gpr_mu_lock(&s->mu);
+  bidirectional_stream_destroy(s->cbs);
+  s->state.state_callback_received[OP_SUCCEEDED] = true;
+  s->cbs = nullptr;
+  null_and_maybe_free_read_buffer(s);
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+  GRPC_CRONET_STREAM_UNREF(s, "cronet transport");
+}
+
+/*
+  Cronet callback
+*/
+static void on_stream_ready(bidirectional_stream* stream) {
+  CRONET_LOG(GPR_DEBUG, "W: on_stream_ready(%p)", stream);
+  grpc_core::ExecCtx exec_ctx;
+  stream_obj* s = static_cast<stream_obj*>(stream->annotation);
+  grpc_cronet_transport* t = s->curr_ct;
+  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 = nullptr;
+  }
+  /* Send the initial metadata on wire if there is no SEND_MESSAGE or
+   * SEND_TRAILING_METADATA ops pending */
+  if (t->use_packet_coalescing) {
+    if (s->state.flush_cronet_when_ready) {
+      CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_flush (%p)", s->cbs);
+      bidirectional_stream_flush(stream);
+    }
+  }
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+}
+
+/*
+  Cronet callback
+*/
+static void on_response_headers_received(
+    bidirectional_stream* stream,
+    const bidirectional_stream_header_array* headers,
+    const char* negotiated_protocol) {
+  grpc_core::ExecCtx exec_ctx;
+  CRONET_LOG(GPR_DEBUG, "R: on_response_headers_received(%p, %p, %s)", stream,
+             headers, negotiated_protocol);
+  stream_obj* s = static_cast<stream_obj*>(stream->annotation);
+
+  /* Identify if this is a header or a trailer (in a trailer-only response case)
+   */
+  for (size_t i = 0; i < headers->count; i++) {
+    if (0 == strcmp("grpc-status", headers->headers[i].key)) {
+      on_response_trailers_received(stream, headers);
+      return;
+    }
+  }
+
+  gpr_mu_lock(&s->mu);
+  convert_cronet_array_to_metadata(headers, &s->state.rs.initial_metadata);
+  s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true;
+  if (!(s->state.state_op_done[OP_CANCEL_ERROR] ||
+        s->state.state_callback_received[OP_FAILED])) {
+    /* Do an extra read to trigger on_succeeded() callback in case connection
+     is closed */
+    GPR_ASSERT(s->state.rs.length_field_received == false);
+    s->state.rs.read_buffer = s->state.rs.grpc_header_bytes;
+    s->state.rs.compressed = false;
+    s->state.rs.received_bytes = 0;
+    s->state.rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES;
+    CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs);
+    bidirectional_stream_read(s->cbs, s->state.rs.read_buffer,
+                              s->state.rs.remaining_bytes);
+    s->state.pending_read_from_cronet = true;
+  }
+  gpr_mu_unlock(&s->mu);
+  execute_from_storage(s);
+}
+
+/*
+  Cronet callback
+*/
+static void on_write_completed(bidirectional_stream* stream, const char* data) {
+  grpc_core::ExecCtx exec_ctx;
+  stream_obj* s = static_cast<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 = nullptr;
+  }
+  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(bidirectional_stream* stream, char* data,
+                              int count) {
+  grpc_core::ExecCtx exec_ctx;
+  stream_obj* s = static_cast<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.pending_read_from_cronet = false;
+  s->state.state_callback_received[OP_RECV_MESSAGE] = true;
+  if (count > 0 && s->state.flush_read) {
+    CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs);
+    bidirectional_stream_read(s->cbs, s->state.rs.read_buffer,
+                              GRPC_FLUSH_READ_SIZE);
+    s->state.pending_read_from_cronet = true;
+    gpr_mu_unlock(&s->mu);
+  } else 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, "bidirectional_stream_read(%p)", s->cbs);
+      s->state.state_op_done[OP_READ_REQ_MADE] = true;
+      bidirectional_stream_read(
+          s->cbs, s->state.rs.read_buffer + s->state.rs.received_bytes,
+          s->state.rs.remaining_bytes);
+      s->state.pending_read_from_cronet = true;
+      gpr_mu_unlock(&s->mu);
+    } else {
+      gpr_mu_unlock(&s->mu);
+      execute_from_storage(s);
+    }
+  } else {
+    null_and_maybe_free_read_buffer(s);
+    s->state.rs.read_stream_closed = true;
+    gpr_mu_unlock(&s->mu);
+    execute_from_storage(s);
+  }
+}
+
+/*
+  Cronet callback
+*/
+static void on_response_trailers_received(
+    bidirectional_stream* stream,
+    const bidirectional_stream_header_array* trailers) {
+  grpc_core::ExecCtx exec_ctx;
+  CRONET_LOG(GPR_DEBUG, "R: on_response_trailers_received(%p,%p)", stream,
+             trailers);
+  stream_obj* s = static_cast<stream_obj*>(stream->annotation);
+  grpc_cronet_transport* t = s->curr_ct;
+  gpr_mu_lock(&s->mu);
+  s->state.rs.trailing_metadata_valid = false;
+  convert_cronet_array_to_metadata(trailers, &s->state.rs.trailing_metadata);
+  if (trailers->count > 0) {
+    s->state.rs.trailing_metadata_valid = true;
+  }
+  for (size_t i = 0; i < trailers->count; i++) {
+    if (0 == strcmp(trailers->headers[i].key, "grpc-status") &&
+        0 != strcmp(trailers->headers[i].value, "0")) {
+      s->state.fail_state = true;
+      maybe_flush_read(s);
+    }
+  }
+  s->state.state_callback_received[OP_RECV_TRAILING_METADATA] = true;
+  /* Send a EOS when server terminates the stream (testServerFinishesRequest) to
+   * trigger on_succeeded */
+  if (!s->state.state_op_done[OP_SEND_TRAILING_METADATA] &&
+      !(s->state.state_op_done[OP_CANCEL_ERROR] ||
+        s->state.state_callback_received[OP_FAILED])) {
+    CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, 0)", s->cbs);
+    s->state.state_callback_received[OP_SEND_MESSAGE] = false;
+    bidirectional_stream_write(s->cbs, "", 0, true);
+    if (t->use_packet_coalescing) {
+      CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs);
+      bidirectional_stream_flush(s->cbs);
+    }
+    s->state.state_op_done[OP_SEND_TRAILING_METADATA] = true;
+
+    gpr_mu_unlock(&s->mu);
+  } else {
+    gpr_mu_unlock(&s->mu);
+    execute_from_storage(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(grpc_slice_buffer* write_slice_buffer,
+                              char** pp_write_buffer,
+                              size_t* p_write_buffer_size, uint32_t flags) {
+  grpc_slice slice = grpc_slice_buffer_take_first(write_slice_buffer);
+  size_t length = GRPC_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 =
+      static_cast<char*>(gpr_malloc(length + GRPC_HEADER_SIZE_IN_BYTES));
+  *pp_write_buffer = write_buffer;
+  uint8_t* p = reinterpret_cast<uint8_t*>(write_buffer);
+  /* Append 5 byte header */
+  /* Compressed flag */
+  *p++ = static_cast<uint8_t>((flags & GRPC_WRITE_INTERNAL_COMPRESS) ? 1 : 0);
+  /* Message length */
+  *p++ = static_cast<uint8_t>(length >> 24);
+  *p++ = static_cast<uint8_t>(length >> 16);
+  *p++ = static_cast<uint8_t>(length >> 8);
+  *p++ = static_cast<uint8_t>(length);
+  /* append actual data */
+  memcpy(p, GRPC_SLICE_START_PTR(slice), length);
+  grpc_slice_unref_internal(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,
+    bidirectional_stream_header** pp_headers, size_t* p_num_headers,
+    const char** method) {
+  grpc_linked_mdelem* curr = head;
+  /* Walk the linked list and get number of header fields */
+  size_t num_headers_available = 0;
+  while (curr != nullptr) {
+    curr = curr->next;
+    num_headers_available++;
+  }
+  /* Allocate enough memory. It is freed in the on_stream_ready callback
+   */
+  bidirectional_stream_header* headers =
+      static_cast<bidirectional_stream_header*>(gpr_malloc(
+          sizeof(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;
+    char* key = grpc_slice_to_c_string(GRPC_MDKEY(mdelem));
+    char* value;
+    if (grpc_is_binary_header(GRPC_MDKEY(mdelem))) {
+      grpc_slice wire_value = grpc_chttp2_base64_encode(GRPC_MDVALUE(mdelem));
+      value = grpc_slice_to_c_string(wire_value);
+      grpc_slice_unref_internal(wire_value);
+    } else {
+      value = grpc_slice_to_c_string(GRPC_MDVALUE(mdelem));
+    }
+    if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_SCHEME) ||
+        grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_AUTHORITY)) {
+      /* Cronet populates these fields on its own */
+      gpr_free(key);
+      gpr_free(value);
+      continue;
+    }
+    if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_METHOD)) {
+      if (grpc_slice_eq(GRPC_MDVALUE(mdelem), GRPC_MDSTR_PUT)) {
+        *method = "PUT";
+      } else {
+        /* POST method in default*/
+        *method = "POST";
+      }
+      gpr_free(key);
+      gpr_free(value);
+      continue;
+    }
+    if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_PATH)) {
+      /* Create URL by appending :path value to the hostname */
+      gpr_asprintf(pp_url, "https://%s%s", host, value);
+      gpr_free(key);
+      gpr_free(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 == nullptr) {
+      break;
+    }
+  }
+  *p_num_headers = num_headers;
+}
+
+static void parse_grpc_header(const uint8_t* data, int* length,
+                              bool* compressed) {
+  const uint8_t c = *data;
+  const uint8_t* p = data + 1;
+  *compressed = ((c & 0x01) == 0x01);
+  *length = 0;
+  *length |= (*p++) << 24;
+  *length |= (*p++) << 16;
+  *length |= (*p++) << 8;
+  *length |= (*p++);
+}
+
+static bool header_has_authority(grpc_linked_mdelem* head) {
+  while (head != nullptr) {
+    if (grpc_slice_eq(GRPC_MDKEY(head->md), GRPC_MDSTR_AUTHORITY)) {
+      return true;
+    }
+    head = head->next;
+  }
+  return false;
+}
+
+/*
+  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_batch* curr_op,
+                          struct stream_obj* s, struct op_state* op_state,
+                          enum e_op_id op_id) {
+  struct op_state* stream_state = &s->state;
+  grpc_cronet_transport* t = s->curr_ct;
+  bool result = true;
+  /* When call is canceled, every op can be run, except under following
+  conditions
+  */
+  bool is_canceled_or_failed = stream_state->state_op_done[OP_CANCEL_ERROR] ||
+                               stream_state->state_callback_received[OP_FAILED];
+  if (is_canceled_or_failed) {
+    if (op_id == OP_SEND_INITIAL_METADATA) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    }
+    if (op_id == OP_SEND_MESSAGE) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    }
+    if (op_id == OP_SEND_TRAILING_METADATA) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    }
+    if (op_id == OP_CANCEL_ERROR) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    }
+    /* already executed */
+    if (op_id == OP_RECV_INITIAL_METADATA &&
+        stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    }
+    if (op_id == OP_RECV_MESSAGE && op_state->state_op_done[OP_RECV_MESSAGE]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    }
+    if (op_id == OP_RECV_TRAILING_METADATA &&
+        stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    }
+    /* ON_COMPLETE can be processed if one of the following conditions is met:
+     * 1. the stream failed
+     * 2. the stream is cancelled, and the callback is received
+     * 3. the stream succeeded before cancel is effective
+     * 4. the stream is cancelled, and the stream is never started */
+    if (op_id == OP_ON_COMPLETE &&
+        !(stream_state->state_callback_received[OP_FAILED] ||
+          stream_state->state_callback_received[OP_CANCELED] ||
+          stream_state->state_callback_received[OP_SUCCEEDED] ||
+          !stream_state->state_op_done[OP_SEND_INITIAL_METADATA])) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      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] &&
+             !stream_state->state_op_done[OP_RECV_TRAILING_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] &&
+             !stream_state->state_op_done[OP_RECV_TRAILING_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 (stream_state->pending_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] &&
+             !(t->use_packet_coalescing &&
+               stream_state->pending_write_for_trailer))
+      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. */
+    /* TODO(muxi): We should not consider the recv ops here, since they
+     * have their own callbacks.  We should invoke a batch's on_complete
+     * as soon as all of the batch's send ops are complete, even if
+     * there are still recv ops pending. */
+    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 &&
+               !op_state->state_op_done[OP_RECV_MESSAGE]) {
+      CRONET_LOG(GPR_DEBUG, "Because");
+      result = false;
+    } else if (curr_op->cancel_stream &&
+               !stream_state->state_callback_received[OP_CANCELED]) {
+      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;
+      }
+      /* 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;
+        }
+      }
+    }
+    /* 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;
+  }
+  CRONET_LOG(GPR_DEBUG, "op_can_be_run %s : %s", op_id_string(op_id),
+             result ? "YES" : "NO");
+  return result;
+}
+
+/*
+  TODO (makdharma): Break down this function in smaller chunks for readability.
+*/
+static enum e_op_result execute_stream_op(struct op_and_state* oas) {
+  grpc_transport_stream_op_batch* stream_op = &oas->op;
+  struct stream_obj* s = oas->s;
+  grpc_cronet_transport* t = s->curr_ct;
+  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, s, &oas->state, OP_SEND_INITIAL_METADATA)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_INITIAL_METADATA", oas);
+    /* Start new cronet stream. It is destroyed in on_succeeded, on_canceled,
+     * on_failed */
+    GPR_ASSERT(s->cbs == nullptr);
+    GPR_ASSERT(!stream_state->state_op_done[OP_SEND_INITIAL_METADATA]);
+    s->cbs =
+        bidirectional_stream_create(t->engine, s->curr_gs, &cronet_callbacks);
+    CRONET_LOG(GPR_DEBUG, "%p = bidirectional_stream_create()", s->cbs);
+    if (t->use_packet_coalescing) {
+      bidirectional_stream_disable_auto_flush(s->cbs, true);
+      bidirectional_stream_delay_request_headers_until_flush(s->cbs, true);
+    }
+    char* url = nullptr;
+    const char* method = "POST";
+    s->header_array.headers = nullptr;
+    convert_metadata_to_cronet_headers(stream_op->payload->send_initial_metadata
+                                           .send_initial_metadata->list.head,
+                                       t->host, &url, &s->header_array.headers,
+                                       &s->header_array.count, &method);
+    s->header_array.capacity = s->header_array.count;
+    CRONET_LOG(GPR_DEBUG, "bidirectional_stream_start(%p, %s)", s->cbs, url);
+    bidirectional_stream_start(s->cbs, url, 0, method, &s->header_array, false);
+    if (url) {
+      gpr_free(url);
+    }
+    unsigned int header_index;
+    for (header_index = 0; header_index < s->header_array.count;
+         header_index++) {
+      gpr_free((void*)s->header_array.headers[header_index].key);
+      gpr_free((void*)s->header_array.headers[header_index].value);
+    }
+    stream_state->state_op_done[OP_SEND_INITIAL_METADATA] = true;
+    if (t->use_packet_coalescing) {
+      if (!stream_op->send_message && !stream_op->send_trailing_metadata) {
+        s->state.flush_cronet_when_ready = true;
+      }
+    }
+    result = ACTION_TAKEN_WITH_CALLBACK;
+  } else if (stream_op->send_message &&
+             op_can_be_run(stream_op, s, &oas->state, OP_SEND_MESSAGE)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_SEND_MESSAGE", oas);
+    stream_state->pending_send_message = false;
+    if (stream_state->state_callback_received[OP_FAILED]) {
+      result = NO_ACTION_POSSIBLE;
+      CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed.");
+    } else {
+      grpc_slice_buffer write_slice_buffer;
+      grpc_slice slice;
+      grpc_slice_buffer_init(&write_slice_buffer);
+      if (1 != stream_op->payload->send_message.send_message->Next(
+                   stream_op->payload->send_message.send_message->length(),
+                   nullptr)) {
+        /* Should never reach here */
+        GPR_ASSERT(false);
+      }
+      if (GRPC_ERROR_NONE !=
+          stream_op->payload->send_message.send_message->Pull(&slice)) {
+        /* Should never reach here */
+        GPR_ASSERT(false);
+      }
+      grpc_slice_buffer_add(&write_slice_buffer, slice);
+      if (GPR_UNLIKELY(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,
+            stream_op->payload->send_message.send_message->flags());
+        CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs,
+                   stream_state->ws.write_buffer);
+        stream_state->state_callback_received[OP_SEND_MESSAGE] = false;
+        bidirectional_stream_write(s->cbs, stream_state->ws.write_buffer,
+                                   static_cast<int>(write_buffer_size), false);
+        grpc_slice_buffer_destroy_internal(&write_slice_buffer);
+        if (t->use_packet_coalescing) {
+          if (!stream_op->send_trailing_metadata) {
+            CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs);
+            bidirectional_stream_flush(s->cbs);
+            result = ACTION_TAKEN_WITH_CALLBACK;
+          } else {
+            stream_state->pending_write_for_trailer = true;
+            result = ACTION_TAKEN_NO_CALLBACK;
+          }
+        } else {
+          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;
+    stream_op->payload->send_message.send_message.reset();
+  } else if (stream_op->send_trailing_metadata &&
+             op_can_be_run(stream_op, s, &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, "bidirectional_stream_write (%p, 0)", s->cbs);
+      stream_state->state_callback_received[OP_SEND_MESSAGE] = false;
+      bidirectional_stream_write(s->cbs, "", 0, true);
+      if (t->use_packet_coalescing) {
+        CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs);
+        bidirectional_stream_flush(s->cbs);
+      }
+      result = ACTION_TAKEN_WITH_CALLBACK;
+    }
+    stream_state->state_op_done[OP_SEND_TRAILING_METADATA] = true;
+  } else if (stream_op->recv_initial_metadata &&
+             op_can_be_run(stream_op, s, &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]) {
+      GRPC_CLOSURE_SCHED(
+          stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready,
+          GRPC_ERROR_NONE);
+    } else if (stream_state->state_callback_received[OP_FAILED]) {
+      GRPC_CLOSURE_SCHED(
+          stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready,
+          GRPC_ERROR_NONE);
+    } else if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) {
+      GRPC_CLOSURE_SCHED(
+          stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready,
+          GRPC_ERROR_NONE);
+    } else {
+      grpc_chttp2_incoming_metadata_buffer_publish(
+          &oas->s->state.rs.initial_metadata,
+          stream_op->payload->recv_initial_metadata.recv_initial_metadata);
+      GRPC_CLOSURE_SCHED(
+          stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready,
+          GRPC_ERROR_NONE);
+    }
+    stream_state->state_op_done[OP_RECV_INITIAL_METADATA] = true;
+    result = ACTION_TAKEN_NO_CALLBACK;
+  } else if (stream_op->recv_message &&
+             op_can_be_run(stream_op, s, &oas->state, OP_RECV_MESSAGE)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_MESSAGE", oas);
+    if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
+      CRONET_LOG(GPR_DEBUG, "Stream is cancelled.");
+      GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready,
+                         GRPC_ERROR_NONE);
+      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->state_callback_received[OP_FAILED]) {
+      CRONET_LOG(GPR_DEBUG, "Stream failed.");
+      GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready,
+                         GRPC_ERROR_NONE);
+      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.read_stream_closed == true) {
+      /* No more data will be received */
+      CRONET_LOG(GPR_DEBUG, "read stream closed");
+      GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready,
+                         GRPC_ERROR_NONE);
+      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->flush_read) {
+      CRONET_LOG(GPR_DEBUG, "flush read");
+      GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready,
+                         GRPC_ERROR_NONE);
+      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.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;
+        parse_grpc_header(
+            reinterpret_cast<const uint8_t*>(stream_state->rs.read_buffer),
+            &stream_state->rs.length_field, &stream_state->rs.compressed);
+        CRONET_LOG(GPR_DEBUG, "length field = %d",
+                   stream_state->rs.length_field);
+        if (stream_state->rs.length_field > 0) {
+          stream_state->rs.read_buffer = static_cast<char*>(
+              gpr_malloc(static_cast<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, "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 */
+          bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer,
+                                    stream_state->rs.remaining_bytes);
+          stream_state->pending_read_from_cronet = true;
+          result = ACTION_TAKEN_WITH_CALLBACK;
+        } else {
+          stream_state->rs.remaining_bytes = 0;
+          CRONET_LOG(GPR_DEBUG, "read operation complete. Empty response.");
+          /* Clean up read_slice_buffer in case there is unread data. */
+          grpc_slice_buffer_destroy_internal(
+              &stream_state->rs.read_slice_buffer);
+          grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer);
+          uint32_t flags = 0;
+          if (stream_state->rs.compressed) {
+            flags |= GRPC_WRITE_INTERNAL_COMPRESS;
+          }
+          stream_state->rs.sbs.Init(&stream_state->rs.read_slice_buffer, flags);
+          stream_op->payload->recv_message.recv_message->reset(
+              stream_state->rs.sbs.get());
+          GRPC_CLOSURE_SCHED(
+              stream_op->payload->recv_message.recv_message_ready,
+              GRPC_ERROR_NONE);
+          stream_state->state_op_done[OP_RECV_MESSAGE] = true;
+          oas->state.state_op_done[OP_RECV_MESSAGE] = true;
+
+          /* Extra read to trigger on_succeed */
+          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;
+          stream_state->rs.compressed = false;
+          stream_state->rs.length_field_received = false;
+          CRONET_LOG(GPR_DEBUG, "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 */
+          bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer,
+                                    stream_state->rs.remaining_bytes);
+          stream_state->pending_read_from_cronet = 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;
+        stream_state->rs.compressed = false;
+        CRONET_LOG(GPR_DEBUG, "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 */
+        bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer,
+                                  stream_state->rs.remaining_bytes);
+        stream_state->pending_read_from_cronet = true;
+        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");
+      grpc_slice read_data_slice =
+          GRPC_SLICE_MALLOC((uint32_t)stream_state->rs.length_field);
+      uint8_t* dst_p = GRPC_SLICE_START_PTR(read_data_slice);
+      memcpy(dst_p, stream_state->rs.read_buffer,
+             static_cast<size_t>(stream_state->rs.length_field));
+      null_and_maybe_free_read_buffer(s);
+      /* Clean up read_slice_buffer in case there is unread data. */
+      grpc_slice_buffer_destroy_internal(&stream_state->rs.read_slice_buffer);
+      grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer);
+      grpc_slice_buffer_add(&stream_state->rs.read_slice_buffer,
+                            read_data_slice);
+      uint32_t flags = 0;
+      if (stream_state->rs.compressed) {
+        flags = GRPC_WRITE_INTERNAL_COMPRESS;
+      }
+      stream_state->rs.sbs.Init(&stream_state->rs.read_slice_buffer, flags);
+      stream_op->payload->recv_message.recv_message->reset(
+          stream_state->rs.sbs.get());
+      GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready,
+                         GRPC_ERROR_NONE);
+      stream_state->state_op_done[OP_RECV_MESSAGE] = true;
+      oas->state.state_op_done[OP_RECV_MESSAGE] = true;
+      /* Do an extra read to trigger on_succeeded() callback in case connection
+         is closed */
+      stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes;
+      stream_state->rs.compressed = false;
+      stream_state->rs.received_bytes = 0;
+      stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES;
+      stream_state->rs.length_field_received = false;
+      CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs);
+      bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer,
+                                stream_state->rs.remaining_bytes);
+      stream_state->pending_read_from_cronet = true;
+      result = ACTION_TAKEN_NO_CALLBACK;
+    }
+  } else if (stream_op->recv_trailing_metadata &&
+             op_can_be_run(stream_op, s, &oas->state,
+                           OP_RECV_TRAILING_METADATA)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_TRAILING_METADATA", oas);
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
+      error = GRPC_ERROR_REF(stream_state->cancel_error);
+    } else if (stream_state->state_callback_received[OP_FAILED]) {
+      error = make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable.");
+    } else if (oas->s->state.rs.trailing_metadata_valid) {
+      grpc_chttp2_incoming_metadata_buffer_publish(
+          &oas->s->state.rs.trailing_metadata,
+          stream_op->payload->recv_trailing_metadata.recv_trailing_metadata);
+      stream_state->rs.trailing_metadata_valid = false;
+    }
+    GRPC_CLOSURE_SCHED(
+        stream_op->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+        error);
+    stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true;
+    result = ACTION_TAKEN_NO_CALLBACK;
+  } else if (stream_op->cancel_stream &&
+             op_can_be_run(stream_op, s, &oas->state, OP_CANCEL_ERROR)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_CANCEL_ERROR", oas);
+    if (s->cbs) {
+      CRONET_LOG(GPR_DEBUG, "W: bidirectional_stream_cancel(%p)", s->cbs);
+      bidirectional_stream_cancel(s->cbs);
+      result = ACTION_TAKEN_WITH_CALLBACK;
+    } else {
+      result = ACTION_TAKEN_NO_CALLBACK;
+    }
+    stream_state->state_op_done[OP_CANCEL_ERROR] = true;
+    if (!stream_state->cancel_error) {
+      stream_state->cancel_error =
+          GRPC_ERROR_REF(stream_op->payload->cancel_stream.cancel_error);
+    }
+  } else if (stream_op->on_complete &&
+             op_can_be_run(stream_op, s, &oas->state, OP_ON_COMPLETE)) {
+    CRONET_LOG(GPR_DEBUG, "running: %p  OP_ON_COMPLETE", oas);
+    if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
+      GRPC_CLOSURE_SCHED(stream_op->on_complete,
+                         GRPC_ERROR_REF(stream_state->cancel_error));
+    } else if (stream_state->state_callback_received[OP_FAILED]) {
+      GRPC_CLOSURE_SCHED(
+          stream_op->on_complete,
+          make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable."));
+    } else {
+      /* All actions in this stream_op are complete. Call the on_complete
+       * callback
+       */
+      GRPC_CLOSURE_SCHED(stream_op->on_complete, GRPC_ERROR_NONE);
+    }
+    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.
+*/
+
+inline stream_obj::stream_obj(grpc_transport* gt, grpc_stream* gs,
+                              grpc_stream_refcount* refcount, gpr_arena* arena)
+    : arena(arena),
+      curr_ct(reinterpret_cast<grpc_cronet_transport*>(gt)),
+      curr_gs(gs),
+      state(arena),
+      refcount(refcount) {
+  GRPC_CRONET_STREAM_REF(this, "cronet transport");
+  gpr_mu_init(&mu);
+}
+
+inline stream_obj::~stream_obj() {
+  null_and_maybe_free_read_buffer(this);
+  /* Clean up read_slice_buffer in case there is unread data. */
+  grpc_slice_buffer_destroy_internal(&state.rs.read_slice_buffer);
+  GRPC_ERROR_UNREF(state.cancel_error);
+}
+
+static int init_stream(grpc_transport* gt, grpc_stream* gs,
+                       grpc_stream_refcount* refcount, const void* server_data,
+                       gpr_arena* arena) {
+  new (gs) stream_obj(gt, gs, refcount, arena);
+  return 0;
+}
+
+static void set_pollset_do_nothing(grpc_transport* gt, grpc_stream* gs,
+                                   grpc_pollset* pollset) {}
+
+static void set_pollset_set_do_nothing(grpc_transport* gt, grpc_stream* gs,
+                                       grpc_pollset_set* pollset_set) {}
+
+static void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
+                              grpc_transport_stream_op_batch* op) {
+  CRONET_LOG(GPR_DEBUG, "perform_stream_op");
+  if (op->send_initial_metadata &&
+      header_has_authority(op->payload->send_initial_metadata
+                               .send_initial_metadata->list.head)) {
+    /* Cronet does not support :authority header field. We cancel the call when
+     this field is present in metadata */
+    if (op->recv_initial_metadata) {
+      GRPC_CLOSURE_SCHED(
+          op->payload->recv_initial_metadata.recv_initial_metadata_ready,
+          GRPC_ERROR_CANCELLED);
+    }
+    if (op->recv_message) {
+      GRPC_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready,
+                         GRPC_ERROR_CANCELLED);
+    }
+    if (op->recv_trailing_metadata) {
+      GRPC_CLOSURE_SCHED(
+          op->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+          GRPC_ERROR_CANCELLED);
+    }
+    GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_CANCELLED);
+    return;
+  }
+  stream_obj* s = reinterpret_cast<stream_obj*>(gs);
+  add_to_storage(s, op);
+  execute_from_storage(s);
+}
+
+static void destroy_stream(grpc_transport* gt, grpc_stream* gs,
+                           grpc_closure* then_schedule_closure) {
+  stream_obj* s = reinterpret_cast<stream_obj*>(gs);
+  s->~stream_obj();
+  GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE);
+}
+
+static void destroy_transport(grpc_transport* gt) {}
+
+static grpc_endpoint* get_endpoint(grpc_transport* gt) { return nullptr; }
+
+static void perform_op(grpc_transport* gt, grpc_transport_op* op) {}
+
+static 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,
+    perform_op,
+    destroy_stream,
+    destroy_transport,
+    get_endpoint};
+
+grpc_transport* grpc_create_cronet_transport(void* engine, const char* target,
+                                             const grpc_channel_args* args,
+                                             void* reserved) {
+  grpc_cronet_transport* ct = static_cast<grpc_cronet_transport*>(
+      gpr_malloc(sizeof(grpc_cronet_transport)));
+  if (!ct) {
+    goto error;
+  }
+  ct->base.vtable = &grpc_cronet_vtable;
+  ct->engine = static_cast<stream_engine*>(engine);
+  ct->host = static_cast<char*>(gpr_malloc(strlen(target) + 1));
+  if (!ct->host) {
+    goto error;
+  }
+  strcpy(ct->host, target);
+
+  ct->use_packet_coalescing = true;
+  if (args) {
+    for (size_t i = 0; i < args->num_args; i++) {
+      if (0 ==
+          strcmp(args->args[i].key, GRPC_ARG_USE_CRONET_PACKET_COALESCING)) {
+        if (GPR_UNLIKELY(args->args[i].type != GRPC_ARG_INTEGER)) {
+          gpr_log(GPR_ERROR, "%s ignored: it must be an integer",
+                  GRPC_ARG_USE_CRONET_PACKET_COALESCING);
+        } else {
+          ct->use_packet_coalescing = (args->args[i].value.integer != 0);
+        }
+      }
+    }
+  }
+
+  return &ct->base;
+
+error:
+  if (ct) {
+    if (ct->host) {
+      gpr_free(ct->host);
+    }
+    gpr_free(ct);
+  }
+
+  return nullptr;
+}
diff --git a/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.h b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.h
new file mode 100644
index 0000000..fb7e149
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H
+#define GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/transport.h"
+
+grpc_transport* grpc_create_cronet_transport(void* engine, const char* target,
+                                             const grpc_channel_args* args,
+                                             void* reserved);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H */
diff --git a/third_party/grpc/src/core/ext/transport/inproc/inproc_plugin.cc b/third_party/grpc/src/core/ext/transport/inproc/inproc_plugin.cc
new file mode 100644
index 0000000..8e251fa
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/inproc/inproc_plugin.cc
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/inproc/inproc_transport.h"
+#include "src/core/lib/debug/trace.h"
+
+grpc_core::TraceFlag grpc_inproc_trace(false, "inproc");
+
+void grpc_inproc_plugin_init(void) { grpc_inproc_transport_init(); }
+
+void grpc_inproc_plugin_shutdown(void) { grpc_inproc_transport_shutdown(); }
diff --git a/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.cc b/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.cc
new file mode 100644
index 0000000..0b9bf5d
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.cc
@@ -0,0 +1,1258 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include <string.h>
+#include "src/core/ext/transport/inproc/inproc_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/channel_stack_type.h"
+#include "src/core/lib/surface/server.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/transport/transport_impl.h"
+
+#define INPROC_LOG(...)                                    \
+  do {                                                     \
+    if (grpc_inproc_trace.enabled()) gpr_log(__VA_ARGS__); \
+  } while (0)
+
+namespace {
+grpc_slice g_empty_slice;
+grpc_slice g_fake_path_key;
+grpc_slice g_fake_path_value;
+grpc_slice g_fake_auth_key;
+grpc_slice g_fake_auth_value;
+
+struct inproc_stream;
+bool cancel_stream_locked(inproc_stream* s, grpc_error* error);
+void op_state_machine(void* arg, grpc_error* error);
+void log_metadata(const grpc_metadata_batch* md_batch, bool is_client,
+                  bool is_initial);
+grpc_error* fill_in_metadata(inproc_stream* s,
+                             const grpc_metadata_batch* metadata,
+                             uint32_t flags, grpc_metadata_batch* out_md,
+                             uint32_t* outflags, bool* markfilled);
+
+struct shared_mu {
+  shared_mu() {
+    // Share one lock between both sides since both sides get affected
+    gpr_mu_init(&mu);
+    gpr_ref_init(&refs, 2);
+  }
+
+  gpr_mu mu;
+  gpr_refcount refs;
+};
+
+struct inproc_transport {
+  inproc_transport(const grpc_transport_vtable* vtable, shared_mu* mu,
+                   bool is_client)
+      : mu(mu), is_client(is_client) {
+    base.vtable = vtable;
+    // Start each side of transport with 2 refs since they each have a ref
+    // to the other
+    gpr_ref_init(&refs, 2);
+    grpc_connectivity_state_init(&connectivity, GRPC_CHANNEL_READY,
+                                 is_client ? "inproc_client" : "inproc_server");
+  }
+
+  ~inproc_transport() {
+    grpc_connectivity_state_destroy(&connectivity);
+    if (gpr_unref(&mu->refs)) {
+      gpr_free(mu);
+    }
+  }
+
+  void ref() {
+    INPROC_LOG(GPR_INFO, "ref_transport %p", this);
+    gpr_ref(&refs);
+  }
+
+  void unref() {
+    INPROC_LOG(GPR_INFO, "unref_transport %p", this);
+    if (!gpr_unref(&refs)) {
+      return;
+    }
+    INPROC_LOG(GPR_INFO, "really_destroy_transport %p", this);
+    this->~inproc_transport();
+    gpr_free(this);
+  }
+
+  grpc_transport base;
+  shared_mu* mu;
+  gpr_refcount refs;
+  bool is_client;
+  grpc_connectivity_state_tracker connectivity;
+  void (*accept_stream_cb)(void* user_data, grpc_transport* transport,
+                           const void* server_data);
+  void* accept_stream_data;
+  bool is_closed = false;
+  struct inproc_transport* other_side;
+  struct inproc_stream* stream_list = nullptr;
+};
+
+struct inproc_stream {
+  inproc_stream(inproc_transport* t, grpc_stream_refcount* refcount,
+                const void* server_data, gpr_arena* arena)
+      : t(t), refs(refcount), arena(arena) {
+    // Ref this stream right now for ctor and list.
+    ref("inproc_init_stream:init");
+    ref("inproc_init_stream:list");
+
+    grpc_metadata_batch_init(&to_read_initial_md);
+    grpc_metadata_batch_init(&to_read_trailing_md);
+    GRPC_CLOSURE_INIT(&op_closure, op_state_machine, this,
+                      grpc_schedule_on_exec_ctx);
+    grpc_metadata_batch_init(&write_buffer_initial_md);
+    grpc_metadata_batch_init(&write_buffer_trailing_md);
+
+    stream_list_prev = nullptr;
+    gpr_mu_lock(&t->mu->mu);
+    stream_list_next = t->stream_list;
+    if (t->stream_list) {
+      t->stream_list->stream_list_prev = this;
+    }
+    t->stream_list = this;
+    gpr_mu_unlock(&t->mu->mu);
+
+    if (!server_data) {
+      t->ref();
+      inproc_transport* st = t->other_side;
+      st->ref();
+      other_side = nullptr;  // will get filled in soon
+      // Pass the client-side stream address to the server-side for a ref
+      ref("inproc_init_stream:clt");  // ref it now on behalf of server
+                                      // side to avoid destruction
+      INPROC_LOG(GPR_INFO, "calling accept stream cb %p %p",
+                 st->accept_stream_cb, st->accept_stream_data);
+      (*st->accept_stream_cb)(st->accept_stream_data, &st->base, (void*)this);
+    } else {
+      // This is the server-side and is being called through accept_stream_cb
+      inproc_stream* cs = (inproc_stream*)server_data;
+      other_side = cs;
+      // Ref the server-side stream on behalf of the client now
+      ref("inproc_init_stream:srv");
+
+      // Now we are about to affect the other side, so lock the transport
+      // to make sure that it doesn't get destroyed
+      gpr_mu_lock(&t->mu->mu);
+      cs->other_side = this;
+      // Now transfer from the other side's write_buffer if any to the to_read
+      // buffer
+      if (cs->write_buffer_initial_md_filled) {
+        fill_in_metadata(this, &cs->write_buffer_initial_md,
+                         cs->write_buffer_initial_md_flags, &to_read_initial_md,
+                         &to_read_initial_md_flags, &to_read_initial_md_filled);
+        deadline = GPR_MIN(deadline, cs->write_buffer_deadline);
+        grpc_metadata_batch_clear(&cs->write_buffer_initial_md);
+        cs->write_buffer_initial_md_filled = false;
+      }
+      if (cs->write_buffer_trailing_md_filled) {
+        fill_in_metadata(this, &cs->write_buffer_trailing_md, 0,
+                         &to_read_trailing_md, nullptr,
+                         &to_read_trailing_md_filled);
+        grpc_metadata_batch_clear(&cs->write_buffer_trailing_md);
+        cs->write_buffer_trailing_md_filled = false;
+      }
+      if (cs->write_buffer_cancel_error != GRPC_ERROR_NONE) {
+        cancel_other_error = cs->write_buffer_cancel_error;
+        cs->write_buffer_cancel_error = GRPC_ERROR_NONE;
+      }
+
+      gpr_mu_unlock(&t->mu->mu);
+    }
+  }
+
+  ~inproc_stream() {
+    GRPC_ERROR_UNREF(write_buffer_cancel_error);
+    GRPC_ERROR_UNREF(cancel_self_error);
+    GRPC_ERROR_UNREF(cancel_other_error);
+
+    if (recv_inited) {
+      grpc_slice_buffer_destroy_internal(&recv_message);
+    }
+
+    t->unref();
+
+    if (closure_at_destroy) {
+      GRPC_CLOSURE_SCHED(closure_at_destroy, GRPC_ERROR_NONE);
+    }
+  }
+
+#ifndef NDEBUG
+#define STREAM_REF(refs, reason) grpc_stream_ref(refs, reason)
+#define STREAM_UNREF(refs, reason) grpc_stream_unref(refs, reason)
+#else
+#define STREAM_REF(refs, reason) grpc_stream_ref(refs)
+#define STREAM_UNREF(refs, reason) grpc_stream_unref(refs)
+#endif
+  void ref(const char* reason) {
+    INPROC_LOG(GPR_INFO, "ref_stream %p %s", this, reason);
+    STREAM_REF(refs, reason);
+  }
+
+  void unref(const char* reason) {
+    INPROC_LOG(GPR_INFO, "unref_stream %p %s", this, reason);
+    STREAM_UNREF(refs, reason);
+  }
+#undef STREAM_REF
+#undef STREAM_UNREF
+
+  inproc_transport* t;
+  grpc_metadata_batch to_read_initial_md;
+  uint32_t to_read_initial_md_flags = 0;
+  bool to_read_initial_md_filled = false;
+  grpc_metadata_batch to_read_trailing_md;
+  bool to_read_trailing_md_filled = false;
+  bool ops_needed = false;
+  bool op_closure_scheduled = false;
+  grpc_closure op_closure;
+  // Write buffer used only during gap at init time when client-side
+  // stream is set up but server side stream is not yet set up
+  grpc_metadata_batch write_buffer_initial_md;
+  bool write_buffer_initial_md_filled = false;
+  uint32_t write_buffer_initial_md_flags = 0;
+  grpc_millis write_buffer_deadline = GRPC_MILLIS_INF_FUTURE;
+  grpc_metadata_batch write_buffer_trailing_md;
+  bool write_buffer_trailing_md_filled = false;
+  grpc_error* write_buffer_cancel_error = GRPC_ERROR_NONE;
+
+  struct inproc_stream* other_side;
+  bool other_side_closed = false;               // won't talk anymore
+  bool write_buffer_other_side_closed = false;  // on hold
+  grpc_stream_refcount* refs;
+  grpc_closure* closure_at_destroy = nullptr;
+
+  gpr_arena* arena;
+
+  grpc_transport_stream_op_batch* send_message_op = nullptr;
+  grpc_transport_stream_op_batch* send_trailing_md_op = nullptr;
+  grpc_transport_stream_op_batch* recv_initial_md_op = nullptr;
+  grpc_transport_stream_op_batch* recv_message_op = nullptr;
+  grpc_transport_stream_op_batch* recv_trailing_md_op = nullptr;
+
+  grpc_slice_buffer recv_message;
+  grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> recv_stream;
+  bool recv_inited = false;
+
+  bool initial_md_sent = false;
+  bool trailing_md_sent = false;
+  bool initial_md_recvd = false;
+  bool trailing_md_recvd = false;
+
+  bool closed = false;
+
+  grpc_error* cancel_self_error = GRPC_ERROR_NONE;
+  grpc_error* cancel_other_error = GRPC_ERROR_NONE;
+
+  grpc_millis deadline = GRPC_MILLIS_INF_FUTURE;
+
+  bool listed = true;
+  struct inproc_stream* stream_list_prev;
+  struct inproc_stream* stream_list_next;
+};
+
+void log_metadata(const grpc_metadata_batch* md_batch, bool is_client,
+                  bool is_initial) {
+  for (grpc_linked_mdelem* md = md_batch->list.head; md != nullptr;
+       md = md->next) {
+    char* key = grpc_slice_to_c_string(GRPC_MDKEY(md->md));
+    char* value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md));
+    gpr_log(GPR_INFO, "INPROC:%s:%s: %s: %s", is_initial ? "HDR" : "TRL",
+            is_client ? "CLI" : "SVR", key, value);
+    gpr_free(key);
+    gpr_free(value);
+  }
+}
+
+grpc_error* fill_in_metadata(inproc_stream* s,
+                             const grpc_metadata_batch* metadata,
+                             uint32_t flags, grpc_metadata_batch* out_md,
+                             uint32_t* outflags, bool* markfilled) {
+  if (grpc_inproc_trace.enabled()) {
+    log_metadata(metadata, s->t->is_client, outflags != nullptr);
+  }
+
+  if (outflags != nullptr) {
+    *outflags = flags;
+  }
+  if (markfilled != nullptr) {
+    *markfilled = true;
+  }
+  grpc_error* error = GRPC_ERROR_NONE;
+  for (grpc_linked_mdelem* elem = metadata->list.head;
+       (elem != nullptr) && (error == GRPC_ERROR_NONE); elem = elem->next) {
+    grpc_linked_mdelem* nelem = static_cast<grpc_linked_mdelem*>(
+        gpr_arena_alloc(s->arena, sizeof(*nelem)));
+    nelem->md =
+        grpc_mdelem_from_slices(grpc_slice_intern(GRPC_MDKEY(elem->md)),
+                                grpc_slice_intern(GRPC_MDVALUE(elem->md)));
+
+    error = grpc_metadata_batch_link_tail(out_md, nelem);
+  }
+  return error;
+}
+
+int init_stream(grpc_transport* gt, grpc_stream* gs,
+                grpc_stream_refcount* refcount, const void* server_data,
+                gpr_arena* arena) {
+  INPROC_LOG(GPR_INFO, "init_stream %p %p %p", gt, gs, server_data);
+  inproc_transport* t = reinterpret_cast<inproc_transport*>(gt);
+  new (gs) inproc_stream(t, refcount, server_data, arena);
+  return 0;  // return value is not important
+}
+
+void close_stream_locked(inproc_stream* s) {
+  if (!s->closed) {
+    // Release the metadata that we would have written out
+    grpc_metadata_batch_destroy(&s->write_buffer_initial_md);
+    grpc_metadata_batch_destroy(&s->write_buffer_trailing_md);
+
+    if (s->listed) {
+      inproc_stream* p = s->stream_list_prev;
+      inproc_stream* n = s->stream_list_next;
+      if (p != nullptr) {
+        p->stream_list_next = n;
+      } else {
+        s->t->stream_list = n;
+      }
+      if (n != nullptr) {
+        n->stream_list_prev = p;
+      }
+      s->listed = false;
+      s->unref("close_stream:list");
+    }
+    s->closed = true;
+    s->unref("close_stream:closing");
+  }
+}
+
+// This function means that we are done talking/listening to the other side
+void close_other_side_locked(inproc_stream* s, const char* reason) {
+  if (s->other_side != nullptr) {
+    // First release the metadata that came from the other side's arena
+    grpc_metadata_batch_destroy(&s->to_read_initial_md);
+    grpc_metadata_batch_destroy(&s->to_read_trailing_md);
+
+    s->other_side->unref(reason);
+    s->other_side_closed = true;
+    s->other_side = nullptr;
+  } else if (!s->other_side_closed) {
+    s->write_buffer_other_side_closed = true;
+  }
+}
+
+// Call the on_complete closure associated with this stream_op_batch if
+// this stream_op_batch is only one of the pending operations for this
+// stream. This is called when one of the pending operations for the stream
+// is done and about to be NULLed out
+void complete_if_batch_end_locked(inproc_stream* s, grpc_error* error,
+                                  grpc_transport_stream_op_batch* op,
+                                  const char* msg) {
+  int is_sm = static_cast<int>(op == s->send_message_op);
+  int is_stm = static_cast<int>(op == s->send_trailing_md_op);
+  // TODO(vjpai): We should not consider the recv ops here, since they
+  // have their own callbacks.  We should invoke a batch's on_complete
+  // as soon as all of the batch's send ops are complete, even if there
+  // are still recv ops pending.
+  int is_rim = static_cast<int>(op == s->recv_initial_md_op);
+  int is_rm = static_cast<int>(op == s->recv_message_op);
+  int is_rtm = static_cast<int>(op == s->recv_trailing_md_op);
+
+  if ((is_sm + is_stm + is_rim + is_rm + is_rtm) == 1) {
+    INPROC_LOG(GPR_INFO, "%s %p %p %p", msg, s, op, error);
+    GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_REF(error));
+  }
+}
+
+void maybe_schedule_op_closure_locked(inproc_stream* s, grpc_error* error) {
+  if (s && s->ops_needed && !s->op_closure_scheduled) {
+    GRPC_CLOSURE_SCHED(&s->op_closure, GRPC_ERROR_REF(error));
+    s->op_closure_scheduled = true;
+    s->ops_needed = false;
+  }
+}
+
+void fail_helper_locked(inproc_stream* s, grpc_error* error) {
+  INPROC_LOG(GPR_INFO, "op_state_machine %p fail_helper", s);
+  // If we're failing this side, we need to make sure that
+  // we also send or have already sent trailing metadata
+  if (!s->trailing_md_sent) {
+    // Send trailing md to the other side indicating cancellation
+    s->trailing_md_sent = true;
+
+    grpc_metadata_batch fake_md;
+    grpc_metadata_batch_init(&fake_md);
+
+    inproc_stream* other = s->other_side;
+    grpc_metadata_batch* dest = (other == nullptr)
+                                    ? &s->write_buffer_trailing_md
+                                    : &other->to_read_trailing_md;
+    bool* destfilled = (other == nullptr) ? &s->write_buffer_trailing_md_filled
+                                          : &other->to_read_trailing_md_filled;
+    fill_in_metadata(s, &fake_md, 0, dest, nullptr, destfilled);
+    grpc_metadata_batch_destroy(&fake_md);
+
+    if (other != nullptr) {
+      if (other->cancel_other_error == GRPC_ERROR_NONE) {
+        other->cancel_other_error = GRPC_ERROR_REF(error);
+      }
+      maybe_schedule_op_closure_locked(other, error);
+    } else if (s->write_buffer_cancel_error == GRPC_ERROR_NONE) {
+      s->write_buffer_cancel_error = GRPC_ERROR_REF(error);
+    }
+  }
+  if (s->recv_initial_md_op) {
+    grpc_error* err;
+    if (!s->t->is_client) {
+      // If this is a server, provide initial metadata with a path and authority
+      // since it expects that as well as no error yet
+      grpc_metadata_batch fake_md;
+      grpc_metadata_batch_init(&fake_md);
+      grpc_linked_mdelem* path_md = static_cast<grpc_linked_mdelem*>(
+          gpr_arena_alloc(s->arena, sizeof(*path_md)));
+      path_md->md = grpc_mdelem_from_slices(g_fake_path_key, g_fake_path_value);
+      GPR_ASSERT(grpc_metadata_batch_link_tail(&fake_md, path_md) ==
+                 GRPC_ERROR_NONE);
+      grpc_linked_mdelem* auth_md = static_cast<grpc_linked_mdelem*>(
+          gpr_arena_alloc(s->arena, sizeof(*auth_md)));
+      auth_md->md = grpc_mdelem_from_slices(g_fake_auth_key, g_fake_auth_value);
+      GPR_ASSERT(grpc_metadata_batch_link_tail(&fake_md, auth_md) ==
+                 GRPC_ERROR_NONE);
+
+      fill_in_metadata(
+          s, &fake_md, 0,
+          s->recv_initial_md_op->payload->recv_initial_metadata
+              .recv_initial_metadata,
+          s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags,
+          nullptr);
+      grpc_metadata_batch_destroy(&fake_md);
+      err = GRPC_ERROR_NONE;
+    } else {
+      err = GRPC_ERROR_REF(error);
+    }
+    if (s->recv_initial_md_op->payload->recv_initial_metadata
+            .trailing_metadata_available != nullptr) {
+      // Set to true unconditionally, because we're failing the call, so even
+      // if we haven't actually seen the send_trailing_metadata op from the
+      // other side, we're going to return trailing metadata anyway.
+      *s->recv_initial_md_op->payload->recv_initial_metadata
+           .trailing_metadata_available = true;
+    }
+    INPROC_LOG(GPR_INFO,
+               "fail_helper %p scheduling initial-metadata-ready %p %p", s,
+               error, err);
+    GRPC_CLOSURE_SCHED(s->recv_initial_md_op->payload->recv_initial_metadata
+                           .recv_initial_metadata_ready,
+                       err);
+    // Last use of err so no need to REF and then UNREF it
+
+    complete_if_batch_end_locked(
+        s, error, s->recv_initial_md_op,
+        "fail_helper scheduling recv-initial-metadata-on-complete");
+    s->recv_initial_md_op = nullptr;
+  }
+  if (s->recv_message_op) {
+    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling message-ready %p", s,
+               error);
+    GRPC_CLOSURE_SCHED(
+        s->recv_message_op->payload->recv_message.recv_message_ready,
+        GRPC_ERROR_REF(error));
+    complete_if_batch_end_locked(
+        s, error, s->recv_message_op,
+        "fail_helper scheduling recv-message-on-complete");
+    s->recv_message_op = nullptr;
+  }
+  if (s->send_message_op) {
+    s->send_message_op->payload->send_message.send_message.reset();
+    complete_if_batch_end_locked(
+        s, error, s->send_message_op,
+        "fail_helper scheduling send-message-on-complete");
+    s->send_message_op = nullptr;
+  }
+  if (s->send_trailing_md_op) {
+    complete_if_batch_end_locked(
+        s, error, s->send_trailing_md_op,
+        "fail_helper scheduling send-trailng-md-on-complete");
+    s->send_trailing_md_op = nullptr;
+  }
+  if (s->recv_trailing_md_op) {
+    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-metadata-ready %p",
+               s, error);
+    GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata
+                           .recv_trailing_metadata_ready,
+                       GRPC_ERROR_REF(error));
+    INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-md-on-complete %p",
+               s, error);
+    complete_if_batch_end_locked(
+        s, error, s->recv_trailing_md_op,
+        "fail_helper scheduling recv-trailing-metadata-on-complete");
+    s->recv_trailing_md_op = nullptr;
+  }
+  close_other_side_locked(s, "fail_helper:other_side");
+  close_stream_locked(s);
+
+  GRPC_ERROR_UNREF(error);
+}
+
+// TODO(vjpai): It should not be necessary to drain the incoming byte
+// stream and create a new one; instead, we should simply pass the byte
+// stream from the sender directly to the receiver as-is.
+//
+// Note that fixing this will also avoid the assumption in this code
+// that the incoming byte stream's next() call will always return
+// synchronously.  That assumption is true today but may not always be
+// true in the future.
+void message_transfer_locked(inproc_stream* sender, inproc_stream* receiver) {
+  size_t remaining =
+      sender->send_message_op->payload->send_message.send_message->length();
+  if (receiver->recv_inited) {
+    grpc_slice_buffer_destroy_internal(&receiver->recv_message);
+  }
+  grpc_slice_buffer_init(&receiver->recv_message);
+  receiver->recv_inited = true;
+  do {
+    grpc_slice message_slice;
+    grpc_closure unused;
+    GPR_ASSERT(
+        sender->send_message_op->payload->send_message.send_message->Next(
+            SIZE_MAX, &unused));
+    grpc_error* error =
+        sender->send_message_op->payload->send_message.send_message->Pull(
+            &message_slice);
+    if (error != GRPC_ERROR_NONE) {
+      cancel_stream_locked(sender, GRPC_ERROR_REF(error));
+      break;
+    }
+    GPR_ASSERT(error == GRPC_ERROR_NONE);
+    remaining -= GRPC_SLICE_LENGTH(message_slice);
+    grpc_slice_buffer_add(&receiver->recv_message, message_slice);
+  } while (remaining > 0);
+  sender->send_message_op->payload->send_message.send_message.reset();
+
+  receiver->recv_stream.Init(&receiver->recv_message, 0);
+  receiver->recv_message_op->payload->recv_message.recv_message->reset(
+      receiver->recv_stream.get());
+  INPROC_LOG(GPR_INFO, "message_transfer_locked %p scheduling message-ready",
+             receiver);
+  GRPC_CLOSURE_SCHED(
+      receiver->recv_message_op->payload->recv_message.recv_message_ready,
+      GRPC_ERROR_NONE);
+  complete_if_batch_end_locked(
+      sender, GRPC_ERROR_NONE, sender->send_message_op,
+      "message_transfer scheduling sender on_complete");
+  complete_if_batch_end_locked(
+      receiver, GRPC_ERROR_NONE, receiver->recv_message_op,
+      "message_transfer scheduling receiver on_complete");
+
+  receiver->recv_message_op = nullptr;
+  sender->send_message_op = nullptr;
+}
+
+void op_state_machine(void* arg, grpc_error* error) {
+  // This function gets called when we have contents in the unprocessed reads
+  // Get what we want based on our ops wanted
+  // Schedule our appropriate closures
+  // and then return to ops_needed state if still needed
+
+  // Since this is a closure directly invoked by the combiner, it should not
+  // unref the error parameter explicitly; the combiner will do that implicitly
+  grpc_error* new_err = GRPC_ERROR_NONE;
+
+  bool needs_close = false;
+
+  INPROC_LOG(GPR_INFO, "op_state_machine %p", arg);
+  inproc_stream* s = static_cast<inproc_stream*>(arg);
+  gpr_mu* mu = &s->t->mu->mu;  // keep aside in case s gets closed
+  gpr_mu_lock(mu);
+  s->op_closure_scheduled = false;
+  // cancellation takes precedence
+  inproc_stream* other = s->other_side;
+
+  if (s->cancel_self_error != GRPC_ERROR_NONE) {
+    fail_helper_locked(s, GRPC_ERROR_REF(s->cancel_self_error));
+    goto done;
+  } else if (s->cancel_other_error != GRPC_ERROR_NONE) {
+    fail_helper_locked(s, GRPC_ERROR_REF(s->cancel_other_error));
+    goto done;
+  } else if (error != GRPC_ERROR_NONE) {
+    fail_helper_locked(s, GRPC_ERROR_REF(error));
+    goto done;
+  }
+
+  if (s->send_message_op && other) {
+    if (other->recv_message_op) {
+      message_transfer_locked(s, other);
+      maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE);
+    } else if (!s->t->is_client && s->trailing_md_sent) {
+      // A server send will never be matched if the server already sent status
+      s->send_message_op->payload->send_message.send_message.reset();
+      complete_if_batch_end_locked(
+          s, GRPC_ERROR_NONE, s->send_message_op,
+          "op_state_machine scheduling send-message-on-complete");
+      s->send_message_op = nullptr;
+    }
+  }
+  // Pause a send trailing metadata if there is still an outstanding
+  // send message unless we know that the send message will never get
+  // matched to a receive. This happens on the client if the server has
+  // already sent status or on the server if the client has requested
+  // status
+  if (s->send_trailing_md_op &&
+      (!s->send_message_op ||
+       (s->t->is_client &&
+        (s->trailing_md_recvd || s->to_read_trailing_md_filled)) ||
+       (!s->t->is_client && other &&
+        (other->trailing_md_recvd || other->to_read_trailing_md_filled ||
+         other->recv_trailing_md_op)))) {
+    grpc_metadata_batch* dest = (other == nullptr)
+                                    ? &s->write_buffer_trailing_md
+                                    : &other->to_read_trailing_md;
+    bool* destfilled = (other == nullptr) ? &s->write_buffer_trailing_md_filled
+                                          : &other->to_read_trailing_md_filled;
+    if (*destfilled || s->trailing_md_sent) {
+      // The buffer is already in use; that's an error!
+      INPROC_LOG(GPR_INFO, "Extra trailing metadata %p", s);
+      new_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra trailing metadata");
+      fail_helper_locked(s, GRPC_ERROR_REF(new_err));
+      goto done;
+    } else {
+      if (!other || !other->closed) {
+        fill_in_metadata(s,
+                         s->send_trailing_md_op->payload->send_trailing_metadata
+                             .send_trailing_metadata,
+                         0, dest, nullptr, destfilled);
+      }
+      s->trailing_md_sent = true;
+      if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) {
+        INPROC_LOG(GPR_INFO,
+                   "op_state_machine %p scheduling trailing-metadata-ready", s);
+        GRPC_CLOSURE_SCHED(
+            s->recv_trailing_md_op->payload->recv_trailing_metadata
+                .recv_trailing_metadata_ready,
+            GRPC_ERROR_NONE);
+        INPROC_LOG(GPR_INFO,
+                   "op_state_machine %p scheduling trailing-md-on-complete", s);
+        GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete,
+                           GRPC_ERROR_NONE);
+        s->recv_trailing_md_op = nullptr;
+        needs_close = true;
+      }
+    }
+    maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE);
+    complete_if_batch_end_locked(
+        s, GRPC_ERROR_NONE, s->send_trailing_md_op,
+        "op_state_machine scheduling send-trailing-metadata-on-complete");
+    s->send_trailing_md_op = nullptr;
+  }
+  if (s->recv_initial_md_op) {
+    if (s->initial_md_recvd) {
+      new_err =
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd initial md");
+      INPROC_LOG(
+          GPR_INFO,
+          "op_state_machine %p scheduling on_complete errors for already "
+          "recvd initial md %p",
+          s, new_err);
+      fail_helper_locked(s, GRPC_ERROR_REF(new_err));
+      goto done;
+    }
+
+    if (s->to_read_initial_md_filled) {
+      s->initial_md_recvd = true;
+      new_err = fill_in_metadata(
+          s, &s->to_read_initial_md, s->to_read_initial_md_flags,
+          s->recv_initial_md_op->payload->recv_initial_metadata
+              .recv_initial_metadata,
+          s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags,
+          nullptr);
+      s->recv_initial_md_op->payload->recv_initial_metadata
+          .recv_initial_metadata->deadline = s->deadline;
+      if (s->recv_initial_md_op->payload->recv_initial_metadata
+              .trailing_metadata_available != nullptr) {
+        *s->recv_initial_md_op->payload->recv_initial_metadata
+             .trailing_metadata_available =
+            (other != nullptr && other->send_trailing_md_op != nullptr);
+      }
+      grpc_metadata_batch_clear(&s->to_read_initial_md);
+      s->to_read_initial_md_filled = false;
+      INPROC_LOG(GPR_INFO,
+                 "op_state_machine %p scheduling initial-metadata-ready %p", s,
+                 new_err);
+      GRPC_CLOSURE_SCHED(s->recv_initial_md_op->payload->recv_initial_metadata
+                             .recv_initial_metadata_ready,
+                         GRPC_ERROR_REF(new_err));
+      complete_if_batch_end_locked(
+          s, new_err, s->recv_initial_md_op,
+          "op_state_machine scheduling recv-initial-metadata-on-complete");
+      s->recv_initial_md_op = nullptr;
+
+      if (new_err != GRPC_ERROR_NONE) {
+        INPROC_LOG(GPR_INFO,
+                   "op_state_machine %p scheduling on_complete errors2 %p", s,
+                   new_err);
+        fail_helper_locked(s, GRPC_ERROR_REF(new_err));
+        goto done;
+      }
+    }
+  }
+  if (s->recv_message_op) {
+    if (other && other->send_message_op) {
+      message_transfer_locked(other, s);
+      maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE);
+    }
+  }
+  if (s->to_read_trailing_md_filled) {
+    if (s->trailing_md_recvd) {
+      new_err =
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd trailing md");
+      INPROC_LOG(
+          GPR_INFO,
+          "op_state_machine %p scheduling on_complete errors for already "
+          "recvd trailing md %p",
+          s, new_err);
+      fail_helper_locked(s, GRPC_ERROR_REF(new_err));
+      goto done;
+    }
+    if (s->recv_message_op != nullptr) {
+      // This message needs to be wrapped up because it will never be
+      // satisfied
+      *s->recv_message_op->payload->recv_message.recv_message = nullptr;
+      INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling message-ready", s);
+      GRPC_CLOSURE_SCHED(
+          s->recv_message_op->payload->recv_message.recv_message_ready,
+          GRPC_ERROR_NONE);
+      complete_if_batch_end_locked(
+          s, new_err, s->recv_message_op,
+          "op_state_machine scheduling recv-message-on-complete");
+      s->recv_message_op = nullptr;
+    }
+    if ((s->trailing_md_sent || s->t->is_client) && s->send_message_op) {
+      // Nothing further will try to receive from this stream, so finish off
+      // any outstanding send_message op
+      s->send_message_op->payload->send_message.send_message.reset();
+      complete_if_batch_end_locked(
+          s, new_err, s->send_message_op,
+          "op_state_machine scheduling send-message-on-complete");
+      s->send_message_op = nullptr;
+    }
+    if (s->recv_trailing_md_op != nullptr) {
+      // We wanted trailing metadata and we got it
+      s->trailing_md_recvd = true;
+      new_err =
+          fill_in_metadata(s, &s->to_read_trailing_md, 0,
+                           s->recv_trailing_md_op->payload
+                               ->recv_trailing_metadata.recv_trailing_metadata,
+                           nullptr, nullptr);
+      grpc_metadata_batch_clear(&s->to_read_trailing_md);
+      s->to_read_trailing_md_filled = false;
+
+      // We should schedule the recv_trailing_md_op completion if
+      // 1. this stream is the client-side
+      // 2. this stream is the server-side AND has already sent its trailing md
+      //    (If the server hasn't already sent its trailing md, it doesn't have
+      //     a final status, so don't mark this op complete)
+      if (s->t->is_client || s->trailing_md_sent) {
+        INPROC_LOG(GPR_INFO,
+                   "op_state_machine %p scheduling trailing-md-on-complete %p",
+                   s, new_err);
+        GRPC_CLOSURE_SCHED(
+            s->recv_trailing_md_op->payload->recv_trailing_metadata
+                .recv_trailing_metadata_ready,
+            GRPC_ERROR_REF(new_err));
+        GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete,
+                           GRPC_ERROR_REF(new_err));
+        s->recv_trailing_md_op = nullptr;
+        needs_close = true;
+      } else {
+        INPROC_LOG(GPR_INFO,
+                   "op_state_machine %p server needs to delay handling "
+                   "trailing-md-on-complete %p",
+                   s, new_err);
+      }
+    } else {
+      INPROC_LOG(
+          GPR_INFO,
+          "op_state_machine %p has trailing md but not yet waiting for it", s);
+    }
+  }
+  if (s->trailing_md_recvd && s->recv_message_op) {
+    // No further message will come on this stream, so finish off the
+    // recv_message_op
+    INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling message-ready", s);
+    *s->recv_message_op->payload->recv_message.recv_message = nullptr;
+    GRPC_CLOSURE_SCHED(
+        s->recv_message_op->payload->recv_message.recv_message_ready,
+        GRPC_ERROR_NONE);
+    complete_if_batch_end_locked(
+        s, new_err, s->recv_message_op,
+        "op_state_machine scheduling recv-message-on-complete");
+    s->recv_message_op = nullptr;
+  }
+  if (s->trailing_md_recvd && (s->trailing_md_sent || s->t->is_client) &&
+      s->send_message_op) {
+    // Nothing further will try to receive from this stream, so finish off
+    // any outstanding send_message op
+    s->send_message_op->payload->send_message.send_message.reset();
+    complete_if_batch_end_locked(
+        s, new_err, s->send_message_op,
+        "op_state_machine scheduling send-message-on-complete");
+    s->send_message_op = nullptr;
+  }
+  if (s->send_message_op || s->send_trailing_md_op || s->recv_initial_md_op ||
+      s->recv_message_op || s->recv_trailing_md_op) {
+    // Didn't get the item we wanted so we still need to get
+    // rescheduled
+    INPROC_LOG(
+        GPR_INFO, "op_state_machine %p still needs closure %p %p %p %p %p", s,
+        s->send_message_op, s->send_trailing_md_op, s->recv_initial_md_op,
+        s->recv_message_op, s->recv_trailing_md_op);
+    s->ops_needed = true;
+  }
+done:
+  if (needs_close) {
+    close_other_side_locked(s, "op_state_machine");
+    close_stream_locked(s);
+  }
+  gpr_mu_unlock(mu);
+  GRPC_ERROR_UNREF(new_err);
+}
+
+bool cancel_stream_locked(inproc_stream* s, grpc_error* error) {
+  bool ret = false;  // was the cancel accepted
+  INPROC_LOG(GPR_INFO, "cancel_stream %p with %s", s, grpc_error_string(error));
+  if (s->cancel_self_error == GRPC_ERROR_NONE) {
+    ret = true;
+    s->cancel_self_error = GRPC_ERROR_REF(error);
+    maybe_schedule_op_closure_locked(s, s->cancel_self_error);
+    // Send trailing md to the other side indicating cancellation, even if we
+    // already have
+    s->trailing_md_sent = true;
+
+    grpc_metadata_batch cancel_md;
+    grpc_metadata_batch_init(&cancel_md);
+
+    inproc_stream* other = s->other_side;
+    grpc_metadata_batch* dest = (other == nullptr)
+                                    ? &s->write_buffer_trailing_md
+                                    : &other->to_read_trailing_md;
+    bool* destfilled = (other == nullptr) ? &s->write_buffer_trailing_md_filled
+                                          : &other->to_read_trailing_md_filled;
+    fill_in_metadata(s, &cancel_md, 0, dest, nullptr, destfilled);
+    grpc_metadata_batch_destroy(&cancel_md);
+
+    if (other != nullptr) {
+      if (other->cancel_other_error == GRPC_ERROR_NONE) {
+        other->cancel_other_error = GRPC_ERROR_REF(s->cancel_self_error);
+      }
+      maybe_schedule_op_closure_locked(other, other->cancel_other_error);
+    } else if (s->write_buffer_cancel_error == GRPC_ERROR_NONE) {
+      s->write_buffer_cancel_error = GRPC_ERROR_REF(s->cancel_self_error);
+    }
+
+    // if we are a server and already received trailing md but
+    // couldn't complete that because we hadn't yet sent out trailing
+    // md, now's the chance
+    if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) {
+      GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata
+                             .recv_trailing_metadata_ready,
+                         GRPC_ERROR_REF(s->cancel_self_error));
+      complete_if_batch_end_locked(
+          s, s->cancel_self_error, s->recv_trailing_md_op,
+          "cancel_stream scheduling trailing-md-on-complete");
+      s->recv_trailing_md_op = nullptr;
+    }
+  }
+
+  close_other_side_locked(s, "cancel_stream:other_side");
+  close_stream_locked(s);
+
+  GRPC_ERROR_UNREF(error);
+  return ret;
+}
+
+void do_nothing(void* arg, grpc_error* error) {}
+
+void perform_stream_op(grpc_transport* gt, grpc_stream* gs,
+                       grpc_transport_stream_op_batch* op) {
+  INPROC_LOG(GPR_INFO, "perform_stream_op %p %p %p", gt, gs, op);
+  inproc_stream* s = reinterpret_cast<inproc_stream*>(gs);
+  gpr_mu* mu = &s->t->mu->mu;  // save aside in case s gets closed
+  gpr_mu_lock(mu);
+
+  if (grpc_inproc_trace.enabled()) {
+    if (op->send_initial_metadata) {
+      log_metadata(op->payload->send_initial_metadata.send_initial_metadata,
+                   s->t->is_client, true);
+    }
+    if (op->send_trailing_metadata) {
+      log_metadata(op->payload->send_trailing_metadata.send_trailing_metadata,
+                   s->t->is_client, false);
+    }
+  }
+  grpc_error* error = GRPC_ERROR_NONE;
+  grpc_closure* on_complete = op->on_complete;
+  // TODO(roth): This is a hack needed because we use data inside of the
+  // closure itself to do the barrier calculation (i.e., to ensure that
+  // we don't schedule the closure until all ops in the batch have been
+  // completed).  This can go away once we move to a new C++ closure API
+  // that provides the ability to create a barrier closure.
+  if (on_complete == nullptr) {
+    on_complete = GRPC_CLOSURE_INIT(&op->handler_private.closure, do_nothing,
+                                    nullptr, grpc_schedule_on_exec_ctx);
+  }
+
+  if (op->cancel_stream) {
+    // Call cancel_stream_locked without ref'ing the cancel_error because
+    // this function is responsible to make sure that that field gets unref'ed
+    cancel_stream_locked(s, op->payload->cancel_stream.cancel_error);
+    // this op can complete without an error
+  } else if (s->cancel_self_error != GRPC_ERROR_NONE) {
+    // already self-canceled so still give it an error
+    error = GRPC_ERROR_REF(s->cancel_self_error);
+  } else {
+    INPROC_LOG(GPR_INFO, "perform_stream_op %p %s%s%s%s%s%s%s", s,
+               s->t->is_client ? "client" : "server",
+               op->send_initial_metadata ? " send_initial_metadata" : "",
+               op->send_message ? " send_message" : "",
+               op->send_trailing_metadata ? " send_trailing_metadata" : "",
+               op->recv_initial_metadata ? " recv_initial_metadata" : "",
+               op->recv_message ? " recv_message" : "",
+               op->recv_trailing_metadata ? " recv_trailing_metadata" : "");
+  }
+
+  bool needs_close = false;
+
+  inproc_stream* other = s->other_side;
+  if (error == GRPC_ERROR_NONE &&
+      (op->send_initial_metadata || op->send_trailing_metadata)) {
+    if (s->t->is_closed) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Endpoint already shutdown");
+    }
+    if (error == GRPC_ERROR_NONE && op->send_initial_metadata) {
+      grpc_metadata_batch* dest = (other == nullptr)
+                                      ? &s->write_buffer_initial_md
+                                      : &other->to_read_initial_md;
+      uint32_t* destflags = (other == nullptr)
+                                ? &s->write_buffer_initial_md_flags
+                                : &other->to_read_initial_md_flags;
+      bool* destfilled = (other == nullptr) ? &s->write_buffer_initial_md_filled
+                                            : &other->to_read_initial_md_filled;
+      if (*destfilled || s->initial_md_sent) {
+        // The buffer is already in use; that's an error!
+        INPROC_LOG(GPR_INFO, "Extra initial metadata %p", s);
+        error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra initial metadata");
+      } else {
+        if (!other || !other->closed) {
+          fill_in_metadata(
+              s, op->payload->send_initial_metadata.send_initial_metadata,
+              op->payload->send_initial_metadata.send_initial_metadata_flags,
+              dest, destflags, destfilled);
+        }
+        if (s->t->is_client) {
+          grpc_millis* dl =
+              (other == nullptr) ? &s->write_buffer_deadline : &other->deadline;
+          *dl = GPR_MIN(*dl, op->payload->send_initial_metadata
+                                 .send_initial_metadata->deadline);
+          s->initial_md_sent = true;
+        }
+      }
+      maybe_schedule_op_closure_locked(other, error);
+    }
+  }
+
+  if (error == GRPC_ERROR_NONE &&
+      (op->send_message || op->send_trailing_metadata ||
+       op->recv_initial_metadata || op->recv_message ||
+       op->recv_trailing_metadata)) {
+    // Mark ops that need to be processed by the closure
+    if (op->send_message) {
+      s->send_message_op = op;
+    }
+    if (op->send_trailing_metadata) {
+      s->send_trailing_md_op = op;
+    }
+    if (op->recv_initial_metadata) {
+      s->recv_initial_md_op = op;
+    }
+    if (op->recv_message) {
+      s->recv_message_op = op;
+    }
+    if (op->recv_trailing_metadata) {
+      s->recv_trailing_md_op = op;
+    }
+
+    // We want to initiate the closure if:
+    // 1. We want to send a message and the other side wants to receive
+    // 2. We want to send trailing metadata and there isn't an unmatched send
+    //    or the other side wants trailing metadata
+    // 3. We want initial metadata and the other side has sent it
+    // 4. We want to receive a message and there is a message ready
+    // 5. There is trailing metadata, even if nothing specifically wants
+    //    that because that can shut down the receive message as well
+    if ((op->send_message && other && other->recv_message_op != nullptr) ||
+        (op->send_trailing_metadata &&
+         (!s->send_message_op || (other && other->recv_trailing_md_op))) ||
+        (op->recv_initial_metadata && s->to_read_initial_md_filled) ||
+        (op->recv_message && other && other->send_message_op != nullptr) ||
+        (s->to_read_trailing_md_filled || s->trailing_md_recvd)) {
+      if (!s->op_closure_scheduled) {
+        GRPC_CLOSURE_SCHED(&s->op_closure, GRPC_ERROR_NONE);
+        s->op_closure_scheduled = true;
+      }
+    } else {
+      s->ops_needed = true;
+    }
+  } else {
+    if (error != GRPC_ERROR_NONE) {
+      // Schedule op's closures that we didn't push to op state machine
+      if (op->recv_initial_metadata) {
+        if (op->payload->recv_initial_metadata.trailing_metadata_available !=
+            nullptr) {
+          // Set to true unconditionally, because we're failing the call, so
+          // even if we haven't actually seen the send_trailing_metadata op
+          // from the other side, we're going to return trailing metadata
+          // anyway.
+          *op->payload->recv_initial_metadata.trailing_metadata_available =
+              true;
+        }
+        INPROC_LOG(
+            GPR_INFO,
+            "perform_stream_op error %p scheduling initial-metadata-ready %p",
+            s, error);
+        GRPC_CLOSURE_SCHED(
+            op->payload->recv_initial_metadata.recv_initial_metadata_ready,
+            GRPC_ERROR_REF(error));
+      }
+      if (op->recv_message) {
+        INPROC_LOG(
+            GPR_INFO,
+            "perform_stream_op error %p scheduling recv message-ready %p", s,
+            error);
+        GRPC_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready,
+                           GRPC_ERROR_REF(error));
+      }
+      if (op->recv_trailing_metadata) {
+        INPROC_LOG(
+            GPR_INFO,
+            "perform_stream_op error %p scheduling trailing-metadata-ready %p",
+            s, error);
+        GRPC_CLOSURE_SCHED(
+            op->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+            GRPC_ERROR_REF(error));
+      }
+    }
+    INPROC_LOG(GPR_INFO, "perform_stream_op %p scheduling on_complete %p", s,
+               error);
+    GRPC_CLOSURE_SCHED(on_complete, GRPC_ERROR_REF(error));
+  }
+  if (needs_close) {
+    close_other_side_locked(s, "perform_stream_op:other_side");
+    close_stream_locked(s);
+  }
+  gpr_mu_unlock(mu);
+  GRPC_ERROR_UNREF(error);
+}
+
+void close_transport_locked(inproc_transport* t) {
+  INPROC_LOG(GPR_INFO, "close_transport %p %d", t, t->is_closed);
+  grpc_connectivity_state_set(
+      &t->connectivity, GRPC_CHANNEL_SHUTDOWN,
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Closing transport."),
+      "close transport");
+  if (!t->is_closed) {
+    t->is_closed = true;
+    /* Also end all streams on this transport */
+    while (t->stream_list != nullptr) {
+      // cancel_stream_locked also adjusts stream list
+      cancel_stream_locked(
+          t->stream_list,
+          grpc_error_set_int(
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport closed"),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
+    }
+  }
+}
+
+void perform_transport_op(grpc_transport* gt, grpc_transport_op* op) {
+  inproc_transport* t = reinterpret_cast<inproc_transport*>(gt);
+  INPROC_LOG(GPR_INFO, "perform_transport_op %p %p", t, op);
+  gpr_mu_lock(&t->mu->mu);
+  if (op->on_connectivity_state_change) {
+    grpc_connectivity_state_notify_on_state_change(
+        &t->connectivity, op->connectivity_state,
+        op->on_connectivity_state_change);
+  }
+  if (op->set_accept_stream) {
+    t->accept_stream_cb = op->set_accept_stream_fn;
+    t->accept_stream_data = op->set_accept_stream_user_data;
+  }
+  if (op->on_consumed) {
+    GRPC_CLOSURE_SCHED(op->on_consumed, GRPC_ERROR_NONE);
+  }
+
+  bool do_close = false;
+  if (op->goaway_error != GRPC_ERROR_NONE) {
+    do_close = true;
+    GRPC_ERROR_UNREF(op->goaway_error);
+  }
+  if (op->disconnect_with_error != GRPC_ERROR_NONE) {
+    do_close = true;
+    GRPC_ERROR_UNREF(op->disconnect_with_error);
+  }
+
+  if (do_close) {
+    close_transport_locked(t);
+  }
+  gpr_mu_unlock(&t->mu->mu);
+}
+
+void destroy_stream(grpc_transport* gt, grpc_stream* gs,
+                    grpc_closure* then_schedule_closure) {
+  INPROC_LOG(GPR_INFO, "destroy_stream %p %p", gs, then_schedule_closure);
+  inproc_stream* s = reinterpret_cast<inproc_stream*>(gs);
+  s->closure_at_destroy = then_schedule_closure;
+  s->~inproc_stream();
+}
+
+void destroy_transport(grpc_transport* gt) {
+  inproc_transport* t = reinterpret_cast<inproc_transport*>(gt);
+  INPROC_LOG(GPR_INFO, "destroy_transport %p", t);
+  gpr_mu_lock(&t->mu->mu);
+  close_transport_locked(t);
+  gpr_mu_unlock(&t->mu->mu);
+  t->other_side->unref();
+  t->unref();
+}
+
+/*******************************************************************************
+ * INTEGRATION GLUE
+ */
+
+void set_pollset(grpc_transport* gt, grpc_stream* gs, grpc_pollset* pollset) {
+  // Nothing to do here
+}
+
+void set_pollset_set(grpc_transport* gt, grpc_stream* gs,
+                     grpc_pollset_set* pollset_set) {
+  // Nothing to do here
+}
+
+grpc_endpoint* get_endpoint(grpc_transport* t) { return nullptr; }
+
+const grpc_transport_vtable inproc_vtable = {
+    sizeof(inproc_stream), "inproc",        init_stream,
+    set_pollset,           set_pollset_set, perform_stream_op,
+    perform_transport_op,  destroy_stream,  destroy_transport,
+    get_endpoint};
+
+/*******************************************************************************
+ * Main inproc transport functions
+ */
+void inproc_transports_create(grpc_transport** server_transport,
+                              const grpc_channel_args* server_args,
+                              grpc_transport** client_transport,
+                              const grpc_channel_args* client_args) {
+  INPROC_LOG(GPR_INFO, "inproc_transports_create");
+  shared_mu* mu = new (gpr_malloc(sizeof(*mu))) shared_mu();
+  inproc_transport* st = new (gpr_malloc(sizeof(*st)))
+      inproc_transport(&inproc_vtable, mu, /*is_client=*/false);
+  inproc_transport* ct = new (gpr_malloc(sizeof(*ct)))
+      inproc_transport(&inproc_vtable, mu, /*is_client=*/true);
+  st->other_side = ct;
+  ct->other_side = st;
+  *server_transport = reinterpret_cast<grpc_transport*>(st);
+  *client_transport = reinterpret_cast<grpc_transport*>(ct);
+}
+}  // namespace
+
+/*******************************************************************************
+ * GLOBAL INIT AND DESTROY
+ */
+void grpc_inproc_transport_init(void) {
+  grpc_core::ExecCtx exec_ctx;
+  g_empty_slice = grpc_slice_from_static_buffer(nullptr, 0);
+
+  grpc_slice key_tmp = grpc_slice_from_static_string(":path");
+  g_fake_path_key = grpc_slice_intern(key_tmp);
+  grpc_slice_unref_internal(key_tmp);
+
+  g_fake_path_value = grpc_slice_from_static_string("/");
+
+  grpc_slice auth_tmp = grpc_slice_from_static_string(":authority");
+  g_fake_auth_key = grpc_slice_intern(auth_tmp);
+  grpc_slice_unref_internal(auth_tmp);
+
+  g_fake_auth_value = grpc_slice_from_static_string("inproc-fail");
+}
+
+grpc_channel* grpc_inproc_channel_create(grpc_server* server,
+                                         grpc_channel_args* args,
+                                         void* reserved) {
+  GRPC_API_TRACE("grpc_inproc_channel_create(server=%p, args=%p)", 2,
+                 (server, args));
+
+  grpc_core::ExecCtx exec_ctx;
+
+  const grpc_channel_args* server_args = grpc_server_get_channel_args(server);
+
+  // Add a default authority channel argument for the client
+
+  grpc_arg default_authority_arg;
+  default_authority_arg.type = GRPC_ARG_STRING;
+  default_authority_arg.key = (char*)GRPC_ARG_DEFAULT_AUTHORITY;
+  default_authority_arg.value.string = (char*)"inproc.authority";
+  grpc_channel_args* client_args =
+      grpc_channel_args_copy_and_add(args, &default_authority_arg, 1);
+
+  grpc_transport* server_transport;
+  grpc_transport* client_transport;
+  inproc_transports_create(&server_transport, server_args, &client_transport,
+                           client_args);
+
+  // TODO(ncteisen): design and support channelz GetSocket for inproc.
+  grpc_server_setup_transport(server, server_transport, nullptr, server_args,
+                              nullptr);
+  grpc_channel* channel = grpc_channel_create(
+      "inproc", client_args, GRPC_CLIENT_DIRECT_CHANNEL, client_transport);
+
+  // Free up created channel args
+  grpc_channel_args_destroy(client_args);
+
+  // Now finish scheduled operations
+
+  return channel;
+}
+
+void grpc_inproc_transport_shutdown(void) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_slice_unref_internal(g_empty_slice);
+  grpc_slice_unref_internal(g_fake_path_key);
+  grpc_slice_unref_internal(g_fake_path_value);
+  grpc_slice_unref_internal(g_fake_auth_key);
+  grpc_slice_unref_internal(g_fake_auth_value);
+}
diff --git a/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.h b/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.h
new file mode 100644
index 0000000..049d140
--- /dev/null
+++ b/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H
+#define GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/transport_impl.h"
+
+grpc_channel* grpc_inproc_channel_create(grpc_server* server,
+                                         grpc_channel_args* args,
+                                         void* reserved);
+
+extern grpc_core::TraceFlag grpc_inproc_trace;
+
+void grpc_inproc_transport_init(void);
+void grpc_inproc_transport_shutdown(void);
+
+#endif /* GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H */
diff --git a/third_party/grpc/src/core/lib/README.md b/third_party/grpc/src/core/lib/README.md
new file mode 100644
index 0000000..69b6bce
--- /dev/null
+++ b/third_party/grpc/src/core/lib/README.md
@@ -0,0 +1,6 @@
+Required elements of gRPC Core: Each module in this directory is required to
+build gRPC. If it's possible to envisage a configuration where code is not
+required, then that code belongs in ext/ instead.
+
+NOTE: The movement of code between lib and ext is an ongoing effort, so this
+directory currently contains too much of the core library.
diff --git a/third_party/grpc/src/core/lib/avl/avl.cc b/third_party/grpc/src/core/lib/avl/avl.cc
new file mode 100644
index 0000000..ec106dd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/avl/avl.cc
@@ -0,0 +1,306 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/avl/avl.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+grpc_avl grpc_avl_create(const grpc_avl_vtable* vtable) {
+  grpc_avl out;
+  out.vtable = vtable;
+  out.root = nullptr;
+  return out;
+}
+
+static grpc_avl_node* ref_node(grpc_avl_node* node) {
+  if (node) {
+    gpr_ref(&node->refs);
+  }
+  return node;
+}
+
+static void unref_node(const grpc_avl_vtable* vtable, grpc_avl_node* node,
+                       void* user_data) {
+  if (node == nullptr) {
+    return;
+  }
+  if (gpr_unref(&node->refs)) {
+    vtable->destroy_key(node->key, user_data);
+    vtable->destroy_value(node->value, user_data);
+    unref_node(vtable, node->left, user_data);
+    unref_node(vtable, node->right, user_data);
+    gpr_free(node);
+  }
+}
+
+static long node_height(grpc_avl_node* node) {
+  return node == nullptr ? 0 : node->height;
+}
+
+#ifndef NDEBUG
+static long calculate_height(grpc_avl_node* node) {
+  return node == nullptr ? 0
+                         : 1 + GPR_MAX(calculate_height(node->left),
+                                       calculate_height(node->right));
+}
+
+static grpc_avl_node* assert_invariants(grpc_avl_node* n) {
+  if (n == nullptr) return nullptr;
+  assert_invariants(n->left);
+  assert_invariants(n->right);
+  assert(calculate_height(n) == n->height);
+  assert(labs(node_height(n->left) - node_height(n->right)) <= 1);
+  return n;
+}
+#else
+static grpc_avl_node* assert_invariants(grpc_avl_node* n) { return n; }
+#endif
+
+grpc_avl_node* new_node(void* key, void* value, grpc_avl_node* left,
+                        grpc_avl_node* right) {
+  grpc_avl_node* node = static_cast<grpc_avl_node*>(gpr_malloc(sizeof(*node)));
+  gpr_ref_init(&node->refs, 1);
+  node->key = key;
+  node->value = value;
+  node->left = assert_invariants(left);
+  node->right = assert_invariants(right);
+  node->height = 1 + GPR_MAX(node_height(left), node_height(right));
+  return node;
+}
+
+static grpc_avl_node* get(const grpc_avl_vtable* vtable, grpc_avl_node* node,
+                          void* key, void* user_data) {
+  long cmp;
+
+  if (node == nullptr) {
+    return nullptr;
+  }
+
+  cmp = vtable->compare_keys(node->key, key, user_data);
+  if (cmp == 0) {
+    return node;
+  } else if (cmp > 0) {
+    return get(vtable, node->left, key, user_data);
+  } else {
+    return get(vtable, node->right, key, user_data);
+  }
+}
+
+void* grpc_avl_get(grpc_avl avl, void* key, void* user_data) {
+  grpc_avl_node* node = get(avl.vtable, avl.root, key, user_data);
+  return node ? node->value : nullptr;
+}
+
+int grpc_avl_maybe_get(grpc_avl avl, void* key, void** value, void* user_data) {
+  grpc_avl_node* node = get(avl.vtable, avl.root, key, user_data);
+  if (node != nullptr) {
+    *value = node->value;
+    return 1;
+  }
+  return 0;
+}
+
+static grpc_avl_node* rotate_left(const grpc_avl_vtable* vtable, void* key,
+                                  void* value, grpc_avl_node* left,
+                                  grpc_avl_node* right, void* user_data) {
+  grpc_avl_node* n = new_node(vtable->copy_key(right->key, user_data),
+                              vtable->copy_value(right->value, user_data),
+                              new_node(key, value, left, ref_node(right->left)),
+                              ref_node(right->right));
+  unref_node(vtable, right, user_data);
+  return n;
+}
+
+static grpc_avl_node* rotate_right(const grpc_avl_vtable* vtable, void* key,
+                                   void* value, grpc_avl_node* left,
+                                   grpc_avl_node* right, void* user_data) {
+  grpc_avl_node* n =
+      new_node(vtable->copy_key(left->key, user_data),
+               vtable->copy_value(left->value, user_data), ref_node(left->left),
+               new_node(key, value, ref_node(left->right), right));
+  unref_node(vtable, left, user_data);
+  return n;
+}
+
+static grpc_avl_node* rotate_left_right(const grpc_avl_vtable* vtable,
+                                        void* key, void* value,
+                                        grpc_avl_node* left,
+                                        grpc_avl_node* right, void* user_data) {
+  /* rotate_right(..., rotate_left(left), right) */
+  grpc_avl_node* n =
+      new_node(vtable->copy_key(left->right->key, user_data),
+               vtable->copy_value(left->right->value, user_data),
+               new_node(vtable->copy_key(left->key, user_data),
+                        vtable->copy_value(left->value, user_data),
+                        ref_node(left->left), ref_node(left->right->left)),
+               new_node(key, value, ref_node(left->right->right), right));
+  unref_node(vtable, left, user_data);
+  return n;
+}
+
+static grpc_avl_node* rotate_right_left(const grpc_avl_vtable* vtable,
+                                        void* key, void* value,
+                                        grpc_avl_node* left,
+                                        grpc_avl_node* right, void* user_data) {
+  /* rotate_left(..., left, rotate_right(right)) */
+  grpc_avl_node* n =
+      new_node(vtable->copy_key(right->left->key, user_data),
+               vtable->copy_value(right->left->value, user_data),
+               new_node(key, value, left, ref_node(right->left->left)),
+               new_node(vtable->copy_key(right->key, user_data),
+                        vtable->copy_value(right->value, user_data),
+                        ref_node(right->left->right), ref_node(right->right)));
+  unref_node(vtable, right, user_data);
+  return n;
+}
+
+static grpc_avl_node* rebalance(const grpc_avl_vtable* vtable, void* key,
+                                void* value, grpc_avl_node* left,
+                                grpc_avl_node* right, void* user_data) {
+  switch (node_height(left) - node_height(right)) {
+    case 2:
+      if (node_height(left->left) - node_height(left->right) == -1) {
+        return assert_invariants(
+            rotate_left_right(vtable, key, value, left, right, user_data));
+      } else {
+        return assert_invariants(
+            rotate_right(vtable, key, value, left, right, user_data));
+      }
+    case -2:
+      if (node_height(right->left) - node_height(right->right) == 1) {
+        return assert_invariants(
+            rotate_right_left(vtable, key, value, left, right, user_data));
+      } else {
+        return assert_invariants(
+            rotate_left(vtable, key, value, left, right, user_data));
+      }
+    default:
+      return assert_invariants(new_node(key, value, left, right));
+  }
+}
+
+static grpc_avl_node* add_key(const grpc_avl_vtable* vtable,
+                              grpc_avl_node* node, void* key, void* value,
+                              void* user_data) {
+  long cmp;
+  if (node == nullptr) {
+    return new_node(key, value, nullptr, nullptr);
+  }
+  cmp = vtable->compare_keys(node->key, key, user_data);
+  if (cmp == 0) {
+    return new_node(key, value, ref_node(node->left), ref_node(node->right));
+  } else if (cmp > 0) {
+    return rebalance(vtable, vtable->copy_key(node->key, user_data),
+                     vtable->copy_value(node->value, user_data),
+                     add_key(vtable, node->left, key, value, user_data),
+                     ref_node(node->right), user_data);
+  } else {
+    return rebalance(
+        vtable, vtable->copy_key(node->key, user_data),
+        vtable->copy_value(node->value, user_data), ref_node(node->left),
+        add_key(vtable, node->right, key, value, user_data), user_data);
+  }
+}
+
+grpc_avl grpc_avl_add(grpc_avl avl, void* key, void* value, void* user_data) {
+  grpc_avl_node* old_root = avl.root;
+  avl.root = add_key(avl.vtable, avl.root, key, value, user_data);
+  assert_invariants(avl.root);
+  unref_node(avl.vtable, old_root, user_data);
+  return avl;
+}
+
+static grpc_avl_node* in_order_head(grpc_avl_node* node) {
+  while (node->left != nullptr) {
+    node = node->left;
+  }
+  return node;
+}
+
+static grpc_avl_node* in_order_tail(grpc_avl_node* node) {
+  while (node->right != nullptr) {
+    node = node->right;
+  }
+  return node;
+}
+
+static grpc_avl_node* remove_key(const grpc_avl_vtable* vtable,
+                                 grpc_avl_node* node, void* key,
+                                 void* user_data) {
+  long cmp;
+  if (node == nullptr) {
+    return nullptr;
+  }
+  cmp = vtable->compare_keys(node->key, key, user_data);
+  if (cmp == 0) {
+    if (node->left == nullptr) {
+      return ref_node(node->right);
+    } else if (node->right == nullptr) {
+      return ref_node(node->left);
+    } else if (node->left->height < node->right->height) {
+      grpc_avl_node* h = in_order_head(node->right);
+      return rebalance(
+          vtable, vtable->copy_key(h->key, user_data),
+          vtable->copy_value(h->value, user_data), ref_node(node->left),
+          remove_key(vtable, node->right, h->key, user_data), user_data);
+    } else {
+      grpc_avl_node* h = in_order_tail(node->left);
+      return rebalance(vtable, vtable->copy_key(h->key, user_data),
+                       vtable->copy_value(h->value, user_data),
+                       remove_key(vtable, node->left, h->key, user_data),
+                       ref_node(node->right), user_data);
+    }
+  } else if (cmp > 0) {
+    return rebalance(vtable, vtable->copy_key(node->key, user_data),
+                     vtable->copy_value(node->value, user_data),
+                     remove_key(vtable, node->left, key, user_data),
+                     ref_node(node->right), user_data);
+  } else {
+    return rebalance(
+        vtable, vtable->copy_key(node->key, user_data),
+        vtable->copy_value(node->value, user_data), ref_node(node->left),
+        remove_key(vtable, node->right, key, user_data), user_data);
+  }
+}
+
+grpc_avl grpc_avl_remove(grpc_avl avl, void* key, void* user_data) {
+  grpc_avl_node* old_root = avl.root;
+  avl.root = remove_key(avl.vtable, avl.root, key, user_data);
+  assert_invariants(avl.root);
+  unref_node(avl.vtable, old_root, user_data);
+  return avl;
+}
+
+grpc_avl grpc_avl_ref(grpc_avl avl, void* user_data) {
+  ref_node(avl.root);
+  return avl;
+}
+
+void grpc_avl_unref(grpc_avl avl, void* user_data) {
+  unref_node(avl.vtable, avl.root, user_data);
+}
+
+int grpc_avl_is_empty(grpc_avl avl) { return avl.root == nullptr; }
diff --git a/third_party/grpc/src/core/lib/avl/avl.h b/third_party/grpc/src/core/lib/avl/avl.h
new file mode 100644
index 0000000..15a9d56
--- /dev/null
+++ b/third_party/grpc/src/core/lib/avl/avl.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_AVL_AVL_H
+#define GRPC_CORE_LIB_AVL_AVL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/sync.h>
+
+/** internal node of an AVL tree */
+typedef struct grpc_avl_node {
+  gpr_refcount refs;
+  void* key;
+  void* value;
+  struct grpc_avl_node* left;
+  struct grpc_avl_node* right;
+  long height;
+} grpc_avl_node;
+
+/** vtable for the AVL tree
+ * The optional user_data is propagated from the top level grpc_avl_XXX API.
+ * From the same API call, multiple vtable functions may be called multiple
+ * times.
+ */
+typedef struct grpc_avl_vtable {
+  /** destroy a key */
+  void (*destroy_key)(void* key, void* user_data);
+  /** copy a key, returning new value */
+  void* (*copy_key)(void* key, void* user_data);
+  /** compare key1, key2; return <0 if key1 < key2,
+      >0 if key1 > key2, 0 if key1 == key2 */
+  long (*compare_keys)(void* key1, void* key2, void* user_data);
+  /** destroy a value */
+  void (*destroy_value)(void* value, void* user_data);
+  /** copy a value */
+  void* (*copy_value)(void* value, void* user_data);
+} grpc_avl_vtable;
+
+/** "pointer" to an AVL tree - this is a reference
+    counted object - use grpc_avl_ref to add a reference,
+    grpc_avl_unref when done with a reference */
+typedef struct grpc_avl {
+  const grpc_avl_vtable* vtable;
+  grpc_avl_node* root;
+} grpc_avl;
+
+/** Create an immutable AVL tree. */
+grpc_avl grpc_avl_create(const grpc_avl_vtable* vtable);
+/** Add a reference to an existing tree - returns
+    the tree as a convenience. The optional user_data will be passed to vtable
+    functions. */
+grpc_avl grpc_avl_ref(grpc_avl avl, void* user_data);
+/** Remove a reference to a tree - destroying it if there
+    are no references left. The optional user_data will be passed to vtable
+    functions. */
+void grpc_avl_unref(grpc_avl avl, void* user_data);
+/** Return a new tree with (key, value) added to avl.
+    implicitly unrefs avl to allow easy chaining.
+    if key exists in avl, the new tree's key entry updated
+    (i.e. a duplicate is not created). The optional user_data will be passed to
+    vtable functions. */
+grpc_avl grpc_avl_add(grpc_avl avl, void* key, void* value, void* user_data);
+/** Return a new tree with key deleted
+    implicitly unrefs avl to allow easy chaining. The optional user_data will be
+    passed to vtable functions. */
+grpc_avl grpc_avl_remove(grpc_avl avl, void* key, void* user_data);
+/** Lookup key, and return the associated value.
+    Does not mutate avl.
+    Returns NULL if key is not found. The optional user_data will be passed to
+    vtable functions.*/
+void* grpc_avl_get(grpc_avl avl, void* key, void* user_data);
+/** Return 1 if avl contains key, 0 otherwise; if it has the key, sets *value to
+    its value. The optional user_data will be passed to vtable functions. */
+int grpc_avl_maybe_get(grpc_avl avl, void* key, void** value, void* user_data);
+/** Return 1 if avl is empty, 0 otherwise */
+int grpc_avl_is_empty(grpc_avl avl);
+
+#endif /* GRPC_CORE_LIB_AVL_AVL_H */
diff --git a/third_party/grpc/src/core/lib/backoff/backoff.cc b/third_party/grpc/src/core/lib/backoff/backoff.cc
new file mode 100644
index 0000000..e536abd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/backoff/backoff.cc
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/backoff/backoff.h"
+
+#include <algorithm>
+
+#include "src/core/lib/gpr/useful.h"
+
+namespace grpc_core {
+
+namespace {
+
+/* Generate a random number between 0 and 1. We roll our own RNG because seeding
+ * rand() modifies a global variable we have no control over. */
+double generate_uniform_random_number(uint32_t* rng_state) {
+  constexpr uint32_t two_raise_31 = uint32_t(1) << 31;
+  *rng_state = (1103515245 * *rng_state + 12345) % two_raise_31;
+  return *rng_state / static_cast<double>(two_raise_31);
+}
+
+double generate_uniform_random_number_between(uint32_t* rng_state, double a,
+                                              double b) {
+  if (a == b) return a;
+  if (a > b) GPR_SWAP(double, a, b);  // make sure a < b
+  const double range = b - a;
+  return a + generate_uniform_random_number(rng_state) * range;
+}
+
+}  // namespace
+
+BackOff::BackOff(const Options& options)
+    : options_(options),
+      rng_state_(static_cast<uint32_t>(gpr_now(GPR_CLOCK_REALTIME).tv_nsec)) {
+  Reset();
+}
+
+grpc_millis BackOff::NextAttemptTime() {
+  if (initial_) {
+    initial_ = false;
+    return current_backoff_ + grpc_core::ExecCtx::Get()->Now();
+  }
+  current_backoff_ = static_cast<grpc_millis>(
+      std::min(current_backoff_ * options_.multiplier(),
+               static_cast<double>(options_.max_backoff())));
+  const double jitter = generate_uniform_random_number_between(
+      &rng_state_, -options_.jitter() * current_backoff_,
+      options_.jitter() * current_backoff_);
+  const grpc_millis next_timeout =
+      static_cast<grpc_millis>(current_backoff_ + jitter);
+  return next_timeout + grpc_core::ExecCtx::Get()->Now();
+}
+
+void BackOff::Reset() {
+  current_backoff_ = options_.initial_backoff();
+  initial_ = true;
+}
+
+void BackOff::SetRandomSeed(uint32_t seed) { rng_state_ = seed; }
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/backoff/backoff.h b/third_party/grpc/src/core/lib/backoff/backoff.h
new file mode 100644
index 0000000..e769d15
--- /dev/null
+++ b/third_party/grpc/src/core/lib/backoff/backoff.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_BACKOFF_BACKOFF_H
+#define GRPC_CORE_LIB_BACKOFF_BACKOFF_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+namespace grpc_core {
+
+/// Implementation of the backoff mechanism described in
+/// doc/connection-backoff.md
+class BackOff {
+ public:
+  class Options;
+
+  /// Initialize backoff machinery - does not need to be destroyed
+  explicit BackOff(const Options& options);
+
+  /// Returns the time at which the next attempt should start.
+  grpc_millis NextAttemptTime();
+
+  /// Reset the backoff, so the next value returned by NextAttemptTime()
+  /// will be the time of the second attempt (rather than the Nth).
+  void Reset();
+
+  void SetRandomSeed(unsigned int seed);
+
+  class Options {
+   public:
+    Options& set_initial_backoff(grpc_millis initial_backoff) {
+      initial_backoff_ = initial_backoff;
+      return *this;
+    }
+    Options& set_multiplier(double multiplier) {
+      multiplier_ = multiplier;
+      return *this;
+    }
+    Options& set_jitter(double jitter) {
+      jitter_ = jitter;
+      return *this;
+    }
+    Options& set_max_backoff(grpc_millis max_backoff) {
+      max_backoff_ = max_backoff;
+      return *this;
+    }
+    /// how long to wait after the first failure before retrying
+    grpc_millis initial_backoff() const { return initial_backoff_; }
+    /// factor with which to multiply backoff after a failed retry
+    double multiplier() const { return multiplier_; }
+    /// amount to randomize backoffs
+    double jitter() const { return jitter_; }
+    /// maximum time between retries
+    grpc_millis max_backoff() const { return max_backoff_; }
+
+   private:
+    grpc_millis initial_backoff_;
+    double multiplier_;
+    double jitter_;
+    grpc_millis max_backoff_;
+  };  // class Options
+
+ private:
+  const Options options_;
+  uint32_t rng_state_;
+  bool initial_;
+  /// current delay before retries
+  grpc_millis current_backoff_;
+};
+
+}  // namespace grpc_core
+#endif /* GRPC_CORE_LIB_BACKOFF_BACKOFF_H */
diff --git a/third_party/grpc/src/core/lib/channel/README.md b/third_party/grpc/src/core/lib/channel/README.md
new file mode 100644
index 0000000..2dfcfe6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/README.md
@@ -0,0 +1,4 @@
+# Channel
+
+Provides channel/call stack implementation, and implementation of common filters
+for that implementation.
diff --git a/third_party/grpc/src/core/lib/channel/channel_args.cc b/third_party/grpc/src/core/lib/channel/channel_args.cc
new file mode 100644
index 0000000..e49d532
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channel_args.cc
@@ -0,0 +1,441 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <limits.h>
+#include <string.h>
+
+#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 "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+
+static grpc_arg copy_arg(const grpc_arg* src) {
+  grpc_arg dst;
+  dst.type = src->type;
+  dst.key = gpr_strdup(src->key);
+  switch (dst.type) {
+    case GRPC_ARG_STRING:
+      dst.value.string = gpr_strdup(src->value.string);
+      break;
+    case GRPC_ARG_INTEGER:
+      dst.value.integer = src->value.integer;
+      break;
+    case GRPC_ARG_POINTER:
+      dst.value.pointer = src->value.pointer;
+      dst.value.pointer.p =
+          src->value.pointer.vtable->copy(src->value.pointer.p);
+      break;
+  }
+  return dst;
+}
+
+grpc_channel_args* grpc_channel_args_copy_and_add(const grpc_channel_args* src,
+                                                  const grpc_arg* to_add,
+                                                  size_t num_to_add) {
+  return grpc_channel_args_copy_and_add_and_remove(src, nullptr, 0, to_add,
+                                                   num_to_add);
+}
+
+grpc_channel_args* grpc_channel_args_copy_and_remove(
+    const grpc_channel_args* src, const char** to_remove,
+    size_t num_to_remove) {
+  return grpc_channel_args_copy_and_add_and_remove(src, to_remove,
+                                                   num_to_remove, nullptr, 0);
+}
+
+static bool should_remove_arg(const grpc_arg* arg, const char** to_remove,
+                              size_t num_to_remove) {
+  for (size_t i = 0; i < num_to_remove; ++i) {
+    if (strcmp(arg->key, to_remove[i]) == 0) return true;
+  }
+  return false;
+}
+
+grpc_channel_args* grpc_channel_args_copy_and_add_and_remove(
+    const grpc_channel_args* src, const char** to_remove, size_t num_to_remove,
+    const grpc_arg* to_add, size_t num_to_add) {
+  // Figure out how many args we'll be copying.
+  size_t num_args_to_copy = 0;
+  if (src != nullptr) {
+    for (size_t i = 0; i < src->num_args; ++i) {
+      if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) {
+        ++num_args_to_copy;
+      }
+    }
+  }
+  // Create result.
+  grpc_channel_args* dst =
+      static_cast<grpc_channel_args*>(gpr_malloc(sizeof(grpc_channel_args)));
+  dst->num_args = num_args_to_copy + num_to_add;
+  if (dst->num_args == 0) {
+    dst->args = nullptr;
+    return dst;
+  }
+  dst->args =
+      static_cast<grpc_arg*>(gpr_malloc(sizeof(grpc_arg) * dst->num_args));
+  // Copy args from src that are not being removed.
+  size_t dst_idx = 0;
+  if (src != nullptr) {
+    for (size_t i = 0; i < src->num_args; ++i) {
+      if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) {
+        dst->args[dst_idx++] = copy_arg(&src->args[i]);
+      }
+    }
+  }
+  // Add args from to_add.
+  for (size_t i = 0; i < num_to_add; ++i) {
+    dst->args[dst_idx++] = copy_arg(&to_add[i]);
+  }
+  GPR_ASSERT(dst_idx == dst->num_args);
+  return dst;
+}
+
+grpc_channel_args* grpc_channel_args_copy(const grpc_channel_args* src) {
+  return grpc_channel_args_copy_and_add(src, nullptr, 0);
+}
+
+grpc_channel_args* grpc_channel_args_union(const grpc_channel_args* a,
+                                           const grpc_channel_args* b) {
+  const size_t max_out = (a->num_args + b->num_args);
+  grpc_arg* uniques =
+      static_cast<grpc_arg*>(gpr_malloc(sizeof(*uniques) * max_out));
+  for (size_t i = 0; i < a->num_args; ++i) uniques[i] = a->args[i];
+
+  size_t uniques_idx = a->num_args;
+  for (size_t i = 0; i < b->num_args; ++i) {
+    const char* b_key = b->args[i].key;
+    if (grpc_channel_args_find(a, b_key) == nullptr) {  // not found
+      uniques[uniques_idx++] = b->args[i];
+    }
+  }
+  grpc_channel_args* result =
+      grpc_channel_args_copy_and_add(nullptr, uniques, uniques_idx);
+  gpr_free(uniques);
+  return result;
+}
+
+static int cmp_arg(const grpc_arg* a, const grpc_arg* b) {
+  int c = GPR_ICMP(a->type, b->type);
+  if (c != 0) return c;
+  c = strcmp(a->key, b->key);
+  if (c != 0) return c;
+  switch (a->type) {
+    case GRPC_ARG_STRING:
+      return strcmp(a->value.string, b->value.string);
+    case GRPC_ARG_INTEGER:
+      return GPR_ICMP(a->value.integer, b->value.integer);
+    case GRPC_ARG_POINTER:
+      c = GPR_ICMP(a->value.pointer.p, b->value.pointer.p);
+      if (c != 0) {
+        c = GPR_ICMP(a->value.pointer.vtable, b->value.pointer.vtable);
+        if (c == 0) {
+          c = a->value.pointer.vtable->cmp(a->value.pointer.p,
+                                           b->value.pointer.p);
+        }
+      }
+      return c;
+  }
+  GPR_UNREACHABLE_CODE(return 0);
+}
+
+/* stabilizing comparison function: since channel_args ordering matters for
+ * keys with the same name, we need to preserve that ordering */
+static int cmp_key_stable(const void* ap, const void* bp) {
+  const grpc_arg* const* a = static_cast<const grpc_arg* const*>(ap);
+  const grpc_arg* const* b = static_cast<const grpc_arg* const*>(bp);
+  int c = strcmp((*a)->key, (*b)->key);
+  if (c == 0) c = GPR_ICMP(*a, *b);
+  return c;
+}
+
+grpc_channel_args* grpc_channel_args_normalize(const grpc_channel_args* a) {
+  grpc_arg** args =
+      static_cast<grpc_arg**>(gpr_malloc(sizeof(grpc_arg*) * a->num_args));
+  for (size_t i = 0; i < a->num_args; i++) {
+    args[i] = &a->args[i];
+  }
+  if (a->num_args > 1)
+    qsort(args, a->num_args, sizeof(grpc_arg*), cmp_key_stable);
+
+  grpc_channel_args* b =
+      static_cast<grpc_channel_args*>(gpr_malloc(sizeof(grpc_channel_args)));
+  b->num_args = a->num_args;
+  b->args = static_cast<grpc_arg*>(gpr_malloc(sizeof(grpc_arg) * b->num_args));
+  for (size_t i = 0; i < a->num_args; i++) {
+    b->args[i] = copy_arg(args[i]);
+  }
+
+  gpr_free(args);
+  return b;
+}
+
+void grpc_channel_args_destroy(grpc_channel_args* a) {
+  size_t i;
+  if (!a) return;
+  for (i = 0; i < a->num_args; i++) {
+    switch (a->args[i].type) {
+      case GRPC_ARG_STRING:
+        gpr_free(a->args[i].value.string);
+        break;
+      case GRPC_ARG_INTEGER:
+        break;
+      case GRPC_ARG_POINTER:
+        a->args[i].value.pointer.vtable->destroy(a->args[i].value.pointer.p);
+        break;
+    }
+    gpr_free(a->args[i].key);
+  }
+  gpr_free(a->args);
+  gpr_free(a);
+}
+
+grpc_compression_algorithm grpc_channel_args_get_compression_algorithm(
+    const grpc_channel_args* a) {
+  size_t i;
+  if (a == nullptr) return GRPC_COMPRESS_NONE;
+  for (i = 0; i < a->num_args; ++i) {
+    if (a->args[i].type == GRPC_ARG_INTEGER &&
+        !strcmp(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, a->args[i].key)) {
+      return static_cast<grpc_compression_algorithm>(a->args[i].value.integer);
+      break;
+    }
+  }
+  return GRPC_COMPRESS_NONE;
+}
+
+grpc_channel_args* grpc_channel_args_set_compression_algorithm(
+    grpc_channel_args* a, grpc_compression_algorithm algorithm) {
+  GPR_ASSERT(algorithm < GRPC_COMPRESS_ALGORITHMS_COUNT);
+  grpc_arg tmp;
+  tmp.type = GRPC_ARG_INTEGER;
+  tmp.key = (char*)GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM;
+  tmp.value.integer = algorithm;
+  return grpc_channel_args_copy_and_add(a, &tmp, 1);
+}
+
+/** Returns 1 if the argument for compression algorithm's enabled states bitset
+ * was found in \a a, returning the arg's value in \a states. Otherwise, returns
+ * 0. */
+static int find_compression_algorithm_states_bitset(const grpc_channel_args* a,
+                                                    int** states_arg) {
+  if (a != nullptr) {
+    size_t i;
+    for (i = 0; i < a->num_args; ++i) {
+      if (a->args[i].type == GRPC_ARG_INTEGER &&
+          !strcmp(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET,
+                  a->args[i].key)) {
+        *states_arg = &a->args[i].value.integer;
+        **states_arg |= 0x1; /* forcefully enable support for no compression */
+        return 1;
+      }
+    }
+  }
+  return 0; /* GPR_FALSE */
+}
+
+grpc_channel_args* grpc_channel_args_compression_algorithm_set_state(
+    grpc_channel_args** a, grpc_compression_algorithm algorithm, int state) {
+  int* states_arg = nullptr;
+  grpc_channel_args* result = *a;
+  const int states_arg_found =
+      find_compression_algorithm_states_bitset(*a, &states_arg);
+
+  if (grpc_channel_args_get_compression_algorithm(*a) == algorithm &&
+      state == 0) {
+    const char* algo_name = nullptr;
+    GPR_ASSERT(grpc_compression_algorithm_name(algorithm, &algo_name) != 0);
+    gpr_log(GPR_ERROR,
+            "Tried to disable default compression algorithm '%s'. The "
+            "operation has been ignored.",
+            algo_name);
+  } else if (states_arg_found) {
+    if (state != 0) {
+      GPR_BITSET((unsigned*)states_arg, algorithm);
+    } else if (algorithm != GRPC_COMPRESS_NONE) {
+      GPR_BITCLEAR((unsigned*)states_arg, algorithm);
+    }
+  } else {
+    /* create a new arg */
+    grpc_arg tmp;
+    tmp.type = GRPC_ARG_INTEGER;
+    tmp.key = (char*)GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET;
+    /* all enabled by default */
+    tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
+    if (state != 0) {
+      GPR_BITSET((unsigned*)&tmp.value.integer, algorithm);
+    } else if (algorithm != GRPC_COMPRESS_NONE) {
+      GPR_BITCLEAR((unsigned*)&tmp.value.integer, algorithm);
+    }
+    result = grpc_channel_args_copy_and_add(*a, &tmp, 1);
+    grpc_channel_args_destroy(*a);
+    *a = result;
+  }
+  return result;
+}
+
+uint32_t grpc_channel_args_compression_algorithm_get_states(
+    const grpc_channel_args* a) {
+  int* states_arg;
+  if (find_compression_algorithm_states_bitset(a, &states_arg)) {
+    return static_cast<uint32_t>(*states_arg);
+  } else {
+    return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */
+  }
+}
+
+grpc_channel_args* grpc_channel_args_set_socket_mutator(
+    grpc_channel_args* a, grpc_socket_mutator* mutator) {
+  grpc_arg tmp = grpc_socket_mutator_to_arg(mutator);
+  return grpc_channel_args_copy_and_add(a, &tmp, 1);
+}
+
+int grpc_channel_args_compare(const grpc_channel_args* a,
+                              const grpc_channel_args* b) {
+  int c = GPR_ICMP(a->num_args, b->num_args);
+  if (c != 0) return c;
+  for (size_t i = 0; i < a->num_args; i++) {
+    c = cmp_arg(&a->args[i], &b->args[i]);
+    if (c != 0) return c;
+  }
+  return 0;
+}
+
+const grpc_arg* grpc_channel_args_find(const grpc_channel_args* args,
+                                       const char* name) {
+  if (args != nullptr) {
+    for (size_t i = 0; i < args->num_args; ++i) {
+      if (strcmp(args->args[i].key, name) == 0) {
+        return &args->args[i];
+      }
+    }
+  }
+  return nullptr;
+}
+
+int grpc_channel_arg_get_integer(const grpc_arg* arg,
+                                 const grpc_integer_options options) {
+  if (arg == nullptr) return options.default_value;
+  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;
+}
+
+char* grpc_channel_arg_get_string(const grpc_arg* arg) {
+  if (arg == nullptr) return nullptr;
+  if (arg->type != GRPC_ARG_STRING) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be an string", arg->key);
+    return nullptr;
+  }
+  return arg->value.string;
+}
+
+bool grpc_channel_arg_get_bool(const grpc_arg* arg, bool default_value) {
+  if (arg == nullptr) return default_value;
+  if (arg->type != GRPC_ARG_INTEGER) {
+    gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key);
+    return default_value;
+  }
+  switch (arg->value.integer) {
+    case 0:
+      return false;
+    case 1:
+      return true;
+    default:
+      gpr_log(GPR_ERROR, "%s treated as bool but set to %d (assuming true)",
+              arg->key, arg->value.integer);
+      return true;
+  }
+}
+
+bool grpc_channel_args_want_minimal_stack(const grpc_channel_args* args) {
+  return grpc_channel_arg_get_bool(
+      grpc_channel_args_find(args, GRPC_ARG_MINIMAL_STACK), false);
+}
+
+grpc_arg grpc_channel_arg_string_create(char* name, char* value) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = name;
+  arg.value.string = value;
+  return arg;
+}
+
+grpc_arg grpc_channel_arg_integer_create(char* name, int value) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_INTEGER;
+  arg.key = name;
+  arg.value.integer = value;
+  return arg;
+}
+
+grpc_arg grpc_channel_arg_pointer_create(
+    char* name, void* value, const grpc_arg_pointer_vtable* vtable) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_POINTER;
+  arg.key = name;
+  arg.value.pointer.p = value;
+  arg.value.pointer.vtable = vtable;
+  return arg;
+}
+
+char* grpc_channel_args_string(const grpc_channel_args* args) {
+  if (args == nullptr) return nullptr;
+  gpr_strvec v;
+  gpr_strvec_init(&v);
+  for (size_t i = 0; i < args->num_args; ++i) {
+    const grpc_arg& arg = args->args[i];
+    char* s;
+    switch (arg.type) {
+      case GRPC_ARG_INTEGER:
+        gpr_asprintf(&s, "%s=%d", arg.key, arg.value.integer);
+        break;
+      case GRPC_ARG_STRING:
+        gpr_asprintf(&s, "%s=%s", arg.key, arg.value.string);
+        break;
+      case GRPC_ARG_POINTER:
+        gpr_asprintf(&s, "%s=%p", arg.key, arg.value.pointer.p);
+        break;
+      default:
+        gpr_asprintf(&s, "arg with unknown type");
+    }
+    gpr_strvec_add(&v, s);
+  }
+  char* result =
+      gpr_strjoin_sep(const_cast<const char**>(v.strs), v.count, ", ", nullptr);
+  gpr_strvec_destroy(&v);
+  return result;
+}
diff --git a/third_party/grpc/src/core/lib/channel/channel_args.h b/third_party/grpc/src/core/lib/channel/channel_args.h
new file mode 100644
index 0000000..5ff303a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channel_args.h
@@ -0,0 +1,131 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include "src/core/lib/iomgr/socket_mutator.h"
+
+// Channel args are intentionally immutable, to avoid the need for locking.
+
+/** Copy the arguments in \a src into a new instance */
+grpc_channel_args* grpc_channel_args_copy(const grpc_channel_args* src);
+
+/** Copy the arguments in \a src into a new instance, stably sorting keys */
+grpc_channel_args* grpc_channel_args_normalize(const grpc_channel_args* src);
+
+/** Copy the arguments in \a src and append \a to_add. If \a to_add is NULL, it
+ * is equivalent to calling \a grpc_channel_args_copy. */
+grpc_channel_args* grpc_channel_args_copy_and_add(const grpc_channel_args* src,
+                                                  const grpc_arg* to_add,
+                                                  size_t num_to_add);
+
+/** Copies the arguments in \a src except for those whose keys are in
+    \a to_remove. */
+grpc_channel_args* grpc_channel_args_copy_and_remove(
+    const grpc_channel_args* src, const char** to_remove, size_t num_to_remove);
+
+/** Copies the arguments from \a src except for those whose keys are in
+    \a to_remove and appends the arguments in \a to_add. */
+grpc_channel_args* grpc_channel_args_copy_and_add_and_remove(
+    const grpc_channel_args* src, const char** to_remove, size_t num_to_remove,
+    const grpc_arg* to_add, size_t num_to_add);
+
+/** Perform the union of \a a and \a b, prioritizing \a a entries */
+grpc_channel_args* grpc_channel_args_union(const grpc_channel_args* a,
+                                           const grpc_channel_args* b);
+
+/** Destroy arguments created by \a grpc_channel_args_copy */
+void grpc_channel_args_destroy(grpc_channel_args* a);
+
+/** Returns the compression algorithm set in \a a. */
+grpc_compression_algorithm grpc_channel_args_get_compression_algorithm(
+    const grpc_channel_args* a);
+
+/** Returns a channel arg instance with compression enabled. If \a a is
+ * non-NULL, its args are copied. N.B. GRPC_COMPRESS_NONE disables compression
+ * for the channel. */
+grpc_channel_args* grpc_channel_args_set_compression_algorithm(
+    grpc_channel_args* a, grpc_compression_algorithm algorithm);
+
+/** Sets the support for the given compression algorithm. By default, all
+ * compression algorithms are enabled. It's an error to disable an algorithm set
+ * by grpc_channel_args_set_compression_algorithm.
+ *
+ * Returns an instance with the updated algorithm states. The \a a pointer is
+ * modified to point to the returned instance (which may be different from the
+ * input value of \a a). */
+grpc_channel_args* grpc_channel_args_compression_algorithm_set_state(
+    grpc_channel_args** a, grpc_compression_algorithm algorithm, int enabled);
+
+/** Returns the bitset representing the support state (true for enabled, false
+ * for disabled) for compression algorithms.
+ *
+ * The i-th bit of the returned bitset corresponds to the i-th entry in the
+ * grpc_compression_algorithm enum. */
+uint32_t grpc_channel_args_compression_algorithm_get_states(
+    const grpc_channel_args* a);
+
+int grpc_channel_args_compare(const grpc_channel_args* a,
+                              const grpc_channel_args* b);
+
+/** Returns a channel arg instance with socket mutator added. The socket mutator
+ * will perform its mutate_fd method on all file descriptors used by the
+ * channel.
+ * If \a a is non-MULL, its args are copied. */
+grpc_channel_args* grpc_channel_args_set_socket_mutator(
+    grpc_channel_args* a, grpc_socket_mutator* mutator);
+
+/** Returns the value of argument \a name from \a args, or NULL if not found. */
+const grpc_arg* grpc_channel_args_find(const grpc_channel_args* args,
+                                       const char* name);
+
+bool grpc_channel_args_want_minimal_stack(const grpc_channel_args* args);
+
+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(const grpc_arg* arg,
+                                 const grpc_integer_options options);
+
+/** Returns the value of \a arg if \a arg is of type GRPC_ARG_STRING.
+    Otherwise, emits a warning log, and returns nullptr.
+    If arg is nullptr, returns nullptr, and does not emit a warning. */
+char* grpc_channel_arg_get_string(const grpc_arg* arg);
+
+bool grpc_channel_arg_get_bool(const grpc_arg* arg, bool default_value);
+
+// Helpers for creating channel args.
+grpc_arg grpc_channel_arg_string_create(char* name, char* value);
+grpc_arg grpc_channel_arg_integer_create(char* name, int value);
+grpc_arg grpc_channel_arg_pointer_create(char* name, void* value,
+                                         const grpc_arg_pointer_vtable* vtable);
+
+// Returns a string representing channel args in human-readable form.
+// Callers takes ownership of result.
+char* grpc_channel_args_string(const grpc_channel_args* args);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H */
diff --git a/third_party/grpc/src/core/lib/channel/channel_stack.cc b/third_party/grpc/src/core/lib/channel/channel_stack.cc
new file mode 100644
index 0000000..df956c7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channel_stack.cc
@@ -0,0 +1,252 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/gpr/alloc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+grpc_core::TraceFlag grpc_trace_channel(false, "channel");
+
+/* Memory layouts.
+
+   Channel stack is laid out as: {
+     grpc_channel_stack stk;
+     padding to GPR_MAX_ALIGNMENT
+     grpc_channel_element[stk.count];
+     per-filter memory, aligned to GPR_MAX_ALIGNMENT
+   }
+
+   Call stack is laid out as: {
+     grpc_call_stack stk;
+     padding to GPR_MAX_ALIGNMENT
+     grpc_call_element[stk.count];
+     per-filter memory, aligned to GPR_MAX_ALIGNMENT
+   } */
+
+size_t grpc_channel_stack_size(const grpc_channel_filter** filters,
+                               size_t filter_count) {
+  /* always need the header, and size for the channel elements */
+  size_t size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)) +
+                GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filter_count *
+                                               sizeof(grpc_channel_element));
+  size_t i;
+
+  GPR_ASSERT((GPR_MAX_ALIGNMENT & (GPR_MAX_ALIGNMENT - 1)) == 0 &&
+             "GPR_MAX_ALIGNMENT must be a power of two");
+
+  /* add the size for each filter */
+  for (i = 0; i < filter_count; i++) {
+    size += GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data);
+  }
+
+  return size;
+}
+
+#define CHANNEL_ELEMS_FROM_STACK(stk)                                     \
+  ((grpc_channel_element*)((char*)(stk) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \
+                                              sizeof(grpc_channel_stack))))
+
+#define CALL_ELEMS_FROM_STACK(stk)                                     \
+  ((grpc_call_element*)((char*)(stk) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \
+                                           sizeof(grpc_call_stack))))
+
+grpc_channel_element* grpc_channel_stack_element(
+    grpc_channel_stack* channel_stack, size_t index) {
+  return CHANNEL_ELEMS_FROM_STACK(channel_stack) + index;
+}
+
+grpc_channel_element* grpc_channel_stack_last_element(
+    grpc_channel_stack* channel_stack) {
+  return grpc_channel_stack_element(channel_stack, channel_stack->count - 1);
+}
+
+grpc_call_element* grpc_call_stack_element(grpc_call_stack* call_stack,
+                                           size_t index) {
+  return CALL_ELEMS_FROM_STACK(call_stack) + index;
+}
+
+grpc_error* grpc_channel_stack_init(
+    int initial_refs, grpc_iomgr_cb_func destroy, void* destroy_arg,
+    const grpc_channel_filter** filters, size_t filter_count,
+    const grpc_channel_args* channel_args, grpc_transport* optional_transport,
+    const char* name, grpc_channel_stack* stack) {
+  size_t call_size =
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) +
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_call_element));
+  grpc_channel_element* elems;
+  grpc_channel_element_args args;
+  char* user_data;
+  size_t i;
+
+  stack->count = filter_count;
+  GRPC_STREAM_REF_INIT(&stack->refcount, initial_refs, destroy, destroy_arg,
+                       name);
+  elems = CHANNEL_ELEMS_FROM_STACK(stack);
+  user_data = (reinterpret_cast<char*>(elems)) +
+              GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filter_count *
+                                             sizeof(grpc_channel_element));
+
+  /* init per-filter data */
+  grpc_error* first_error = GRPC_ERROR_NONE;
+  for (i = 0; i < filter_count; i++) {
+    args.channel_stack = stack;
+    args.channel_args = channel_args;
+    args.optional_transport = optional_transport;
+    args.is_first = i == 0;
+    args.is_last = i == (filter_count - 1);
+    elems[i].filter = filters[i];
+    elems[i].channel_data = user_data;
+    grpc_error* error = elems[i].filter->init_channel_elem(&elems[i], &args);
+    if (error != GRPC_ERROR_NONE) {
+      if (first_error == GRPC_ERROR_NONE) {
+        first_error = error;
+      } else {
+        GRPC_ERROR_UNREF(error);
+      }
+    }
+    user_data +=
+        GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data);
+    call_size += GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data);
+  }
+
+  GPR_ASSERT(user_data > (char*)stack);
+  GPR_ASSERT((uintptr_t)(user_data - (char*)stack) ==
+             grpc_channel_stack_size(filters, filter_count));
+
+  stack->call_stack_size = call_size;
+  return first_error;
+}
+
+void grpc_channel_stack_destroy(grpc_channel_stack* stack) {
+  grpc_channel_element* channel_elems = CHANNEL_ELEMS_FROM_STACK(stack);
+  size_t count = stack->count;
+  size_t i;
+
+  /* destroy per-filter data */
+  for (i = 0; i < count; i++) {
+    channel_elems[i].filter->destroy_channel_elem(&channel_elems[i]);
+  }
+}
+
+grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack,
+                                 int initial_refs, grpc_iomgr_cb_func destroy,
+                                 void* destroy_arg,
+                                 const grpc_call_element_args* elem_args) {
+  grpc_channel_element* channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack);
+  size_t count = channel_stack->count;
+  grpc_call_element* call_elems;
+  char* user_data;
+
+  elem_args->call_stack->count = count;
+  GRPC_STREAM_REF_INIT(&elem_args->call_stack->refcount, initial_refs, destroy,
+                       destroy_arg, "CALL_STACK");
+  call_elems = CALL_ELEMS_FROM_STACK(elem_args->call_stack);
+  user_data = (reinterpret_cast<char*>(call_elems)) +
+              GPR_ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element));
+
+  /* init per-filter data */
+  grpc_error* first_error = GRPC_ERROR_NONE;
+  for (size_t i = 0; i < count; i++) {
+    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;
+    user_data +=
+        GPR_ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data);
+  }
+  for (size_t i = 0; i < count; i++) {
+    grpc_error* error =
+        call_elems[i].filter->init_call_elem(&call_elems[i], elem_args);
+    if (error != GRPC_ERROR_NONE) {
+      if (first_error == GRPC_ERROR_NONE) {
+        first_error = error;
+      } else {
+        GRPC_ERROR_UNREF(error);
+      }
+    }
+  }
+  return first_error;
+}
+
+void grpc_call_stack_set_pollset_or_pollset_set(grpc_call_stack* call_stack,
+                                                grpc_polling_entity* pollent) {
+  size_t count = call_stack->count;
+  grpc_call_element* call_elems;
+  size_t i;
+
+  call_elems = CALL_ELEMS_FROM_STACK(call_stack);
+
+  /* init per-filter data */
+  for (i = 0; i < count; i++) {
+    call_elems[i].filter->set_pollset_or_pollset_set(&call_elems[i], pollent);
+  }
+}
+
+void grpc_call_stack_ignore_set_pollset_or_pollset_set(
+    grpc_call_element* elem, grpc_polling_entity* pollent) {}
+
+void grpc_call_stack_destroy(grpc_call_stack* stack,
+                             const grpc_call_final_info* final_info,
+                             grpc_closure* then_schedule_closure) {
+  grpc_call_element* elems = CALL_ELEMS_FROM_STACK(stack);
+  size_t count = stack->count;
+  size_t i;
+
+  /* destroy per-filter data */
+  for (i = 0; i < count; i++) {
+    elems[i].filter->destroy_call_elem(
+        &elems[i], final_info,
+        i == count - 1 ? then_schedule_closure : nullptr);
+  }
+}
+
+void grpc_call_next_op(grpc_call_element* elem,
+                       grpc_transport_stream_op_batch* op) {
+  grpc_call_element* next_elem = elem + 1;
+  GRPC_CALL_LOG_OP(GPR_INFO, next_elem, op);
+  next_elem->filter->start_transport_stream_op_batch(next_elem, op);
+}
+
+void grpc_channel_next_get_info(grpc_channel_element* elem,
+                                const grpc_channel_info* channel_info) {
+  grpc_channel_element* next_elem = elem + 1;
+  next_elem->filter->get_channel_info(next_elem, channel_info);
+}
+
+void grpc_channel_next_op(grpc_channel_element* elem, grpc_transport_op* op) {
+  grpc_channel_element* next_elem = elem + 1;
+  next_elem->filter->start_transport_op(next_elem, op);
+}
+
+grpc_channel_stack* grpc_channel_stack_from_top_element(
+    grpc_channel_element* elem) {
+  return reinterpret_cast<grpc_channel_stack*>(
+      reinterpret_cast<char*>(elem) -
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)));
+}
+
+grpc_call_stack* grpc_call_stack_from_top_element(grpc_call_element* elem) {
+  return reinterpret_cast<grpc_call_stack*>(
+      reinterpret_cast<char*>(elem) -
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)));
+}
diff --git a/third_party/grpc/src/core/lib/channel/channel_stack.h b/third_party/grpc/src/core/lib/channel/channel_stack.h
new file mode 100644
index 0000000..0de8c67
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channel_stack.h
@@ -0,0 +1,280 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H
+
+//////////////////////////////////////////////////////////////////////////////
+// IMPORTANT NOTE:
+//
+// When you update this API, please make the corresponding changes to
+// the C++ API in src/cpp/common/channel_filter.{h,cc}
+//////////////////////////////////////////////////////////////////////////////
+
+/* A channel filter defines how operations on a channel are implemented.
+   Channel filters are chained together to create full channels, and if those
+   chains are linear, then channel stacks provide a mechanism to minimize
+   allocations for that chain.
+   Call stacks are created by channel stacks and represent the per-call data
+   for that stack. */
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/iomgr/call_combiner.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/transport/transport.h"
+
+typedef struct grpc_channel_element grpc_channel_element;
+typedef struct grpc_call_element grpc_call_element;
+
+typedef struct grpc_channel_stack grpc_channel_stack;
+typedef struct grpc_call_stack grpc_call_stack;
+
+typedef struct {
+  grpc_channel_stack* channel_stack;
+  const grpc_channel_args* channel_args;
+  /** Transport, iff it is known */
+  grpc_transport* optional_transport;
+  int is_first;
+  int is_last;
+} grpc_channel_element_args;
+
+typedef struct {
+  grpc_call_stack* call_stack;
+  const void* server_transport_data;
+  grpc_call_context_element* context;
+  grpc_slice path;
+  gpr_timespec start_time;
+  grpc_millis deadline;
+  gpr_arena* arena;
+  grpc_call_combiner* call_combiner;
+} 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_call_stats;
+
+/** Information about the call upon completion. */
+struct grpc_call_final_info {
+  grpc_call_stats stats;
+  grpc_status_code final_status = GRPC_STATUS_OK;
+  const char* error_string = nullptr;
+};
+
+/* Channel filters specify:
+   1. the amount of memory needed in the channel & call (via the sizeof_XXX
+      members)
+   2. functions to initialize and destroy channel & call data
+      (init_XXX, destroy_XXX)
+   3. functions to implement call operations and channel operations (call_op,
+      channel_op)
+   4. a name, which is useful when debugging
+
+   Members are laid out in approximate frequency of use order. */
+typedef struct {
+  /* Called to eg. send/receive data on a call.
+     See grpc_call_next_op on how to call the next element in the stack */
+  void (*start_transport_stream_op_batch)(grpc_call_element* elem,
+                                          grpc_transport_stream_op_batch* op);
+  /* Called to handle channel level operations - e.g. new calls, or transport
+     closure.
+     See grpc_channel_next_op on how to call the next element in the stack */
+  void (*start_transport_op)(grpc_channel_element* elem, grpc_transport_op* op);
+
+  /* sizeof(per call data) */
+  size_t sizeof_call_data;
+  /* Initialize per call data.
+     elem is initialized at the start of the call, and elem->call_data is what
+     needs initializing.
+     The filter does not need to do any chaining.
+     server_transport_data is an opaque pointer. If it is NULL, this call is
+     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.
+     Implementations may assume that elem->call_data is all zeros. */
+  grpc_error* (*init_call_elem)(grpc_call_element* elem,
+                                const grpc_call_element_args* args);
+  void (*set_pollset_or_pollset_set)(grpc_call_element* elem,
+                                     grpc_polling_entity* pollent);
+  /* Destroy per call data.
+     The filter does not need to do any chaining.
+     The bottom filter of a stack will be passed a non-NULL pointer to
+     \a then_schedule_closure that should be passed to GRPC_CLOSURE_SCHED when
+     destruction is complete. \a final_info contains data about the completed
+     call, mainly for reporting purposes. */
+  void (*destroy_call_elem)(grpc_call_element* elem,
+                            const grpc_call_final_info* final_info,
+                            grpc_closure* then_schedule_closure);
+
+  /* sizeof(per channel data) */
+  size_t sizeof_channel_data;
+  /* Initialize per-channel data.
+     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.
+     Implementations may assume that elem->channel_data is all zeros. */
+  grpc_error* (*init_channel_elem)(grpc_channel_element* elem,
+                                   grpc_channel_element_args* args);
+  /* Destroy per channel data.
+     The filter does not need to do any chaining */
+  void (*destroy_channel_elem)(grpc_channel_element* elem);
+
+  /* Implement grpc_channel_get_info() */
+  void (*get_channel_info)(grpc_channel_element* elem,
+                           const grpc_channel_info* channel_info);
+
+  /* The name of this filter */
+  const char* name;
+} grpc_channel_filter;
+
+/* A channel_element tracks its filter and the filter requested memory within
+   a channel allocation */
+struct grpc_channel_element {
+  const grpc_channel_filter* filter;
+  void* channel_data;
+};
+
+/* A call_element tracks its filter, the filter requested memory within
+   a channel allocation, and the filter requested memory within a call
+   allocation */
+struct grpc_call_element {
+  const grpc_channel_filter* filter;
+  void* channel_data;
+  void* call_data;
+};
+
+/* A channel stack tracks a set of related filters for one channel, and
+   guarantees they live within a single malloc() allocation */
+struct grpc_channel_stack {
+  grpc_stream_refcount refcount;
+  size_t count;
+  /* Memory required for a call stack (computed at channel stack
+     initialization) */
+  size_t call_stack_size;
+};
+
+/* A call stack tracks a set of related filters for one call, and guarantees
+   they live within a single malloc() allocation */
+struct grpc_call_stack {
+  /* shared refcount for this channel stack.
+     MUST be the first element: the underlying code calls destroy
+     with the address of the refcount, but higher layers prefer to think
+     about the address of the call stack itself. */
+  grpc_stream_refcount refcount;
+  size_t count;
+};
+
+/* Get a channel element given a channel stack and its index */
+grpc_channel_element* grpc_channel_stack_element(grpc_channel_stack* stack,
+                                                 size_t i);
+/* Get the last channel element in a channel stack */
+grpc_channel_element* grpc_channel_stack_last_element(
+    grpc_channel_stack* stack);
+/* Get a call stack element given a call stack and an index */
+grpc_call_element* grpc_call_stack_element(grpc_call_stack* stack, size_t i);
+
+/* Determine memory required for a channel stack containing a set of filters */
+size_t grpc_channel_stack_size(const grpc_channel_filter** filters,
+                               size_t filter_count);
+/* Initialize a channel stack given some filters */
+grpc_error* grpc_channel_stack_init(
+    int initial_refs, grpc_iomgr_cb_func destroy, void* destroy_arg,
+    const grpc_channel_filter** filters, size_t filter_count,
+    const grpc_channel_args* args, grpc_transport* optional_transport,
+    const char* name, grpc_channel_stack* stack);
+/* Destroy a channel stack */
+void grpc_channel_stack_destroy(grpc_channel_stack* stack);
+
+/* 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. */
+grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack,
+                                 int initial_refs, grpc_iomgr_cb_func destroy,
+                                 void* destroy_arg,
+                                 const grpc_call_element_args* elem_args);
+/* 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_call_stack* call_stack,
+                                                grpc_polling_entity* pollent);
+
+#ifndef NDEBUG
+#define GRPC_CALL_STACK_REF(call_stack, reason) \
+  grpc_stream_ref(&(call_stack)->refcount, reason)
+#define GRPC_CALL_STACK_UNREF(call_stack, reason) \
+  grpc_stream_unref(&(call_stack)->refcount, reason)
+#define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \
+  grpc_stream_ref(&(channel_stack)->refcount, reason)
+#define GRPC_CHANNEL_STACK_UNREF(channel_stack, reason) \
+  grpc_stream_unref(&(channel_stack)->refcount, reason)
+#else
+#define GRPC_CALL_STACK_REF(call_stack, reason) \
+  grpc_stream_ref(&(call_stack)->refcount)
+#define GRPC_CALL_STACK_UNREF(call_stack, reason) \
+  grpc_stream_unref(&(call_stack)->refcount)
+#define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \
+  grpc_stream_ref(&(channel_stack)->refcount)
+#define GRPC_CHANNEL_STACK_UNREF(channel_stack, reason) \
+  grpc_stream_unref(&(channel_stack)->refcount)
+#endif
+
+/* Destroy a call stack */
+void grpc_call_stack_destroy(grpc_call_stack* stack,
+                             const grpc_call_final_info* final_info,
+                             grpc_closure* then_schedule_closure);
+
+/* Ignore set pollset{_set} - used by filters if they don't care about pollsets
+ * at all. Does nothing. */
+void grpc_call_stack_ignore_set_pollset_or_pollset_set(
+    grpc_call_element* elem, grpc_polling_entity* pollent);
+/* Call the next operation in a call stack */
+void grpc_call_next_op(grpc_call_element* elem,
+                       grpc_transport_stream_op_batch* op);
+/* Call the next operation (depending on call directionality) in a channel
+   stack */
+void grpc_channel_next_op(grpc_channel_element* elem, grpc_transport_op* op);
+/* Pass through a request to get_channel_info() to the next child element */
+void grpc_channel_next_get_info(grpc_channel_element* elem,
+                                const grpc_channel_info* channel_info);
+
+/* Given the top element of a channel stack, get the channel stack itself */
+grpc_channel_stack* grpc_channel_stack_from_top_element(
+    grpc_channel_element* elem);
+/* Given the top element of a call stack, get the call stack itself */
+grpc_call_stack* grpc_call_stack_from_top_element(grpc_call_element* elem);
+
+void grpc_call_log_op(const char* file, int line, gpr_log_severity severity,
+                      grpc_call_element* elem,
+                      grpc_transport_stream_op_batch* op);
+
+extern grpc_core::TraceFlag grpc_trace_channel;
+
+#define GRPC_CALL_LOG_OP(sev, elem, op) \
+  if (grpc_trace_channel.enabled()) grpc_call_log_op(sev, elem, op)
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H */
diff --git a/third_party/grpc/src/core/lib/channel/channel_stack_builder.cc b/third_party/grpc/src/core/lib/channel/channel_stack_builder.cc
new file mode 100644
index 0000000..8b3008f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channel_stack_builder.cc
@@ -0,0 +1,323 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack_builder.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+typedef struct filter_node {
+  struct filter_node* next;
+  struct filter_node* prev;
+  const grpc_channel_filter* filter;
+  grpc_post_filter_create_init_func init;
+  void* init_arg;
+} filter_node;
+
+struct grpc_channel_stack_builder {
+  // sentinel nodes for filters that have been added
+  filter_node begin;
+  filter_node end;
+  // various set/get-able parameters
+  grpc_channel_args* args;
+  grpc_transport* transport;
+  grpc_resource_user* resource_user;
+  char* target;
+  const char* name;
+};
+
+struct grpc_channel_stack_builder_iterator {
+  grpc_channel_stack_builder* builder;
+  filter_node* node;
+};
+
+grpc_channel_stack_builder* grpc_channel_stack_builder_create(void) {
+  grpc_channel_stack_builder* b =
+      static_cast<grpc_channel_stack_builder*>(gpr_zalloc(sizeof(*b)));
+
+  b->begin.filter = nullptr;
+  b->end.filter = nullptr;
+  b->begin.next = &b->end;
+  b->begin.prev = &b->end;
+  b->end.next = &b->begin;
+  b->end.prev = &b->begin;
+
+  return b;
+}
+
+void grpc_channel_stack_builder_set_target(grpc_channel_stack_builder* b,
+                                           const char* target) {
+  gpr_free(b->target);
+  b->target = gpr_strdup(target);
+}
+
+const char* grpc_channel_stack_builder_get_target(
+    grpc_channel_stack_builder* b) {
+  return b->target;
+}
+
+static grpc_channel_stack_builder_iterator* create_iterator_at_filter_node(
+    grpc_channel_stack_builder* builder, filter_node* node) {
+  grpc_channel_stack_builder_iterator* it =
+      static_cast<grpc_channel_stack_builder_iterator*>(
+          gpr_malloc(sizeof(*it)));
+  it->builder = builder;
+  it->node = node;
+  return it;
+}
+
+void grpc_channel_stack_builder_iterator_destroy(
+    grpc_channel_stack_builder_iterator* it) {
+  gpr_free(it);
+}
+
+grpc_channel_stack_builder_iterator*
+grpc_channel_stack_builder_create_iterator_at_first(
+    grpc_channel_stack_builder* builder) {
+  return create_iterator_at_filter_node(builder, &builder->begin);
+}
+
+grpc_channel_stack_builder_iterator*
+grpc_channel_stack_builder_create_iterator_at_last(
+    grpc_channel_stack_builder* builder) {
+  return create_iterator_at_filter_node(builder, &builder->end);
+}
+
+bool grpc_channel_stack_builder_iterator_is_end(
+    grpc_channel_stack_builder_iterator* iterator) {
+  return iterator->node == &iterator->builder->end;
+}
+
+const char* grpc_channel_stack_builder_iterator_filter_name(
+    grpc_channel_stack_builder_iterator* iterator) {
+  if (iterator->node->filter == nullptr) return nullptr;
+  return iterator->node->filter->name;
+}
+
+bool grpc_channel_stack_builder_move_next(
+    grpc_channel_stack_builder_iterator* iterator) {
+  if (iterator->node == &iterator->builder->end) return false;
+  iterator->node = iterator->node->next;
+  return true;
+}
+
+bool grpc_channel_stack_builder_move_prev(
+    grpc_channel_stack_builder_iterator* iterator) {
+  if (iterator->node == &iterator->builder->begin) return false;
+  iterator->node = iterator->node->prev;
+  return true;
+}
+
+grpc_channel_stack_builder_iterator* grpc_channel_stack_builder_iterator_find(
+    grpc_channel_stack_builder* builder, const char* filter_name) {
+  GPR_ASSERT(filter_name != nullptr);
+  grpc_channel_stack_builder_iterator* it =
+      grpc_channel_stack_builder_create_iterator_at_first(builder);
+  while (grpc_channel_stack_builder_move_next(it)) {
+    if (grpc_channel_stack_builder_iterator_is_end(it)) break;
+    const char* filter_name_at_it =
+        grpc_channel_stack_builder_iterator_filter_name(it);
+    if (strcmp(filter_name, filter_name_at_it) == 0) break;
+  }
+  return it;
+}
+
+bool grpc_channel_stack_builder_move_prev(
+    grpc_channel_stack_builder_iterator* iterator);
+
+void grpc_channel_stack_builder_set_name(grpc_channel_stack_builder* builder,
+                                         const char* name) {
+  GPR_ASSERT(builder->name == nullptr);
+  builder->name = name;
+}
+
+void grpc_channel_stack_builder_set_channel_arguments(
+    grpc_channel_stack_builder* builder, const grpc_channel_args* args) {
+  if (builder->args != nullptr) {
+    grpc_channel_args_destroy(builder->args);
+  }
+  builder->args = grpc_channel_args_copy(args);
+}
+
+const grpc_channel_args* grpc_channel_stack_builder_get_channel_arguments(
+    grpc_channel_stack_builder* builder) {
+  return builder->args;
+}
+
+void grpc_channel_stack_builder_set_transport(
+    grpc_channel_stack_builder* builder, grpc_transport* transport) {
+  GPR_ASSERT(builder->transport == nullptr);
+  builder->transport = transport;
+}
+
+grpc_transport* grpc_channel_stack_builder_get_transport(
+    grpc_channel_stack_builder* builder) {
+  return builder->transport;
+}
+
+void grpc_channel_stack_builder_set_resource_user(
+    grpc_channel_stack_builder* builder, grpc_resource_user* resource_user) {
+  GPR_ASSERT(builder->resource_user == nullptr);
+  builder->resource_user = resource_user;
+}
+
+grpc_resource_user* grpc_channel_stack_builder_get_resource_user(
+    grpc_channel_stack_builder* builder) {
+  return builder->resource_user;
+}
+
+bool grpc_channel_stack_builder_append_filter(
+    grpc_channel_stack_builder* builder, const grpc_channel_filter* filter,
+    grpc_post_filter_create_init_func post_init_func, void* user_data) {
+  grpc_channel_stack_builder_iterator* it =
+      grpc_channel_stack_builder_create_iterator_at_last(builder);
+  bool ok = grpc_channel_stack_builder_add_filter_before(
+      it, filter, post_init_func, user_data);
+  grpc_channel_stack_builder_iterator_destroy(it);
+  return ok;
+}
+
+bool grpc_channel_stack_builder_remove_filter(
+    grpc_channel_stack_builder* builder, const char* filter_name) {
+  grpc_channel_stack_builder_iterator* it =
+      grpc_channel_stack_builder_iterator_find(builder, filter_name);
+  if (grpc_channel_stack_builder_iterator_is_end(it)) {
+    grpc_channel_stack_builder_iterator_destroy(it);
+    return false;
+  }
+  it->node->prev->next = it->node->next;
+  it->node->next->prev = it->node->prev;
+  gpr_free(it->node);
+  grpc_channel_stack_builder_iterator_destroy(it);
+  return true;
+}
+
+bool grpc_channel_stack_builder_prepend_filter(
+    grpc_channel_stack_builder* builder, const grpc_channel_filter* filter,
+    grpc_post_filter_create_init_func post_init_func, void* user_data) {
+  grpc_channel_stack_builder_iterator* it =
+      grpc_channel_stack_builder_create_iterator_at_first(builder);
+  bool ok = grpc_channel_stack_builder_add_filter_after(
+      it, filter, post_init_func, user_data);
+  grpc_channel_stack_builder_iterator_destroy(it);
+  return ok;
+}
+
+static void add_after(filter_node* before, const grpc_channel_filter* filter,
+                      grpc_post_filter_create_init_func post_init_func,
+                      void* user_data) {
+  filter_node* new_node =
+      static_cast<filter_node*>(gpr_malloc(sizeof(*new_node)));
+  new_node->next = before->next;
+  new_node->prev = before;
+  new_node->next->prev = new_node->prev->next = new_node;
+  new_node->filter = filter;
+  new_node->init = post_init_func;
+  new_node->init_arg = user_data;
+}
+
+bool grpc_channel_stack_builder_add_filter_before(
+    grpc_channel_stack_builder_iterator* iterator,
+    const grpc_channel_filter* filter,
+    grpc_post_filter_create_init_func post_init_func, void* user_data) {
+  if (iterator->node == &iterator->builder->begin) return false;
+  add_after(iterator->node->prev, filter, post_init_func, user_data);
+  return true;
+}
+
+bool grpc_channel_stack_builder_add_filter_after(
+    grpc_channel_stack_builder_iterator* iterator,
+    const grpc_channel_filter* filter,
+    grpc_post_filter_create_init_func post_init_func, void* user_data) {
+  if (iterator->node == &iterator->builder->end) return false;
+  add_after(iterator->node, filter, post_init_func, user_data);
+  return true;
+}
+
+void grpc_channel_stack_builder_destroy(grpc_channel_stack_builder* builder) {
+  filter_node* p = builder->begin.next;
+  while (p != &builder->end) {
+    filter_node* next = p->next;
+    gpr_free(p);
+    p = next;
+  }
+  if (builder->args != nullptr) {
+    grpc_channel_args_destroy(builder->args);
+  }
+  gpr_free(builder->target);
+  gpr_free(builder);
+}
+
+grpc_error* grpc_channel_stack_builder_finish(
+    grpc_channel_stack_builder* builder, size_t prefix_bytes, int initial_refs,
+    grpc_iomgr_cb_func destroy, void* destroy_arg, void** result) {
+  // count the number of filters
+  size_t num_filters = 0;
+  for (filter_node* p = builder->begin.next; p != &builder->end; p = p->next) {
+    num_filters++;
+  }
+
+  // create an array of filters
+  const grpc_channel_filter** filters =
+      static_cast<const grpc_channel_filter**>(
+          gpr_malloc(sizeof(*filters) * num_filters));
+  size_t i = 0;
+  for (filter_node* p = builder->begin.next; p != &builder->end; p = p->next) {
+    filters[i++] = p->filter;
+  }
+
+  // calculate the size of the channel stack
+  size_t channel_stack_size = grpc_channel_stack_size(filters, num_filters);
+
+  // allocate memory, with prefix_bytes followed by channel_stack_size
+  *result = gpr_zalloc(prefix_bytes + channel_stack_size);
+  // fetch a pointer to the channel stack
+  grpc_channel_stack* channel_stack = reinterpret_cast<grpc_channel_stack*>(
+      static_cast<char*>(*result) + prefix_bytes);
+  // and initialize it
+  grpc_error* error = grpc_channel_stack_init(
+      initial_refs, destroy, destroy_arg == nullptr ? *result : destroy_arg,
+      filters, num_filters, builder->args, builder->transport, builder->name,
+      channel_stack);
+
+  if (error != GRPC_ERROR_NONE) {
+    grpc_channel_stack_destroy(channel_stack);
+    gpr_free(*result);
+    *result = nullptr;
+  } else {
+    // run post-initialization functions
+    i = 0;
+    for (filter_node* p = builder->begin.next; p != &builder->end;
+         p = p->next) {
+      if (p->init != nullptr) {
+        p->init(channel_stack, grpc_channel_stack_element(channel_stack, i),
+                p->init_arg);
+      }
+      i++;
+    }
+  }
+
+  grpc_channel_stack_builder_destroy(builder);
+  gpr_free(const_cast<grpc_channel_filter**>(filters));
+
+  return error;
+}
diff --git a/third_party/grpc/src/core/lib/channel/channel_stack_builder.h b/third_party/grpc/src/core/lib/channel/channel_stack_builder.h
new file mode 100644
index 0000000..89c30e0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channel_stack_builder.h
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/channel_stack.h"
+
+/// grpc_channel_stack_builder offers a programmatic interface to selected
+/// and order channel filters
+typedef struct grpc_channel_stack_builder grpc_channel_stack_builder;
+typedef struct grpc_channel_stack_builder_iterator
+    grpc_channel_stack_builder_iterator;
+
+/// Create a new channel stack builder
+grpc_channel_stack_builder* grpc_channel_stack_builder_create(void);
+
+/// Assign a name to the channel stack: \a name must be statically allocated
+void grpc_channel_stack_builder_set_name(grpc_channel_stack_builder* builder,
+                                         const char* name);
+
+/// Set the target uri
+void grpc_channel_stack_builder_set_target(grpc_channel_stack_builder* b,
+                                           const char* target);
+
+const char* grpc_channel_stack_builder_get_target(
+    grpc_channel_stack_builder* b);
+
+/// Attach \a transport to the builder (does not take ownership)
+void grpc_channel_stack_builder_set_transport(
+    grpc_channel_stack_builder* builder, grpc_transport* transport);
+
+/// Fetch attached transport
+grpc_transport* grpc_channel_stack_builder_get_transport(
+    grpc_channel_stack_builder* builder);
+
+/// Attach \a resource_user to the builder (does not take ownership)
+void grpc_channel_stack_builder_set_resource_user(
+    grpc_channel_stack_builder* builder, grpc_resource_user* resource_user);
+
+/// Fetch attached resource user
+grpc_resource_user* grpc_channel_stack_builder_get_resource_user(
+    grpc_channel_stack_builder* builder);
+
+/// Set channel arguments: copies args
+void grpc_channel_stack_builder_set_channel_arguments(
+    grpc_channel_stack_builder* builder, const grpc_channel_args* args);
+
+/// Return a borrowed pointer to the channel arguments
+const grpc_channel_args* grpc_channel_stack_builder_get_channel_arguments(
+    grpc_channel_stack_builder* builder);
+
+/// Begin iterating over already defined filters in the builder at the beginning
+grpc_channel_stack_builder_iterator*
+grpc_channel_stack_builder_create_iterator_at_first(
+    grpc_channel_stack_builder* builder);
+
+/// Begin iterating over already defined filters in the builder at the end
+grpc_channel_stack_builder_iterator*
+grpc_channel_stack_builder_create_iterator_at_last(
+    grpc_channel_stack_builder* builder);
+
+/// Is an iterator at the first element?
+bool grpc_channel_stack_builder_iterator_is_first(
+    grpc_channel_stack_builder_iterator* iterator);
+
+/// Is an iterator at the end?
+bool grpc_channel_stack_builder_iterator_is_end(
+    grpc_channel_stack_builder_iterator* iterator);
+
+/// What is the name of the filter at this iterator position?
+const char* grpc_channel_stack_builder_iterator_filter_name(
+    grpc_channel_stack_builder_iterator* iterator);
+
+/// Move an iterator to the next item
+bool grpc_channel_stack_builder_move_next(
+    grpc_channel_stack_builder_iterator* iterator);
+
+/// Move an iterator to the previous item
+bool grpc_channel_stack_builder_move_prev(
+    grpc_channel_stack_builder_iterator* iterator);
+
+/// Return an iterator at \a filter_name, or at the end of the list if not
+/// found.
+grpc_channel_stack_builder_iterator* grpc_channel_stack_builder_iterator_find(
+    grpc_channel_stack_builder* builder, const char* filter_name);
+
+typedef void (*grpc_post_filter_create_init_func)(
+    grpc_channel_stack* channel_stack, grpc_channel_element* elem, void* arg);
+
+/// Add \a filter to the stack, after \a iterator.
+/// Call \a post_init_func(..., \a user_data) once the channel stack is
+/// created.
+bool grpc_channel_stack_builder_add_filter_after(
+    grpc_channel_stack_builder_iterator* iterator,
+    const grpc_channel_filter* filter,
+    grpc_post_filter_create_init_func post_init_func,
+    void* user_data) GRPC_MUST_USE_RESULT;
+
+/// Add \a filter to the stack, before \a iterator.
+/// Call \a post_init_func(..., \a user_data) once the channel stack is
+/// created.
+bool grpc_channel_stack_builder_add_filter_before(
+    grpc_channel_stack_builder_iterator* iterator,
+    const grpc_channel_filter* filter,
+    grpc_post_filter_create_init_func post_init_func,
+    void* user_data) GRPC_MUST_USE_RESULT;
+
+/// Add \a filter to the beginning of the filter list.
+/// Call \a post_init_func(..., \a user_data) once the channel stack is
+/// created.
+bool grpc_channel_stack_builder_prepend_filter(
+    grpc_channel_stack_builder* builder, const grpc_channel_filter* filter,
+    grpc_post_filter_create_init_func post_init_func,
+    void* user_data) GRPC_MUST_USE_RESULT;
+
+/// Add \a filter to the end of the filter list.
+/// Call \a post_init_func(..., \a user_data) once the channel stack is
+/// created.
+bool grpc_channel_stack_builder_append_filter(
+    grpc_channel_stack_builder* builder, const grpc_channel_filter* filter,
+    grpc_post_filter_create_init_func post_init_func,
+    void* user_data) GRPC_MUST_USE_RESULT;
+
+/// Remove any filter whose name is \a filter_name from \a builder. Returns true
+/// if \a filter_name was not found.
+bool grpc_channel_stack_builder_remove_filter(
+    grpc_channel_stack_builder* builder, const char* filter_name);
+
+/// Terminate iteration and destroy \a iterator
+void grpc_channel_stack_builder_iterator_destroy(
+    grpc_channel_stack_builder_iterator* iterator);
+
+/// Destroy the builder, return the freshly minted channel stack in \a result.
+/// Allocates \a prefix_bytes bytes before the channel stack
+/// Returns the base pointer of the allocated block
+/// \a initial_refs, \a destroy, \a destroy_arg are as per
+/// grpc_channel_stack_init
+grpc_error* grpc_channel_stack_builder_finish(
+    grpc_channel_stack_builder* builder, size_t prefix_bytes, int initial_refs,
+    grpc_iomgr_cb_func destroy, void* destroy_arg, void** result);
+
+/// Destroy the builder without creating a channel stack
+void grpc_channel_stack_builder_destroy(grpc_channel_stack_builder* builder);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H */
diff --git a/third_party/grpc/src/core/lib/channel/channel_trace.cc b/third_party/grpc/src/core/lib/channel/channel_trace.cc
new file mode 100644
index 0000000..f0d21db
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channel_trace.cc
@@ -0,0 +1,203 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/impl/codegen/port_platform.h>
+
+#include "src/core/lib/channel/channel_trace.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
+
+namespace grpc_core {
+namespace channelz {
+
+ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data,
+                                     RefCountedPtr<BaseNode> referenced_entity)
+    : severity_(severity),
+      data_(data),
+      timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(),
+                                         GPR_CLOCK_REALTIME)),
+      next_(nullptr),
+      referenced_entity_(std::move(referenced_entity)),
+      memory_usage_(sizeof(TraceEvent) + grpc_slice_memory_usage(data)) {}
+
+ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data)
+    : severity_(severity),
+      data_(data),
+      timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(),
+                                         GPR_CLOCK_REALTIME)),
+      next_(nullptr),
+      memory_usage_(sizeof(TraceEvent) + grpc_slice_memory_usage(data)) {}
+
+ChannelTrace::TraceEvent::~TraceEvent() { grpc_slice_unref_internal(data_); }
+
+ChannelTrace::ChannelTrace(size_t max_event_memory)
+    : num_events_logged_(0),
+      event_list_memory_usage_(0),
+      max_event_memory_(max_event_memory),
+      head_trace_(nullptr),
+      tail_trace_(nullptr) {
+  if (max_event_memory_ == 0)
+    return;  // tracing is disabled if max_event_memory_ == 0
+  gpr_mu_init(&tracer_mu_);
+  time_created_ = grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(),
+                                          GPR_CLOCK_REALTIME);
+}
+
+ChannelTrace::~ChannelTrace() {
+  if (max_event_memory_ == 0)
+    return;  // tracing is disabled if max_event_memory_ == 0
+  TraceEvent* it = head_trace_;
+  while (it != nullptr) {
+    TraceEvent* to_free = it;
+    it = it->next();
+    Delete<TraceEvent>(to_free);
+  }
+  gpr_mu_destroy(&tracer_mu_);
+}
+
+void ChannelTrace::AddTraceEventHelper(TraceEvent* new_trace_event) {
+  ++num_events_logged_;
+  // first event case
+  if (head_trace_ == nullptr) {
+    head_trace_ = tail_trace_ = new_trace_event;
+  }
+  // regular event add case
+  else {
+    tail_trace_->set_next(new_trace_event);
+    tail_trace_ = tail_trace_->next();
+  }
+  event_list_memory_usage_ += new_trace_event->memory_usage();
+  // maybe garbage collect the tail until we are under the memory limit.
+  while (event_list_memory_usage_ > max_event_memory_) {
+    TraceEvent* to_free = head_trace_;
+    event_list_memory_usage_ -= to_free->memory_usage();
+    head_trace_ = head_trace_->next();
+    Delete<TraceEvent>(to_free);
+  }
+}
+
+void ChannelTrace::AddTraceEvent(Severity severity, grpc_slice data) {
+  if (max_event_memory_ == 0) {
+    grpc_slice_unref_internal(data);
+    return;  // tracing is disabled if max_event_memory_ == 0
+  }
+  AddTraceEventHelper(New<TraceEvent>(severity, data));
+}
+
+void ChannelTrace::AddTraceEventWithReference(
+    Severity severity, grpc_slice data,
+    RefCountedPtr<BaseNode> referenced_entity) {
+  if (max_event_memory_ == 0) {
+    grpc_slice_unref_internal(data);
+    return;  // tracing is disabled if max_event_memory_ == 0
+  }
+  // create and fill up the new event
+  AddTraceEventHelper(
+      New<TraceEvent>(severity, data, std::move(referenced_entity)));
+}
+
+namespace {
+
+const char* severity_string(ChannelTrace::Severity severity) {
+  switch (severity) {
+    case ChannelTrace::Severity::Info:
+      return "CT_INFO";
+    case ChannelTrace::Severity::Warning:
+      return "CT_WARNING";
+    case ChannelTrace::Severity::Error:
+      return "CT_ERROR";
+    default:
+      GPR_UNREACHABLE_CODE(return "CT_UNKNOWN");
+  }
+}
+
+}  // anonymous namespace
+
+void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const {
+  grpc_json* json_iterator = nullptr;
+  json_iterator = grpc_json_create_child(json_iterator, json, "description",
+                                         grpc_slice_to_c_string(data_),
+                                         GRPC_JSON_STRING, true);
+  json_iterator = grpc_json_create_child(json_iterator, json, "severity",
+                                         severity_string(severity_),
+                                         GRPC_JSON_STRING, false);
+  json_iterator = grpc_json_create_child(json_iterator, json, "timestamp",
+                                         gpr_format_timespec(timestamp_),
+                                         GRPC_JSON_STRING, true);
+  if (referenced_entity_ != nullptr) {
+    const bool is_channel =
+        (referenced_entity_->type() == BaseNode::EntityType::kTopLevelChannel ||
+         referenced_entity_->type() == BaseNode::EntityType::kInternalChannel);
+    char* uuid_str;
+    gpr_asprintf(&uuid_str, "%" PRIdPTR, referenced_entity_->uuid());
+    grpc_json* child_ref = grpc_json_create_child(
+        json_iterator, json, is_channel ? "channelRef" : "subchannelRef",
+        nullptr, GRPC_JSON_OBJECT, false);
+    json_iterator = grpc_json_create_child(
+        nullptr, child_ref, is_channel ? "channelId" : "subchannelId", uuid_str,
+        GRPC_JSON_STRING, true);
+    json_iterator = child_ref;
+  }
+}
+
+grpc_json* ChannelTrace::RenderJson() const {
+  if (max_event_memory_ == 0)
+    return nullptr;  // tracing is disabled if max_event_memory_ == 0
+  grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json_iterator = nullptr;
+  if (num_events_logged_ > 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "numEventsLogged", num_events_logged_);
+  }
+  json_iterator = grpc_json_create_child(
+      json_iterator, json, "creationTimestamp",
+      gpr_format_timespec(time_created_), GRPC_JSON_STRING, true);
+  // only add in the event list if it is non-empty.
+  if (head_trace_ != nullptr) {
+    grpc_json* events = grpc_json_create_child(json_iterator, json, "events",
+                                               nullptr, GRPC_JSON_ARRAY, false);
+    json_iterator = nullptr;
+    TraceEvent* it = head_trace_;
+    while (it != nullptr) {
+      json_iterator = grpc_json_create_child(json_iterator, events, nullptr,
+                                             nullptr, GRPC_JSON_OBJECT, false);
+      it->RenderTraceEvent(json_iterator);
+      it = it->next();
+    }
+  }
+  return json;
+}
+
+}  // namespace channelz
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/channel/channel_trace.h b/third_party/grpc/src/core/lib/channel/channel_trace.h
new file mode 100644
index 0000000..8ff91ee
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channel_trace.h
@@ -0,0 +1,134 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/grpc.h>
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/json/json.h"
+
+namespace grpc_core {
+namespace channelz {
+
+namespace testing {
+size_t GetSizeofTraceEvent(void);
+}
+
+class BaseNode;
+
+// Object used to hold live data for a channel. This data is exposed via the
+// channelz service:
+// https://github.com/grpc/proposal/blob/master/A14-channelz.md
+class ChannelTrace {
+ public:
+  ChannelTrace(size_t max_event_memory);
+  ~ChannelTrace();
+
+  enum Severity {
+    Unset = 0,  // never to be used
+    Info,       // we start at 1 to avoid using proto default values
+    Warning,
+    Error
+  };
+
+  // Adds a new trace event to the tracing object
+  //
+  // NOTE: each ChannelTrace tracks the memory used by its list of trace
+  // events, so adding an event with a large amount of data could cause other
+  // trace event to be evicted. If a single trace is larger than the limit, it
+  // will cause all events to be evicted. The limit is set with the arg:
+  // GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE.
+  //
+  // TODO(ncteisen): as this call is used more and more throughout the gRPC
+  // stack, determine if it makes more sense to accept a char* instead of a
+  // slice.
+  void AddTraceEvent(Severity severity, grpc_slice data);
+
+  // Adds a new trace event to the tracing object. This trace event refers to a
+  // an event that concerns a different channelz entity. For example, if this
+  // channel has created a new subchannel, then it would record that with
+  // a TraceEvent referencing the new subchannel.
+  //
+  // NOTE: see the note in the method above.
+  //
+  // TODO(ncteisen): see the todo in the method above.
+  void AddTraceEventWithReference(Severity severity, grpc_slice data,
+                                  RefCountedPtr<BaseNode> referenced_entity);
+
+  // Creates and returns the raw grpc_json object, so a parent channelz
+  // object may incorporate the json before rendering.
+  grpc_json* RenderJson() const;
+
+ private:
+  friend size_t testing::GetSizeofTraceEvent(void);
+
+  // Private class to encapsulate all the data and bookkeeping needed for a
+  // a trace event.
+  class TraceEvent {
+   public:
+    // Constructor for a TraceEvent that references a channel.
+    TraceEvent(Severity severity, grpc_slice data,
+               RefCountedPtr<BaseNode> referenced_entity_);
+
+    // Constructor for a TraceEvent that does not reverence a different
+    // channel.
+    TraceEvent(Severity severity, grpc_slice data);
+
+    ~TraceEvent();
+
+    // Renders the data inside of this TraceEvent into a json object. This is
+    // used by the ChannelTrace, when it is rendering itself.
+    void RenderTraceEvent(grpc_json* json) const;
+
+    // set and get for the next_ pointer.
+    TraceEvent* next() const { return next_; }
+    void set_next(TraceEvent* next) { next_ = next; }
+
+    size_t memory_usage() const { return memory_usage_; }
+
+   private:
+    Severity severity_;
+    grpc_slice data_;
+    gpr_timespec timestamp_;
+    TraceEvent* next_;
+    // the tracer object for the (sub)channel that this trace event refers to.
+    RefCountedPtr<BaseNode> referenced_entity_;
+    size_t memory_usage_;
+  };  // TraceEvent
+
+  // Internal helper to add and link in a trace event
+  void AddTraceEventHelper(TraceEvent* new_trace_event);
+
+  gpr_mu tracer_mu_;
+  uint64_t num_events_logged_;
+  size_t event_list_memory_usage_;
+  size_t max_event_memory_;
+  TraceEvent* head_trace_;
+  TraceEvent* tail_trace_;
+  gpr_timespec time_created_;
+};
+
+}  // namespace channelz
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H */
diff --git a/third_party/grpc/src/core/lib/channel/channelz.cc b/third_party/grpc/src/core/lib/channel/channelz.cc
new file mode 100644
index 0000000..8a596ad
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channelz.cc
@@ -0,0 +1,460 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/impl/codegen/port_platform.h>
+
+#include "src/core/lib/channel/channelz.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/core/lib/channel/channelz_registry.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/b64.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/server.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+namespace grpc_core {
+namespace channelz {
+
+BaseNode::BaseNode(EntityType type) : type_(type), uuid_(-1) {
+  // The registry will set uuid_ under its lock.
+  ChannelzRegistry::Register(this);
+}
+
+BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
+
+char* BaseNode::RenderJsonString() {
+  grpc_json* json = RenderJson();
+  GPR_ASSERT(json != nullptr);
+  char* json_str = grpc_json_dump_to_string(json, 0);
+  grpc_json_destroy(json);
+  return json_str;
+}
+
+CallCountingHelper::CallCountingHelper() {
+  num_cores_ = GPR_MAX(1, gpr_cpu_num_cores());
+  per_cpu_counter_data_storage_ = static_cast<AtomicCounterData*>(
+      gpr_zalloc(sizeof(AtomicCounterData) * num_cores_));
+}
+
+CallCountingHelper::~CallCountingHelper() {
+  gpr_free(per_cpu_counter_data_storage_);
+}
+
+void CallCountingHelper::RecordCallStarted() {
+  gpr_atm_no_barrier_fetch_add(
+      &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
+           .calls_started,
+      static_cast<gpr_atm>(1));
+  gpr_atm_no_barrier_store(
+      &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
+           .last_call_started_millis,
+      (gpr_atm)ExecCtx::Get()->Now());
+}
+
+void CallCountingHelper::RecordCallFailed() {
+  gpr_atm_no_barrier_fetch_add(
+      &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
+           .calls_failed,
+      static_cast<gpr_atm>(1));
+}
+
+void CallCountingHelper::RecordCallSucceeded() {
+  gpr_atm_no_barrier_fetch_add(
+      &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
+           .calls_succeeded,
+      static_cast<gpr_atm>(1));
+}
+
+void CallCountingHelper::CollectData(CounterData* out) {
+  for (size_t core = 0; core < num_cores_; ++core) {
+    out->calls_started += gpr_atm_no_barrier_load(
+        &per_cpu_counter_data_storage_[core].calls_started);
+    out->calls_succeeded += gpr_atm_no_barrier_load(
+        &per_cpu_counter_data_storage_[core].calls_succeeded);
+    out->calls_failed += gpr_atm_no_barrier_load(
+        &per_cpu_counter_data_storage_[core].calls_failed);
+    gpr_atm last_call = gpr_atm_no_barrier_load(
+        &per_cpu_counter_data_storage_[core].last_call_started_millis);
+    if (last_call > out->last_call_started_millis) {
+      out->last_call_started_millis = last_call;
+    }
+  }
+}
+
+void CallCountingHelper::PopulateCallCounts(grpc_json* json) {
+  grpc_json* json_iterator = nullptr;
+  CounterData data;
+  CollectData(&data);
+  if (data.calls_started != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "callsStarted", data.calls_started);
+  }
+  if (data.calls_succeeded != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "callsSucceeded", data.calls_succeeded);
+  }
+  if (data.calls_failed) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "callsFailed", data.calls_failed);
+  }
+  if (data.calls_started != 0) {
+    gpr_timespec ts = grpc_millis_to_timespec(data.last_call_started_millis,
+                                              GPR_CLOCK_REALTIME);
+    json_iterator =
+        grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
+                               gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+  }
+}
+
+ChannelNode::ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
+                         bool is_top_level_channel)
+    : BaseNode(is_top_level_channel ? EntityType::kTopLevelChannel
+                                    : EntityType::kInternalChannel),
+      channel_(channel),
+      target_(UniquePtr<char>(grpc_channel_get_target(channel_))),
+      trace_(channel_tracer_max_nodes) {}
+
+ChannelNode::~ChannelNode() {}
+
+grpc_json* ChannelNode::RenderJson() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "channelId", uuid());
+  // reset json iterators to top level object
+  json = top_level_json;
+  json_iterator = nullptr;
+  // create and fill the data child.
+  grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
+                                           GRPC_JSON_OBJECT, false);
+  json = data;
+  json_iterator = nullptr;
+  // template method. Child classes may override this to add their specific
+  // functionality.
+  PopulateConnectivityState(json);
+  // populate the target.
+  GPR_ASSERT(target_.get() != nullptr);
+  grpc_json_create_child(nullptr, json, "target", target_.get(),
+                         GRPC_JSON_STRING, false);
+  // fill in the channel trace if applicable
+  grpc_json* trace_json = trace_.RenderJson();
+  if (trace_json != nullptr) {
+    trace_json->key = "trace";  // this object is named trace in channelz.proto
+    grpc_json_link_child(json, trace_json, nullptr);
+  }
+  // ask CallCountingHelper to populate trace and call count data.
+  call_counter_.PopulateCallCounts(json);
+  json = top_level_json;
+  // template method. Child classes may override this to add their specific
+  // functionality.
+  PopulateChildRefs(json);
+  return top_level_json;
+}
+
+RefCountedPtr<ChannelNode> ChannelNode::MakeChannelNode(
+    grpc_channel* channel, size_t channel_tracer_max_nodes,
+    bool is_top_level_channel) {
+  return MakeRefCounted<grpc_core::channelz::ChannelNode>(
+      channel, channel_tracer_max_nodes, is_top_level_channel);
+}
+
+ServerNode::ServerNode(grpc_server* server, size_t channel_tracer_max_nodes)
+    : BaseNode(EntityType::kServer),
+      server_(server),
+      trace_(channel_tracer_max_nodes) {}
+
+ServerNode::~ServerNode() {}
+
+char* ServerNode::RenderServerSockets(intptr_t start_socket_id,
+                                      intptr_t max_results) {
+  // if user does not set max_results, we choose 500.
+  size_t pagination_limit = max_results == 0 ? 500 : max_results;
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  ChildSocketsList socket_refs;
+  grpc_server_populate_server_sockets(server_, &socket_refs, start_socket_id);
+  // declared early so it can be used outside of the loop.
+  size_t i = 0;
+  if (!socket_refs.empty()) {
+    // create list of socket refs
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false);
+    for (i = 0; i < GPR_MIN(socket_refs.size(), pagination_limit); ++i) {
+      grpc_json* socket_ref_json = grpc_json_create_child(
+          nullptr, array_parent, nullptr, nullptr, GRPC_JSON_OBJECT, false);
+      json_iterator = grpc_json_add_number_string_child(
+          socket_ref_json, nullptr, "socketId", socket_refs[i]->uuid());
+      grpc_json_create_child(json_iterator, socket_ref_json, "name",
+                             socket_refs[i]->remote(), GRPC_JSON_STRING, false);
+    }
+  }
+  if (i == socket_refs.size()) {
+    json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
+                                           GRPC_JSON_TRUE, false);
+  }
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
+
+grpc_json* ServerNode::RenderJson() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "serverId", uuid());
+  // reset json iterators to top level object
+  json = top_level_json;
+  json_iterator = nullptr;
+  // create and fill the data child.
+  grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
+                                           GRPC_JSON_OBJECT, false);
+  json = data;
+  json_iterator = nullptr;
+  // fill in the channel trace if applicable
+  grpc_json* trace_json = trace_.RenderJson();
+  if (trace_json != nullptr) {
+    trace_json->key = "trace";  // this object is named trace in channelz.proto
+    grpc_json_link_child(json, trace_json, nullptr);
+  }
+  // ask CallCountingHelper to populate trace and call count data.
+  call_counter_.PopulateCallCounts(json);
+  json = top_level_json;
+  ChildRefsList listen_sockets;
+  grpc_server_populate_listen_sockets(server_, &listen_sockets);
+  if (!listen_sockets.empty()) {
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false);
+    for (size_t i = 0; i < listen_sockets.size(); ++i) {
+      json_iterator =
+          grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
+                                 GRPC_JSON_OBJECT, false);
+      grpc_json_add_number_string_child(json_iterator, nullptr, "socketId",
+                                        listen_sockets[i]);
+    }
+  }
+  return top_level_json;
+}
+
+static void PopulateSocketAddressJson(grpc_json* json, const char* name,
+                                      const char* addr_str) {
+  if (addr_str == nullptr) return;
+  grpc_json* json_iterator = nullptr;
+  json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  grpc_uri* uri = grpc_uri_parse(addr_str, true);
+  if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) ||
+                           (strcmp(uri->scheme, "ipv6") == 0))) {
+    const char* host_port = uri->path;
+    if (*host_port == '/') ++host_port;
+    char* host = nullptr;
+    char* port = nullptr;
+    GPR_ASSERT(gpr_split_host_port(host_port, &host, &port));
+    int port_num = -1;
+    if (port != nullptr) {
+      port_num = atoi(port);
+    }
+    char* b64_host = grpc_base64_encode(host, strlen(host), false, false);
+    json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address",
+                                           nullptr, GRPC_JSON_OBJECT, false);
+    json = json_iterator;
+    json_iterator = nullptr;
+    json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                      "port", port_num);
+    json_iterator = grpc_json_create_child(json_iterator, json, "ip_address",
+                                           b64_host, GRPC_JSON_STRING, true);
+    gpr_free(host);
+    gpr_free(port);
+
+  } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) {
+    json_iterator = grpc_json_create_child(json_iterator, json, "uds_address",
+                                           nullptr, GRPC_JSON_OBJECT, false);
+    json = json_iterator;
+    json_iterator = nullptr;
+    json_iterator =
+        grpc_json_create_child(json_iterator, json, "filename",
+                               gpr_strdup(uri->path), GRPC_JSON_STRING, true);
+  } else {
+    json_iterator = grpc_json_create_child(json_iterator, json, "other_address",
+                                           nullptr, GRPC_JSON_OBJECT, false);
+    json = json_iterator;
+    json_iterator = nullptr;
+    json_iterator = grpc_json_create_child(json_iterator, json, "name",
+                                           addr_str, GRPC_JSON_STRING, false);
+  }
+  grpc_uri_destroy(uri);
+}
+
+SocketNode::SocketNode(UniquePtr<char> local, UniquePtr<char> remote)
+    : BaseNode(EntityType::kSocket),
+      local_(std::move(local)),
+      remote_(std::move(remote)) {}
+
+void SocketNode::RecordStreamStartedFromLocal() {
+  gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
+  gpr_atm_no_barrier_store(&last_local_stream_created_millis_,
+                           (gpr_atm)ExecCtx::Get()->Now());
+}
+
+void SocketNode::RecordStreamStartedFromRemote() {
+  gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
+  gpr_atm_no_barrier_store(&last_remote_stream_created_millis_,
+                           (gpr_atm)ExecCtx::Get()->Now());
+}
+
+void SocketNode::RecordMessagesSent(uint32_t num_sent) {
+  gpr_atm_no_barrier_fetch_add(&messages_sent_, static_cast<gpr_atm>(num_sent));
+  gpr_atm_no_barrier_store(&last_message_sent_millis_,
+                           (gpr_atm)ExecCtx::Get()->Now());
+}
+
+void SocketNode::RecordMessageReceived() {
+  gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast<gpr_atm>(1));
+  gpr_atm_no_barrier_store(&last_message_received_millis_,
+                           (gpr_atm)ExecCtx::Get()->Now());
+}
+
+grpc_json* SocketNode::RenderJson() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "socketId", uuid());
+  json = top_level_json;
+  PopulateSocketAddressJson(json, "remote", remote_.get());
+  PopulateSocketAddressJson(json, "local", local_.get());
+  // reset json iterators to top level object
+  json = top_level_json;
+  json_iterator = nullptr;
+  // create and fill the data child.
+  grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
+                                           GRPC_JSON_OBJECT, false);
+  json = data;
+  json_iterator = nullptr;
+  gpr_timespec ts;
+  if (streams_started_ != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "streamsStarted", streams_started_);
+    if (last_local_stream_created_millis_ != 0) {
+      ts = grpc_millis_to_timespec(last_local_stream_created_millis_,
+                                   GPR_CLOCK_REALTIME);
+      json_iterator = grpc_json_create_child(
+          json_iterator, json, "lastLocalStreamCreatedTimestamp",
+          gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    }
+    if (last_remote_stream_created_millis_ != 0) {
+      ts = grpc_millis_to_timespec(last_remote_stream_created_millis_,
+                                   GPR_CLOCK_REALTIME);
+      json_iterator = grpc_json_create_child(
+          json_iterator, json, "lastRemoteStreamCreatedTimestamp",
+          gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    }
+  }
+  if (streams_succeeded_ != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "streamsSucceeded", streams_succeeded_);
+  }
+  if (streams_failed_) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "streamsFailed", streams_failed_);
+  }
+  if (messages_sent_ != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "messagesSent", messages_sent_);
+    ts = grpc_millis_to_timespec(last_message_sent_millis_, GPR_CLOCK_REALTIME);
+    json_iterator =
+        grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp",
+                               gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+  }
+  if (messages_received_ != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "messagesReceived", messages_received_);
+    ts = grpc_millis_to_timespec(last_message_received_millis_,
+                                 GPR_CLOCK_REALTIME);
+    json_iterator = grpc_json_create_child(
+        json_iterator, json, "lastMessageReceivedTimestamp",
+        gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+  }
+  if (keepalives_sent_ != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "keepAlivesSent", keepalives_sent_);
+  }
+  return top_level_json;
+}
+
+ListenSocketNode::ListenSocketNode(UniquePtr<char> local_addr)
+    : BaseNode(EntityType::kSocket), local_addr_(std::move(local_addr)) {}
+
+grpc_json* ListenSocketNode::RenderJson() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "socketId", uuid());
+  json = top_level_json;
+  PopulateSocketAddressJson(json, "local", local_addr_.get());
+
+  return top_level_json;
+}
+
+}  // namespace channelz
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/channel/channelz.h b/third_party/grpc/src/core/lib/channel/channelz.h
new file mode 100644
index 0000000..e437921
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channelz.h
@@ -0,0 +1,296 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CHANNELZ_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNELZ_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/json/json.h"
+
+// Channel arg key for client channel factory.
+#define GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC \
+  "grpc.channelz_channel_node_creation_func"
+
+// Channel arg key to signal that the channel is an internal channel.
+#define GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL \
+  "grpc.channelz_channel_is_internal_channel"
+
+/** This is the default value for whether or not to enable channelz. If
+ * GRPC_ARG_ENABLE_CHANNELZ is set, it will override this default value. */
+#define GRPC_ENABLE_CHANNELZ_DEFAULT true
+
+/** This is the default value for the maximum amount of memory used by trace
+ * events per channel trace node. If
+ * GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE is set, it will override
+ * this default value. */
+#define GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT 1024 * 4
+
+namespace grpc_core {
+
+namespace channelz {
+
+// TODO(ncteisen), this only contains the uuids of the children for now,
+// since that is all that is strictly needed. In a future enhancement we will
+// add human readable names as in the channelz.proto
+typedef InlinedVector<intptr_t, 10> ChildRefsList;
+
+class SocketNode;
+typedef InlinedVector<SocketNode*, 10> ChildSocketsList;
+
+namespace testing {
+class CallCountingHelperPeer;
+class ChannelNodePeer;
+}  // namespace testing
+
+// base class for all channelz entities
+class BaseNode : public RefCounted<BaseNode> {
+ public:
+  // There are only four high level channelz entities. However, to support
+  // GetTopChannelsRequest, we split the Channel entity into two different
+  // types. All children of BaseNode must be one of these types.
+  enum class EntityType {
+    kTopLevelChannel,
+    kInternalChannel,
+    kSubchannel,
+    kServer,
+    kSocket,
+  };
+
+  explicit BaseNode(EntityType type);
+  virtual ~BaseNode();
+
+  // All children must implement this function.
+  virtual grpc_json* RenderJson() GRPC_ABSTRACT;
+
+  // Renders the json and returns allocated string that must be freed by the
+  // caller.
+  char* RenderJsonString();
+
+  EntityType type() const { return type_; }
+  intptr_t uuid() const { return uuid_; }
+
+ private:
+  // to allow the ChannelzRegistry to set uuid_ under its lock.
+  friend class ChannelzRegistry;
+  const EntityType type_;
+  intptr_t uuid_;
+};
+
+// This class is a helper class for channelz entities that deal with Channels,
+// Subchannels, and Servers, since those have similar proto definitions.
+// This class has the ability to:
+//   - track calls_{started,succeeded,failed}
+//   - track last_call_started_timestamp
+//   - perform rendering of the above items
+class CallCountingHelper {
+ public:
+  CallCountingHelper();
+  ~CallCountingHelper();
+
+  void RecordCallStarted();
+  void RecordCallFailed();
+  void RecordCallSucceeded();
+
+  // Common rendering of the call count data and last_call_started_timestamp.
+  void PopulateCallCounts(grpc_json* json);
+
+ private:
+  // testing peer friend.
+  friend class testing::CallCountingHelperPeer;
+
+  struct AtomicCounterData {
+    gpr_atm calls_started = 0;
+    gpr_atm calls_succeeded = 0;
+    gpr_atm calls_failed = 0;
+    gpr_atm last_call_started_millis = 0;
+  };
+
+  struct CounterData {
+    intptr_t calls_started = 0;
+    intptr_t calls_succeeded = 0;
+    intptr_t calls_failed = 0;
+    intptr_t last_call_started_millis = 0;
+  };
+
+  // collects the sharded data into one CounterData struct.
+  void CollectData(CounterData* out);
+
+  AtomicCounterData* per_cpu_counter_data_storage_ = nullptr;
+  size_t num_cores_ = 0;
+};
+
+// Handles channelz bookkeeping for channels
+class ChannelNode : public BaseNode {
+ public:
+  static RefCountedPtr<ChannelNode> MakeChannelNode(
+      grpc_channel* channel, size_t channel_tracer_max_nodes,
+      bool is_top_level_channel);
+
+  ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
+              bool is_top_level_channel);
+  ~ChannelNode() override;
+
+  grpc_json* RenderJson() override;
+
+  // template methods. RenderJSON uses these methods to render its JSON
+  // representation. These are virtual so that children classes may provide
+  // their specific mechanism for populating these parts of the channelz
+  // object.
+  //
+  // ChannelNode does not have a notion of connectivity state or child refs,
+  // so it leaves these implementations blank.
+  //
+  // This is utilizing the template method design pattern.
+  //
+  // TODO(ncteisen): remove these template methods in favor of manual traversal
+  // and mutation of the grpc_json object.
+  virtual void PopulateConnectivityState(grpc_json* json) {}
+  virtual void PopulateChildRefs(grpc_json* json) {}
+
+  void MarkChannelDestroyed() {
+    GPR_ASSERT(channel_ != nullptr);
+    channel_ = nullptr;
+  }
+
+  bool ChannelIsDestroyed() { return channel_ == nullptr; }
+
+  // proxy methods to composed classes.
+  void AddTraceEvent(ChannelTrace::Severity severity, grpc_slice data) {
+    trace_.AddTraceEvent(severity, data);
+  }
+  void AddTraceEventWithReference(ChannelTrace::Severity severity,
+                                  grpc_slice data,
+                                  RefCountedPtr<BaseNode> referenced_channel) {
+    trace_.AddTraceEventWithReference(severity, data,
+                                      std::move(referenced_channel));
+  }
+  void RecordCallStarted() { call_counter_.RecordCallStarted(); }
+  void RecordCallFailed() { call_counter_.RecordCallFailed(); }
+  void RecordCallSucceeded() { call_counter_.RecordCallSucceeded(); }
+
+ private:
+  // to allow the channel trace test to access trace_.
+  friend class testing::ChannelNodePeer;
+  grpc_channel* channel_ = nullptr;
+  UniquePtr<char> target_;
+  CallCountingHelper call_counter_;
+  ChannelTrace trace_;
+};
+
+// Handles channelz bookkeeping for servers
+class ServerNode : public BaseNode {
+ public:
+  ServerNode(grpc_server* server, size_t channel_tracer_max_nodes);
+  ~ServerNode() override;
+
+  grpc_json* RenderJson() override;
+
+  char* RenderServerSockets(intptr_t start_socket_id,
+                            intptr_t pagination_limit);
+
+  // proxy methods to composed classes.
+  void AddTraceEvent(ChannelTrace::Severity severity, grpc_slice data) {
+    trace_.AddTraceEvent(severity, data);
+  }
+  void AddTraceEventWithReference(ChannelTrace::Severity severity,
+                                  grpc_slice data,
+                                  RefCountedPtr<BaseNode> referenced_channel) {
+    trace_.AddTraceEventWithReference(severity, data,
+                                      std::move(referenced_channel));
+  }
+  void RecordCallStarted() { call_counter_.RecordCallStarted(); }
+  void RecordCallFailed() { call_counter_.RecordCallFailed(); }
+  void RecordCallSucceeded() { call_counter_.RecordCallSucceeded(); }
+
+ private:
+  grpc_server* server_;
+  CallCountingHelper call_counter_;
+  ChannelTrace trace_;
+};
+
+// Handles channelz bookkeeping for sockets
+class SocketNode : public BaseNode {
+ public:
+  SocketNode(UniquePtr<char> local, UniquePtr<char> remote);
+  ~SocketNode() override {}
+
+  grpc_json* RenderJson() override;
+
+  void RecordStreamStartedFromLocal();
+  void RecordStreamStartedFromRemote();
+  void RecordStreamSucceeded() {
+    gpr_atm_no_barrier_fetch_add(&streams_succeeded_, static_cast<gpr_atm>(1));
+  }
+  void RecordStreamFailed() {
+    gpr_atm_no_barrier_fetch_add(&streams_failed_, static_cast<gpr_atm>(1));
+  }
+  void RecordMessagesSent(uint32_t num_sent);
+  void RecordMessageReceived();
+  void RecordKeepaliveSent() {
+    gpr_atm_no_barrier_fetch_add(&keepalives_sent_, static_cast<gpr_atm>(1));
+  }
+
+  const char* remote() { return remote_.get(); }
+
+ private:
+  gpr_atm streams_started_ = 0;
+  gpr_atm streams_succeeded_ = 0;
+  gpr_atm streams_failed_ = 0;
+  gpr_atm messages_sent_ = 0;
+  gpr_atm messages_received_ = 0;
+  gpr_atm keepalives_sent_ = 0;
+  gpr_atm last_local_stream_created_millis_ = 0;
+  gpr_atm last_remote_stream_created_millis_ = 0;
+  gpr_atm last_message_sent_millis_ = 0;
+  gpr_atm last_message_received_millis_ = 0;
+  UniquePtr<char> local_;
+  UniquePtr<char> remote_;
+};
+
+// Handles channelz bookkeeping for listen sockets
+class ListenSocketNode : public BaseNode {
+ public:
+  // ListenSocketNode takes ownership of host.
+  explicit ListenSocketNode(UniquePtr<char> local_addr);
+  ~ListenSocketNode() override {}
+
+  grpc_json* RenderJson() override;
+
+ private:
+  UniquePtr<char> local_addr_;
+};
+
+// Creation functions
+
+typedef RefCountedPtr<ChannelNode> (*ChannelNodeCreationFunc)(grpc_channel*,
+                                                              size_t, bool);
+
+}  // namespace channelz
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNELZ_H */
diff --git a/third_party/grpc/src/core/lib/channel/channelz_registry.cc b/third_party/grpc/src/core/lib/channel/channelz_registry.cc
new file mode 100644
index 0000000..7cca247
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channelz_registry.cc
@@ -0,0 +1,324 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/impl/codegen/port_platform.h>
+
+#include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/channel/channelz.h"
+#include "src/core/lib/channel/channelz_registry.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/mutex_lock.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include <cstring>
+
+namespace grpc_core {
+namespace channelz {
+namespace {
+
+// singleton instance of the registry.
+ChannelzRegistry* g_channelz_registry = nullptr;
+
+const int kPaginationLimit = 100;
+
+}  // anonymous namespace
+
+void ChannelzRegistry::Init() { g_channelz_registry = New<ChannelzRegistry>(); }
+
+void ChannelzRegistry::Shutdown() { Delete(g_channelz_registry); }
+
+ChannelzRegistry* ChannelzRegistry::Default() {
+  GPR_DEBUG_ASSERT(g_channelz_registry != nullptr);
+  return g_channelz_registry;
+}
+
+ChannelzRegistry::ChannelzRegistry() { gpr_mu_init(&mu_); }
+
+ChannelzRegistry::~ChannelzRegistry() { gpr_mu_destroy(&mu_); }
+
+void ChannelzRegistry::InternalRegister(BaseNode* node) {
+  MutexLock lock(&mu_);
+  entities_.push_back(node);
+  node->uuid_ = ++uuid_generator_;
+}
+
+void ChannelzRegistry::MaybePerformCompactionLocked() {
+  constexpr double kEmptinessTheshold = 1 / 3;
+  double emptiness_ratio =
+      double(num_empty_slots_) / double(entities_.capacity());
+  if (emptiness_ratio > kEmptinessTheshold) {
+    int front = 0;
+    for (size_t i = 0; i < entities_.size(); ++i) {
+      if (entities_[i] != nullptr) {
+        entities_[front++] = entities_[i];
+      }
+    }
+    for (int i = 0; i < num_empty_slots_; ++i) {
+      entities_.pop_back();
+    }
+    num_empty_slots_ = 0;
+  }
+}
+
+int ChannelzRegistry::FindByUuidLocked(intptr_t target_uuid,
+                                       bool direct_hit_needed) {
+  int left = 0;
+  int right = int(entities_.size() - 1);
+  while (left <= right) {
+    int true_middle = left + (right - left) / 2;
+    int first_non_null = true_middle;
+    while (first_non_null < right && entities_[first_non_null] == nullptr) {
+      first_non_null++;
+    }
+    if (entities_[first_non_null] == nullptr) {
+      right = true_middle - 1;
+      continue;
+    }
+    intptr_t uuid = entities_[first_non_null]->uuid();
+    if (uuid == target_uuid) {
+      return int(first_non_null);
+    }
+    if (uuid < target_uuid) {
+      left = first_non_null + 1;
+    } else {
+      right = true_middle - 1;
+    }
+  }
+  return direct_hit_needed ? -1 : left;
+}
+
+void ChannelzRegistry::InternalUnregister(intptr_t uuid) {
+  GPR_ASSERT(uuid >= 1);
+  MutexLock lock(&mu_);
+  GPR_ASSERT(uuid <= uuid_generator_);
+  int idx = FindByUuidLocked(uuid, true);
+  GPR_ASSERT(idx >= 0);
+  entities_[idx] = nullptr;
+  num_empty_slots_++;
+  MaybePerformCompactionLocked();
+}
+
+BaseNode* ChannelzRegistry::InternalGet(intptr_t uuid) {
+  MutexLock lock(&mu_);
+  if (uuid < 1 || uuid > uuid_generator_) {
+    return nullptr;
+  }
+  int idx = FindByUuidLocked(uuid, true);
+  return idx < 0 ? nullptr : entities_[idx];
+}
+
+char* ChannelzRegistry::InternalGetTopChannels(intptr_t start_channel_id) {
+  MutexLock lock(&mu_);
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  InlinedVector<BaseNode*, 10> top_level_channels;
+  bool reached_pagination_limit = false;
+  int start_idx = GPR_MAX(FindByUuidLocked(start_channel_id, false), 0);
+  for (size_t i = start_idx; i < entities_.size(); ++i) {
+    if (entities_[i] != nullptr &&
+        entities_[i]->type() ==
+            grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel &&
+        entities_[i]->uuid() >= start_channel_id) {
+      // check if we are over pagination limit to determine if we need to set
+      // the "end" element. If we don't go through this block, we know that
+      // when the loop terminates, we have <= to kPaginationLimit.
+      if (top_level_channels.size() == kPaginationLimit) {
+        reached_pagination_limit = true;
+        break;
+      }
+      top_level_channels.push_back(entities_[i]);
+    }
+  }
+  if (!top_level_channels.empty()) {
+    // create list of channels
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "channel", nullptr, GRPC_JSON_ARRAY, false);
+    for (size_t i = 0; i < top_level_channels.size(); ++i) {
+      grpc_json* channel_json = top_level_channels[i]->RenderJson();
+      json_iterator =
+          grpc_json_link_child(array_parent, channel_json, json_iterator);
+    }
+  }
+  if (!reached_pagination_limit) {
+    grpc_json_create_child(nullptr, json, "end", nullptr, GRPC_JSON_TRUE,
+                           false);
+  }
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
+
+char* ChannelzRegistry::InternalGetServers(intptr_t start_server_id) {
+  MutexLock lock(&mu_);
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  InlinedVector<BaseNode*, 10> servers;
+  bool reached_pagination_limit = false;
+  int start_idx = GPR_MAX(FindByUuidLocked(start_server_id, false), 0);
+  for (size_t i = start_idx; i < entities_.size(); ++i) {
+    if (entities_[i] != nullptr &&
+        entities_[i]->type() ==
+            grpc_core::channelz::BaseNode::EntityType::kServer &&
+        entities_[i]->uuid() >= start_server_id) {
+      // check if we are over pagination limit to determine if we need to set
+      // the "end" element. If we don't go through this block, we know that
+      // when the loop terminates, we have <= to kPaginationLimit.
+      if (servers.size() == kPaginationLimit) {
+        reached_pagination_limit = true;
+        break;
+      }
+      servers.push_back(entities_[i]);
+    }
+  }
+  if (!servers.empty()) {
+    // create list of servers
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "server", nullptr, GRPC_JSON_ARRAY, false);
+    for (size_t i = 0; i < servers.size(); ++i) {
+      grpc_json* server_json = servers[i]->RenderJson();
+      json_iterator =
+          grpc_json_link_child(array_parent, server_json, json_iterator);
+    }
+  }
+  if (!reached_pagination_limit) {
+    grpc_json_create_child(nullptr, json, "end", nullptr, GRPC_JSON_TRUE,
+                           false);
+  }
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
+
+void ChannelzRegistry::InternalLogAllEntities() {
+  MutexLock lock(&mu_);
+  for (size_t i = 0; i < entities_.size(); ++i) {
+    if (entities_[i] != nullptr) {
+      char* json = entities_[i]->RenderJsonString();
+      gpr_log(GPR_INFO, "%s", json);
+      gpr_free(json);
+    }
+  }
+}
+
+}  // namespace channelz
+}  // namespace grpc_core
+
+char* grpc_channelz_get_top_channels(intptr_t start_channel_id) {
+  return grpc_core::channelz::ChannelzRegistry::GetTopChannels(
+      start_channel_id);
+}
+
+char* grpc_channelz_get_servers(intptr_t start_server_id) {
+  return grpc_core::channelz::ChannelzRegistry::GetServers(start_server_id);
+}
+
+char* grpc_channelz_get_server(intptr_t server_id) {
+  grpc_core::channelz::BaseNode* server_node =
+      grpc_core::channelz::ChannelzRegistry::Get(server_id);
+  if (server_node == nullptr ||
+      server_node->type() !=
+          grpc_core::channelz::BaseNode::EntityType::kServer) {
+    return nullptr;
+  }
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* channel_json = server_node->RenderJson();
+  channel_json->key = "server";
+  grpc_json_link_child(json, channel_json, nullptr);
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
+
+char* grpc_channelz_get_server_sockets(intptr_t server_id,
+                                       intptr_t start_socket_id,
+                                       intptr_t max_results) {
+  grpc_core::channelz::BaseNode* base_node =
+      grpc_core::channelz::ChannelzRegistry::Get(server_id);
+  if (base_node == nullptr ||
+      base_node->type() != grpc_core::channelz::BaseNode::EntityType::kServer) {
+    return nullptr;
+  }
+  // This cast is ok since we have just checked to make sure base_node is
+  // actually a server node
+  grpc_core::channelz::ServerNode* server_node =
+      static_cast<grpc_core::channelz::ServerNode*>(base_node);
+  return server_node->RenderServerSockets(start_socket_id, max_results);
+}
+
+char* grpc_channelz_get_channel(intptr_t channel_id) {
+  grpc_core::channelz::BaseNode* channel_node =
+      grpc_core::channelz::ChannelzRegistry::Get(channel_id);
+  if (channel_node == nullptr ||
+      (channel_node->type() !=
+           grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel &&
+       channel_node->type() !=
+           grpc_core::channelz::BaseNode::EntityType::kInternalChannel)) {
+    return nullptr;
+  }
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* channel_json = channel_node->RenderJson();
+  channel_json->key = "channel";
+  grpc_json_link_child(json, channel_json, nullptr);
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
+
+char* grpc_channelz_get_subchannel(intptr_t subchannel_id) {
+  grpc_core::channelz::BaseNode* subchannel_node =
+      grpc_core::channelz::ChannelzRegistry::Get(subchannel_id);
+  if (subchannel_node == nullptr ||
+      subchannel_node->type() !=
+          grpc_core::channelz::BaseNode::EntityType::kSubchannel) {
+    return nullptr;
+  }
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* subchannel_json = subchannel_node->RenderJson();
+  subchannel_json->key = "subchannel";
+  grpc_json_link_child(json, subchannel_json, nullptr);
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
+
+char* grpc_channelz_get_socket(intptr_t socket_id) {
+  grpc_core::channelz::BaseNode* socket_node =
+      grpc_core::channelz::ChannelzRegistry::Get(socket_id);
+  if (socket_node == nullptr ||
+      socket_node->type() !=
+          grpc_core::channelz::BaseNode::EntityType::kSocket) {
+    return nullptr;
+  }
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* socket_json = socket_node->RenderJson();
+  socket_json->key = "socket";
+  grpc_json_link_child(json, socket_json, nullptr);
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
diff --git a/third_party/grpc/src/core/lib/channel/channelz_registry.h b/third_party/grpc/src/core/lib/channel/channelz_registry.h
new file mode 100644
index 0000000..73b3307
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/channelz_registry.h
@@ -0,0 +1,115 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CHANNELZ_REGISTRY_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNELZ_REGISTRY_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/channel/channelz.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+
+#include <stdint.h>
+
+namespace grpc_core {
+namespace channelz {
+
+namespace testing {
+class ChannelzRegistryPeer;
+}
+
+// singleton registry object to track all objects that are needed to support
+// channelz bookkeeping. All objects share globally distributed uuids.
+class ChannelzRegistry {
+ public:
+  // To be called in grpc_init()
+  static void Init();
+
+  // To be called in grpc_shutdown();
+  static void Shutdown();
+
+  static void Register(BaseNode* node) {
+    return Default()->InternalRegister(node);
+  }
+  static void Unregister(intptr_t uuid) { Default()->InternalUnregister(uuid); }
+  static BaseNode* Get(intptr_t uuid) { return Default()->InternalGet(uuid); }
+
+  // Returns the allocated JSON string that represents the proto
+  // GetTopChannelsResponse as per channelz.proto.
+  static char* GetTopChannels(intptr_t start_channel_id) {
+    return Default()->InternalGetTopChannels(start_channel_id);
+  }
+
+  // Returns the allocated JSON string that represents the proto
+  // GetServersResponse as per channelz.proto.
+  static char* GetServers(intptr_t start_server_id) {
+    return Default()->InternalGetServers(start_server_id);
+  }
+
+  // Test only helper function to dump the JSON representation to std out.
+  // This can aid in debugging channelz code.
+  static void LogAllEntities() { Default()->InternalLogAllEntities(); }
+
+ private:
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+  friend class testing::ChannelzRegistryPeer;
+
+  ChannelzRegistry();
+  ~ChannelzRegistry();
+
+  // Returned the singleton instance of ChannelzRegistry;
+  static ChannelzRegistry* Default();
+
+  // globally registers an Entry. Returns its unique uuid
+  void InternalRegister(BaseNode* node);
+
+  // globally unregisters the object that is associated to uuid. Also does
+  // sanity check that an object doesn't try to unregister the wrong type.
+  void InternalUnregister(intptr_t uuid);
+
+  // if object with uuid has previously been registered as the correct type,
+  // returns the void* associated with that uuid. Else returns nullptr.
+  BaseNode* InternalGet(intptr_t uuid);
+
+  char* InternalGetTopChannels(intptr_t start_channel_id);
+  char* InternalGetServers(intptr_t start_server_id);
+
+  // If entities_ has over a certain threshold of empty slots, it will
+  // compact the vector and move all used slots to the front.
+  void MaybePerformCompactionLocked();
+
+  // Performs binary search on entities_ to find the index with that uuid.
+  // If direct_hit_needed, then will return -1 in case of absence.
+  // Else, will return idx of the first uuid higher than the target.
+  int FindByUuidLocked(intptr_t uuid, bool direct_hit_needed);
+
+  void InternalLogAllEntities();
+
+  // protects members
+  gpr_mu mu_;
+  InlinedVector<BaseNode*, 20> entities_;
+  intptr_t uuid_generator_ = 0;
+  int num_empty_slots_ = 0;
+};
+
+}  // namespace channelz
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNELZ_REGISTRY_H */
diff --git a/third_party/grpc/src/core/lib/channel/connected_channel.cc b/third_party/grpc/src/core/lib/channel/connected_channel.cc
new file mode 100644
index 0000000..e2ea334
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/connected_channel.cc
@@ -0,0 +1,243 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/connected_channel.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/transport/transport.h"
+
+#define MAX_BUFFER_LENGTH 8192
+
+typedef struct connected_channel_channel_data {
+  grpc_transport* transport;
+} channel_data;
+
+typedef struct {
+  grpc_closure closure;
+  grpc_closure* original_closure;
+  grpc_call_combiner* call_combiner;
+  const char* reason;
+} callback_state;
+
+typedef struct connected_channel_call_data {
+  grpc_call_combiner* call_combiner;
+  // Closures used for returning results on the call combiner.
+  callback_state on_complete[6];  // Max number of pending batches.
+  callback_state recv_initial_metadata_ready;
+  callback_state recv_message_ready;
+  callback_state recv_trailing_metadata_ready;
+} call_data;
+
+static void run_in_call_combiner(void* arg, grpc_error* error) {
+  callback_state* state = static_cast<callback_state*>(arg);
+  GRPC_CALL_COMBINER_START(state->call_combiner, state->original_closure,
+                           GRPC_ERROR_REF(error), state->reason);
+}
+
+static void run_cancel_in_call_combiner(void* arg, grpc_error* error) {
+  run_in_call_combiner(arg, error);
+  gpr_free(arg);
+}
+
+static void intercept_callback(call_data* calld, callback_state* state,
+                               bool free_when_done, const char* reason,
+                               grpc_closure** original_closure) {
+  state->original_closure = *original_closure;
+  state->call_combiner = calld->call_combiner;
+  state->reason = reason;
+  *original_closure = GRPC_CLOSURE_INIT(
+      &state->closure,
+      free_when_done ? run_cancel_in_call_combiner : run_in_call_combiner,
+      state, grpc_schedule_on_exec_ctx);
+}
+
+static callback_state* get_state_for_batch(
+    call_data* calld, grpc_transport_stream_op_batch* batch) {
+  if (batch->send_initial_metadata) return &calld->on_complete[0];
+  if (batch->send_message) return &calld->on_complete[1];
+  if (batch->send_trailing_metadata) return &calld->on_complete[2];
+  if (batch->recv_initial_metadata) return &calld->on_complete[3];
+  if (batch->recv_message) return &calld->on_complete[4];
+  if (batch->recv_trailing_metadata) return &calld->on_complete[5];
+  GPR_UNREACHABLE_CODE(return nullptr);
+}
+
+/* We perform a small hack to locate transport data alongside the connected
+   channel data in call allocations, to allow everything to be pulled in minimal
+   cache line requests */
+#define TRANSPORT_STREAM_FROM_CALL_DATA(calld) ((grpc_stream*)((calld) + 1))
+#define CALL_DATA_FROM_TRANSPORT_STREAM(transport_stream) \
+  (((call_data*)(transport_stream)) - 1)
+
+/* Intercept a call operation and either push it directly up or translate it
+   into transport stream operations */
+static void con_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  if (batch->recv_initial_metadata) {
+    callback_state* state = &calld->recv_initial_metadata_ready;
+    intercept_callback(
+        calld, state, false, "recv_initial_metadata_ready",
+        &batch->payload->recv_initial_metadata.recv_initial_metadata_ready);
+  }
+  if (batch->recv_message) {
+    callback_state* state = &calld->recv_message_ready;
+    intercept_callback(calld, state, false, "recv_message_ready",
+                       &batch->payload->recv_message.recv_message_ready);
+  }
+  if (batch->recv_trailing_metadata) {
+    callback_state* state = &calld->recv_trailing_metadata_ready;
+    intercept_callback(
+        calld, state, false, "recv_trailing_metadata_ready",
+        &batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready);
+  }
+  if (batch->cancel_stream) {
+    // There can be more than one cancellation batch in flight at any
+    // given time, so we can't just pick out a fixed index into
+    // calld->on_complete like we can for the other ops.  However,
+    // cancellation isn't in the fast path, so we just allocate a new
+    // closure for each one.
+    callback_state* state =
+        static_cast<callback_state*>(gpr_malloc(sizeof(*state)));
+    intercept_callback(calld, state, true, "on_complete (cancel_stream)",
+                       &batch->on_complete);
+  } else if (batch->on_complete != nullptr) {
+    callback_state* state = get_state_for_batch(calld, batch);
+    intercept_callback(calld, state, false, "on_complete", &batch->on_complete);
+  }
+  grpc_transport_perform_stream_op(
+      chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), batch);
+  GRPC_CALL_COMBINER_STOP(calld->call_combiner, "passed batch to transport");
+}
+
+static void con_start_transport_op(grpc_channel_element* elem,
+                                   grpc_transport_op* op) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  grpc_transport_perform_op(chand->transport, op);
+}
+
+/* Constructor for call_data */
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  calld->call_combiner = args->call_combiner;
+  int r = grpc_transport_init_stream(
+      chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld),
+      &args->call_stack->refcount, args->server_transport_data, args->arena);
+  return r == 0 ? GRPC_ERROR_NONE
+                : GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                      "transport stream initialization failed");
+}
+
+static void set_pollset_or_pollset_set(grpc_call_element* elem,
+                                       grpc_polling_entity* pollent) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  grpc_transport_set_pops(chand->transport,
+                          TRANSPORT_STREAM_FROM_CALL_DATA(calld), pollent);
+}
+
+/* Destructor for call_data */
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* then_schedule_closure) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  grpc_transport_destroy_stream(chand->transport,
+                                TRANSPORT_STREAM_FROM_CALL_DATA(calld),
+                                then_schedule_closure);
+}
+
+/* Constructor for channel_data */
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  channel_data* cd = static_cast<channel_data*>(elem->channel_data);
+  GPR_ASSERT(args->is_last);
+  cd->transport = nullptr;
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for channel_data */
+static void destroy_channel_elem(grpc_channel_element* elem) {
+  channel_data* cd = static_cast<channel_data*>(elem->channel_data);
+  if (cd->transport) {
+    grpc_transport_destroy(cd->transport);
+  }
+}
+
+/* No-op. */
+static void con_get_channel_info(grpc_channel_element* elem,
+                                 const grpc_channel_info* channel_info) {}
+
+const grpc_channel_filter grpc_connected_filter = {
+    con_start_transport_stream_op_batch,
+    con_start_transport_op,
+    sizeof(call_data),
+    init_call_elem,
+    set_pollset_or_pollset_set,
+    destroy_call_elem,
+    sizeof(channel_data),
+    init_channel_elem,
+    destroy_channel_elem,
+    con_get_channel_info,
+    "connected",
+};
+
+static void bind_transport(grpc_channel_stack* channel_stack,
+                           grpc_channel_element* elem, void* t) {
+  channel_data* cd = static_cast<channel_data*>(elem->channel_data);
+  GPR_ASSERT(elem->filter == &grpc_connected_filter);
+  GPR_ASSERT(cd->transport == nullptr);
+  cd->transport = static_cast<grpc_transport*>(t);
+
+  /* HACK(ctiller): increase call stack size for the channel to make space
+     for channel data. We need a cleaner (but performant) way to do this,
+     and I'm not sure what that is yet.
+     This is only "safe" because call stacks place no additional data after
+     the last call element, and the last call element MUST be the connected
+     channel. */
+  channel_stack->call_stack_size +=
+      grpc_transport_stream_size(static_cast<grpc_transport*>(t));
+}
+
+bool grpc_add_connected_filter(grpc_channel_stack_builder* builder,
+                               void* arg_must_be_null) {
+  GPR_ASSERT(arg_must_be_null == nullptr);
+  grpc_transport* t = grpc_channel_stack_builder_get_transport(builder);
+  GPR_ASSERT(t != nullptr);
+  return grpc_channel_stack_builder_append_filter(
+      builder, &grpc_connected_filter, bind_transport, t);
+}
+
+grpc_stream* grpc_connected_channel_get_stream(grpc_call_element* elem) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  return TRANSPORT_STREAM_FROM_CALL_DATA(calld);
+}
diff --git a/third_party/grpc/src/core/lib/channel/connected_channel.h b/third_party/grpc/src/core/lib/channel/connected_channel.h
new file mode 100644
index 0000000..faa1c73
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/connected_channel.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H
+#define GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack_builder.h"
+
+extern const grpc_channel_filter grpc_connected_filter;
+
+bool grpc_add_connected_filter(grpc_channel_stack_builder* builder,
+                               void* arg_must_be_null);
+
+/* Debug helper to dig the transport stream out of a call element */
+grpc_stream* grpc_connected_channel_get_stream(grpc_call_element* elem);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H */
diff --git a/third_party/grpc/src/core/lib/channel/context.h b/third_party/grpc/src/core/lib/channel/context.h
new file mode 100644
index 0000000..763e4ff
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/context.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CONTEXT_H
+#define GRPC_CORE_LIB_CHANNEL_CONTEXT_H
+
+/// 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,
+
+  /// Reserved for traffic_class_context.
+  GRPC_CONTEXT_TRAFFIC,
+
+  /// Value is a \a grpc_grpclb_client_stats.
+  GRPC_GRPCLB_CLIENT_STATS,
+
+  GRPC_CONTEXT_COUNT
+} grpc_context_index;
+
+struct grpc_call_context_element {
+  void* value = nullptr;
+  void (*destroy)(void*) = nullptr;
+};
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CONTEXT_H */
diff --git a/third_party/grpc/src/core/lib/channel/handshaker.cc b/third_party/grpc/src/core/lib/channel/handshaker.cc
new file mode 100644
index 0000000..e516b56
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/handshaker.cc
@@ -0,0 +1,331 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.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"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+grpc_core::TraceFlag grpc_handshaker_trace(false, "handshaker");
+
+//
+// grpc_handshaker
+//
+
+void grpc_handshaker_init(const grpc_handshaker_vtable* vtable,
+                          grpc_handshaker* handshaker) {
+  handshaker->vtable = vtable;
+}
+
+void grpc_handshaker_destroy(grpc_handshaker* handshaker) {
+  handshaker->vtable->destroy(handshaker);
+}
+
+void grpc_handshaker_shutdown(grpc_handshaker* handshaker, grpc_error* why) {
+  handshaker->vtable->shutdown(handshaker, why);
+}
+
+void grpc_handshaker_do_handshake(grpc_handshaker* handshaker,
+                                  grpc_tcp_server_acceptor* acceptor,
+                                  grpc_closure* on_handshake_done,
+                                  grpc_handshaker_args* args) {
+  handshaker->vtable->do_handshake(handshaker, acceptor, on_handshake_done,
+                                   args);
+}
+
+const char* grpc_handshaker_name(grpc_handshaker* handshaker) {
+  return handshaker->vtable->name;
+}
+
+//
+// grpc_handshake_manager
+//
+
+struct grpc_handshake_manager {
+  gpr_mu mu;
+  gpr_refcount refs;
+  bool shutdown;
+  // An array of handshakers added via grpc_handshake_manager_add().
+  size_t count;
+  grpc_handshaker** handshakers;
+  // The index of the handshaker to invoke next and closure to invoke it.
+  size_t index;
+  grpc_closure call_next_handshaker;
+  // The acceptor to call the handshakers with.
+  grpc_tcp_server_acceptor* acceptor;
+  // Deadline timer across all handshakers.
+  grpc_timer deadline_timer;
+  grpc_closure on_timeout;
+  // The final callback and user_data to invoke after the last handshaker.
+  grpc_closure on_handshake_done;
+  void* user_data;
+  // Handshaker args.
+  grpc_handshaker_args args;
+  // Links to the previous and next managers in a list of all pending handshakes
+  // Used at server side only.
+  grpc_handshake_manager* prev;
+  grpc_handshake_manager* next;
+};
+
+grpc_handshake_manager* grpc_handshake_manager_create() {
+  grpc_handshake_manager* mgr = static_cast<grpc_handshake_manager*>(
+      gpr_zalloc(sizeof(grpc_handshake_manager)));
+  gpr_mu_init(&mgr->mu);
+  gpr_ref_init(&mgr->refs, 1);
+  return mgr;
+}
+
+void grpc_handshake_manager_pending_list_add(grpc_handshake_manager** head,
+                                             grpc_handshake_manager* mgr) {
+  GPR_ASSERT(mgr->prev == nullptr);
+  GPR_ASSERT(mgr->next == nullptr);
+  mgr->next = *head;
+  if (*head) {
+    (*head)->prev = mgr;
+  }
+  *head = mgr;
+}
+
+void grpc_handshake_manager_pending_list_remove(grpc_handshake_manager** head,
+                                                grpc_handshake_manager* mgr) {
+  if (mgr->next != nullptr) {
+    mgr->next->prev = mgr->prev;
+  }
+  if (mgr->prev != nullptr) {
+    mgr->prev->next = mgr->next;
+  } else {
+    GPR_ASSERT(*head == mgr);
+    *head = mgr->next;
+  }
+}
+
+void grpc_handshake_manager_pending_list_shutdown_all(
+    grpc_handshake_manager* head, grpc_error* why) {
+  while (head != nullptr) {
+    grpc_handshake_manager_shutdown(head, GRPC_ERROR_REF(why));
+    head = head->next;
+  }
+  GRPC_ERROR_UNREF(why);
+}
+
+static bool is_power_of_2(size_t n) { return (n & (n - 1)) == 0; }
+
+void grpc_handshake_manager_add(grpc_handshake_manager* mgr,
+                                grpc_handshaker* handshaker) {
+  if (grpc_handshaker_trace.enabled()) {
+    gpr_log(
+        GPR_INFO,
+        "handshake_manager %p: adding handshaker %s [%p] at index %" PRIuPTR,
+        mgr, grpc_handshaker_name(handshaker), handshaker, mgr->count);
+  }
+  gpr_mu_lock(&mgr->mu);
+  // To avoid allocating memory for each handshaker we add, we double
+  // the number of elements every time we need more.
+  size_t realloc_count = 0;
+  if (mgr->count == 0) {
+    realloc_count = 2;
+  } else if (mgr->count >= 2 && is_power_of_2(mgr->count)) {
+    realloc_count = mgr->count * 2;
+  }
+  if (realloc_count > 0) {
+    mgr->handshakers = static_cast<grpc_handshaker**>(gpr_realloc(
+        mgr->handshakers, realloc_count * sizeof(grpc_handshaker*)));
+  }
+  mgr->handshakers[mgr->count++] = handshaker;
+  gpr_mu_unlock(&mgr->mu);
+}
+
+static void grpc_handshake_manager_unref(grpc_handshake_manager* mgr) {
+  if (gpr_unref(&mgr->refs)) {
+    for (size_t i = 0; i < mgr->count; ++i) {
+      grpc_handshaker_destroy(mgr->handshakers[i]);
+    }
+    gpr_free(mgr->handshakers);
+    gpr_mu_destroy(&mgr->mu);
+    gpr_free(mgr);
+  }
+}
+
+void grpc_handshake_manager_destroy(grpc_handshake_manager* mgr) {
+  grpc_handshake_manager_unref(mgr);
+}
+
+void grpc_handshake_manager_shutdown(grpc_handshake_manager* mgr,
+                                     grpc_error* why) {
+  gpr_mu_lock(&mgr->mu);
+  // Shutdown the handshaker that's currently in progress, if any.
+  if (!mgr->shutdown && mgr->index > 0) {
+    mgr->shutdown = true;
+    grpc_handshaker_shutdown(mgr->handshakers[mgr->index - 1],
+                             GRPC_ERROR_REF(why));
+  }
+  gpr_mu_unlock(&mgr->mu);
+  GRPC_ERROR_UNREF(why);
+}
+
+static char* handshaker_args_string(grpc_handshaker_args* args) {
+  char* args_str = grpc_channel_args_string(args->args);
+  size_t num_args = args->args != nullptr ? args->args->num_args : 0;
+  size_t read_buffer_length =
+      args->read_buffer != nullptr ? args->read_buffer->length : 0;
+  char* str;
+  gpr_asprintf(&str,
+               "{endpoint=%p, args=%p {size=%" PRIuPTR
+               ": %s}, read_buffer=%p (length=%" PRIuPTR "), exit_early=%d}",
+               args->endpoint, args->args, num_args, args_str,
+               args->read_buffer, read_buffer_length, args->exit_early);
+  gpr_free(args_str);
+  return str;
+}
+
+// Helper function to call either the next handshaker or the
+// on_handshake_done callback.
+// Returns true if we've scheduled the on_handshake_done callback.
+static bool call_next_handshaker_locked(grpc_handshake_manager* mgr,
+                                        grpc_error* error) {
+  if (grpc_handshaker_trace.enabled()) {
+    char* args_str = handshaker_args_string(&mgr->args);
+    gpr_log(GPR_INFO,
+            "handshake_manager %p: error=%s shutdown=%d index=%" PRIuPTR
+            ", args=%s",
+            mgr, grpc_error_string(error), mgr->shutdown, mgr->index, args_str);
+    gpr_free(args_str);
+  }
+  GPR_ASSERT(mgr->index <= mgr->count);
+  // If we got an error or we've been shut down or we're exiting early or
+  // we've finished the last handshaker, invoke the on_handshake_done
+  // callback.  Otherwise, call the next handshaker.
+  if (error != GRPC_ERROR_NONE || mgr->shutdown || mgr->args.exit_early ||
+      mgr->index == mgr->count) {
+    if (error == GRPC_ERROR_NONE && mgr->shutdown) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("handshaker shutdown");
+      // It is possible that the endpoint has already been destroyed by
+      // a shutdown call while this callback was sitting on the ExecCtx
+      // with no error.
+      if (mgr->args.endpoint != nullptr) {
+        // TODO(roth): It is currently necessary to shutdown endpoints
+        // before destroying then, even when we know that there are no
+        // pending read/write callbacks.  This should be fixed, at which
+        // point this can be removed.
+        grpc_endpoint_shutdown(mgr->args.endpoint, GRPC_ERROR_REF(error));
+        grpc_endpoint_destroy(mgr->args.endpoint);
+        mgr->args.endpoint = nullptr;
+        grpc_channel_args_destroy(mgr->args.args);
+        mgr->args.args = nullptr;
+        grpc_slice_buffer_destroy_internal(mgr->args.read_buffer);
+        gpr_free(mgr->args.read_buffer);
+        mgr->args.read_buffer = nullptr;
+      }
+    }
+    if (grpc_handshaker_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "handshake_manager %p: handshaking complete -- scheduling "
+              "on_handshake_done with error=%s",
+              mgr, grpc_error_string(error));
+    }
+    // Cancel deadline timer, since we're invoking the on_handshake_done
+    // callback now.
+    grpc_timer_cancel(&mgr->deadline_timer);
+    GRPC_CLOSURE_SCHED(&mgr->on_handshake_done, error);
+    mgr->shutdown = true;
+  } else {
+    if (grpc_handshaker_trace.enabled()) {
+      gpr_log(
+          GPR_INFO,
+          "handshake_manager %p: calling handshaker %s [%p] at index %" PRIuPTR,
+          mgr, grpc_handshaker_name(mgr->handshakers[mgr->index]),
+          mgr->handshakers[mgr->index], mgr->index);
+    }
+    grpc_handshaker_do_handshake(mgr->handshakers[mgr->index], mgr->acceptor,
+                                 &mgr->call_next_handshaker, &mgr->args);
+  }
+  ++mgr->index;
+  return mgr->shutdown;
+}
+
+// A function used as the handshaker-done callback when chaining
+// handshakers together.
+static void call_next_handshaker(void* arg, grpc_error* error) {
+  grpc_handshake_manager* mgr = static_cast<grpc_handshake_manager*>(arg);
+  gpr_mu_lock(&mgr->mu);
+  bool done = call_next_handshaker_locked(mgr, GRPC_ERROR_REF(error));
+  gpr_mu_unlock(&mgr->mu);
+  // If we're invoked the final callback, we won't be coming back
+  // to this function, so we can release our reference to the
+  // handshake manager.
+  if (done) {
+    grpc_handshake_manager_unref(mgr);
+  }
+}
+
+// Callback invoked when deadline is exceeded.
+static void on_timeout(void* arg, grpc_error* error) {
+  grpc_handshake_manager* mgr = static_cast<grpc_handshake_manager*>(arg);
+  if (error == GRPC_ERROR_NONE) {  // Timer fired, rather than being cancelled.
+    grpc_handshake_manager_shutdown(
+        mgr, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake timed out"));
+  }
+  grpc_handshake_manager_unref(mgr);
+}
+
+void grpc_handshake_manager_do_handshake(grpc_handshake_manager* mgr,
+                                         grpc_endpoint* endpoint,
+                                         const grpc_channel_args* channel_args,
+                                         grpc_millis deadline,
+                                         grpc_tcp_server_acceptor* acceptor,
+                                         grpc_iomgr_cb_func on_handshake_done,
+                                         void* user_data) {
+  gpr_mu_lock(&mgr->mu);
+  GPR_ASSERT(mgr->index == 0);
+  GPR_ASSERT(!mgr->shutdown);
+  // Construct handshaker args.  These will be passed through all
+  // handshakers and eventually be freed by the on_handshake_done callback.
+  mgr->args.endpoint = endpoint;
+  mgr->args.args = grpc_channel_args_copy(channel_args);
+  mgr->args.user_data = user_data;
+  mgr->args.read_buffer = static_cast<grpc_slice_buffer*>(
+      gpr_malloc(sizeof(*mgr->args.read_buffer)));
+  grpc_slice_buffer_init(mgr->args.read_buffer);
+  // Initialize state needed for calling handshakers.
+  mgr->acceptor = acceptor;
+  GRPC_CLOSURE_INIT(&mgr->call_next_handshaker, call_next_handshaker, mgr,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&mgr->on_handshake_done, on_handshake_done, &mgr->args,
+                    grpc_schedule_on_exec_ctx);
+  // Start deadline timer, which owns a ref.
+  gpr_ref(&mgr->refs);
+  GRPC_CLOSURE_INIT(&mgr->on_timeout, on_timeout, mgr,
+                    grpc_schedule_on_exec_ctx);
+  grpc_timer_init(&mgr->deadline_timer, deadline, &mgr->on_timeout);
+  // Start first handshaker, which also owns a ref.
+  gpr_ref(&mgr->refs);
+  bool done = call_next_handshaker_locked(mgr, GRPC_ERROR_NONE);
+  gpr_mu_unlock(&mgr->mu);
+  if (done) {
+    grpc_handshake_manager_unref(mgr);
+  }
+}
diff --git a/third_party/grpc/src/core/lib/channel/handshaker.h b/third_party/grpc/src/core/lib/channel/handshaker.h
new file mode 100644
index 0000000..a65990f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/handshaker.h
@@ -0,0 +1,169 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H
+#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+
+/// Handshakers are used to perform initial handshakes on a connection
+/// before the client sends the initial request.  Some examples of what
+/// a handshaker can be used for includes support for HTTP CONNECT on
+/// the client side and various types of security initialization.
+///
+/// In general, handshakers should be used via a handshake manager.
+
+///
+/// grpc_handshaker
+///
+
+typedef struct grpc_handshaker grpc_handshaker;
+
+/// Arguments passed through handshakers and to the on_handshake_done callback.
+///
+/// For handshakers, all members are input/output parameters; for
+/// example, a handshaker may read from or write to \a endpoint and
+/// then later replace it with a wrapped endpoint.  Similarly, a
+/// handshaker may modify \a args.
+///
+/// A handshaker takes ownership of the members while a handshake is in
+/// progress.  Upon failure or shutdown of an in-progress handshaker,
+/// the handshaker is responsible for destroying the members and setting
+/// them to NULL before invoking the on_handshake_done callback.
+///
+/// For the on_handshake_done callback, all members are input arguments,
+/// which the callback takes ownership of.
+typedef struct {
+  grpc_endpoint* endpoint;
+  grpc_channel_args* args;
+  grpc_slice_buffer* read_buffer;
+  // A handshaker may set this to true before invoking on_handshake_done
+  // to indicate that subsequent handshakers should be skipped.
+  bool exit_early;
+  // User data passed through the handshake manager.  Not used by
+  // individual handshakers.
+  void* user_data;
+} grpc_handshaker_args;
+
+typedef struct {
+  /// Destroys the handshaker.
+  void (*destroy)(grpc_handshaker* handshaker);
+
+  /// Shuts down the handshaker (e.g., to clean up when the operation is
+  /// aborted in the middle).
+  void (*shutdown)(grpc_handshaker* handshaker, grpc_error* why);
+
+  /// Performs handshaking, modifying \a args as needed (e.g., to
+  /// replace \a endpoint with a wrapped endpoint).
+  /// When finished, invokes \a on_handshake_done.
+  /// \a acceptor will be NULL for client-side handshakers.
+  void (*do_handshake)(grpc_handshaker* handshaker,
+                       grpc_tcp_server_acceptor* acceptor,
+                       grpc_closure* on_handshake_done,
+                       grpc_handshaker_args* args);
+
+  /// The name of the handshaker, for debugging purposes.
+  const char* name;
+} grpc_handshaker_vtable;
+
+/// Base struct.  To subclass, make this the first member of the
+/// implementation struct.
+struct grpc_handshaker {
+  const grpc_handshaker_vtable* vtable;
+};
+
+/// Called by concrete implementations to initialize the base struct.
+void grpc_handshaker_init(const grpc_handshaker_vtable* vtable,
+                          grpc_handshaker* handshaker);
+
+void grpc_handshaker_destroy(grpc_handshaker* handshaker);
+void grpc_handshaker_shutdown(grpc_handshaker* handshaker, grpc_error* why);
+void grpc_handshaker_do_handshake(grpc_handshaker* handshaker,
+                                  grpc_tcp_server_acceptor* acceptor,
+                                  grpc_closure* on_handshake_done,
+                                  grpc_handshaker_args* args);
+const char* grpc_handshaker_name(grpc_handshaker* handshaker);
+
+///
+/// grpc_handshake_manager
+///
+
+typedef struct grpc_handshake_manager grpc_handshake_manager;
+
+/// Creates a new handshake manager.  Caller takes ownership.
+grpc_handshake_manager* grpc_handshake_manager_create();
+
+/// Adds a handshaker to the handshake manager.
+/// Takes ownership of \a handshaker.
+void grpc_handshake_manager_add(grpc_handshake_manager* mgr,
+                                grpc_handshaker* handshaker);
+
+/// Destroys the handshake manager.
+void grpc_handshake_manager_destroy(grpc_handshake_manager* mgr);
+
+/// Shuts down the handshake manager (e.g., to clean up when the operation is
+/// aborted in the middle).
+/// The caller must still call grpc_handshake_manager_destroy() after
+/// calling this function.
+void grpc_handshake_manager_shutdown(grpc_handshake_manager* mgr,
+                                     grpc_error* why);
+
+/// Invokes handshakers in the order they were added.
+/// Takes ownership of \a endpoint, and then passes that ownership to
+/// the \a on_handshake_done callback.
+/// Does NOT take ownership of \a channel_args.  Instead, makes a copy before
+/// invoking the first handshaker.
+/// \a acceptor will be nullptr for client-side handshakers.
+///
+/// When done, invokes \a on_handshake_done with a grpc_handshaker_args
+/// object as its argument.  If the callback is invoked with error !=
+/// GRPC_ERROR_NONE, then handshaking failed and the handshaker has done
+/// the necessary clean-up.  Otherwise, the callback takes ownership of
+/// the arguments.
+void grpc_handshake_manager_do_handshake(grpc_handshake_manager* mgr,
+                                         grpc_endpoint* endpoint,
+                                         const grpc_channel_args* channel_args,
+                                         grpc_millis deadline,
+                                         grpc_tcp_server_acceptor* acceptor,
+                                         grpc_iomgr_cb_func on_handshake_done,
+                                         void* user_data);
+
+/// Add \a mgr to the server side list of all pending handshake managers, the
+/// list starts with \a *head.
+// Not thread-safe. Caller needs to synchronize.
+void grpc_handshake_manager_pending_list_add(grpc_handshake_manager** head,
+                                             grpc_handshake_manager* mgr);
+
+/// Remove \a mgr from the server side list of all pending handshake managers.
+// Not thread-safe. Caller needs to synchronize.
+void grpc_handshake_manager_pending_list_remove(grpc_handshake_manager** head,
+                                                grpc_handshake_manager* mgr);
+
+/// Shutdown all pending handshake managers on the server side.
+// Not thread-safe. Caller needs to synchronize.
+void grpc_handshake_manager_pending_list_shutdown_all(
+    grpc_handshake_manager* head, grpc_error* why);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H */
diff --git a/third_party/grpc/src/core/lib/channel/handshaker_factory.cc b/third_party/grpc/src/core/lib/channel/handshaker_factory.cc
new file mode 100644
index 0000000..8ade8fe
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/handshaker_factory.cc
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/handshaker_factory.h"
+
+#include <grpc/support/log.h>
+
+void grpc_handshaker_factory_add_handshakers(
+    grpc_handshaker_factory* handshaker_factory, const grpc_channel_args* args,
+    grpc_pollset_set* interested_parties,
+    grpc_handshake_manager* handshake_mgr) {
+  if (handshaker_factory != nullptr) {
+    GPR_ASSERT(handshaker_factory->vtable != nullptr);
+    handshaker_factory->vtable->add_handshakers(
+        handshaker_factory, args, interested_parties, handshake_mgr);
+  }
+}
+
+void grpc_handshaker_factory_destroy(
+    grpc_handshaker_factory* handshaker_factory) {
+  if (handshaker_factory != nullptr) {
+    GPR_ASSERT(handshaker_factory->vtable != nullptr);
+    handshaker_factory->vtable->destroy(handshaker_factory);
+  }
+}
diff --git a/third_party/grpc/src/core/lib/channel/handshaker_factory.h b/third_party/grpc/src/core/lib/channel/handshaker_factory.h
new file mode 100644
index 0000000..e17a678
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/handshaker_factory.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H
+#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/channel/handshaker.h"
+
+// A handshaker factory is used to create handshakers.
+
+typedef struct grpc_handshaker_factory grpc_handshaker_factory;
+
+typedef struct {
+  void (*add_handshakers)(grpc_handshaker_factory* handshaker_factory,
+                          const grpc_channel_args* args,
+                          grpc_pollset_set* interested_parties,
+                          grpc_handshake_manager* handshake_mgr);
+  void (*destroy)(grpc_handshaker_factory* handshaker_factory);
+} grpc_handshaker_factory_vtable;
+
+struct grpc_handshaker_factory {
+  const grpc_handshaker_factory_vtable* vtable;
+};
+
+void grpc_handshaker_factory_add_handshakers(
+    grpc_handshaker_factory* handshaker_factory, const grpc_channel_args* args,
+    grpc_pollset_set* interested_parties,
+    grpc_handshake_manager* handshake_mgr);
+
+void grpc_handshaker_factory_destroy(
+    grpc_handshaker_factory* handshaker_factory);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H */
diff --git a/third_party/grpc/src/core/lib/channel/handshaker_registry.cc b/third_party/grpc/src/core/lib/channel/handshaker_registry.cc
new file mode 100644
index 0000000..fbafc43
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/handshaker_registry.cc
@@ -0,0 +1,101 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/handshaker_registry.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+//
+// grpc_handshaker_factory_list
+//
+
+typedef struct {
+  grpc_handshaker_factory** list;
+  size_t num_factories;
+} grpc_handshaker_factory_list;
+
+static void grpc_handshaker_factory_list_register(
+    grpc_handshaker_factory_list* list, bool at_start,
+    grpc_handshaker_factory* factory) {
+  list->list = static_cast<grpc_handshaker_factory**>(gpr_realloc(
+      list->list,
+      (list->num_factories + 1) * sizeof(grpc_handshaker_factory*)));
+  if (at_start) {
+    memmove(list->list + 1, list->list,
+            sizeof(grpc_handshaker_factory*) * list->num_factories);
+    list->list[0] = factory;
+  } else {
+    list->list[list->num_factories] = factory;
+  }
+  ++list->num_factories;
+}
+
+static void grpc_handshaker_factory_list_add_handshakers(
+    grpc_handshaker_factory_list* list, const grpc_channel_args* args,
+    grpc_pollset_set* interested_parties,
+    grpc_handshake_manager* handshake_mgr) {
+  for (size_t i = 0; i < list->num_factories; ++i) {
+    grpc_handshaker_factory_add_handshakers(list->list[i], args,
+                                            interested_parties, handshake_mgr);
+  }
+}
+
+static void grpc_handshaker_factory_list_destroy(
+    grpc_handshaker_factory_list* list) {
+  for (size_t i = 0; i < list->num_factories; ++i) {
+    grpc_handshaker_factory_destroy(list->list[i]);
+  }
+  gpr_free(list->list);
+}
+
+//
+// plugin
+//
+
+static grpc_handshaker_factory_list
+    g_handshaker_factory_lists[NUM_HANDSHAKER_TYPES];
+
+void grpc_handshaker_factory_registry_init() {
+  memset(g_handshaker_factory_lists, 0, sizeof(g_handshaker_factory_lists));
+}
+
+void grpc_handshaker_factory_registry_shutdown() {
+  for (size_t i = 0; i < NUM_HANDSHAKER_TYPES; ++i) {
+    grpc_handshaker_factory_list_destroy(&g_handshaker_factory_lists[i]);
+  }
+}
+
+void grpc_handshaker_factory_register(bool at_start,
+                                      grpc_handshaker_type handshaker_type,
+                                      grpc_handshaker_factory* factory) {
+  grpc_handshaker_factory_list_register(
+      &g_handshaker_factory_lists[handshaker_type], at_start, factory);
+}
+
+void grpc_handshakers_add(grpc_handshaker_type handshaker_type,
+                          const grpc_channel_args* args,
+                          grpc_pollset_set* interested_parties,
+                          grpc_handshake_manager* handshake_mgr) {
+  grpc_handshaker_factory_list_add_handshakers(
+      &g_handshaker_factory_lists[handshaker_type], args, interested_parties,
+      handshake_mgr);
+}
diff --git a/third_party/grpc/src/core/lib/channel/handshaker_registry.h b/third_party/grpc/src/core/lib/channel/handshaker_registry.h
new file mode 100644
index 0000000..3dd4316
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/handshaker_registry.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H
+#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#include "src/core/lib/channel/handshaker_factory.h"
+
+typedef enum {
+  HANDSHAKER_CLIENT = 0,
+  HANDSHAKER_SERVER,
+  NUM_HANDSHAKER_TYPES,  // Must be last.
+} grpc_handshaker_type;
+
+void grpc_handshaker_factory_registry_init();
+void grpc_handshaker_factory_registry_shutdown();
+
+/// Registers a new handshaker factory.  Takes ownership.
+/// If \a at_start is true, the new handshaker will be at the beginning of
+/// the list.  Otherwise, it will be added to the end.
+void grpc_handshaker_factory_register(bool at_start,
+                                      grpc_handshaker_type handshaker_type,
+                                      grpc_handshaker_factory* factory);
+
+void grpc_handshakers_add(grpc_handshaker_type handshaker_type,
+                          const grpc_channel_args* args,
+                          grpc_pollset_set* interested_parties,
+                          grpc_handshake_manager* handshake_mgr);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H */
diff --git a/third_party/grpc/src/core/lib/channel/status_util.cc b/third_party/grpc/src/core/lib/channel/status_util.cc
new file mode 100644
index 0000000..563db40
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/status_util.cc
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/status_util.h"
+
+#include "src/core/lib/gpr/useful.h"
+
+typedef struct {
+  const char* str;
+  grpc_status_code status;
+} status_string_entry;
+
+static const status_string_entry g_status_string_entries[] = {
+    {"OK", GRPC_STATUS_OK},
+    {"CANCELLED", GRPC_STATUS_CANCELLED},
+    {"UNKNOWN", GRPC_STATUS_UNKNOWN},
+    {"INVALID_ARGUMENT", GRPC_STATUS_INVALID_ARGUMENT},
+    {"DEADLINE_EXCEEDED", GRPC_STATUS_DEADLINE_EXCEEDED},
+    {"NOT_FOUND", GRPC_STATUS_NOT_FOUND},
+    {"ALREADY_EXISTS", GRPC_STATUS_ALREADY_EXISTS},
+    {"PERMISSION_DENIED", GRPC_STATUS_PERMISSION_DENIED},
+    {"UNAUTHENTICATED", GRPC_STATUS_UNAUTHENTICATED},
+    {"RESOURCE_EXHAUSTED", GRPC_STATUS_RESOURCE_EXHAUSTED},
+    {"FAILED_PRECONDITION", GRPC_STATUS_FAILED_PRECONDITION},
+    {"ABORTED", GRPC_STATUS_ABORTED},
+    {"OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE},
+    {"UNIMPLEMENTED", GRPC_STATUS_UNIMPLEMENTED},
+    {"INTERNAL", GRPC_STATUS_INTERNAL},
+    {"UNAVAILABLE", GRPC_STATUS_UNAVAILABLE},
+    {"DATA_LOSS", GRPC_STATUS_DATA_LOSS},
+};
+
+bool grpc_status_code_from_string(const char* status_str,
+                                  grpc_status_code* status) {
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(g_status_string_entries); ++i) {
+    if (strcmp(status_str, g_status_string_entries[i].str) == 0) {
+      *status = g_status_string_entries[i].status;
+      return true;
+    }
+  }
+  return false;
+}
+
+const char* grpc_status_code_to_string(grpc_status_code status) {
+  switch (status) {
+    case GRPC_STATUS_OK:
+      return "OK";
+    case GRPC_STATUS_CANCELLED:
+      return "CANCELLED";
+    case GRPC_STATUS_UNKNOWN:
+      return "UNKNOWN";
+    case GRPC_STATUS_INVALID_ARGUMENT:
+      return "INVALID_ARGUMENT";
+    case GRPC_STATUS_DEADLINE_EXCEEDED:
+      return "DEADLINE_EXCEEDED";
+    case GRPC_STATUS_NOT_FOUND:
+      return "NOT_FOUND";
+    case GRPC_STATUS_ALREADY_EXISTS:
+      return "ALREADY_EXISTS";
+    case GRPC_STATUS_PERMISSION_DENIED:
+      return "PERMISSION_DENIED";
+    case GRPC_STATUS_UNAUTHENTICATED:
+      return "UNAUTHENTICATED";
+    case GRPC_STATUS_RESOURCE_EXHAUSTED:
+      return "RESOURCE_EXHAUSTED";
+    case GRPC_STATUS_FAILED_PRECONDITION:
+      return "FAILED_PRECONDITION";
+    case GRPC_STATUS_ABORTED:
+      return "ABORTED";
+    case GRPC_STATUS_OUT_OF_RANGE:
+      return "OUT_OF_RANGE";
+    case GRPC_STATUS_UNIMPLEMENTED:
+      return "UNIMPLEMENTED";
+    case GRPC_STATUS_INTERNAL:
+      return "INTERNAL";
+    case GRPC_STATUS_UNAVAILABLE:
+      return "UNAVAILABLE";
+    case GRPC_STATUS_DATA_LOSS:
+      return "DATA_LOSS";
+    default:
+      return "UNKNOWN";
+  }
+}
diff --git a/third_party/grpc/src/core/lib/channel/status_util.h b/third_party/grpc/src/core/lib/channel/status_util.h
new file mode 100644
index 0000000..5409de6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/channel/status_util.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_STATUS_UTIL_H
+#define GRPC_CORE_LIB_CHANNEL_STATUS_UTIL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/status.h>
+
+#include <stdbool.h>
+#include <string.h>
+
+/// If \a status_str is a valid status string, sets \a status to the
+/// corresponding status value and returns true.
+bool grpc_status_code_from_string(const char* status_str,
+                                  grpc_status_code* status);
+
+/// Returns the string form of \a status, or "UNKNOWN" if invalid.
+const char* grpc_status_code_to_string(grpc_status_code status);
+
+namespace grpc_core {
+namespace internal {
+
+/// A set of grpc_status_code values.
+class StatusCodeSet {
+ public:
+  bool Empty() const { return status_code_mask_ == 0; }
+
+  void Add(grpc_status_code status) { status_code_mask_ |= (1 << status); }
+
+  bool Contains(grpc_status_code status) const {
+    return status_code_mask_ & (1 << status);
+  }
+
+ private:
+  int status_code_mask_ = 0;  // A bitfield of status codes in the set.
+};
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_CHANNEL_STATUS_UTIL_H */
diff --git a/third_party/grpc/src/core/lib/compression/algorithm_metadata.h b/third_party/grpc/src/core/lib/compression/algorithm_metadata.h
new file mode 100644
index 0000000..1be79e5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/algorithm_metadata.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H
+#define GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/compression.h>
+#include "src/core/lib/compression/compression_internal.h"
+#include "src/core/lib/transport/metadata.h"
+
+/** Return compression algorithm based metadata value */
+grpc_slice grpc_compression_algorithm_slice(
+    grpc_compression_algorithm algorithm);
+
+/** Find compression algorithm based on passed in mdstr - returns
+ *  GRPC_COMPRESS_ALGORITHM_COUNT on failure */
+grpc_compression_algorithm grpc_compression_algorithm_from_slice(
+    grpc_slice str);
+
+/** Return compression algorithm based metadata element */
+grpc_mdelem grpc_compression_encoding_mdelem(
+    grpc_compression_algorithm algorithm);
+
+/** Return message compression algorithm based metadata element (grpc-encoding:
+ * xxx) */
+grpc_mdelem grpc_message_compression_encoding_mdelem(
+    grpc_message_compression_algorithm algorithm);
+
+/** Return stream compression algorithm based metadata element
+ * (content-encoding: xxx) */
+grpc_mdelem grpc_stream_compression_encoding_mdelem(
+    grpc_stream_compression_algorithm algorithm);
+
+/** Find compression algorithm based on passed in mdstr - returns
+ * GRPC_COMPRESS_ALGORITHM_COUNT on failure */
+grpc_message_compression_algorithm
+grpc_message_compression_algorithm_from_slice(grpc_slice str);
+
+/** Find stream compression algorithm based on passed in mdstr - returns
+ * GRPC_STREAM_COMPRESS_ALGORITHM_COUNT on failure */
+grpc_stream_compression_algorithm grpc_stream_compression_algorithm_from_slice(
+    grpc_slice str);
+
+#endif /* GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H */
diff --git a/third_party/grpc/src/core/lib/compression/compression.cc b/third_party/grpc/src/core/lib/compression/compression.cc
new file mode 100644
index 0000000..4871754
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/compression.cc
@@ -0,0 +1,174 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+
+#include "src/core/lib/compression/algorithm_metadata.h"
+#include "src/core/lib/compression/compression_internal.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+int grpc_compression_algorithm_is_message(
+    grpc_compression_algorithm algorithm) {
+  return (algorithm >= GRPC_COMPRESS_DEFLATE && algorithm <= GRPC_COMPRESS_GZIP)
+             ? 1
+             : 0;
+}
+
+int grpc_compression_algorithm_is_stream(grpc_compression_algorithm algorithm) {
+  return (algorithm == GRPC_COMPRESS_STREAM_GZIP) ? 1 : 0;
+}
+
+int grpc_compression_algorithm_parse(grpc_slice name,
+                                     grpc_compression_algorithm* algorithm) {
+  if (grpc_slice_eq(name, GRPC_MDSTR_IDENTITY)) {
+    *algorithm = GRPC_COMPRESS_NONE;
+    return 1;
+  } else if (grpc_slice_eq(name, GRPC_MDSTR_DEFLATE)) {
+    *algorithm = GRPC_COMPRESS_DEFLATE;
+    return 1;
+  } else if (grpc_slice_eq(name, GRPC_MDSTR_GZIP)) {
+    *algorithm = GRPC_COMPRESS_GZIP;
+    return 1;
+  } else if (grpc_slice_eq(name, GRPC_MDSTR_STREAM_SLASH_GZIP)) {
+    *algorithm = GRPC_COMPRESS_STREAM_GZIP;
+    return 1;
+  } else {
+    return 0;
+  }
+  return 0;
+}
+
+int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm,
+                                    const char** name) {
+  GRPC_API_TRACE("grpc_compression_algorithm_parse(algorithm=%d, name=%p)", 2,
+                 ((int)algorithm, name));
+  switch (algorithm) {
+    case GRPC_COMPRESS_NONE:
+      *name = "identity";
+      return 1;
+    case GRPC_COMPRESS_DEFLATE:
+      *name = "deflate";
+      return 1;
+    case GRPC_COMPRESS_GZIP:
+      *name = "gzip";
+      return 1;
+    case GRPC_COMPRESS_STREAM_GZIP:
+      *name = "stream/gzip";
+      return 1;
+    case GRPC_COMPRESS_ALGORITHMS_COUNT:
+      return 0;
+  }
+  return 0;
+}
+
+grpc_compression_algorithm grpc_compression_algorithm_for_level(
+    grpc_compression_level level, uint32_t accepted_encodings) {
+  grpc_compression_algorithm algo;
+  if (level == GRPC_COMPRESS_LEVEL_NONE) {
+    return GRPC_COMPRESS_NONE;
+  } else if (level <= GRPC_COMPRESS_LEVEL_HIGH) {
+    // TODO(mxyan): Design algorithm to select from all algorithms, including
+    // stream compression algorithm
+    if (!grpc_compression_algorithm_from_message_stream_compression_algorithm(
+            &algo,
+            grpc_message_compression_algorithm_for_level(
+                level,
+                grpc_compression_bitset_to_message_bitset(accepted_encodings)),
+            static_cast<grpc_stream_compression_algorithm>(0))) {
+      gpr_log(GPR_ERROR, "Parse compression level error");
+      return GRPC_COMPRESS_NONE;
+    }
+    return algo;
+  } else {
+    gpr_log(GPR_ERROR, "Unknown compression level: %d", level);
+    return GRPC_COMPRESS_NONE;
+  }
+}
+
+void grpc_compression_options_init(grpc_compression_options* opts) {
+  memset(opts, 0, sizeof(*opts));
+  /* all enabled by default */
+  opts->enabled_algorithms_bitset = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
+}
+
+void grpc_compression_options_enable_algorithm(
+    grpc_compression_options* opts, grpc_compression_algorithm algorithm) {
+  GPR_BITSET(&opts->enabled_algorithms_bitset, algorithm);
+}
+
+void grpc_compression_options_disable_algorithm(
+    grpc_compression_options* opts, grpc_compression_algorithm algorithm) {
+  GPR_BITCLEAR(&opts->enabled_algorithms_bitset, algorithm);
+}
+
+int grpc_compression_options_is_algorithm_enabled(
+    const grpc_compression_options* opts,
+    grpc_compression_algorithm algorithm) {
+  return GPR_BITGET(opts->enabled_algorithms_bitset, algorithm);
+}
+
+grpc_slice grpc_compression_algorithm_slice(
+    grpc_compression_algorithm algorithm) {
+  switch (algorithm) {
+    case GRPC_COMPRESS_NONE:
+      return GRPC_MDSTR_IDENTITY;
+    case GRPC_COMPRESS_DEFLATE:
+      return GRPC_MDSTR_DEFLATE;
+    case GRPC_COMPRESS_GZIP:
+      return GRPC_MDSTR_GZIP;
+    case GRPC_COMPRESS_STREAM_GZIP:
+      return GRPC_MDSTR_STREAM_SLASH_GZIP;
+    case GRPC_COMPRESS_ALGORITHMS_COUNT:
+      return grpc_empty_slice();
+  }
+  return grpc_empty_slice();
+}
+
+grpc_compression_algorithm grpc_compression_algorithm_from_slice(
+    grpc_slice str) {
+  if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) return GRPC_COMPRESS_NONE;
+  if (grpc_slice_eq(str, GRPC_MDSTR_DEFLATE)) return GRPC_COMPRESS_DEFLATE;
+  if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_COMPRESS_GZIP;
+  if (grpc_slice_eq(str, GRPC_MDSTR_STREAM_SLASH_GZIP))
+    return GRPC_COMPRESS_STREAM_GZIP;
+  return GRPC_COMPRESS_ALGORITHMS_COUNT;
+}
+
+grpc_mdelem grpc_compression_encoding_mdelem(
+    grpc_compression_algorithm algorithm) {
+  switch (algorithm) {
+    case GRPC_COMPRESS_NONE:
+      return GRPC_MDELEM_GRPC_ENCODING_IDENTITY;
+    case GRPC_COMPRESS_DEFLATE:
+      return GRPC_MDELEM_GRPC_ENCODING_DEFLATE;
+    case GRPC_COMPRESS_GZIP:
+      return GRPC_MDELEM_GRPC_ENCODING_GZIP;
+    case GRPC_COMPRESS_STREAM_GZIP:
+      return GRPC_MDELEM_GRPC_ENCODING_GZIP;
+    default:
+      break;
+  }
+  return GRPC_MDNULL;
+}
diff --git a/third_party/grpc/src/core/lib/compression/compression_internal.cc b/third_party/grpc/src/core/lib/compression/compression_internal.cc
new file mode 100644
index 0000000..538514c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/compression_internal.cc
@@ -0,0 +1,276 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+
+#include "src/core/lib/compression/algorithm_metadata.h"
+#include "src/core/lib/compression/compression_internal.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+/* Interfaces related to MD */
+
+grpc_message_compression_algorithm
+grpc_message_compression_algorithm_from_slice(grpc_slice str) {
+  if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY))
+    return GRPC_MESSAGE_COMPRESS_NONE;
+  if (grpc_slice_eq(str, GRPC_MDSTR_DEFLATE))
+    return GRPC_MESSAGE_COMPRESS_DEFLATE;
+  if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_MESSAGE_COMPRESS_GZIP;
+  return GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT;
+}
+
+grpc_stream_compression_algorithm grpc_stream_compression_algorithm_from_slice(
+    grpc_slice str) {
+  if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) return GRPC_STREAM_COMPRESS_NONE;
+  if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_STREAM_COMPRESS_GZIP;
+  return GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT;
+}
+
+grpc_mdelem grpc_message_compression_encoding_mdelem(
+    grpc_message_compression_algorithm algorithm) {
+  switch (algorithm) {
+    case GRPC_MESSAGE_COMPRESS_NONE:
+      return GRPC_MDELEM_GRPC_ENCODING_IDENTITY;
+    case GRPC_MESSAGE_COMPRESS_DEFLATE:
+      return GRPC_MDELEM_GRPC_ENCODING_DEFLATE;
+    case GRPC_MESSAGE_COMPRESS_GZIP:
+      return GRPC_MDELEM_GRPC_ENCODING_GZIP;
+    default:
+      break;
+  }
+  return GRPC_MDNULL;
+}
+
+grpc_mdelem grpc_stream_compression_encoding_mdelem(
+    grpc_stream_compression_algorithm algorithm) {
+  switch (algorithm) {
+    case GRPC_STREAM_COMPRESS_NONE:
+      return GRPC_MDELEM_CONTENT_ENCODING_IDENTITY;
+    case GRPC_STREAM_COMPRESS_GZIP:
+      return GRPC_MDELEM_CONTENT_ENCODING_GZIP;
+    default:
+      break;
+  }
+  return GRPC_MDNULL;
+}
+
+/* Interfaces performing transformation between compression algorithms and
+ * levels. */
+grpc_message_compression_algorithm
+grpc_compression_algorithm_to_message_compression_algorithm(
+    grpc_compression_algorithm algo) {
+  switch (algo) {
+    case GRPC_COMPRESS_DEFLATE:
+      return GRPC_MESSAGE_COMPRESS_DEFLATE;
+    case GRPC_COMPRESS_GZIP:
+      return GRPC_MESSAGE_COMPRESS_GZIP;
+    default:
+      return GRPC_MESSAGE_COMPRESS_NONE;
+  }
+}
+
+grpc_stream_compression_algorithm
+grpc_compression_algorithm_to_stream_compression_algorithm(
+    grpc_compression_algorithm algo) {
+  switch (algo) {
+    case GRPC_COMPRESS_STREAM_GZIP:
+      return GRPC_STREAM_COMPRESS_GZIP;
+    default:
+      return GRPC_STREAM_COMPRESS_NONE;
+  }
+}
+
+uint32_t grpc_compression_bitset_to_message_bitset(uint32_t bitset) {
+  return bitset & ((1u << GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT) - 1);
+}
+
+uint32_t grpc_compression_bitset_to_stream_bitset(uint32_t bitset) {
+  uint32_t identity = (bitset & 1u);
+  uint32_t other_bits =
+      (bitset >> (GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT - 1)) &
+      ((1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - 2);
+  return identity | other_bits;
+}
+
+uint32_t grpc_compression_bitset_from_message_stream_compression_bitset(
+    uint32_t message_bitset, uint32_t stream_bitset) {
+  uint32_t offset_stream_bitset =
+      (stream_bitset & 1u) |
+      ((stream_bitset & (~1u)) << (GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT - 1));
+  return message_bitset | offset_stream_bitset;
+}
+
+int grpc_compression_algorithm_from_message_stream_compression_algorithm(
+    grpc_compression_algorithm* algorithm,
+    grpc_message_compression_algorithm message_algorithm,
+    grpc_stream_compression_algorithm stream_algorithm) {
+  if (message_algorithm != GRPC_MESSAGE_COMPRESS_NONE &&
+      stream_algorithm != GRPC_STREAM_COMPRESS_NONE) {
+    *algorithm = GRPC_COMPRESS_NONE;
+    return 0;
+  }
+  if (message_algorithm == GRPC_MESSAGE_COMPRESS_NONE) {
+    switch (stream_algorithm) {
+      case GRPC_STREAM_COMPRESS_NONE:
+        *algorithm = GRPC_COMPRESS_NONE;
+        return 1;
+      case GRPC_STREAM_COMPRESS_GZIP:
+        *algorithm = GRPC_COMPRESS_STREAM_GZIP;
+        return 1;
+      default:
+        *algorithm = GRPC_COMPRESS_NONE;
+        return 0;
+    }
+  } else {
+    switch (message_algorithm) {
+      case GRPC_MESSAGE_COMPRESS_NONE:
+        *algorithm = GRPC_COMPRESS_NONE;
+        return 1;
+      case GRPC_MESSAGE_COMPRESS_DEFLATE:
+        *algorithm = GRPC_COMPRESS_DEFLATE;
+        return 1;
+      case GRPC_MESSAGE_COMPRESS_GZIP:
+        *algorithm = GRPC_COMPRESS_GZIP;
+        return 1;
+      default:
+        *algorithm = GRPC_COMPRESS_NONE;
+        return 0;
+    }
+  }
+  return 0;
+}
+
+/* Interfaces for message compression. */
+
+int grpc_message_compression_algorithm_name(
+    grpc_message_compression_algorithm algorithm, const char** name) {
+  GRPC_API_TRACE(
+      "grpc_message_compression_algorithm_parse(algorithm=%d, name=%p)", 2,
+      ((int)algorithm, name));
+  switch (algorithm) {
+    case GRPC_MESSAGE_COMPRESS_NONE:
+      *name = "identity";
+      return 1;
+    case GRPC_MESSAGE_COMPRESS_DEFLATE:
+      *name = "deflate";
+      return 1;
+    case GRPC_MESSAGE_COMPRESS_GZIP:
+      *name = "gzip";
+      return 1;
+    case GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT:
+      return 0;
+  }
+  return 0;
+}
+
+/* TODO(dgq): Add the ability to specify parameters to the individual
+ * compression algorithms */
+grpc_message_compression_algorithm grpc_message_compression_algorithm_for_level(
+    grpc_compression_level level, uint32_t accepted_encodings) {
+  GRPC_API_TRACE("grpc_message_compression_algorithm_for_level(level=%d)", 1,
+                 ((int)level));
+  if (level > GRPC_COMPRESS_LEVEL_HIGH) {
+    gpr_log(GPR_ERROR, "Unknown message compression level %d.",
+            static_cast<int>(level));
+    abort();
+  }
+
+  const size_t num_supported =
+      GPR_BITCOUNT(accepted_encodings) - 1; /* discard NONE */
+  if (level == GRPC_COMPRESS_LEVEL_NONE || num_supported == 0) {
+    return GRPC_MESSAGE_COMPRESS_NONE;
+  }
+
+  GPR_ASSERT(level > 0);
+
+  /* Establish a "ranking" or compression algorithms in increasing order of
+   * compression.
+   * This is simplistic and we will probably want to introduce other dimensions
+   * in the future (cpu/memory cost, etc). */
+  const grpc_message_compression_algorithm algos_ranking[] = {
+      GRPC_MESSAGE_COMPRESS_GZIP, GRPC_MESSAGE_COMPRESS_DEFLATE};
+
+  /* intersect algos_ranking with the supported ones keeping the ranked order */
+  grpc_message_compression_algorithm
+      sorted_supported_algos[GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT];
+  size_t algos_supported_idx = 0;
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(algos_ranking); i++) {
+    const grpc_message_compression_algorithm alg = algos_ranking[i];
+    for (size_t j = 0; j < num_supported; j++) {
+      if (GPR_BITGET(accepted_encodings, alg) == 1) {
+        /* if \a alg in supported */
+        sorted_supported_algos[algos_supported_idx++] = alg;
+        break;
+      }
+    }
+    if (algos_supported_idx == num_supported) break;
+  }
+
+  switch (level) {
+    case GRPC_COMPRESS_LEVEL_NONE:
+      abort(); /* should have been handled already */
+    case GRPC_COMPRESS_LEVEL_LOW:
+      return sorted_supported_algos[0];
+    case GRPC_COMPRESS_LEVEL_MED:
+      return sorted_supported_algos[num_supported / 2];
+    case GRPC_COMPRESS_LEVEL_HIGH:
+      return sorted_supported_algos[num_supported - 1];
+    default:
+      abort();
+  };
+}
+
+int grpc_message_compression_algorithm_parse(
+    grpc_slice value, grpc_message_compression_algorithm* algorithm) {
+  if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) {
+    *algorithm = GRPC_MESSAGE_COMPRESS_NONE;
+    return 1;
+  } else if (grpc_slice_eq(value, GRPC_MDSTR_DEFLATE)) {
+    *algorithm = GRPC_MESSAGE_COMPRESS_DEFLATE;
+    return 1;
+  } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) {
+    *algorithm = GRPC_MESSAGE_COMPRESS_GZIP;
+    return 1;
+  } else {
+    return 0;
+  }
+  return 0;
+}
+
+/* Interfaces for stream compression. */
+
+int grpc_stream_compression_algorithm_parse(
+    grpc_slice value, grpc_stream_compression_algorithm* algorithm) {
+  if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) {
+    *algorithm = GRPC_STREAM_COMPRESS_NONE;
+    return 1;
+  } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) {
+    *algorithm = GRPC_STREAM_COMPRESS_GZIP;
+    return 1;
+  } else {
+    return 0;
+  }
+  return 0;
+}
diff --git a/third_party/grpc/src/core/lib/compression/compression_internal.h b/third_party/grpc/src/core/lib/compression/compression_internal.h
new file mode 100644
index 0000000..da00736
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/compression_internal.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H
+#define GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/compression_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  GRPC_MESSAGE_COMPRESS_NONE = 0,
+  GRPC_MESSAGE_COMPRESS_DEFLATE,
+  GRPC_MESSAGE_COMPRESS_GZIP,
+  /* TODO(ctiller): snappy */
+  GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT
+} grpc_message_compression_algorithm;
+
+/** Stream compresssion algorithms supported by gRPC */
+typedef enum {
+  GRPC_STREAM_COMPRESS_NONE = 0,
+  GRPC_STREAM_COMPRESS_GZIP,
+  GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT
+} grpc_stream_compression_algorithm;
+
+/* Interfaces performing transformation between compression algorithms and
+ * levels. */
+
+grpc_message_compression_algorithm
+grpc_compression_algorithm_to_message_compression_algorithm(
+    grpc_compression_algorithm algo);
+
+grpc_stream_compression_algorithm
+grpc_compression_algorithm_to_stream_compression_algorithm(
+    grpc_compression_algorithm algo);
+
+uint32_t grpc_compression_bitset_to_message_bitset(uint32_t bitset);
+
+uint32_t grpc_compression_bitset_to_stream_bitset(uint32_t bitset);
+
+uint32_t grpc_compression_bitset_from_message_stream_compression_bitset(
+    uint32_t message_bitset, uint32_t stream_bitset);
+
+int grpc_compression_algorithm_from_message_stream_compression_algorithm(
+    grpc_compression_algorithm* algorithm,
+    grpc_message_compression_algorithm message_algorithm,
+    grpc_stream_compression_algorithm stream_algorithm);
+
+/* Interfaces for message compression. */
+
+int grpc_message_compression_algorithm_name(
+    grpc_message_compression_algorithm algorithm, const char** name);
+
+grpc_message_compression_algorithm grpc_message_compression_algorithm_for_level(
+    grpc_compression_level level, uint32_t accepted_encodings);
+
+int grpc_message_compression_algorithm_parse(
+    grpc_slice value, grpc_message_compression_algorithm* algorithm);
+
+/* Interfaces for stream compression. */
+
+int grpc_stream_compression_algorithm_parse(
+    grpc_slice value, grpc_stream_compression_algorithm* algorithm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H */
diff --git a/third_party/grpc/src/core/lib/compression/message_compress.cc b/third_party/grpc/src/core/lib/compression/message_compress.cc
new file mode 100644
index 0000000..e06454f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/message_compress.cc
@@ -0,0 +1,187 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/compression/message_compress.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include <zlib.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+
+#define OUTPUT_BLOCK_SIZE 1024
+
+static int zlib_body(z_stream* zs, grpc_slice_buffer* input,
+                     grpc_slice_buffer* output,
+                     int (*flate)(z_stream* zs, int flush)) {
+  int r;
+  int flush;
+  size_t i;
+  grpc_slice outbuf = GRPC_SLICE_MALLOC(OUTPUT_BLOCK_SIZE);
+  const uInt uint_max = ~static_cast<uInt>(0);
+
+  GPR_ASSERT(GRPC_SLICE_LENGTH(outbuf) <= uint_max);
+  zs->avail_out = static_cast<uInt> GRPC_SLICE_LENGTH(outbuf);
+  zs->next_out = GRPC_SLICE_START_PTR(outbuf);
+  flush = Z_NO_FLUSH;
+  for (i = 0; i < input->count; i++) {
+    if (i == input->count - 1) flush = Z_FINISH;
+    GPR_ASSERT(GRPC_SLICE_LENGTH(input->slices[i]) <= uint_max);
+    zs->avail_in = static_cast<uInt> GRPC_SLICE_LENGTH(input->slices[i]);
+    zs->next_in = GRPC_SLICE_START_PTR(input->slices[i]);
+    do {
+      if (zs->avail_out == 0) {
+        grpc_slice_buffer_add_indexed(output, outbuf);
+        outbuf = GRPC_SLICE_MALLOC(OUTPUT_BLOCK_SIZE);
+        GPR_ASSERT(GRPC_SLICE_LENGTH(outbuf) <= uint_max);
+        zs->avail_out = static_cast<uInt> GRPC_SLICE_LENGTH(outbuf);
+        zs->next_out = GRPC_SLICE_START_PTR(outbuf);
+      }
+      r = flate(zs, flush);
+      if (r < 0 && r != Z_BUF_ERROR /* not fatal */) {
+        gpr_log(GPR_INFO, "zlib error (%d)", r);
+        goto error;
+      }
+    } while (zs->avail_out == 0);
+    if (zs->avail_in) {
+      gpr_log(GPR_INFO, "zlib: not all input consumed");
+      goto error;
+    }
+  }
+
+  GPR_ASSERT(outbuf.refcount);
+  outbuf.data.refcounted.length -= zs->avail_out;
+  grpc_slice_buffer_add_indexed(output, outbuf);
+
+  return 1;
+
+error:
+  grpc_slice_unref_internal(outbuf);
+  return 0;
+}
+
+static void* zalloc_gpr(void* opaque, unsigned int items, unsigned int size) {
+  return gpr_malloc(items * size);
+}
+
+static void zfree_gpr(void* opaque, void* address) { gpr_free(address); }
+
+static int zlib_compress(grpc_slice_buffer* input, grpc_slice_buffer* output,
+                         int gzip) {
+  z_stream zs;
+  int r;
+  size_t i;
+  size_t count_before = output->count;
+  size_t length_before = output->length;
+  memset(&zs, 0, sizeof(zs));
+  zs.zalloc = zalloc_gpr;
+  zs.zfree = zfree_gpr;
+  r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0),
+                   8, Z_DEFAULT_STRATEGY);
+  GPR_ASSERT(r == Z_OK);
+  r = zlib_body(&zs, input, output, deflate) && output->length < input->length;
+  if (!r) {
+    for (i = count_before; i < output->count; i++) {
+      grpc_slice_unref_internal(output->slices[i]);
+    }
+    output->count = count_before;
+    output->length = length_before;
+  }
+  deflateEnd(&zs);
+  return r;
+}
+
+static int zlib_decompress(grpc_slice_buffer* input, grpc_slice_buffer* output,
+                           int gzip) {
+  z_stream zs;
+  int r;
+  size_t i;
+  size_t count_before = output->count;
+  size_t length_before = output->length;
+  memset(&zs, 0, sizeof(zs));
+  zs.zalloc = zalloc_gpr;
+  zs.zfree = zfree_gpr;
+  r = inflateInit2(&zs, 15 | (gzip ? 16 : 0));
+  GPR_ASSERT(r == Z_OK);
+  r = zlib_body(&zs, input, output, inflate);
+  if (!r) {
+    for (i = count_before; i < output->count; i++) {
+      grpc_slice_unref_internal(output->slices[i]);
+    }
+    output->count = count_before;
+    output->length = length_before;
+  }
+  inflateEnd(&zs);
+  return r;
+}
+
+static int copy(grpc_slice_buffer* input, grpc_slice_buffer* output) {
+  size_t i;
+  for (i = 0; i < input->count; i++) {
+    grpc_slice_buffer_add(output, grpc_slice_ref_internal(input->slices[i]));
+  }
+  return 1;
+}
+
+static int compress_inner(grpc_message_compression_algorithm algorithm,
+                          grpc_slice_buffer* input, grpc_slice_buffer* output) {
+  switch (algorithm) {
+    case GRPC_MESSAGE_COMPRESS_NONE:
+      /* the fallback path always needs to be send uncompressed: we simply
+         rely on that here */
+      return 0;
+    case GRPC_MESSAGE_COMPRESS_DEFLATE:
+      return zlib_compress(input, output, 0);
+    case GRPC_MESSAGE_COMPRESS_GZIP:
+      return zlib_compress(input, output, 1);
+    case GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT:
+      break;
+  }
+  gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm);
+  return 0;
+}
+
+int grpc_msg_compress(grpc_message_compression_algorithm algorithm,
+                      grpc_slice_buffer* input, grpc_slice_buffer* output) {
+  if (!compress_inner(algorithm, input, output)) {
+    copy(input, output);
+    return 0;
+  }
+  return 1;
+}
+
+int grpc_msg_decompress(grpc_message_compression_algorithm algorithm,
+                        grpc_slice_buffer* input, grpc_slice_buffer* output) {
+  switch (algorithm) {
+    case GRPC_MESSAGE_COMPRESS_NONE:
+      return copy(input, output);
+    case GRPC_MESSAGE_COMPRESS_DEFLATE:
+      return zlib_decompress(input, output, 0);
+    case GRPC_MESSAGE_COMPRESS_GZIP:
+      return zlib_decompress(input, output, 1);
+    case GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT:
+      break;
+  }
+  gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm);
+  return 0;
+}
diff --git a/third_party/grpc/src/core/lib/compression/message_compress.h b/third_party/grpc/src/core/lib/compression/message_compress.h
new file mode 100644
index 0000000..91654e4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/message_compress.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H
+#define GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+
+#include "src/core/lib/compression/compression_internal.h"
+
+/* compress 'input' to 'output' using 'algorithm'.
+   On success, appends compressed slices to output and returns 1.
+   On failure, appends uncompressed slices to output and returns 0. */
+int grpc_msg_compress(grpc_message_compression_algorithm algorithm,
+                      grpc_slice_buffer* input, grpc_slice_buffer* output);
+
+/* decompress 'input' to 'output' using 'algorithm'.
+   On success, appends slices to output and returns 1.
+   On failure, output is unchanged, and returns 0. */
+int grpc_msg_decompress(grpc_message_compression_algorithm algorithm,
+                        grpc_slice_buffer* input, grpc_slice_buffer* output);
+
+#endif /* GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H */
diff --git a/third_party/grpc/src/core/lib/compression/stream_compression.cc b/third_party/grpc/src/core/lib/compression/stream_compression.cc
new file mode 100644
index 0000000..46cb3da
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/stream_compression.cc
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/compression/stream_compression.h"
+#include "src/core/lib/compression/stream_compression_gzip.h"
+
+extern const grpc_stream_compression_vtable
+    grpc_stream_compression_identity_vtable;
+
+bool grpc_stream_compress(grpc_stream_compression_context* ctx,
+                          grpc_slice_buffer* in, grpc_slice_buffer* out,
+                          size_t* output_size, size_t max_output_size,
+                          grpc_stream_compression_flush flush) {
+  return ctx->vtable->compress(ctx, in, out, output_size, max_output_size,
+                               flush);
+}
+
+bool grpc_stream_decompress(grpc_stream_compression_context* ctx,
+                            grpc_slice_buffer* in, grpc_slice_buffer* out,
+                            size_t* output_size, size_t max_output_size,
+                            bool* end_of_context) {
+  return ctx->vtable->decompress(ctx, in, out, output_size, max_output_size,
+                                 end_of_context);
+}
+
+grpc_stream_compression_context* grpc_stream_compression_context_create(
+    grpc_stream_compression_method method) {
+  switch (method) {
+    case GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS:
+    case GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS:
+      return grpc_stream_compression_identity_vtable.context_create(method);
+    case GRPC_STREAM_COMPRESSION_GZIP_COMPRESS:
+    case GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS:
+      return grpc_stream_compression_gzip_vtable.context_create(method);
+    default:
+      gpr_log(GPR_ERROR, "Unknown stream compression method: %d", method);
+      return nullptr;
+  }
+}
+
+void grpc_stream_compression_context_destroy(
+    grpc_stream_compression_context* ctx) {
+  ctx->vtable->context_destroy(ctx);
+}
+
+int grpc_stream_compression_method_parse(
+    grpc_slice value, bool is_compress,
+    grpc_stream_compression_method* method) {
+  if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) {
+    *method = is_compress ? GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS
+                          : GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
+    return 1;
+  } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) {
+    *method = is_compress ? GRPC_STREAM_COMPRESSION_GZIP_COMPRESS
+                          : GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS;
+    return 1;
+  } else {
+    return 0;
+  }
+}
diff --git a/third_party/grpc/src/core/lib/compression/stream_compression.h b/third_party/grpc/src/core/lib/compression/stream_compression.h
new file mode 100644
index 0000000..c80f2f8
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/stream_compression.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_H
+#define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include <grpc/slice_buffer.h>
+#include <zlib.h>
+
+#include "src/core/lib/transport/static_metadata.h"
+
+typedef struct grpc_stream_compression_vtable grpc_stream_compression_vtable;
+
+/* Stream compression/decompression context */
+typedef struct grpc_stream_compression_context {
+  const grpc_stream_compression_vtable* vtable;
+} grpc_stream_compression_context;
+
+typedef enum grpc_stream_compression_method {
+  GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS = 0,
+  GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS,
+  GRPC_STREAM_COMPRESSION_GZIP_COMPRESS,
+  GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS,
+  GRPC_STREAM_COMPRESSION_METHOD_COUNT
+} grpc_stream_compression_method;
+
+typedef enum grpc_stream_compression_flush {
+  GRPC_STREAM_COMPRESSION_FLUSH_NONE = 0,
+  GRPC_STREAM_COMPRESSION_FLUSH_SYNC,
+  GRPC_STREAM_COMPRESSION_FLUSH_FINISH,
+  GRPC_STREAM_COMPRESSION_FLUSH_COUNT
+} grpc_stream_compression_flush;
+
+struct grpc_stream_compression_vtable {
+  bool (*compress)(grpc_stream_compression_context* ctx, grpc_slice_buffer* in,
+                   grpc_slice_buffer* out, size_t* output_size,
+                   size_t max_output_size, grpc_stream_compression_flush flush);
+  bool (*decompress)(grpc_stream_compression_context* ctx,
+                     grpc_slice_buffer* in, grpc_slice_buffer* out,
+                     size_t* output_size, size_t max_output_size,
+                     bool* end_of_context);
+  grpc_stream_compression_context* (*context_create)(
+      grpc_stream_compression_method method);
+  void (*context_destroy)(grpc_stream_compression_context* ctx);
+};
+
+/**
+ * Compress bytes provided in \a in with a given context, with an optional flush
+ * at the end of compression. Emits at most \a max_output_size compressed bytes
+ * into \a out. If all the bytes in input buffer \a in are depleted and \a flush
+ * is not GRPC_STREAM_COMPRESSION_FLUSH_NONE, the corresponding flush method is
+ * executed. The total number of bytes emitted is outputed in \a output_size.
+ *
+ * A SYNC flush indicates that the entire messages in \a in can be decompressed
+ * from \a out. A FINISH flush implies a SYNC flush, and that any further
+ * compression will not be dependent on the state of the current context and any
+ * previous compressed bytes. It allows corresponding decompression context to
+ * be dropped when reaching this boundary.
+ */
+bool grpc_stream_compress(grpc_stream_compression_context* ctx,
+                          grpc_slice_buffer* in, grpc_slice_buffer* out,
+                          size_t* output_size, size_t max_output_size,
+                          grpc_stream_compression_flush flush);
+
+/**
+ * Decompress bytes provided in \a in with a given context. Emits at most \a
+ * max_output_size decompressed bytes into \a out. If decompression process
+ * reached the end of a gzip stream, \a end_of_context is set to true; otherwise
+ * it is set to false. The total number of bytes emitted is outputed in \a
+ * output_size.
+ */
+bool grpc_stream_decompress(grpc_stream_compression_context* ctx,
+                            grpc_slice_buffer* in, grpc_slice_buffer* out,
+                            size_t* output_size, size_t max_output_size,
+                            bool* end_of_context);
+
+/**
+ * Creates a stream compression context. \a pending_bytes_buffer is the input
+ * buffer for compression/decompression operations. \a method specifies whether
+ * the context is for compression or decompression.
+ */
+grpc_stream_compression_context* grpc_stream_compression_context_create(
+    grpc_stream_compression_method method);
+
+/**
+ * Destroys a stream compression context.
+ */
+void grpc_stream_compression_context_destroy(
+    grpc_stream_compression_context* ctx);
+
+/**
+ * Parse stream compression method based on algorithm name
+ */
+int grpc_stream_compression_method_parse(
+    grpc_slice value, bool is_compress, grpc_stream_compression_method* method);
+
+#endif
diff --git a/third_party/grpc/src/core/lib/compression/stream_compression_gzip.cc b/third_party/grpc/src/core/lib/compression/stream_compression_gzip.cc
new file mode 100644
index 0000000..682f712
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/stream_compression_gzip.cc
@@ -0,0 +1,230 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/compression/stream_compression_gzip.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+#define OUTPUT_BLOCK_SIZE (1024)
+
+typedef struct grpc_stream_compression_context_gzip {
+  grpc_stream_compression_context base;
+
+  z_stream zs;
+  int (*flate)(z_stream* zs, int flush);
+} grpc_stream_compression_context_gzip;
+
+static bool gzip_flate(grpc_stream_compression_context_gzip* ctx,
+                       grpc_slice_buffer* in, grpc_slice_buffer* out,
+                       size_t* output_size, size_t max_output_size, int flush,
+                       bool* end_of_context) {
+  GPR_ASSERT(flush == 0 || flush == Z_SYNC_FLUSH || flush == Z_FINISH);
+  /* Full flush is not allowed when inflating. */
+  GPR_ASSERT(!(ctx->flate == inflate && (flush == Z_FINISH)));
+
+  grpc_core::ExecCtx exec_ctx;
+  int r;
+  bool eoc = false;
+  size_t original_max_output_size = max_output_size;
+  while (max_output_size > 0 && (in->length > 0 || flush) && !eoc) {
+    size_t slice_size = max_output_size < OUTPUT_BLOCK_SIZE ? max_output_size
+                                                            : OUTPUT_BLOCK_SIZE;
+    grpc_slice slice_out = GRPC_SLICE_MALLOC(slice_size);
+    ctx->zs.avail_out = static_cast<uInt>(slice_size);
+    ctx->zs.next_out = GRPC_SLICE_START_PTR(slice_out);
+    while (ctx->zs.avail_out > 0 && in->length > 0 && !eoc) {
+      grpc_slice slice = grpc_slice_buffer_take_first(in);
+      ctx->zs.avail_in = static_cast<uInt> GRPC_SLICE_LENGTH(slice);
+      ctx->zs.next_in = GRPC_SLICE_START_PTR(slice);
+      r = ctx->flate(&ctx->zs, Z_NO_FLUSH);
+      if (r < 0 && r != Z_BUF_ERROR) {
+        gpr_log(GPR_ERROR, "zlib error (%d)", r);
+        grpc_slice_unref_internal(slice_out);
+
+        return false;
+      } else if (r == Z_STREAM_END && ctx->flate == inflate) {
+        eoc = true;
+      }
+      if (ctx->zs.avail_in > 0) {
+        grpc_slice_buffer_undo_take_first(
+            in,
+            grpc_slice_sub(slice, GRPC_SLICE_LENGTH(slice) - ctx->zs.avail_in,
+                           GRPC_SLICE_LENGTH(slice)));
+      }
+      grpc_slice_unref_internal(slice);
+    }
+    if (flush != 0 && ctx->zs.avail_out > 0 && !eoc) {
+      GPR_ASSERT(in->length == 0);
+      r = ctx->flate(&ctx->zs, flush);
+      if (flush == Z_SYNC_FLUSH) {
+        switch (r) {
+          case Z_OK:
+            /* Maybe flush is not complete; just made some partial progress. */
+            if (ctx->zs.avail_out > 0) {
+              flush = 0;
+            }
+            break;
+          case Z_BUF_ERROR:
+          case Z_STREAM_END:
+            flush = 0;
+            break;
+          default:
+            gpr_log(GPR_ERROR, "zlib error (%d)", r);
+            grpc_slice_unref_internal(slice_out);
+
+            return false;
+        }
+      } else if (flush == Z_FINISH) {
+        switch (r) {
+          case Z_OK:
+          case Z_BUF_ERROR:
+            /* Wait for the next loop to assign additional output space. */
+            GPR_ASSERT(ctx->zs.avail_out == 0);
+            break;
+          case Z_STREAM_END:
+            flush = 0;
+            break;
+          default:
+            gpr_log(GPR_ERROR, "zlib error (%d)", r);
+            grpc_slice_unref_internal(slice_out);
+
+            return false;
+        }
+      }
+    }
+
+    if (ctx->zs.avail_out == 0) {
+      grpc_slice_buffer_add(out, slice_out);
+    } else if (ctx->zs.avail_out < slice_size) {
+      size_t len = GRPC_SLICE_LENGTH(slice_out);
+      GRPC_SLICE_SET_LENGTH(slice_out, len - ctx->zs.avail_out);
+      grpc_slice_buffer_add(out, slice_out);
+    } else {
+      grpc_slice_unref_internal(slice_out);
+    }
+    max_output_size -= (slice_size - ctx->zs.avail_out);
+  }
+
+  if (end_of_context) {
+    *end_of_context = eoc;
+  }
+  if (output_size) {
+    *output_size = original_max_output_size - max_output_size;
+  }
+  return true;
+}
+
+static bool grpc_stream_compress_gzip(grpc_stream_compression_context* ctx,
+                                      grpc_slice_buffer* in,
+                                      grpc_slice_buffer* out,
+                                      size_t* output_size,
+                                      size_t max_output_size,
+                                      grpc_stream_compression_flush flush) {
+  if (ctx == nullptr) {
+    return false;
+  }
+  grpc_stream_compression_context_gzip* gzip_ctx =
+      reinterpret_cast<grpc_stream_compression_context_gzip*>(ctx);
+  GPR_ASSERT(gzip_ctx->flate == deflate);
+  int gzip_flush;
+  switch (flush) {
+    case GRPC_STREAM_COMPRESSION_FLUSH_NONE:
+      gzip_flush = 0;
+      break;
+    case GRPC_STREAM_COMPRESSION_FLUSH_SYNC:
+      gzip_flush = Z_SYNC_FLUSH;
+      break;
+    case GRPC_STREAM_COMPRESSION_FLUSH_FINISH:
+      gzip_flush = Z_FINISH;
+      break;
+    default:
+      gzip_flush = 0;
+  }
+  return gzip_flate(gzip_ctx, in, out, output_size, max_output_size, gzip_flush,
+                    nullptr);
+}
+
+static bool grpc_stream_decompress_gzip(grpc_stream_compression_context* ctx,
+                                        grpc_slice_buffer* in,
+                                        grpc_slice_buffer* out,
+                                        size_t* output_size,
+                                        size_t max_output_size,
+                                        bool* end_of_context) {
+  if (ctx == nullptr) {
+    return false;
+  }
+  grpc_stream_compression_context_gzip* gzip_ctx =
+      reinterpret_cast<grpc_stream_compression_context_gzip*>(ctx);
+  GPR_ASSERT(gzip_ctx->flate == inflate);
+  return gzip_flate(gzip_ctx, in, out, output_size, max_output_size,
+                    Z_SYNC_FLUSH, end_of_context);
+}
+
+static grpc_stream_compression_context*
+grpc_stream_compression_context_create_gzip(
+    grpc_stream_compression_method method) {
+  GPR_ASSERT(method == GRPC_STREAM_COMPRESSION_GZIP_COMPRESS ||
+             method == GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS);
+  grpc_stream_compression_context_gzip* gzip_ctx =
+      static_cast<grpc_stream_compression_context_gzip*>(
+          gpr_zalloc(sizeof(grpc_stream_compression_context_gzip)));
+  int r;
+  if (gzip_ctx == nullptr) {
+    return nullptr;
+  }
+  if (method == GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS) {
+    r = inflateInit2(&gzip_ctx->zs, 0x1F);
+    gzip_ctx->flate = inflate;
+  } else {
+    r = deflateInit2(&gzip_ctx->zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8,
+                     Z_DEFAULT_STRATEGY);
+    gzip_ctx->flate = deflate;
+  }
+  if (r != Z_OK) {
+    gpr_free(gzip_ctx);
+    return nullptr;
+  }
+
+  gzip_ctx->base.vtable = &grpc_stream_compression_gzip_vtable;
+  return reinterpret_cast<grpc_stream_compression_context*>(gzip_ctx);
+}
+
+static void grpc_stream_compression_context_destroy_gzip(
+    grpc_stream_compression_context* ctx) {
+  if (ctx == nullptr) {
+    return;
+  }
+  grpc_stream_compression_context_gzip* gzip_ctx =
+      reinterpret_cast<grpc_stream_compression_context_gzip*>(ctx);
+  if (gzip_ctx->flate == inflate) {
+    inflateEnd(&gzip_ctx->zs);
+  } else {
+    deflateEnd(&gzip_ctx->zs);
+  }
+  gpr_free(ctx);
+}
+
+const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable = {
+    grpc_stream_compress_gzip, grpc_stream_decompress_gzip,
+    grpc_stream_compression_context_create_gzip,
+    grpc_stream_compression_context_destroy_gzip};
diff --git a/third_party/grpc/src/core/lib/compression/stream_compression_gzip.h b/third_party/grpc/src/core/lib/compression/stream_compression_gzip.h
new file mode 100644
index 0000000..740f097
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/stream_compression_gzip.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H
+#define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/compression/stream_compression.h"
+
+extern const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable;
+
+#endif
diff --git a/third_party/grpc/src/core/lib/compression/stream_compression_identity.cc b/third_party/grpc/src/core/lib/compression/stream_compression_identity.cc
new file mode 100644
index 0000000..b798139
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/stream_compression_identity.cc
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/compression/stream_compression_identity.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+#define OUTPUT_BLOCK_SIZE (1024)
+
+/* Singleton context used for all identity streams. */
+static grpc_stream_compression_context identity_ctx = {
+    &grpc_stream_compression_identity_vtable};
+
+static void grpc_stream_compression_pass_through(grpc_slice_buffer* in,
+                                                 grpc_slice_buffer* out,
+                                                 size_t* output_size,
+                                                 size_t max_output_size) {
+  if (max_output_size >= in->length) {
+    if (output_size) {
+      *output_size = in->length;
+    }
+    grpc_slice_buffer_move_into(in, out);
+  } else {
+    if (output_size) {
+      *output_size = max_output_size;
+    }
+    grpc_slice_buffer_move_first(in, max_output_size, out);
+  }
+}
+
+static bool grpc_stream_compress_identity(grpc_stream_compression_context* ctx,
+                                          grpc_slice_buffer* in,
+                                          grpc_slice_buffer* out,
+                                          size_t* output_size,
+                                          size_t max_output_size,
+                                          grpc_stream_compression_flush flush) {
+  if (ctx == nullptr) {
+    return false;
+  }
+  grpc_stream_compression_pass_through(in, out, output_size, max_output_size);
+  return true;
+}
+
+static bool grpc_stream_decompress_identity(
+    grpc_stream_compression_context* ctx, grpc_slice_buffer* in,
+    grpc_slice_buffer* out, size_t* output_size, size_t max_output_size,
+    bool* end_of_context) {
+  if (ctx == nullptr) {
+    return false;
+  }
+  grpc_stream_compression_pass_through(in, out, output_size, max_output_size);
+  if (end_of_context) {
+    *end_of_context = false;
+  }
+  return true;
+}
+
+static grpc_stream_compression_context*
+grpc_stream_compression_context_create_identity(
+    grpc_stream_compression_method method) {
+  GPR_ASSERT(method == GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS ||
+             method == GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS);
+  /* No context needed in this case. Use fake context instead. */
+  return &identity_ctx;
+}
+
+static void grpc_stream_compression_context_destroy_identity(
+    grpc_stream_compression_context* ctx) {
+  return;
+}
+
+const grpc_stream_compression_vtable grpc_stream_compression_identity_vtable = {
+    grpc_stream_compress_identity, grpc_stream_decompress_identity,
+    grpc_stream_compression_context_create_identity,
+    grpc_stream_compression_context_destroy_identity};
diff --git a/third_party/grpc/src/core/lib/compression/stream_compression_identity.h b/third_party/grpc/src/core/lib/compression/stream_compression_identity.h
new file mode 100644
index 0000000..cc77b63
--- /dev/null
+++ b/third_party/grpc/src/core/lib/compression/stream_compression_identity.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H
+#define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/compression/stream_compression.h"
+
+extern const grpc_stream_compression_vtable
+    grpc_stream_compression_identity_vtable;
+
+#endif
diff --git a/third_party/grpc/src/core/lib/debug/stats.cc b/third_party/grpc/src/core/lib/debug/stats.cc
new file mode 100644
index 0000000..d8ddf03
--- /dev/null
+++ b/third_party/grpc/src/core/lib/debug/stats.cc
@@ -0,0 +1,178 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/debug/stats.h"
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+
+grpc_stats_data* grpc_stats_per_cpu_storage = nullptr;
+static size_t g_num_cores;
+
+void grpc_stats_init(void) {
+  g_num_cores = GPR_MAX(1, gpr_cpu_num_cores());
+  grpc_stats_per_cpu_storage = static_cast<grpc_stats_data*>(
+      gpr_zalloc(sizeof(grpc_stats_data) * g_num_cores));
+}
+
+void grpc_stats_shutdown(void) { gpr_free(grpc_stats_per_cpu_storage); }
+
+void grpc_stats_collect(grpc_stats_data* output) {
+  memset(output, 0, sizeof(*output));
+  for (size_t core = 0; core < g_num_cores; core++) {
+    for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
+      output->counters[i] += gpr_atm_no_barrier_load(
+          &grpc_stats_per_cpu_storage[core].counters[i]);
+    }
+    for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) {
+      output->histograms[i] += gpr_atm_no_barrier_load(
+          &grpc_stats_per_cpu_storage[core].histograms[i]);
+    }
+  }
+}
+
+void grpc_stats_diff(const grpc_stats_data* b, const grpc_stats_data* a,
+                     grpc_stats_data* c) {
+  for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
+    c->counters[i] = b->counters[i] - a->counters[i];
+  }
+  for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) {
+    c->histograms[i] = b->histograms[i] - a->histograms[i];
+  }
+}
+
+int grpc_stats_histo_find_bucket_slow(int value, const int* table,
+                                      int table_size) {
+  GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS();
+  const int* const start = table;
+  while (table_size > 0) {
+    int step = table_size / 2;
+    const int* it = table + step;
+    if (value >= *it) {
+      table = it + 1;
+      table_size -= step + 1;
+    } else {
+      table_size = step;
+    }
+  }
+  return static_cast<int>(table - start) - 1;
+}
+
+size_t grpc_stats_histo_count(const grpc_stats_data* stats,
+                              grpc_stats_histograms histogram) {
+  size_t sum = 0;
+  for (int i = 0; i < grpc_stats_histo_buckets[histogram]; i++) {
+    sum += static_cast<size_t>(
+        stats->histograms[grpc_stats_histo_start[histogram] + i]);
+  }
+  return sum;
+}
+
+static double threshold_for_count_below(const gpr_atm* bucket_counts,
+                                        const int* bucket_boundaries,
+                                        int num_buckets, double count_below) {
+  double count_so_far;
+  double lower_bound;
+  double upper_bound;
+  int lower_idx;
+  int upper_idx;
+
+  /* find the lowest bucket that gets us above count_below */
+  count_so_far = 0.0;
+  for (lower_idx = 0; lower_idx < num_buckets; lower_idx++) {
+    count_so_far += static_cast<double>(bucket_counts[lower_idx]);
+    if (count_so_far >= count_below) {
+      break;
+    }
+  }
+  if (count_so_far == count_below) {
+    /* this bucket hits the threshold exactly... we should be midway through
+       any run of zero values following the bucket */
+    for (upper_idx = lower_idx + 1; upper_idx < num_buckets; upper_idx++) {
+      if (bucket_counts[upper_idx]) {
+        break;
+      }
+    }
+    return (bucket_boundaries[lower_idx] + bucket_boundaries[upper_idx]) / 2.0;
+  } else {
+    /* treat values as uniform throughout the bucket, and find where this value
+       should lie */
+    lower_bound = bucket_boundaries[lower_idx];
+    upper_bound = bucket_boundaries[lower_idx + 1];
+    return upper_bound - (upper_bound - lower_bound) *
+                             (count_so_far - count_below) /
+                             static_cast<double>(bucket_counts[lower_idx]);
+  }
+}
+
+double grpc_stats_histo_percentile(const grpc_stats_data* stats,
+                                   grpc_stats_histograms histogram,
+                                   double percentile) {
+  size_t count = grpc_stats_histo_count(stats, histogram);
+  if (count == 0) return 0.0;
+  return threshold_for_count_below(
+      stats->histograms + grpc_stats_histo_start[histogram],
+      grpc_stats_histo_bucket_boundaries[histogram],
+      grpc_stats_histo_buckets[histogram],
+      static_cast<double>(count) * percentile / 100.0);
+}
+
+char* grpc_stats_data_as_json(const grpc_stats_data* data) {
+  gpr_strvec v;
+  char* tmp;
+  bool is_first = true;
+  gpr_strvec_init(&v);
+  gpr_strvec_add(&v, gpr_strdup("{"));
+  for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
+    gpr_asprintf(&tmp, "%s\"%s\": %" PRIdPTR, is_first ? "" : ", ",
+                 grpc_stats_counter_name[i], data->counters[i]);
+    gpr_strvec_add(&v, tmp);
+    is_first = false;
+  }
+  for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) {
+    gpr_asprintf(&tmp, "%s\"%s\": [", is_first ? "" : ", ",
+                 grpc_stats_histogram_name[i]);
+    gpr_strvec_add(&v, tmp);
+    for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) {
+      gpr_asprintf(&tmp, "%s%" PRIdPTR, j == 0 ? "" : ",",
+                   data->histograms[grpc_stats_histo_start[i] + j]);
+      gpr_strvec_add(&v, tmp);
+    }
+    gpr_asprintf(&tmp, "], \"%s_bkt\": [", grpc_stats_histogram_name[i]);
+    gpr_strvec_add(&v, tmp);
+    for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) {
+      gpr_asprintf(&tmp, "%s%d", j == 0 ? "" : ",",
+                   grpc_stats_histo_bucket_boundaries[i][j]);
+      gpr_strvec_add(&v, tmp);
+    }
+    gpr_strvec_add(&v, gpr_strdup("]"));
+    is_first = false;
+  }
+  gpr_strvec_add(&v, gpr_strdup("}"));
+  tmp = gpr_strvec_flatten(&v, nullptr);
+  gpr_strvec_destroy(&v);
+  return tmp;
+}
diff --git a/third_party/grpc/src/core/lib/debug/stats.h b/third_party/grpc/src/core/lib/debug/stats.h
new file mode 100644
index 0000000..9e88ad7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/debug/stats.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_DEBUG_STATS_H
+#define GRPC_CORE_LIB_DEBUG_STATS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+#include "src/core/lib/debug/stats_data.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+typedef struct grpc_stats_data {
+  gpr_atm counters[GRPC_STATS_COUNTER_COUNT];
+  gpr_atm histograms[GRPC_STATS_HISTOGRAM_BUCKETS];
+} grpc_stats_data;
+
+extern grpc_stats_data* grpc_stats_per_cpu_storage;
+
+#define GRPC_THREAD_STATS_DATA() \
+  (&grpc_stats_per_cpu_storage[grpc_core::ExecCtx::Get()->starting_cpu()])
+
+/* Only collect stats if GRPC_COLLECT_STATS is defined or it is a debug build.
+ */
+#if defined(GRPC_COLLECT_STATS) || !defined(NDEBUG)
+#define GRPC_STATS_INC_COUNTER(ctr) \
+  (gpr_atm_no_barrier_fetch_add(&GRPC_THREAD_STATS_DATA()->counters[(ctr)], 1))
+
+#define GRPC_STATS_INC_HISTOGRAM(histogram, index)                             \
+  (gpr_atm_no_barrier_fetch_add(                                               \
+      &GRPC_THREAD_STATS_DATA()->histograms[histogram##_FIRST_SLOT + (index)], \
+      1))
+#else /* defined(GRPC_COLLECT_STATS) || !defined(NDEBUG) */
+#define GRPC_STATS_INC_COUNTER(ctr)
+#define GRPC_STATS_INC_HISTOGRAM(histogram, index)
+#endif /* defined(GRPC_COLLECT_STATS) || !defined(NDEBUG) */
+
+void grpc_stats_init(void);
+void grpc_stats_shutdown(void);
+void grpc_stats_collect(grpc_stats_data* output);
+// c = b-a
+void grpc_stats_diff(const grpc_stats_data* b, const grpc_stats_data* a,
+                     grpc_stats_data* c);
+char* grpc_stats_data_as_json(const grpc_stats_data* data);
+int grpc_stats_histo_find_bucket_slow(int value, const int* table,
+                                      int table_size);
+double grpc_stats_histo_percentile(const grpc_stats_data* data,
+                                   grpc_stats_histograms histogram,
+                                   double percentile);
+size_t grpc_stats_histo_count(const grpc_stats_data* data,
+                              grpc_stats_histograms histogram);
+
+#endif
diff --git a/third_party/grpc/src/core/lib/debug/stats_data.cc b/third_party/grpc/src/core/lib/debug/stats_data.cc
new file mode 100644
index 0000000..f8c27db
--- /dev/null
+++ b/third_party/grpc/src/core/lib/debug/stats_data.cc
@@ -0,0 +1,687 @@
+/*
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ */
+
+/*
+ * Automatically generated by tools/codegen/core/gen_stats_data.py
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/debug/stats_data.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+const char* grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT] = {
+    "client_calls_created",
+    "server_calls_created",
+    "cqs_created",
+    "client_channels_created",
+    "client_subchannels_created",
+    "server_channels_created",
+    "syscall_poll",
+    "syscall_wait",
+    "pollset_kick",
+    "pollset_kicked_without_poller",
+    "pollset_kicked_again",
+    "pollset_kick_wakeup_fd",
+    "pollset_kick_wakeup_cv",
+    "pollset_kick_own_thread",
+    "syscall_epoll_ctl",
+    "pollset_fd_cache_hits",
+    "histogram_slow_lookups",
+    "syscall_write",
+    "syscall_read",
+    "tcp_backup_pollers_created",
+    "tcp_backup_poller_polls",
+    "http2_op_batches",
+    "http2_op_cancel",
+    "http2_op_send_initial_metadata",
+    "http2_op_send_message",
+    "http2_op_send_trailing_metadata",
+    "http2_op_recv_initial_metadata",
+    "http2_op_recv_message",
+    "http2_op_recv_trailing_metadata",
+    "http2_settings_writes",
+    "http2_pings_sent",
+    "http2_writes_begun",
+    "http2_writes_offloaded",
+    "http2_writes_continued",
+    "http2_partial_writes",
+    "http2_initiate_write_due_to_initial_write",
+    "http2_initiate_write_due_to_start_new_stream",
+    "http2_initiate_write_due_to_send_message",
+    "http2_initiate_write_due_to_send_initial_metadata",
+    "http2_initiate_write_due_to_send_trailing_metadata",
+    "http2_initiate_write_due_to_retry_send_ping",
+    "http2_initiate_write_due_to_continue_pings",
+    "http2_initiate_write_due_to_goaway_sent",
+    "http2_initiate_write_due_to_rst_stream",
+    "http2_initiate_write_due_to_close_from_api",
+    "http2_initiate_write_due_to_stream_flow_control",
+    "http2_initiate_write_due_to_transport_flow_control",
+    "http2_initiate_write_due_to_send_settings",
+    "http2_initiate_write_due_to_bdp_estimator_ping",
+    "http2_initiate_write_due_to_flow_control_unstalled_by_setting",
+    "http2_initiate_write_due_to_flow_control_unstalled_by_update",
+    "http2_initiate_write_due_to_application_ping",
+    "http2_initiate_write_due_to_keepalive_ping",
+    "http2_initiate_write_due_to_transport_flow_control_unstalled",
+    "http2_initiate_write_due_to_ping_response",
+    "http2_initiate_write_due_to_force_rst_stream",
+    "http2_spurious_writes_begun",
+    "hpack_recv_indexed",
+    "hpack_recv_lithdr_incidx",
+    "hpack_recv_lithdr_incidx_v",
+    "hpack_recv_lithdr_notidx",
+    "hpack_recv_lithdr_notidx_v",
+    "hpack_recv_lithdr_nvridx",
+    "hpack_recv_lithdr_nvridx_v",
+    "hpack_recv_uncompressed",
+    "hpack_recv_huffman",
+    "hpack_recv_binary",
+    "hpack_recv_binary_base64",
+    "hpack_send_indexed",
+    "hpack_send_lithdr_incidx",
+    "hpack_send_lithdr_incidx_v",
+    "hpack_send_lithdr_notidx",
+    "hpack_send_lithdr_notidx_v",
+    "hpack_send_lithdr_nvridx",
+    "hpack_send_lithdr_nvridx_v",
+    "hpack_send_uncompressed",
+    "hpack_send_huffman",
+    "hpack_send_binary",
+    "hpack_send_binary_base64",
+    "combiner_locks_initiated",
+    "combiner_locks_scheduled_items",
+    "combiner_locks_scheduled_final_items",
+    "combiner_locks_offloaded",
+    "call_combiner_locks_initiated",
+    "call_combiner_locks_scheduled_items",
+    "call_combiner_set_notify_on_cancel",
+    "call_combiner_cancelled",
+    "executor_scheduled_short_items",
+    "executor_scheduled_long_items",
+    "executor_scheduled_to_self",
+    "executor_wakeup_initiated",
+    "executor_queue_drained",
+    "executor_push_retries",
+    "server_requested_calls",
+    "server_slowpath_requests_queued",
+    "cq_ev_queue_trylock_failures",
+    "cq_ev_queue_trylock_successes",
+    "cq_ev_queue_transient_pop_failures",
+};
+const char* grpc_stats_counter_doc[GRPC_STATS_COUNTER_COUNT] = {
+    "Number of client side calls created by this process",
+    "Number of server side calls created by this process",
+    "Number of completion queues created",
+    "Number of client channels created",
+    "Number of client subchannels created",
+    "Number of server channels created",
+    "Number of polling syscalls (epoll_wait, poll, etc) made by this process",
+    "Number of sleeping syscalls made by this process",
+    "How many polling wakeups were performed by the process (only valid for "
+    "epoll1 right now)",
+    "How many times was a polling wakeup requested without an active poller "
+    "(only valid for epoll1 right now)",
+    "How many times was the same polling worker awoken repeatedly before "
+    "waking up (only valid for epoll1 right now)",
+    "How many times was an eventfd used as the wakeup vector for a polling "
+    "wakeup (only valid for epoll1 right now)",
+    "How many times was a condition variable used as the wakeup vector for a "
+    "polling wakeup (only valid for epoll1 right now)",
+    "How many times could a polling wakeup be satisfied by keeping the waking "
+    "thread awake? (only valid for epoll1 right now)",
+    "Number of epoll_ctl calls made (only valid for epollex right now)",
+    "Number of epoll_ctl calls skipped because the fd was cached as already "
+    "being added.  (only valid for epollex right now)",
+    "Number of times histogram increments went through the slow (binary "
+    "search) path",
+    "Number of write syscalls (or equivalent - eg sendmsg) made by this "
+    "process",
+    "Number of read syscalls (or equivalent - eg recvmsg) made by this process",
+    "Number of times a backup poller has been created (this can be expensive)",
+    "Number of polls performed on the backup poller",
+    "Number of batches received by HTTP2 transport",
+    "Number of cancelations received by HTTP2 transport",
+    "Number of batches containing send initial metadata",
+    "Number of batches containing send message",
+    "Number of batches containing send trailing metadata",
+    "Number of batches containing receive initial metadata",
+    "Number of batches containing receive message",
+    "Number of batches containing receive trailing metadata",
+    "Number of settings frames sent",
+    "Number of HTTP2 pings sent by process",
+    "Number of HTTP2 writes initiated",
+    "Number of HTTP2 writes offloaded to the executor from application threads",
+    "Number of HTTP2 writes that finished seeing more data needed to be "
+    "written",
+    "Number of HTTP2 writes that were made knowing there was still more data "
+    "to be written (we cap maximum write size to syscall_write)",
+    "Number of HTTP2 writes initiated due to 'initial_write'",
+    "Number of HTTP2 writes initiated due to 'start_new_stream'",
+    "Number of HTTP2 writes initiated due to 'send_message'",
+    "Number of HTTP2 writes initiated due to 'send_initial_metadata'",
+    "Number of HTTP2 writes initiated due to 'send_trailing_metadata'",
+    "Number of HTTP2 writes initiated due to 'retry_send_ping'",
+    "Number of HTTP2 writes initiated due to 'continue_pings'",
+    "Number of HTTP2 writes initiated due to 'goaway_sent'",
+    "Number of HTTP2 writes initiated due to 'rst_stream'",
+    "Number of HTTP2 writes initiated due to 'close_from_api'",
+    "Number of HTTP2 writes initiated due to 'stream_flow_control'",
+    "Number of HTTP2 writes initiated due to 'transport_flow_control'",
+    "Number of HTTP2 writes initiated due to 'send_settings'",
+    "Number of HTTP2 writes initiated due to 'bdp_estimator_ping'",
+    "Number of HTTP2 writes initiated due to "
+    "'flow_control_unstalled_by_setting'",
+    "Number of HTTP2 writes initiated due to "
+    "'flow_control_unstalled_by_update'",
+    "Number of HTTP2 writes initiated due to 'application_ping'",
+    "Number of HTTP2 writes initiated due to 'keepalive_ping'",
+    "Number of HTTP2 writes initiated due to "
+    "'transport_flow_control_unstalled'",
+    "Number of HTTP2 writes initiated due to 'ping_response'",
+    "Number of HTTP2 writes initiated due to 'force_rst_stream'",
+    "Number of HTTP2 writes initiated with nothing to write",
+    "Number of HPACK indexed fields received",
+    "Number of HPACK literal headers received with incremental indexing",
+    "Number of HPACK literal headers received with incremental indexing and "
+    "literal keys",
+    "Number of HPACK literal headers received with no indexing",
+    "Number of HPACK literal headers received with no indexing and literal "
+    "keys",
+    "Number of HPACK literal headers received with never-indexing",
+    "Number of HPACK literal headers received with never-indexing and literal "
+    "keys",
+    "Number of uncompressed strings received in metadata",
+    "Number of huffman encoded strings received in metadata",
+    "Number of binary strings received in metadata",
+    "Number of binary strings received encoded in base64 in metadata",
+    "Number of HPACK indexed fields sent",
+    "Number of HPACK literal headers sent with incremental indexing",
+    "Number of HPACK literal headers sent with incremental indexing and "
+    "literal keys",
+    "Number of HPACK literal headers sent with no indexing",
+    "Number of HPACK literal headers sent with no indexing and literal keys",
+    "Number of HPACK literal headers sent with never-indexing",
+    "Number of HPACK literal headers sent with never-indexing and literal keys",
+    "Number of uncompressed strings sent in metadata",
+    "Number of huffman encoded strings sent in metadata",
+    "Number of binary strings received in metadata",
+    "Number of binary strings received encoded in base64 in metadata",
+    "Number of combiner lock entries by process (first items queued to a "
+    "combiner)",
+    "Number of items scheduled against combiner locks",
+    "Number of final items scheduled against combiner locks",
+    "Number of combiner locks offloaded to different threads",
+    "Number of call combiner lock entries by process (first items queued to a "
+    "call combiner)",
+    "Number of items scheduled against call combiner locks",
+    "Number of times a cancellation callback was set on a call combiner",
+    "Number of times a call combiner was cancelled",
+    "Number of finite runtime closures scheduled against the executor (gRPC "
+    "thread pool)",
+    "Number of potentially infinite runtime closures scheduled against the "
+    "executor (gRPC thread pool)",
+    "Number of closures scheduled by the executor to the executor",
+    "Number of thread wakeups initiated within the executor",
+    "Number of times an executor queue was drained",
+    "Number of times we raced and were forced to retry pushing a closure to "
+    "the executor",
+    "How many calls were requested (not necessarily received) by the server",
+    "How many times was the server slow path taken (indicates too few "
+    "outstanding requests)",
+    "Number of lock (trylock) acquisition failures on completion queue event "
+    "queue. High value here indicates high contention on completion queues",
+    "Number of lock (trylock) acquisition successes on completion queue event "
+    "queue.",
+    "Number of times NULL was popped out of completion queue's event queue "
+    "even though the event queue was not empty",
+};
+const char* grpc_stats_histogram_name[GRPC_STATS_HISTOGRAM_COUNT] = {
+    "call_initial_size",
+    "poll_events_returned",
+    "tcp_write_size",
+    "tcp_write_iov_size",
+    "tcp_read_size",
+    "tcp_read_offer",
+    "tcp_read_offer_iov_size",
+    "http2_send_message_size",
+    "http2_send_initial_metadata_per_write",
+    "http2_send_message_per_write",
+    "http2_send_trailing_metadata_per_write",
+    "http2_send_flowctl_per_write",
+    "server_cqs_checked",
+};
+const char* grpc_stats_histogram_doc[GRPC_STATS_HISTOGRAM_COUNT] = {
+    "Initial size of the grpc_call arena created at call start",
+    "How many events are called for each syscall_poll",
+    "Number of bytes offered to each syscall_write",
+    "Number of byte segments offered to each syscall_write",
+    "Number of bytes received by each syscall_read",
+    "Number of bytes offered to each syscall_read",
+    "Number of byte segments offered to each syscall_read",
+    "Size of messages received by HTTP2 transport",
+    "Number of streams initiated written per TCP write",
+    "Number of streams whose payload was written per TCP write",
+    "Number of streams terminated per TCP write",
+    "Number of flow control updates written per TCP write",
+    "How many completion queues were checked looking for a CQ that had "
+    "requested the incoming call",
+};
+const int grpc_stats_table_0[65] = {
+    0,      1,      2,      3,      4,     5,     7,     9,     11,    14,
+    17,     21,     26,     32,     39,    47,    57,    68,    82,    98,
+    117,    140,    167,    199,    238,   284,   339,   404,   482,   575,
+    685,    816,    972,    1158,   1380,  1644,  1959,  2334,  2780,  3312,
+    3945,   4699,   5597,   6667,   7941,  9459,  11267, 13420, 15984, 19038,
+    22676,  27009,  32169,  38315,  45635, 54353, 64737, 77104, 91834, 109378,
+    130273, 155159, 184799, 220100, 262144};
+const uint8_t grpc_stats_table_1[124] = {
+    0,  0,  0,  1,  1,  1,  2,  2,  3,  3,  3,  4,  4,  5,  5,  6,  6,  6,
+    7,  7,  7,  8,  9,  9,  10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15,
+    15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 22, 23, 24,
+    24, 25, 25, 26, 26, 26, 27, 27, 28, 29, 29, 30, 30, 30, 31, 31, 32, 33,
+    33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41,
+    42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50,
+    51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58};
+const int grpc_stats_table_2[129] = {
+    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
+    15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  30,
+    32,  34,  36,  38,  40,  42,  44,  46,  48,  50,  52,  54,  56,  58,  60,
+    63,  66,  69,  72,  75,  78,  81,  84,  87,  90,  94,  98,  102, 106, 110,
+    114, 118, 122, 126, 131, 136, 141, 146, 151, 156, 162, 168, 174, 180, 186,
+    192, 199, 206, 213, 220, 228, 236, 244, 252, 260, 269, 278, 287, 297, 307,
+    317, 327, 338, 349, 360, 372, 384, 396, 409, 422, 436, 450, 464, 479, 494,
+    510, 526, 543, 560, 578, 596, 615, 634, 654, 674, 695, 717, 739, 762, 785,
+    809, 834, 859, 885, 912, 939, 967, 996, 1024};
+const uint8_t grpc_stats_table_3[166] = {
+    0,  0,  0,  1,  1,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,  7,  7,
+    8,  8,  9,  9,  10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16,
+    17, 17, 18, 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 26, 27, 27, 28,
+    28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, 38, 39,
+    40, 40, 41, 42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 50, 51,
+    51, 52, 52, 53, 53, 54, 54, 55, 56, 57, 58, 59, 59, 60, 61, 62, 63, 63, 64,
+    65, 65, 66, 67, 67, 68, 69, 69, 70, 71, 71, 72, 72, 73, 73, 74, 75, 75, 76,
+    76, 77, 78, 79, 79, 80, 81, 82, 83, 84, 85, 85, 86, 87, 88, 88, 89, 90, 90,
+    91, 92, 92, 93, 94, 94, 95, 95, 96, 97, 97, 98, 98, 99};
+const int grpc_stats_table_4[65] = {
+    0,       1,       2,       3,       4,       6,       8,        11,
+    15,      20,      26,      34,      44,      57,      73,       94,
+    121,     155,     199,     255,     327,     419,     537,      688,
+    881,     1128,    1444,    1848,    2365,    3026,    3872,     4954,
+    6338,    8108,    10373,   13270,   16976,   21717,   27782,    35541,
+    45467,   58165,   74409,   95189,   121772,  155778,  199281,   254933,
+    326126,  417200,  533707,  682750,  873414,  1117323, 1429345,  1828502,
+    2339127, 2992348, 3827987, 4896985, 6264509, 8013925, 10251880, 13114801,
+    16777216};
+const uint8_t grpc_stats_table_5[87] = {
+    0,  0,  1,  1,  2,  3,  3,  4,  4,  5,  6,  6,  7,  8,  8,  9,  10, 11,
+    11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23,
+    24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36,
+    36, 37, 38, 39, 39, 40, 41, 41, 42, 43, 44, 44, 45, 45, 46, 47, 48, 48,
+    49, 50, 51, 51, 52, 53, 53, 54, 55, 56, 56, 57, 58, 58, 59};
+const int grpc_stats_table_6[65] = {
+    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,
+    14,  16,  18,  20,  22,  24,  27,  30,  33,  36,  39,  43,  47,
+    51,  56,  61,  66,  72,  78,  85,  92,  100, 109, 118, 128, 139,
+    151, 164, 178, 193, 209, 226, 244, 264, 285, 308, 333, 359, 387,
+    418, 451, 486, 524, 565, 609, 656, 707, 762, 821, 884, 952, 1024};
+const uint8_t grpc_stats_table_7[102] = {
+    0,  0,  0,  1,  1,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,
+    6,  7,  7,  7,  8,  8,  9,  9,  10, 11, 11, 12, 12, 13, 13, 14, 14,
+    14, 15, 15, 16, 16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23,
+    23, 24, 24, 24, 25, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32,
+    32, 33, 33, 34, 35, 35, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41,
+    42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 50, 51, 51};
+const int grpc_stats_table_8[9] = {0, 1, 2, 4, 7, 13, 23, 39, 64};
+const uint8_t grpc_stats_table_9[9] = {0, 0, 1, 2, 2, 3, 4, 4, 5};
+void grpc_stats_inc_call_initial_size(int value) {
+  value = GPR_CLAMP(value, 0, 262144);
+  if (value < 6) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4651092515166879744ull) {
+    int bucket =
+        grpc_stats_table_1[((_val.uint - 4618441417868443648ull) >> 49)] + 6;
+    _bkt.dbl = grpc_stats_table_0[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_0, 64));
+}
+void grpc_stats_inc_poll_events_returned(int value) {
+  value = GPR_CLAMP(value, 0, 1024);
+  if (value < 29) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4642789003353915392ull) {
+    int bucket =
+        grpc_stats_table_3[((_val.uint - 4628855992006737920ull) >> 47)] + 29;
+    _bkt.dbl = grpc_stats_table_2[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_2, 128));
+}
+void grpc_stats_inc_tcp_write_size(int value) {
+  value = GPR_CLAMP(value, 0, 16777216);
+  if (value < 5) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4683743612465315840ull) {
+    int bucket =
+        grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5;
+    _bkt.dbl = grpc_stats_table_4[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_4, 64));
+}
+void grpc_stats_inc_tcp_write_iov_size(int value) {
+  value = GPR_CLAMP(value, 0, 1024);
+  if (value < 13) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4637863191261478912ull) {
+    int bucket =
+        grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13;
+    _bkt.dbl = grpc_stats_table_6[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64));
+}
+void grpc_stats_inc_tcp_read_size(int value) {
+  value = GPR_CLAMP(value, 0, 16777216);
+  if (value < 5) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4683743612465315840ull) {
+    int bucket =
+        grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5;
+    _bkt.dbl = grpc_stats_table_4[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_TCP_READ_SIZE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_4, 64));
+}
+void grpc_stats_inc_tcp_read_offer(int value) {
+  value = GPR_CLAMP(value, 0, 16777216);
+  if (value < 5) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4683743612465315840ull) {
+    int bucket =
+        grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5;
+    _bkt.dbl = grpc_stats_table_4[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_TCP_READ_OFFER,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_4, 64));
+}
+void grpc_stats_inc_tcp_read_offer_iov_size(int value) {
+  value = GPR_CLAMP(value, 0, 1024);
+  if (value < 13) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE,
+                             value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4637863191261478912ull) {
+    int bucket =
+        grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13;
+    _bkt.dbl = grpc_stats_table_6[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE,
+                             bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64));
+}
+void grpc_stats_inc_http2_send_message_size(int value) {
+  value = GPR_CLAMP(value, 0, 16777216);
+  if (value < 5) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,
+                             value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4683743612465315840ull) {
+    int bucket =
+        grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5;
+    _bkt.dbl = grpc_stats_table_4[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,
+                             bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_4, 64));
+}
+void grpc_stats_inc_http2_send_initial_metadata_per_write(int value) {
+  value = GPR_CLAMP(value, 0, 1024);
+  if (value < 13) {
+    GRPC_STATS_INC_HISTOGRAM(
+        GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4637863191261478912ull) {
+    int bucket =
+        grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13;
+    _bkt.dbl = grpc_stats_table_6[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(
+        GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64));
+}
+void grpc_stats_inc_http2_send_message_per_write(int value) {
+  value = GPR_CLAMP(value, 0, 1024);
+  if (value < 13) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE,
+                             value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4637863191261478912ull) {
+    int bucket =
+        grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13;
+    _bkt.dbl = grpc_stats_table_6[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE,
+                             bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64));
+}
+void grpc_stats_inc_http2_send_trailing_metadata_per_write(int value) {
+  value = GPR_CLAMP(value, 0, 1024);
+  if (value < 13) {
+    GRPC_STATS_INC_HISTOGRAM(
+        GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4637863191261478912ull) {
+    int bucket =
+        grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13;
+    _bkt.dbl = grpc_stats_table_6[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(
+        GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64));
+}
+void grpc_stats_inc_http2_send_flowctl_per_write(int value) {
+  value = GPR_CLAMP(value, 0, 1024);
+  if (value < 13) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE,
+                             value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4637863191261478912ull) {
+    int bucket =
+        grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13;
+    _bkt.dbl = grpc_stats_table_6[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE,
+                             bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64));
+}
+void grpc_stats_inc_server_cqs_checked(int value) {
+  value = GPR_CLAMP(value, 0, 64);
+  if (value < 3) {
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, value);
+    return;
+  }
+  union {
+    double dbl;
+    uint64_t uint;
+  } _val, _bkt;
+  _val.dbl = value;
+  if (_val.uint < 4625196817309499392ull) {
+    int bucket =
+        grpc_stats_table_9[((_val.uint - 4613937818241073152ull) >> 51)] + 3;
+    _bkt.dbl = grpc_stats_table_8[bucket];
+    bucket -= (_val.uint < _bkt.uint);
+    GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, bucket);
+    return;
+  }
+  GRPC_STATS_INC_HISTOGRAM(
+      GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED,
+      grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_8, 8));
+}
+const int grpc_stats_histo_buckets[13] = {64, 128, 64, 64, 64, 64, 64,
+                                          64, 64,  64, 64, 64, 8};
+const int grpc_stats_histo_start[13] = {0,   64,  192, 256, 320, 384, 448,
+                                        512, 576, 640, 704, 768, 832};
+const int* const grpc_stats_histo_bucket_boundaries[13] = {
+    grpc_stats_table_0, grpc_stats_table_2, grpc_stats_table_4,
+    grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_4,
+    grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_6,
+    grpc_stats_table_6, grpc_stats_table_6, grpc_stats_table_6,
+    grpc_stats_table_8};
+void (*const grpc_stats_inc_histogram[13])(int x) = {
+    grpc_stats_inc_call_initial_size,
+    grpc_stats_inc_poll_events_returned,
+    grpc_stats_inc_tcp_write_size,
+    grpc_stats_inc_tcp_write_iov_size,
+    grpc_stats_inc_tcp_read_size,
+    grpc_stats_inc_tcp_read_offer,
+    grpc_stats_inc_tcp_read_offer_iov_size,
+    grpc_stats_inc_http2_send_message_size,
+    grpc_stats_inc_http2_send_initial_metadata_per_write,
+    grpc_stats_inc_http2_send_message_per_write,
+    grpc_stats_inc_http2_send_trailing_metadata_per_write,
+    grpc_stats_inc_http2_send_flowctl_per_write,
+    grpc_stats_inc_server_cqs_checked};
diff --git a/third_party/grpc/src/core/lib/debug/stats_data.h b/third_party/grpc/src/core/lib/debug/stats_data.h
new file mode 100644
index 0000000..1f3861f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/debug/stats_data.h
@@ -0,0 +1,555 @@
+/*
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ */
+
+/*
+ * Automatically generated by tools/codegen/core/gen_stats_data.py
+ */
+
+#ifndef GRPC_CORE_LIB_DEBUG_STATS_DATA_H
+#define GRPC_CORE_LIB_DEBUG_STATS_DATA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <inttypes.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+typedef enum {
+  GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED,
+  GRPC_STATS_COUNTER_SERVER_CALLS_CREATED,
+  GRPC_STATS_COUNTER_CQS_CREATED,
+  GRPC_STATS_COUNTER_CLIENT_CHANNELS_CREATED,
+  GRPC_STATS_COUNTER_CLIENT_SUBCHANNELS_CREATED,
+  GRPC_STATS_COUNTER_SERVER_CHANNELS_CREATED,
+  GRPC_STATS_COUNTER_SYSCALL_POLL,
+  GRPC_STATS_COUNTER_SYSCALL_WAIT,
+  GRPC_STATS_COUNTER_POLLSET_KICK,
+  GRPC_STATS_COUNTER_POLLSET_KICKED_WITHOUT_POLLER,
+  GRPC_STATS_COUNTER_POLLSET_KICKED_AGAIN,
+  GRPC_STATS_COUNTER_POLLSET_KICK_WAKEUP_FD,
+  GRPC_STATS_COUNTER_POLLSET_KICK_WAKEUP_CV,
+  GRPC_STATS_COUNTER_POLLSET_KICK_OWN_THREAD,
+  GRPC_STATS_COUNTER_SYSCALL_EPOLL_CTL,
+  GRPC_STATS_COUNTER_POLLSET_FD_CACHE_HITS,
+  GRPC_STATS_COUNTER_HISTOGRAM_SLOW_LOOKUPS,
+  GRPC_STATS_COUNTER_SYSCALL_WRITE,
+  GRPC_STATS_COUNTER_SYSCALL_READ,
+  GRPC_STATS_COUNTER_TCP_BACKUP_POLLERS_CREATED,
+  GRPC_STATS_COUNTER_TCP_BACKUP_POLLER_POLLS,
+  GRPC_STATS_COUNTER_HTTP2_OP_BATCHES,
+  GRPC_STATS_COUNTER_HTTP2_OP_CANCEL,
+  GRPC_STATS_COUNTER_HTTP2_OP_SEND_INITIAL_METADATA,
+  GRPC_STATS_COUNTER_HTTP2_OP_SEND_MESSAGE,
+  GRPC_STATS_COUNTER_HTTP2_OP_SEND_TRAILING_METADATA,
+  GRPC_STATS_COUNTER_HTTP2_OP_RECV_INITIAL_METADATA,
+  GRPC_STATS_COUNTER_HTTP2_OP_RECV_MESSAGE,
+  GRPC_STATS_COUNTER_HTTP2_OP_RECV_TRAILING_METADATA,
+  GRPC_STATS_COUNTER_HTTP2_SETTINGS_WRITES,
+  GRPC_STATS_COUNTER_HTTP2_PINGS_SENT,
+  GRPC_STATS_COUNTER_HTTP2_WRITES_BEGUN,
+  GRPC_STATS_COUNTER_HTTP2_WRITES_OFFLOADED,
+  GRPC_STATS_COUNTER_HTTP2_WRITES_CONTINUED,
+  GRPC_STATS_COUNTER_HTTP2_PARTIAL_WRITES,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE,
+  GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM,
+  GRPC_STATS_COUNTER_HTTP2_SPURIOUS_WRITES_BEGUN,
+  GRPC_STATS_COUNTER_HPACK_RECV_INDEXED,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX_V,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX_V,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX_V,
+  GRPC_STATS_COUNTER_HPACK_RECV_UNCOMPRESSED,
+  GRPC_STATS_COUNTER_HPACK_RECV_HUFFMAN,
+  GRPC_STATS_COUNTER_HPACK_RECV_BINARY,
+  GRPC_STATS_COUNTER_HPACK_RECV_BINARY_BASE64,
+  GRPC_STATS_COUNTER_HPACK_SEND_INDEXED,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX_V,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX_V,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX_V,
+  GRPC_STATS_COUNTER_HPACK_SEND_UNCOMPRESSED,
+  GRPC_STATS_COUNTER_HPACK_SEND_HUFFMAN,
+  GRPC_STATS_COUNTER_HPACK_SEND_BINARY,
+  GRPC_STATS_COUNTER_HPACK_SEND_BINARY_BASE64,
+  GRPC_STATS_COUNTER_COMBINER_LOCKS_INITIATED,
+  GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_ITEMS,
+  GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS,
+  GRPC_STATS_COUNTER_COMBINER_LOCKS_OFFLOADED,
+  GRPC_STATS_COUNTER_CALL_COMBINER_LOCKS_INITIATED,
+  GRPC_STATS_COUNTER_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS,
+  GRPC_STATS_COUNTER_CALL_COMBINER_SET_NOTIFY_ON_CANCEL,
+  GRPC_STATS_COUNTER_CALL_COMBINER_CANCELLED,
+  GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_SHORT_ITEMS,
+  GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_LONG_ITEMS,
+  GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_TO_SELF,
+  GRPC_STATS_COUNTER_EXECUTOR_WAKEUP_INITIATED,
+  GRPC_STATS_COUNTER_EXECUTOR_QUEUE_DRAINED,
+  GRPC_STATS_COUNTER_EXECUTOR_PUSH_RETRIES,
+  GRPC_STATS_COUNTER_SERVER_REQUESTED_CALLS,
+  GRPC_STATS_COUNTER_SERVER_SLOWPATH_REQUESTS_QUEUED,
+  GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRYLOCK_FAILURES,
+  GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRYLOCK_SUCCESSES,
+  GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES,
+  GRPC_STATS_COUNTER_COUNT
+} grpc_stats_counters;
+extern const char* grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT];
+extern const char* grpc_stats_counter_doc[GRPC_STATS_COUNTER_COUNT];
+typedef enum {
+  GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE,
+  GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED,
+  GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE,
+  GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE,
+  GRPC_STATS_HISTOGRAM_TCP_READ_SIZE,
+  GRPC_STATS_HISTOGRAM_TCP_READ_OFFER,
+  GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE,
+  GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED,
+  GRPC_STATS_HISTOGRAM_COUNT
+} grpc_stats_histograms;
+extern const char* grpc_stats_histogram_name[GRPC_STATS_HISTOGRAM_COUNT];
+extern const char* grpc_stats_histogram_doc[GRPC_STATS_HISTOGRAM_COUNT];
+typedef enum {
+  GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE_FIRST_SLOT = 0,
+  GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED_FIRST_SLOT = 64,
+  GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED_BUCKETS = 128,
+  GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE_FIRST_SLOT = 192,
+  GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE_FIRST_SLOT = 256,
+  GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_TCP_READ_SIZE_FIRST_SLOT = 320,
+  GRPC_STATS_HISTOGRAM_TCP_READ_SIZE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_FIRST_SLOT = 384,
+  GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE_FIRST_SLOT = 448,
+  GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE_FIRST_SLOT = 512,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE_FIRST_SLOT = 576,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE_FIRST_SLOT = 640,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE_FIRST_SLOT = 704,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE_FIRST_SLOT = 768,
+  GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE_BUCKETS = 64,
+  GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED_FIRST_SLOT = 832,
+  GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED_BUCKETS = 8,
+  GRPC_STATS_HISTOGRAM_BUCKETS = 840
+} grpc_stats_histogram_constants;
+#if defined(GRPC_COLLECT_STATS) || !defined(NDEBUG)
+#define GRPC_STATS_INC_CLIENT_CALLS_CREATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED)
+#define GRPC_STATS_INC_SERVER_CALLS_CREATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SERVER_CALLS_CREATED)
+#define GRPC_STATS_INC_CQS_CREATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CQS_CREATED)
+#define GRPC_STATS_INC_CLIENT_CHANNELS_CREATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CLIENT_CHANNELS_CREATED)
+#define GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CLIENT_SUBCHANNELS_CREATED)
+#define GRPC_STATS_INC_SERVER_CHANNELS_CREATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SERVER_CHANNELS_CREATED)
+#define GRPC_STATS_INC_SYSCALL_POLL() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_POLL)
+#define GRPC_STATS_INC_SYSCALL_WAIT() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_WAIT)
+#define GRPC_STATS_INC_POLLSET_KICK() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICK)
+#define GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICKED_WITHOUT_POLLER)
+#define GRPC_STATS_INC_POLLSET_KICKED_AGAIN() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICKED_AGAIN)
+#define GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICK_WAKEUP_FD)
+#define GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICK_WAKEUP_CV)
+#define GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICK_OWN_THREAD)
+#define GRPC_STATS_INC_SYSCALL_EPOLL_CTL() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_EPOLL_CTL)
+#define GRPC_STATS_INC_POLLSET_FD_CACHE_HITS() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_FD_CACHE_HITS)
+#define GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HISTOGRAM_SLOW_LOOKUPS)
+#define GRPC_STATS_INC_SYSCALL_WRITE() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_WRITE)
+#define GRPC_STATS_INC_SYSCALL_READ() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_READ)
+#define GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_TCP_BACKUP_POLLERS_CREATED)
+#define GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_TCP_BACKUP_POLLER_POLLS)
+#define GRPC_STATS_INC_HTTP2_OP_BATCHES() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_BATCHES)
+#define GRPC_STATS_INC_HTTP2_OP_CANCEL() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_CANCEL)
+#define GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_SEND_INITIAL_METADATA)
+#define GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_SEND_MESSAGE)
+#define GRPC_STATS_INC_HTTP2_OP_SEND_TRAILING_METADATA() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_SEND_TRAILING_METADATA)
+#define GRPC_STATS_INC_HTTP2_OP_RECV_INITIAL_METADATA() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_RECV_INITIAL_METADATA)
+#define GRPC_STATS_INC_HTTP2_OP_RECV_MESSAGE() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_RECV_MESSAGE)
+#define GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_RECV_TRAILING_METADATA)
+#define GRPC_STATS_INC_HTTP2_SETTINGS_WRITES() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_SETTINGS_WRITES)
+#define GRPC_STATS_INC_HTTP2_PINGS_SENT() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_PINGS_SENT)
+#define GRPC_STATS_INC_HTTP2_WRITES_BEGUN() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_WRITES_BEGUN)
+#define GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_WRITES_OFFLOADED)
+#define GRPC_STATS_INC_HTTP2_WRITES_CONTINUED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_WRITES_CONTINUED)
+#define GRPC_STATS_INC_HTTP2_PARTIAL_WRITES() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_PARTIAL_WRITES)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE() \
+  GRPC_STATS_INC_COUNTER(                                          \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM() \
+  GRPC_STATS_INC_COUNTER(                                             \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE() \
+  GRPC_STATS_INC_COUNTER(                                         \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA() \
+  GRPC_STATS_INC_COUNTER(                                                  \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA() \
+  GRPC_STATS_INC_COUNTER(                                                   \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING() \
+  GRPC_STATS_INC_COUNTER(                                            \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS() \
+  GRPC_STATS_INC_COUNTER(                                           \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT() \
+  GRPC_STATS_INC_COUNTER(                                        \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM() \
+  GRPC_STATS_INC_COUNTER(                                       \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API() \
+  GRPC_STATS_INC_COUNTER(                                           \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL() \
+  GRPC_STATS_INC_COUNTER(                                                \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL() \
+  GRPC_STATS_INC_COUNTER(                                                   \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS() \
+  GRPC_STATS_INC_COUNTER(                                          \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING() \
+  GRPC_STATS_INC_COUNTER(                                               \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING() \
+  GRPC_STATS_INC_COUNTER(                                                              \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE() \
+  GRPC_STATS_INC_COUNTER(                                                             \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING() \
+  GRPC_STATS_INC_COUNTER(                                             \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING() \
+  GRPC_STATS_INC_COUNTER(                                           \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED() \
+  GRPC_STATS_INC_COUNTER(                                                             \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE() \
+  GRPC_STATS_INC_COUNTER(                                          \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE)
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM() \
+  GRPC_STATS_INC_COUNTER(                                             \
+      GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM)
+#define GRPC_STATS_INC_HTTP2_SPURIOUS_WRITES_BEGUN() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_SPURIOUS_WRITES_BEGUN)
+#define GRPC_STATS_INC_HPACK_RECV_INDEXED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_INDEXED)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX_V)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX_V)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX_V)
+#define GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_UNCOMPRESSED)
+#define GRPC_STATS_INC_HPACK_RECV_HUFFMAN() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_HUFFMAN)
+#define GRPC_STATS_INC_HPACK_RECV_BINARY() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_BINARY)
+#define GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_BINARY_BASE64)
+#define GRPC_STATS_INC_HPACK_SEND_INDEXED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_INDEXED)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX_V)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX_V)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX_V() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX_V)
+#define GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_UNCOMPRESSED)
+#define GRPC_STATS_INC_HPACK_SEND_HUFFMAN() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_HUFFMAN)
+#define GRPC_STATS_INC_HPACK_SEND_BINARY() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_BINARY)
+#define GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_BINARY_BASE64)
+#define GRPC_STATS_INC_COMBINER_LOCKS_INITIATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_COMBINER_LOCKS_INITIATED)
+#define GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_ITEMS() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_ITEMS)
+#define GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS() \
+  GRPC_STATS_INC_COUNTER(                                     \
+      GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS)
+#define GRPC_STATS_INC_COMBINER_LOCKS_OFFLOADED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_COMBINER_LOCKS_OFFLOADED)
+#define GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CALL_COMBINER_LOCKS_INITIATED)
+#define GRPC_STATS_INC_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS)
+#define GRPC_STATS_INC_CALL_COMBINER_SET_NOTIFY_ON_CANCEL() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CALL_COMBINER_SET_NOTIFY_ON_CANCEL)
+#define GRPC_STATS_INC_CALL_COMBINER_CANCELLED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CALL_COMBINER_CANCELLED)
+#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_SHORT_ITEMS)
+#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_LONG_ITEMS() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_LONG_ITEMS)
+#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_TO_SELF)
+#define GRPC_STATS_INC_EXECUTOR_WAKEUP_INITIATED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_WAKEUP_INITIATED)
+#define GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_QUEUE_DRAINED)
+#define GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_PUSH_RETRIES)
+#define GRPC_STATS_INC_SERVER_REQUESTED_CALLS() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SERVER_REQUESTED_CALLS)
+#define GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SERVER_SLOWPATH_REQUESTS_QUEUED)
+#define GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_FAILURES() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRYLOCK_FAILURES)
+#define GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_SUCCESSES() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRYLOCK_SUCCESSES)
+#define GRPC_STATS_INC_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES() \
+  GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES)
+#define GRPC_STATS_INC_CALL_INITIAL_SIZE(value) \
+  grpc_stats_inc_call_initial_size((int)(value))
+void grpc_stats_inc_call_initial_size(int x);
+#define GRPC_STATS_INC_POLL_EVENTS_RETURNED(value) \
+  grpc_stats_inc_poll_events_returned((int)(value))
+void grpc_stats_inc_poll_events_returned(int x);
+#define GRPC_STATS_INC_TCP_WRITE_SIZE(value) \
+  grpc_stats_inc_tcp_write_size((int)(value))
+void grpc_stats_inc_tcp_write_size(int x);
+#define GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(value) \
+  grpc_stats_inc_tcp_write_iov_size((int)(value))
+void grpc_stats_inc_tcp_write_iov_size(int x);
+#define GRPC_STATS_INC_TCP_READ_SIZE(value) \
+  grpc_stats_inc_tcp_read_size((int)(value))
+void grpc_stats_inc_tcp_read_size(int x);
+#define GRPC_STATS_INC_TCP_READ_OFFER(value) \
+  grpc_stats_inc_tcp_read_offer((int)(value))
+void grpc_stats_inc_tcp_read_offer(int x);
+#define GRPC_STATS_INC_TCP_READ_OFFER_IOV_SIZE(value) \
+  grpc_stats_inc_tcp_read_offer_iov_size((int)(value))
+void grpc_stats_inc_tcp_read_offer_iov_size(int x);
+#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE(value) \
+  grpc_stats_inc_http2_send_message_size((int)(value))
+void grpc_stats_inc_http2_send_message_size(int x);
+#define GRPC_STATS_INC_HTTP2_SEND_INITIAL_METADATA_PER_WRITE(value) \
+  grpc_stats_inc_http2_send_initial_metadata_per_write((int)(value))
+void grpc_stats_inc_http2_send_initial_metadata_per_write(int x);
+#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_PER_WRITE(value) \
+  grpc_stats_inc_http2_send_message_per_write((int)(value))
+void grpc_stats_inc_http2_send_message_per_write(int x);
+#define GRPC_STATS_INC_HTTP2_SEND_TRAILING_METADATA_PER_WRITE(value) \
+  grpc_stats_inc_http2_send_trailing_metadata_per_write((int)(value))
+void grpc_stats_inc_http2_send_trailing_metadata_per_write(int x);
+#define GRPC_STATS_INC_HTTP2_SEND_FLOWCTL_PER_WRITE(value) \
+  grpc_stats_inc_http2_send_flowctl_per_write((int)(value))
+void grpc_stats_inc_http2_send_flowctl_per_write(int x);
+#define GRPC_STATS_INC_SERVER_CQS_CHECKED(value) \
+  grpc_stats_inc_server_cqs_checked((int)(value))
+void grpc_stats_inc_server_cqs_checked(int x);
+#else
+#define GRPC_STATS_INC_CLIENT_CALLS_CREATED()
+#define GRPC_STATS_INC_SERVER_CALLS_CREATED()
+#define GRPC_STATS_INC_CQS_CREATED()
+#define GRPC_STATS_INC_CLIENT_CHANNELS_CREATED()
+#define GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED()
+#define GRPC_STATS_INC_SERVER_CHANNELS_CREATED()
+#define GRPC_STATS_INC_SYSCALL_POLL()
+#define GRPC_STATS_INC_SYSCALL_WAIT()
+#define GRPC_STATS_INC_POLLSET_KICK()
+#define GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER()
+#define GRPC_STATS_INC_POLLSET_KICKED_AGAIN()
+#define GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD()
+#define GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV()
+#define GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD()
+#define GRPC_STATS_INC_SYSCALL_EPOLL_CTL()
+#define GRPC_STATS_INC_POLLSET_FD_CACHE_HITS()
+#define GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS()
+#define GRPC_STATS_INC_SYSCALL_WRITE()
+#define GRPC_STATS_INC_SYSCALL_READ()
+#define GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED()
+#define GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS()
+#define GRPC_STATS_INC_HTTP2_OP_BATCHES()
+#define GRPC_STATS_INC_HTTP2_OP_CANCEL()
+#define GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA()
+#define GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE()
+#define GRPC_STATS_INC_HTTP2_OP_SEND_TRAILING_METADATA()
+#define GRPC_STATS_INC_HTTP2_OP_RECV_INITIAL_METADATA()
+#define GRPC_STATS_INC_HTTP2_OP_RECV_MESSAGE()
+#define GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA()
+#define GRPC_STATS_INC_HTTP2_SETTINGS_WRITES()
+#define GRPC_STATS_INC_HTTP2_PINGS_SENT()
+#define GRPC_STATS_INC_HTTP2_WRITES_BEGUN()
+#define GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED()
+#define GRPC_STATS_INC_HTTP2_WRITES_CONTINUED()
+#define GRPC_STATS_INC_HTTP2_PARTIAL_WRITES()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE()
+#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM()
+#define GRPC_STATS_INC_HTTP2_SPURIOUS_WRITES_BEGUN()
+#define GRPC_STATS_INC_HPACK_RECV_INDEXED()
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX()
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V()
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX()
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V()
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX()
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V()
+#define GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED()
+#define GRPC_STATS_INC_HPACK_RECV_HUFFMAN()
+#define GRPC_STATS_INC_HPACK_RECV_BINARY()
+#define GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64()
+#define GRPC_STATS_INC_HPACK_SEND_INDEXED()
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX()
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V()
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX()
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V()
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX()
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX_V()
+#define GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED()
+#define GRPC_STATS_INC_HPACK_SEND_HUFFMAN()
+#define GRPC_STATS_INC_HPACK_SEND_BINARY()
+#define GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64()
+#define GRPC_STATS_INC_COMBINER_LOCKS_INITIATED()
+#define GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_ITEMS()
+#define GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS()
+#define GRPC_STATS_INC_COMBINER_LOCKS_OFFLOADED()
+#define GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED()
+#define GRPC_STATS_INC_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS()
+#define GRPC_STATS_INC_CALL_COMBINER_SET_NOTIFY_ON_CANCEL()
+#define GRPC_STATS_INC_CALL_COMBINER_CANCELLED()
+#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS()
+#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_LONG_ITEMS()
+#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF()
+#define GRPC_STATS_INC_EXECUTOR_WAKEUP_INITIATED()
+#define GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED()
+#define GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES()
+#define GRPC_STATS_INC_SERVER_REQUESTED_CALLS()
+#define GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED()
+#define GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_FAILURES()
+#define GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_SUCCESSES()
+#define GRPC_STATS_INC_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES()
+#define GRPC_STATS_INC_CALL_INITIAL_SIZE(value)
+#define GRPC_STATS_INC_POLL_EVENTS_RETURNED(value)
+#define GRPC_STATS_INC_TCP_WRITE_SIZE(value)
+#define GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(value)
+#define GRPC_STATS_INC_TCP_READ_SIZE(value)
+#define GRPC_STATS_INC_TCP_READ_OFFER(value)
+#define GRPC_STATS_INC_TCP_READ_OFFER_IOV_SIZE(value)
+#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE(value)
+#define GRPC_STATS_INC_HTTP2_SEND_INITIAL_METADATA_PER_WRITE(value)
+#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_PER_WRITE(value)
+#define GRPC_STATS_INC_HTTP2_SEND_TRAILING_METADATA_PER_WRITE(value)
+#define GRPC_STATS_INC_HTTP2_SEND_FLOWCTL_PER_WRITE(value)
+#define GRPC_STATS_INC_SERVER_CQS_CHECKED(value)
+#endif /* defined(GRPC_COLLECT_STATS) || !defined(NDEBUG) */
+extern const int grpc_stats_histo_buckets[13];
+extern const int grpc_stats_histo_start[13];
+extern const int* const grpc_stats_histo_bucket_boundaries[13];
+extern void (*const grpc_stats_inc_histogram[13])(int x);
+
+#endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */
diff --git a/third_party/grpc/src/core/lib/debug/stats_data.yaml b/third_party/grpc/src/core/lib/debug/stats_data.yaml
new file mode 100644
index 0000000..775b09d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/debug/stats_data.yaml
@@ -0,0 +1,300 @@
+# Copyright 2017 gRPC authors.
+#
+# Licensed 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.
+
+# Stats data declaration
+# use tools / codegen / core / gen_stats_data.py to turn this into stats_data.h
+
+# overall
+- counter: client_calls_created
+  doc: Number of client side calls created by this process
+- counter: server_calls_created
+  doc: Number of server side calls created by this process
+- histogram: call_initial_size
+  max: 262144
+  buckets: 64
+  doc: Initial size of the grpc_call arena created at call start
+- counter: cqs_created
+  doc: Number of completion queues created
+- counter: client_channels_created
+  doc: Number of client channels created
+- counter: client_subchannels_created
+  doc: Number of client subchannels created
+- counter: server_channels_created
+  doc: Number of server channels created
+# polling
+- counter: syscall_poll
+  doc: Number of polling syscalls (epoll_wait, poll, etc) made by this process
+- counter: syscall_wait
+  doc: Number of sleeping syscalls made by this process
+- histogram: poll_events_returned
+  max: 1024
+  buckets: 128
+  doc: How many events are called for each syscall_poll
+- counter: pollset_kick
+  doc: How many polling wakeups were performed by the process
+       (only valid for epoll1 right now)
+- counter: pollset_kicked_without_poller
+  doc: How many times was a polling wakeup requested without an active poller
+       (only valid for epoll1 right now)
+- counter: pollset_kicked_again
+  doc: How many times was the same polling worker awoken repeatedly before
+       waking up
+       (only valid for epoll1 right now)
+- counter: pollset_kick_wakeup_fd
+  doc: How many times was an eventfd used as the wakeup vector for a polling
+       wakeup
+       (only valid for epoll1 right now)
+- counter: pollset_kick_wakeup_cv
+  doc: How many times was a condition variable used as the wakeup vector for a
+       polling wakeup
+       (only valid for epoll1 right now)
+- counter: pollset_kick_own_thread
+  doc: How many times could a polling wakeup be satisfied by keeping the waking
+       thread awake?
+       (only valid for epoll1 right now)
+# polling
+- counter: syscall_epoll_ctl
+  doc: Number of epoll_ctl calls made (only valid for epollex right now)
+- counter: pollset_fd_cache_hits
+  doc: Number of epoll_ctl calls skipped because the fd was cached as
+       already being added.  (only valid for epollex right now)
+# stats system
+- counter: histogram_slow_lookups
+  doc: Number of times histogram increments went through the slow
+       (binary search) path
+# tcp
+- counter: syscall_write
+  doc: Number of write syscalls (or equivalent - eg sendmsg) made by this process
+- counter: syscall_read
+  doc: Number of read syscalls (or equivalent - eg recvmsg) made by this process
+- histogram: tcp_write_size
+  max: 16777216 # 16 meg max write tracked
+  buckets: 64
+  doc: Number of bytes offered to each syscall_write
+- histogram: tcp_write_iov_size
+  max: 1024
+  buckets: 64
+  doc: Number of byte segments offered to each syscall_write
+- histogram: tcp_read_size
+  max: 16777216
+  buckets: 64
+  doc: Number of bytes received by each syscall_read
+- histogram: tcp_read_offer
+  max: 16777216
+  buckets: 64
+  doc: Number of bytes offered to each syscall_read
+- histogram: tcp_read_offer_iov_size
+  max: 1024
+  buckets: 64
+  doc: Number of byte segments offered to each syscall_read
+- counter: tcp_backup_pollers_created
+  doc: Number of times a backup poller has been created (this can be expensive)
+- counter: tcp_backup_poller_polls
+  doc: Number of polls performed on the backup poller
+# chttp2
+- counter: http2_op_batches
+  doc: Number of batches received by HTTP2 transport
+- counter: http2_op_cancel
+  doc: Number of cancelations received by HTTP2 transport
+- counter: http2_op_send_initial_metadata
+  doc: Number of batches containing send initial metadata
+- counter: http2_op_send_message
+  doc: Number of batches containing send message
+- counter: http2_op_send_trailing_metadata
+  doc: Number of batches containing send trailing metadata
+- counter: http2_op_recv_initial_metadata
+  doc: Number of batches containing receive initial metadata
+- counter: http2_op_recv_message
+  doc: Number of batches containing receive message
+- counter: http2_op_recv_trailing_metadata
+  doc: Number of batches containing receive trailing metadata
+- histogram: http2_send_message_size
+  max: 16777216
+  buckets: 64
+  doc: Size of messages received by HTTP2 transport
+- histogram: http2_send_initial_metadata_per_write
+  max: 1024
+  buckets: 64
+  doc: Number of streams initiated written per TCP write
+- histogram: http2_send_message_per_write
+  max: 1024
+  buckets: 64
+  doc: Number of streams whose payload was written per TCP write
+- histogram: http2_send_trailing_metadata_per_write
+  max: 1024
+  buckets: 64
+  doc: Number of streams terminated per TCP write
+- histogram: http2_send_flowctl_per_write
+  max: 1024
+  buckets: 64
+  doc: Number of flow control updates written per TCP write
+- counter: http2_settings_writes
+  doc: Number of settings frames sent
+- counter: http2_pings_sent
+  doc: Number of HTTP2 pings sent by process
+- counter: http2_writes_begun
+  doc: Number of HTTP2 writes initiated
+- counter: http2_writes_offloaded
+  doc: Number of HTTP2 writes offloaded to the executor from application threads
+- counter: http2_writes_continued
+  doc: Number of HTTP2 writes that finished seeing more data needed to be
+       written
+- counter: http2_partial_writes
+  doc: Number of HTTP2 writes that were made knowing there was still more data
+       to be written (we cap maximum write size to syscall_write)
+- counter: http2_initiate_write_due_to_initial_write
+  doc: Number of HTTP2 writes initiated due to 'initial_write'
+- counter: http2_initiate_write_due_to_start_new_stream
+  doc: Number of HTTP2 writes initiated due to 'start_new_stream'
+- counter: http2_initiate_write_due_to_send_message
+  doc: Number of HTTP2 writes initiated due to 'send_message'
+- counter: http2_initiate_write_due_to_send_initial_metadata
+  doc: Number of HTTP2 writes initiated due to 'send_initial_metadata'
+- counter: http2_initiate_write_due_to_send_trailing_metadata
+  doc: Number of HTTP2 writes initiated due to 'send_trailing_metadata'
+- counter: http2_initiate_write_due_to_retry_send_ping
+  doc: Number of HTTP2 writes initiated due to 'retry_send_ping'
+- counter: http2_initiate_write_due_to_continue_pings
+  doc: Number of HTTP2 writes initiated due to 'continue_pings'
+- counter: http2_initiate_write_due_to_goaway_sent
+  doc: Number of HTTP2 writes initiated due to 'goaway_sent'
+- counter: http2_initiate_write_due_to_rst_stream
+  doc: Number of HTTP2 writes initiated due to 'rst_stream'
+- counter: http2_initiate_write_due_to_close_from_api
+  doc: Number of HTTP2 writes initiated due to 'close_from_api'
+- counter: http2_initiate_write_due_to_stream_flow_control
+  doc: Number of HTTP2 writes initiated due to 'stream_flow_control'
+- counter: http2_initiate_write_due_to_transport_flow_control
+  doc: Number of HTTP2 writes initiated due to 'transport_flow_control'
+- counter: http2_initiate_write_due_to_send_settings
+  doc: Number of HTTP2 writes initiated due to 'send_settings'
+- counter: http2_initiate_write_due_to_bdp_estimator_ping
+  doc: Number of HTTP2 writes initiated due to 'bdp_estimator_ping'
+- counter: http2_initiate_write_due_to_flow_control_unstalled_by_setting
+  doc: Number of HTTP2 writes initiated due to 'flow_control_unstalled_by_setting'
+- counter: http2_initiate_write_due_to_flow_control_unstalled_by_update
+  doc: Number of HTTP2 writes initiated due to 'flow_control_unstalled_by_update'
+- counter: http2_initiate_write_due_to_application_ping
+  doc: Number of HTTP2 writes initiated due to 'application_ping'
+- counter: http2_initiate_write_due_to_keepalive_ping
+  doc: Number of HTTP2 writes initiated due to 'keepalive_ping'
+- counter: http2_initiate_write_due_to_transport_flow_control_unstalled
+  doc: Number of HTTP2 writes initiated due to 'transport_flow_control_unstalled'
+- counter: http2_initiate_write_due_to_ping_response
+  doc: Number of HTTP2 writes initiated due to 'ping_response'
+- counter: http2_initiate_write_due_to_force_rst_stream
+  doc: Number of HTTP2 writes initiated due to 'force_rst_stream'
+- counter: http2_spurious_writes_begun
+  doc: Number of HTTP2 writes initiated with nothing to write
+- counter: hpack_recv_indexed
+  doc: Number of HPACK indexed fields received
+- counter: hpack_recv_lithdr_incidx
+  doc: Number of HPACK literal headers received with incremental indexing
+- counter: hpack_recv_lithdr_incidx_v
+  doc: Number of HPACK literal headers received with incremental indexing and literal keys
+- counter: hpack_recv_lithdr_notidx
+  doc: Number of HPACK literal headers received with no indexing
+- counter: hpack_recv_lithdr_notidx_v
+  doc: Number of HPACK literal headers received with no indexing and literal keys
+- counter: hpack_recv_lithdr_nvridx
+  doc: Number of HPACK literal headers received with never-indexing
+- counter: hpack_recv_lithdr_nvridx_v
+  doc: Number of HPACK literal headers received with never-indexing and literal keys
+- counter: hpack_recv_uncompressed
+  doc: Number of uncompressed strings received in metadata
+- counter: hpack_recv_huffman
+  doc: Number of huffman encoded strings received in metadata
+- counter: hpack_recv_binary
+  doc: Number of binary strings received in metadata
+- counter: hpack_recv_binary_base64
+  doc: Number of binary strings received encoded in base64 in metadata
+- counter: hpack_send_indexed
+  doc: Number of HPACK indexed fields sent
+- counter: hpack_send_lithdr_incidx
+  doc: Number of HPACK literal headers sent with incremental indexing
+- counter: hpack_send_lithdr_incidx_v
+  doc: Number of HPACK literal headers sent with incremental indexing and literal keys
+- counter: hpack_send_lithdr_notidx
+  doc: Number of HPACK literal headers sent with no indexing
+- counter: hpack_send_lithdr_notidx_v
+  doc: Number of HPACK literal headers sent with no indexing and literal keys
+- counter: hpack_send_lithdr_nvridx
+  doc: Number of HPACK literal headers sent with never-indexing
+- counter: hpack_send_lithdr_nvridx_v
+  doc: Number of HPACK literal headers sent with never-indexing and literal keys
+- counter: hpack_send_uncompressed
+  doc: Number of uncompressed strings sent in metadata
+- counter: hpack_send_huffman
+  doc: Number of huffman encoded strings sent in metadata
+- counter: hpack_send_binary
+  doc: Number of binary strings received in metadata
+- counter: hpack_send_binary_base64
+  doc: Number of binary strings received encoded in base64 in metadata
+# combiner locks
+- counter: combiner_locks_initiated
+  doc: Number of combiner lock entries by process
+       (first items queued to a combiner)
+- counter: combiner_locks_scheduled_items
+  doc: Number of items scheduled against combiner locks
+- counter: combiner_locks_scheduled_final_items
+  doc: Number of final items scheduled against combiner locks
+- counter: combiner_locks_offloaded
+  doc: Number of combiner locks offloaded to different threads
+# call combiner locks
+- counter: call_combiner_locks_initiated
+  doc: Number of call combiner lock entries by process
+       (first items queued to a call combiner)
+- counter: call_combiner_locks_scheduled_items
+  doc: Number of items scheduled against call combiner locks
+- counter: call_combiner_set_notify_on_cancel
+  doc: Number of times a cancellation callback was set on a call combiner
+- counter: call_combiner_cancelled
+  doc: Number of times a call combiner was cancelled
+# executor
+- counter: executor_scheduled_short_items
+  doc: Number of finite runtime closures scheduled against the executor
+       (gRPC thread pool)
+- counter: executor_scheduled_long_items
+  doc: Number of potentially infinite runtime closures scheduled against the
+       executor (gRPC thread pool)
+- counter: executor_scheduled_to_self
+  doc: Number of closures scheduled by the executor to the executor
+- counter: executor_wakeup_initiated
+  doc: Number of thread wakeups initiated within the executor
+- counter: executor_queue_drained
+  doc: Number of times an executor queue was drained
+- counter: executor_push_retries
+  doc: Number of times we raced and were forced to retry pushing a closure to
+       the executor
+# server
+- counter: server_requested_calls
+  doc: How many calls were requested (not necessarily received) by the server
+- histogram: server_cqs_checked
+  buckets: 8
+  max: 64
+  doc: How many completion queues were checked looking for a CQ that had
+       requested the incoming call
+- counter: server_slowpath_requests_queued
+  doc: How many times was the server slow path taken (indicates too few
+       outstanding requests)
+# cq
+- counter: cq_ev_queue_trylock_failures
+  doc: Number of lock (trylock) acquisition failures on completion queue event
+       queue. High value here indicates high contention on completion queues
+- counter: cq_ev_queue_trylock_successes
+  doc: Number of lock (trylock) acquisition successes on completion queue event
+       queue.
+- counter: cq_ev_queue_transient_pop_failures
+  doc: Number of times NULL was popped out of completion queue's event queue
+       even though the event queue was not empty
diff --git a/third_party/grpc/src/core/lib/debug/stats_data_bq_schema.sql b/third_party/grpc/src/core/lib/debug/stats_data_bq_schema.sql
new file mode 100644
index 0000000..7d1ab1d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/debug/stats_data_bq_schema.sql
@@ -0,0 +1,98 @@
+client_calls_created_per_iteration:FLOAT,
+server_calls_created_per_iteration:FLOAT,
+cqs_created_per_iteration:FLOAT,
+client_channels_created_per_iteration:FLOAT,
+client_subchannels_created_per_iteration:FLOAT,
+server_channels_created_per_iteration:FLOAT,
+syscall_poll_per_iteration:FLOAT,
+syscall_wait_per_iteration:FLOAT,
+pollset_kick_per_iteration:FLOAT,
+pollset_kicked_without_poller_per_iteration:FLOAT,
+pollset_kicked_again_per_iteration:FLOAT,
+pollset_kick_wakeup_fd_per_iteration:FLOAT,
+pollset_kick_wakeup_cv_per_iteration:FLOAT,
+pollset_kick_own_thread_per_iteration:FLOAT,
+syscall_epoll_ctl_per_iteration:FLOAT,
+pollset_fd_cache_hits_per_iteration:FLOAT,
+histogram_slow_lookups_per_iteration:FLOAT,
+syscall_write_per_iteration:FLOAT,
+syscall_read_per_iteration:FLOAT,
+tcp_backup_pollers_created_per_iteration:FLOAT,
+tcp_backup_poller_polls_per_iteration:FLOAT,
+http2_op_batches_per_iteration:FLOAT,
+http2_op_cancel_per_iteration:FLOAT,
+http2_op_send_initial_metadata_per_iteration:FLOAT,
+http2_op_send_message_per_iteration:FLOAT,
+http2_op_send_trailing_metadata_per_iteration:FLOAT,
+http2_op_recv_initial_metadata_per_iteration:FLOAT,
+http2_op_recv_message_per_iteration:FLOAT,
+http2_op_recv_trailing_metadata_per_iteration:FLOAT,
+http2_settings_writes_per_iteration:FLOAT,
+http2_pings_sent_per_iteration:FLOAT,
+http2_writes_begun_per_iteration:FLOAT,
+http2_writes_offloaded_per_iteration:FLOAT,
+http2_writes_continued_per_iteration:FLOAT,
+http2_partial_writes_per_iteration:FLOAT,
+http2_initiate_write_due_to_initial_write_per_iteration:FLOAT,
+http2_initiate_write_due_to_start_new_stream_per_iteration:FLOAT,
+http2_initiate_write_due_to_send_message_per_iteration:FLOAT,
+http2_initiate_write_due_to_send_initial_metadata_per_iteration:FLOAT,
+http2_initiate_write_due_to_send_trailing_metadata_per_iteration:FLOAT,
+http2_initiate_write_due_to_retry_send_ping_per_iteration:FLOAT,
+http2_initiate_write_due_to_continue_pings_per_iteration:FLOAT,
+http2_initiate_write_due_to_goaway_sent_per_iteration:FLOAT,
+http2_initiate_write_due_to_rst_stream_per_iteration:FLOAT,
+http2_initiate_write_due_to_close_from_api_per_iteration:FLOAT,
+http2_initiate_write_due_to_stream_flow_control_per_iteration:FLOAT,
+http2_initiate_write_due_to_transport_flow_control_per_iteration:FLOAT,
+http2_initiate_write_due_to_send_settings_per_iteration:FLOAT,
+http2_initiate_write_due_to_bdp_estimator_ping_per_iteration:FLOAT,
+http2_initiate_write_due_to_flow_control_unstalled_by_setting_per_iteration:FLOAT,
+http2_initiate_write_due_to_flow_control_unstalled_by_update_per_iteration:FLOAT,
+http2_initiate_write_due_to_application_ping_per_iteration:FLOAT,
+http2_initiate_write_due_to_keepalive_ping_per_iteration:FLOAT,
+http2_initiate_write_due_to_transport_flow_control_unstalled_per_iteration:FLOAT,
+http2_initiate_write_due_to_ping_response_per_iteration:FLOAT,
+http2_initiate_write_due_to_force_rst_stream_per_iteration:FLOAT,
+http2_spurious_writes_begun_per_iteration:FLOAT,
+hpack_recv_indexed_per_iteration:FLOAT,
+hpack_recv_lithdr_incidx_per_iteration:FLOAT,
+hpack_recv_lithdr_incidx_v_per_iteration:FLOAT,
+hpack_recv_lithdr_notidx_per_iteration:FLOAT,
+hpack_recv_lithdr_notidx_v_per_iteration:FLOAT,
+hpack_recv_lithdr_nvridx_per_iteration:FLOAT,
+hpack_recv_lithdr_nvridx_v_per_iteration:FLOAT,
+hpack_recv_uncompressed_per_iteration:FLOAT,
+hpack_recv_huffman_per_iteration:FLOAT,
+hpack_recv_binary_per_iteration:FLOAT,
+hpack_recv_binary_base64_per_iteration:FLOAT,
+hpack_send_indexed_per_iteration:FLOAT,
+hpack_send_lithdr_incidx_per_iteration:FLOAT,
+hpack_send_lithdr_incidx_v_per_iteration:FLOAT,
+hpack_send_lithdr_notidx_per_iteration:FLOAT,
+hpack_send_lithdr_notidx_v_per_iteration:FLOAT,
+hpack_send_lithdr_nvridx_per_iteration:FLOAT,
+hpack_send_lithdr_nvridx_v_per_iteration:FLOAT,
+hpack_send_uncompressed_per_iteration:FLOAT,
+hpack_send_huffman_per_iteration:FLOAT,
+hpack_send_binary_per_iteration:FLOAT,
+hpack_send_binary_base64_per_iteration:FLOAT,
+combiner_locks_initiated_per_iteration:FLOAT,
+combiner_locks_scheduled_items_per_iteration:FLOAT,
+combiner_locks_scheduled_final_items_per_iteration:FLOAT,
+combiner_locks_offloaded_per_iteration:FLOAT,
+call_combiner_locks_initiated_per_iteration:FLOAT,
+call_combiner_locks_scheduled_items_per_iteration:FLOAT,
+call_combiner_set_notify_on_cancel_per_iteration:FLOAT,
+call_combiner_cancelled_per_iteration:FLOAT,
+executor_scheduled_short_items_per_iteration:FLOAT,
+executor_scheduled_long_items_per_iteration:FLOAT,
+executor_scheduled_to_self_per_iteration:FLOAT,
+executor_wakeup_initiated_per_iteration:FLOAT,
+executor_queue_drained_per_iteration:FLOAT,
+executor_push_retries_per_iteration:FLOAT,
+server_requested_calls_per_iteration:FLOAT,
+server_slowpath_requests_queued_per_iteration:FLOAT,
+cq_ev_queue_trylock_failures_per_iteration:FLOAT,
+cq_ev_queue_trylock_successes_per_iteration:FLOAT,
+cq_ev_queue_transient_pop_failures_per_iteration:FLOAT
diff --git a/third_party/grpc/src/core/lib/debug/trace.cc b/third_party/grpc/src/core/lib/debug/trace.cc
new file mode 100644
index 0000000..cafdb15
--- /dev/null
+++ b/third_party/grpc/src/core/lib/debug/trace.cc
@@ -0,0 +1,148 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/debug/trace.h"
+
+#include <string.h>
+#include <type_traits>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/gpr/env.h"
+
+int grpc_tracer_set_enabled(const char* name, int enabled);
+
+namespace grpc_core {
+
+TraceFlag* TraceFlagList::root_tracer_ = nullptr;
+
+bool TraceFlagList::Set(const char* name, bool enabled) {
+  TraceFlag* t;
+  if (0 == strcmp(name, "all")) {
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      t->set_enabled(enabled);
+    }
+  } else if (0 == strcmp(name, "list_tracers")) {
+    LogAllTracers();
+  } else if (0 == strcmp(name, "refcount")) {
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      if (strstr(t->name_, "refcount") != nullptr) {
+        t->set_enabled(enabled);
+      }
+    }
+  } else {
+    bool found = false;
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      if (0 == strcmp(name, t->name_)) {
+        t->set_enabled(enabled);
+        found = true;
+      }
+    }
+    // check for unknowns, but ignore "", to allow to GRPC_TRACE=
+    if (!found && 0 != strcmp(name, "")) {
+      gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name);
+      return false; /* early return */
+    }
+  }
+  return true;
+}
+
+void TraceFlagList::Add(TraceFlag* flag) {
+  flag->next_tracer_ = root_tracer_;
+  root_tracer_ = flag;
+}
+
+void TraceFlagList::LogAllTracers() {
+  gpr_log(GPR_DEBUG, "available tracers:");
+  TraceFlag* t;
+  for (t = root_tracer_; t != nullptr; t = t->next_tracer_) {
+    gpr_log(GPR_DEBUG, "\t%s", t->name_);
+  }
+}
+
+// Flags register themselves on the list during construction
+TraceFlag::TraceFlag(bool default_enabled, const char* name) : name_(name) {
+  static_assert(std::is_trivially_destructible<TraceFlag>::value,
+                "TraceFlag needs to be trivially destructible.");
+  set_enabled(default_enabled);
+  TraceFlagList::Add(this);
+}
+
+}  // namespace grpc_core
+
+static void add(const char* beg, const char* end, char*** ss, size_t* ns) {
+  size_t n = *ns;
+  size_t np = n + 1;
+  char* s;
+  size_t len;
+  GPR_ASSERT(end >= beg);
+  len = static_cast<size_t>(end - beg);
+  s = static_cast<char*>(gpr_malloc(len + 1));
+  memcpy(s, beg, len);
+  s[len] = 0;
+  *ss = static_cast<char**>(gpr_realloc(*ss, sizeof(char**) * np));
+  (*ss)[n] = s;
+  *ns = np;
+}
+
+static void split(const char* s, char*** ss, size_t* ns) {
+  const char* c = strchr(s, ',');
+  if (c == nullptr) {
+    add(s, s + strlen(s), ss, ns);
+  } else {
+    add(s, c, ss, ns);
+    split(c + 1, ss, ns);
+  }
+}
+
+static void parse(const char* s) {
+  char** strings = nullptr;
+  size_t nstrings = 0;
+  size_t i;
+  split(s, &strings, &nstrings);
+
+  for (i = 0; i < nstrings; i++) {
+    if (strings[i][0] == '-') {
+      grpc_core::TraceFlagList::Set(strings[i] + 1, false);
+    } else {
+      grpc_core::TraceFlagList::Set(strings[i], true);
+    }
+  }
+
+  for (i = 0; i < nstrings; i++) {
+    gpr_free(strings[i]);
+  }
+  gpr_free(strings);
+}
+
+void grpc_tracer_init(const char* env_var) {
+  char* e = gpr_getenv(env_var);
+  if (e != nullptr) {
+    parse(e);
+    gpr_free(e);
+  }
+}
+
+void grpc_tracer_shutdown(void) {}
+
+int grpc_tracer_set_enabled(const char* name, int enabled) {
+  return grpc_core::TraceFlagList::Set(name, enabled != 0);
+}
diff --git a/third_party/grpc/src/core/lib/debug/trace.h b/third_party/grpc/src/core/lib/debug/trace.h
new file mode 100644
index 0000000..4623494
--- /dev/null
+++ b/third_party/grpc/src/core/lib/debug/trace.h
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_DEBUG_TRACE_H
+#define GRPC_CORE_LIB_DEBUG_TRACE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+#include <stdbool.h>
+
+void grpc_tracer_init(const char* env_var_name);
+void grpc_tracer_shutdown(void);
+
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#define GRPC_THREADSAFE_TRACER
+#endif
+#endif
+
+namespace grpc_core {
+
+class TraceFlag;
+class TraceFlagList {
+ public:
+  static bool Set(const char* name, bool enabled);
+  static void Add(TraceFlag* flag);
+
+ private:
+  static void LogAllTracers();
+  static TraceFlag* root_tracer_;
+};
+
+namespace testing {
+void grpc_tracer_enable_flag(grpc_core::TraceFlag* flag);
+}
+
+class TraceFlag {
+ public:
+  TraceFlag(bool default_enabled, const char* name);
+  // This needs to be trivially destructible as it is used as global variable.
+  ~TraceFlag() = default;
+
+  const char* name() const { return name_; }
+
+// Use the symbol GRPC_USE_TRACERS to determine if tracers will be enabled in
+// opt builds (tracers are always on in dbg builds). The default in OSS is for
+// tracers to be on since we support binary distributions of gRPC for the
+// wrapped language (wr don't want to force recompilation to get tracing).
+// Internally, however, for performance reasons, we compile them out by
+// default, since internal build systems make recompiling trivial.
+#define GRPC_USE_TRACERS  // tracers on by default in OSS
+#if defined(GRPC_USE_TRACERS) || !defined(NDEBUG)
+  bool enabled() {
+#ifdef GRPC_THREADSAFE_TRACER
+    return gpr_atm_no_barrier_load(&value_) != 0;
+#else
+    return value_;
+#endif  // GRPC_THREADSAFE_TRACER
+  }
+#else
+  bool enabled() { return false; }
+#endif /* defined(GRPC_USE_TRACERS) || !defined(NDEBUG) */
+
+ private:
+  friend void grpc_core::testing::grpc_tracer_enable_flag(TraceFlag* flag);
+  friend class TraceFlagList;
+
+  void set_enabled(bool enabled) {
+#ifdef GRPC_THREADSAFE_TRACER
+    gpr_atm_no_barrier_store(&value_, enabled);
+#else
+    value_ = enabled;
+#endif
+  }
+
+  TraceFlag* next_tracer_;
+  const char* const name_;
+#ifdef GRPC_THREADSAFE_TRACER
+  gpr_atm value_;
+#else
+  bool value_;
+#endif
+};
+
+#ifndef NDEBUG
+typedef TraceFlag DebugOnlyTraceFlag;
+#else
+class DebugOnlyTraceFlag {
+ public:
+  constexpr DebugOnlyTraceFlag(bool default_enabled, const char* name) {}
+  constexpr bool enabled() const { return false; }
+  constexpr const char* name() const { return "DebugOnlyTraceFlag"; }
+
+ private:
+  void set_enabled(bool enabled) {}
+};
+#endif
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_DEBUG_TRACE_H */
diff --git a/third_party/grpc/src/core/lib/gpr/README.md b/third_party/grpc/src/core/lib/gpr/README.md
new file mode 100644
index 0000000..21fb0c7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/README.md
@@ -0,0 +1,8 @@
+# GPR - Google Portable Runtime for C
+
+The files in this directory contain basic utility code and platform
+abstractions for C code.  None of this code is gRPC-specific; anything
+here may also be useful for other open source projects written in C.
+
+Note that this is one of the few places in src/core where we allow
+the use of portability macros.
diff --git a/third_party/grpc/src/core/lib/gpr/alloc.cc b/third_party/grpc/src/core/lib/gpr/alloc.cc
new file mode 100644
index 0000000..611e4cc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/alloc.cc
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+
+#include <grpc/support/log.h>
+#include <stdlib.h>
+#include <string.h>
+#include "src/core/lib/profiling/timers.h"
+
+static void* zalloc_with_calloc(size_t sz) { return calloc(sz, 1); }
+
+static void* zalloc_with_gpr_malloc(size_t sz) {
+  void* p = gpr_malloc(sz);
+  memset(p, 0, sz);
+  return p;
+}
+
+static gpr_allocation_functions g_alloc_functions = {malloc, zalloc_with_calloc,
+                                                     realloc, free};
+
+gpr_allocation_functions gpr_get_allocation_functions() {
+  return g_alloc_functions;
+}
+
+void gpr_set_allocation_functions(gpr_allocation_functions functions) {
+  GPR_ASSERT(functions.malloc_fn != nullptr);
+  GPR_ASSERT(functions.realloc_fn != nullptr);
+  GPR_ASSERT(functions.free_fn != nullptr);
+  if (functions.zalloc_fn == nullptr) {
+    functions.zalloc_fn = zalloc_with_gpr_malloc;
+  }
+  g_alloc_functions = functions;
+}
+
+void* gpr_malloc(size_t size) {
+  GPR_TIMER_SCOPE("gpr_malloc", 0);
+  void* p;
+  if (size == 0) return nullptr;
+  p = g_alloc_functions.malloc_fn(size);
+  if (!p) {
+    abort();
+  }
+  return p;
+}
+
+void* gpr_zalloc(size_t size) {
+  GPR_TIMER_SCOPE("gpr_zalloc", 0);
+  void* p;
+  if (size == 0) return nullptr;
+  p = g_alloc_functions.zalloc_fn(size);
+  if (!p) {
+    abort();
+  }
+  return p;
+}
+
+void gpr_free(void* p) {
+  GPR_TIMER_SCOPE("gpr_free", 0);
+  g_alloc_functions.free_fn(p);
+}
+
+void* gpr_realloc(void* p, size_t size) {
+  GPR_TIMER_SCOPE("gpr_realloc", 0);
+  if ((size == 0) && (p == nullptr)) return nullptr;
+  p = g_alloc_functions.realloc_fn(p, size);
+  if (!p) {
+    abort();
+  }
+  return p;
+}
+
+void* gpr_malloc_aligned(size_t size, size_t alignment) {
+  GPR_ASSERT(((alignment - 1) & alignment) == 0);  // Must be power of 2.
+  size_t extra = alignment - 1 + sizeof(void*);
+  void* p = gpr_malloc(size + extra);
+  void** ret = (void**)(((uintptr_t)p + extra) & ~(alignment - 1));
+  ret[-1] = p;
+  return (void*)ret;
+}
+
+void gpr_free_aligned(void* ptr) { gpr_free((static_cast<void**>(ptr))[-1]); }
diff --git a/third_party/grpc/src/core/lib/gpr/alloc.h b/third_party/grpc/src/core/lib/gpr/alloc.h
new file mode 100644
index 0000000..762b51b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/alloc.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_ALLOC_H
+#define GRPC_CORE_LIB_GPR_ALLOC_H
+
+#include <grpc/support/port_platform.h>
+
+/// Given a size, round up to the next multiple of sizeof(void*).
+#define GPR_ROUND_UP_TO_ALIGNMENT_SIZE(x) \
+  (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u))
+
+#endif /* GRPC_CORE_LIB_GPR_ALLOC_H */
diff --git a/third_party/grpc/src/core/lib/gpr/arena.cc b/third_party/grpc/src/core/lib/gpr/arena.cc
new file mode 100644
index 0000000..836a7ca
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/arena.cc
@@ -0,0 +1,192 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/gpr/arena.h"
+
+#include <string.h>
+#include <new>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gpr/alloc.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace {
+enum init_strategy {
+  NO_INIT,        // Do not initialize the arena blocks.
+  ZERO_INIT,      // Initialize arena blocks with 0.
+  NON_ZERO_INIT,  // Initialize arena blocks with a non-zero value.
+};
+
+gpr_once g_init_strategy_once = GPR_ONCE_INIT;
+init_strategy g_init_strategy = NO_INIT;
+}  // namespace
+
+static void set_strategy_from_env() {
+  char* str = gpr_getenv("GRPC_ARENA_INIT_STRATEGY");
+  if (str == nullptr) {
+    g_init_strategy = NO_INIT;
+  } else if (strcmp(str, "zero_init") == 0) {
+    g_init_strategy = ZERO_INIT;
+  } else if (strcmp(str, "non_zero_init") == 0) {
+    g_init_strategy = NON_ZERO_INIT;
+  } else {
+    g_init_strategy = NO_INIT;
+  }
+  gpr_free(str);
+}
+
+static void* gpr_arena_alloc_maybe_init(size_t size) {
+  void* mem = gpr_malloc_aligned(size, GPR_MAX_ALIGNMENT);
+  gpr_once_init(&g_init_strategy_once, set_strategy_from_env);
+  if (GPR_UNLIKELY(g_init_strategy != NO_INIT)) {
+    if (g_init_strategy == ZERO_INIT) {
+      memset(mem, 0, size);
+    } else {  // NON_ZERO_INIT.
+      memset(mem, 0xFE, size);
+    }
+  }
+  return mem;
+}
+
+void gpr_arena_init() {
+  gpr_once_init(&g_init_strategy_once, set_strategy_from_env);
+}
+
+// Uncomment this to use a simple arena that simply allocates the
+// requested amount of memory for each call to gpr_arena_alloc().  This
+// effectively eliminates the efficiency gain of using an arena, but it
+// may be useful for debugging purposes.
+//#define SIMPLE_ARENA_FOR_DEBUGGING
+#ifdef SIMPLE_ARENA_FOR_DEBUGGING
+
+struct gpr_arena {
+  gpr_arena() { gpr_mu_init(&mu); }
+  ~gpr_arena() {
+    gpr_mu_destroy(&mu);
+    for (size_t i = 0; i < num_ptrs; ++i) {
+      gpr_free_aligned(ptrs[i]);
+    }
+    gpr_free(ptrs);
+  }
+
+  gpr_mu mu;
+  void** ptrs = nullptr;
+  size_t num_ptrs = 0;
+};
+
+gpr_arena* gpr_arena_create(size_t ignored_initial_size) {
+  return grpc_core::New<gpr_arena>();
+}
+
+size_t gpr_arena_destroy(gpr_arena* arena) {
+  grpc_core::Delete(arena);
+  return 1;  // Value doesn't matter, since it won't be used.
+}
+
+void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
+  gpr_mu_lock(&arena->mu);
+  arena->ptrs =
+      (void**)gpr_realloc(arena->ptrs, sizeof(void*) * (arena->num_ptrs + 1));
+  void* retval = arena->ptrs[arena->num_ptrs++] =
+      gpr_arena_alloc_maybe_init(size);
+  gpr_mu_unlock(&arena->mu);
+  return retval;
+}
+
+#else  // SIMPLE_ARENA_FOR_DEBUGGING
+
+// TODO(roth): We currently assume that all callers need alignment of 16
+// bytes, which may be wrong in some cases.  As part of converting the
+// arena API to C++, we should consider replacing gpr_arena_alloc() with a
+// template that takes the type of the value being allocated, which
+// would allow us to use the alignment actually needed by the caller.
+
+typedef struct zone {
+  zone* next = nullptr;
+} zone;
+
+struct gpr_arena {
+  gpr_arena(size_t initial_size)
+      : initial_zone_size(initial_size), last_zone(&initial_zone) {
+    gpr_mu_init(&arena_growth_mutex);
+  }
+  ~gpr_arena() {
+    gpr_mu_destroy(&arena_growth_mutex);
+    zone* z = initial_zone.next;
+    while (z) {
+      zone* next_z = z->next;
+      z->~zone();
+      gpr_free_aligned(z);
+      z = next_z;
+    }
+  }
+
+  // Keep track of the total used size. We use this in our call sizing
+  // historesis.
+  gpr_atm total_used = 0;
+  size_t initial_zone_size;
+  zone initial_zone;
+  zone* last_zone;
+  gpr_mu arena_growth_mutex;
+};
+
+gpr_arena* gpr_arena_create(size_t initial_size) {
+  initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size);
+  return new (gpr_arena_alloc_maybe_init(
+      GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + initial_size))
+      gpr_arena(initial_size);
+}
+
+size_t gpr_arena_destroy(gpr_arena* arena) {
+  const gpr_atm size = gpr_atm_no_barrier_load(&arena->total_used);
+  arena->~gpr_arena();
+  gpr_free_aligned(arena);
+  return static_cast<size_t>(size);
+}
+
+void* gpr_arena_alloc(gpr_arena* arena, size_t size) {
+  size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(size);
+  size_t begin = gpr_atm_no_barrier_fetch_add(&arena->total_used, size);
+  if (begin + size <= arena->initial_zone_size) {
+    return reinterpret_cast<char*>(arena) +
+           GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + begin;
+  } else {
+    // If the allocation isn't able to end in the initial zone, create a new
+    // zone for this allocation, and any unused space in the initial zone is
+    // wasted. This overflowing and wasting is uncommon because of our arena
+    // sizing historesis (that is, most calls should have a large enough initial
+    // zone and will not need to grow the arena).
+    gpr_mu_lock(&arena->arena_growth_mutex);
+    zone* z = new (gpr_arena_alloc_maybe_init(
+        GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) + size)) zone();
+    arena->last_zone->next = z;
+    arena->last_zone = z;
+    gpr_mu_unlock(&arena->arena_growth_mutex);
+    return reinterpret_cast<char*>(z) +
+           GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone));
+  }
+}
+
+#endif  // SIMPLE_ARENA_FOR_DEBUGGING
diff --git a/third_party/grpc/src/core/lib/gpr/arena.h b/third_party/grpc/src/core/lib/gpr/arena.h
new file mode 100644
index 0000000..069892b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/arena.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// \file Arena based allocator
+// Allows very fast allocation of memory, but that memory cannot be freed until
+// the arena as a whole is freed
+// Tracks the total memory allocated against it, so that future arenas can
+// pre-allocate the right amount of memory
+
+#ifndef GRPC_CORE_LIB_GPR_ARENA_H
+#define GRPC_CORE_LIB_GPR_ARENA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+typedef struct gpr_arena gpr_arena;
+
+// Create an arena, with \a initial_size bytes in the first allocated buffer
+gpr_arena* gpr_arena_create(size_t initial_size);
+// Allocate \a size bytes from the arena
+void* gpr_arena_alloc(gpr_arena* arena, size_t size);
+// Destroy an arena, returning the total number of bytes allocated
+size_t gpr_arena_destroy(gpr_arena* arena);
+// Initializes the Arena component.
+void gpr_arena_init();
+
+#endif /* GRPC_CORE_LIB_GPR_ARENA_H */
diff --git a/third_party/grpc/src/core/lib/gpr/atm.cc b/third_party/grpc/src/core/lib/gpr/atm.cc
new file mode 100644
index 0000000..649d400
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/atm.cc
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+gpr_atm gpr_atm_no_barrier_clamped_add(gpr_atm* value, gpr_atm delta,
+                                       gpr_atm min, gpr_atm max) {
+  gpr_atm current_value;
+  gpr_atm new_value;
+  do {
+    current_value = gpr_atm_no_barrier_load(value);
+    new_value = GPR_CLAMP(current_value + delta, min, max);
+    if (new_value == current_value) break;
+  } while (!gpr_atm_no_barrier_cas(value, current_value, new_value));
+  return new_value;
+}
diff --git a/third_party/grpc/src/core/lib/gpr/cpu_iphone.cc b/third_party/grpc/src/core/lib/gpr/cpu_iphone.cc
new file mode 100644
index 0000000..2847e03
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/cpu_iphone.cc
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/cpu.h>
+
+#ifdef GPR_CPU_IPHONE
+
+/* Probably 2 instead of 1, but see comment on gpr_cpu_current_cpu. */
+unsigned gpr_cpu_num_cores(void) { return 1; }
+
+/* Most code that's using this is using it to shard across work queues. So
+   unless profiling shows it's a problem or there appears a way to detect the
+   currently running CPU core, let's have it shard the default way.
+   Note that the interface in cpu.h lets gpr_cpu_num_cores return 0, but doing
+   it makes it impossible for gpr_cpu_current_cpu to satisfy its stated range,
+   and some code might be relying on it. */
+unsigned gpr_cpu_current_cpu(void) { return 0; }
+
+#endif /* GPR_CPU_IPHONE */
diff --git a/third_party/grpc/src/core/lib/gpr/cpu_linux.cc b/third_party/grpc/src/core/lib/gpr/cpu_linux.cc
new file mode 100644
index 0000000..9fc2f0b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/cpu_linux.cc
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif /* _GNU_SOURCE */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_CPU_LINUX
+
+#include <errno.h>
+#include <sched.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+static int ncpus = 0;
+
+static void init_num_cpus() {
+#ifndef GPR_MUSL_LIBC_COMPAT
+  if (sched_getcpu() < 0) {
+    gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno));
+    ncpus = 1;
+    return;
+  }
+#endif
+  /* This must be signed. sysconf returns -1 when the number cannot be
+     determined */
+  ncpus = static_cast<int>(sysconf(_SC_NPROCESSORS_CONF));
+  if (ncpus < 1) {
+    gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1");
+    ncpus = 1;
+  }
+}
+
+unsigned gpr_cpu_num_cores(void) {
+  static gpr_once once = GPR_ONCE_INIT;
+  gpr_once_init(&once, init_num_cpus);
+  return static_cast<unsigned>(ncpus);
+}
+
+unsigned gpr_cpu_current_cpu(void) {
+#ifdef GPR_MUSL_LIBC_COMPAT
+  // sched_getcpu() is undefined on musl
+  return 0;
+#else
+  if (gpr_cpu_num_cores() == 1) {
+    return 0;
+  }
+  int cpu = sched_getcpu();
+  if (cpu < 0) {
+    gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno));
+    return 0;
+  }
+  if (static_cast<unsigned>(cpu) >= gpr_cpu_num_cores()) {
+    gpr_log(GPR_ERROR, "Cannot handle hot-plugged CPUs");
+    return 0;
+  }
+  return static_cast<unsigned>(cpu);
+#endif
+}
+
+#endif /* GPR_CPU_LINUX */
diff --git a/third_party/grpc/src/core/lib/gpr/cpu_posix.cc b/third_party/grpc/src/core/lib/gpr/cpu_posix.cc
new file mode 100644
index 0000000..915fd49
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/cpu_posix.cc
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#if defined(GPR_CPU_POSIX)
+
+#include <errno.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+static long ncpus = 0;
+
+static pthread_key_t thread_id_key;
+
+static void init_ncpus() {
+  ncpus = sysconf(_SC_NPROCESSORS_CONF);
+  if (ncpus < 1 || ncpus > INT32_MAX) {
+    gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1");
+    ncpus = 1;
+  }
+}
+
+unsigned gpr_cpu_num_cores(void) {
+  static gpr_once once = GPR_ONCE_INIT;
+  gpr_once_init(&once, init_ncpus);
+  return (unsigned)ncpus;
+}
+
+static void delete_thread_id(void* value) {
+  if (value) {
+    gpr_free(value);
+  }
+}
+
+static void init_thread_id_key(void) {
+  pthread_key_create(&thread_id_key, delete_thread_id);
+}
+
+unsigned gpr_cpu_current_cpu(void) {
+  /* NOTE: there's no way I know to return the actual cpu index portably...
+     most code that's using this is using it to shard across work queues though,
+     so here we use thread identity instead to achieve a similar though not
+     identical effect */
+  static gpr_once once = GPR_ONCE_INIT;
+  gpr_once_init(&once, init_thread_id_key);
+
+  unsigned int* thread_id =
+      static_cast<unsigned int*>(pthread_getspecific(thread_id_key));
+  if (thread_id == nullptr) {
+    thread_id = static_cast<unsigned int*>(gpr_malloc(sizeof(unsigned int)));
+    pthread_setspecific(thread_id_key, thread_id);
+  }
+
+  return (unsigned)GPR_HASH_POINTER(thread_id, gpr_cpu_num_cores());
+}
+
+#endif /* GPR_CPU_POSIX */
diff --git a/third_party/grpc/src/core/lib/gpr/cpu_windows.cc b/third_party/grpc/src/core/lib/gpr/cpu_windows.cc
new file mode 100644
index 0000000..8d89453
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/cpu_windows.cc
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+
+unsigned gpr_cpu_num_cores(void) {
+  SYSTEM_INFO si;
+  GetSystemInfo(&si);
+  return si.dwNumberOfProcessors;
+}
+
+unsigned gpr_cpu_current_cpu(void) { return GetCurrentProcessorNumber(); }
+
+#endif /* GPR_WINDOWS */
diff --git a/third_party/grpc/src/core/lib/gpr/env.h b/third_party/grpc/src/core/lib/gpr/env.h
new file mode 100644
index 0000000..aec8a31
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/env.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_ENV_H
+#define GRPC_CORE_LIB_GPR_ENV_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdio.h>
+
+/* Env utility functions */
+
+/* Gets the environment variable value with the specified name.
+   Returns a newly allocated string. It is the responsability of the caller to
+   gpr_free the return value if not NULL (which means that the environment
+   variable exists). */
+char* gpr_getenv(const char* name);
+
+/* Sets the environment with the specified name to the specified value. */
+void gpr_setenv(const char* name, const char* value);
+
+/* This is a version of gpr_getenv that does not produce any output if it has to
+   use an insecure version of the function. It is ONLY to be used to solve the
+   problem in which we need to check an env variable to configure the verbosity
+   level of logging. So DO NOT USE THIS. */
+const char* gpr_getenv_silent(const char* name, char** dst);
+
+#endif /* GRPC_CORE_LIB_GPR_ENV_H */
diff --git a/third_party/grpc/src/core/lib/gpr/env_linux.cc b/third_party/grpc/src/core/lib/gpr/env_linux.cc
new file mode 100644
index 0000000..fadc42f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/env_linux.cc
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* for secure_getenv. */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX_ENV
+
+#include "src/core/lib/gpr/env.h"
+
+#include <dlfcn.h>
+#include <features.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+
+const char* gpr_getenv_silent(const char* name, char** dst) {
+  const char* insecure_func_used = nullptr;
+  char* result = nullptr;
+#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE)
+  typedef char* (*getenv_type)(const char*);
+  static getenv_type getenv_func = NULL;
+  /* Check to see which getenv variant is supported (go from most
+   * to least secure) */
+  const char* names[] = {"secure_getenv", "__secure_getenv", "getenv"};
+  for (size_t i = 0; getenv_func == NULL && i < GPR_ARRAY_SIZE(names); i++) {
+    getenv_func = (getenv_type)dlsym(RTLD_DEFAULT, names[i]);
+    if (getenv_func != NULL && strstr(names[i], "secure") == NULL) {
+      insecure_func_used = names[i];
+    }
+  }
+  result = getenv_func(name);
+#elif __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17)
+  result = secure_getenv(name);
+#else
+  result = getenv(name);
+  insecure_func_used = "getenv";
+#endif
+  *dst = result == nullptr ? result : gpr_strdup(result);
+  return insecure_func_used;
+}
+
+char* gpr_getenv(const char* name) {
+  char* result = nullptr;
+  const char* insecure_func_used = gpr_getenv_silent(name, &result);
+  if (insecure_func_used != nullptr) {
+    gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used",
+            insecure_func_used);
+  }
+  return result;
+}
+
+void gpr_setenv(const char* name, const char* value) {
+  int res = setenv(name, value, 1);
+  GPR_ASSERT(res == 0);
+}
+
+#endif /* GPR_LINUX_ENV */
diff --git a/third_party/grpc/src/core/lib/gpr/env_posix.cc b/third_party/grpc/src/core/lib/gpr/env_posix.cc
new file mode 100644
index 0000000..599f85a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/env_posix.cc
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_ENV
+
+#include "src/core/lib/gpr/env.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/log.h>
+
+#include <grpc/support/string_util.h>
+#include "src/core/lib/gpr/string.h"
+
+const char* gpr_getenv_silent(const char* name, char** dst) {
+  *dst = gpr_getenv(name);
+  return nullptr;
+}
+
+char* gpr_getenv(const char* name) {
+  char* result = getenv(name);
+  return result == nullptr ? result : gpr_strdup(result);
+}
+
+void gpr_setenv(const char* name, const char* value) {
+  int res = setenv(name, value, 1);
+  GPR_ASSERT(res == 0);
+}
+
+#endif /* GPR_POSIX_ENV */
diff --git a/third_party/grpc/src/core/lib/gpr/env_windows.cc b/third_party/grpc/src/core/lib/gpr/env_windows.cc
new file mode 100644
index 0000000..cf8ed60
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/env_windows.cc
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS_ENV
+
+#include <windows.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/string_windows.h"
+
+const char* gpr_getenv_silent(const char* name, char** dst) {
+  *dst = gpr_getenv(name);
+  return NULL;
+}
+
+char* gpr_getenv(const char* name) {
+  char* result = NULL;
+  DWORD size;
+  LPTSTR tresult = NULL;
+  LPTSTR tname = gpr_char_to_tchar(name);
+  DWORD ret;
+
+  ret = GetEnvironmentVariable(tname, NULL, 0);
+  if (ret == 0) {
+    gpr_free(tname);
+    return NULL;
+  }
+  size = ret * (DWORD)sizeof(TCHAR);
+  tresult = (LPTSTR)gpr_malloc(size);
+  ret = GetEnvironmentVariable(tname, tresult, size);
+  gpr_free(tname);
+  if (ret == 0) {
+    gpr_free(tresult);
+    return NULL;
+  }
+  result = gpr_tchar_to_char(tresult);
+  gpr_free(tresult);
+  return result;
+}
+
+void gpr_setenv(const char* name, const char* value) {
+  LPTSTR tname = gpr_char_to_tchar(name);
+  LPTSTR tvalue = gpr_char_to_tchar(value);
+  BOOL res = SetEnvironmentVariable(tname, tvalue);
+  gpr_free(tname);
+  gpr_free(tvalue);
+  GPR_ASSERT(res);
+}
+
+#endif /* GPR_WINDOWS_ENV */
diff --git a/third_party/grpc/src/core/lib/gpr/host_port.cc b/third_party/grpc/src/core/lib/gpr/host_port.cc
new file mode 100644
index 0000000..a34e01c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/host_port.cc
@@ -0,0 +1,98 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/gpr/host_port.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+
+int gpr_join_host_port(char** out, const char* host, int port) {
+  if (host[0] != '[' && strchr(host, ':') != nullptr) {
+    /* IPv6 literals must be enclosed in brackets. */
+    return gpr_asprintf(out, "[%s]:%d", host, port);
+  } else {
+    /* Ordinary non-bracketed host:port. */
+    return gpr_asprintf(out, "%s:%d", host, port);
+  }
+}
+
+int gpr_split_host_port(const char* name, char** host, char** port) {
+  const char* host_start;
+  size_t host_len;
+  const char* port_start;
+
+  *host = nullptr;
+  *port = nullptr;
+
+  if (name[0] == '[') {
+    /* Parse a bracketed host, typically an IPv6 literal. */
+    const char* rbracket = strchr(name, ']');
+    if (rbracket == nullptr) {
+      /* Unmatched [ */
+      return 0;
+    }
+    if (rbracket[1] == '\0') {
+      /* ]<end> */
+      port_start = nullptr;
+    } else if (rbracket[1] == ':') {
+      /* ]:<port?> */
+      port_start = rbracket + 2;
+    } else {
+      /* ]<invalid> */
+      return 0;
+    }
+    host_start = name + 1;
+    host_len = static_cast<size_t>(rbracket - host_start);
+    if (memchr(host_start, ':', host_len) == nullptr) {
+      /* Require all bracketed hosts to contain a colon, because a hostname or
+         IPv4 address should never use brackets. */
+      return 0;
+    }
+  } else {
+    const char* colon = strchr(name, ':');
+    if (colon != nullptr && strchr(colon + 1, ':') == nullptr) {
+      /* Exactly 1 colon.  Split into host:port. */
+      host_start = name;
+      host_len = static_cast<size_t>(colon - name);
+      port_start = colon + 1;
+    } else {
+      /* 0 or 2+ colons.  Bare hostname or IPv6 litearal. */
+      host_start = name;
+      host_len = strlen(name);
+      port_start = nullptr;
+    }
+  }
+
+  /* Allocate return values. */
+  *host = static_cast<char*>(gpr_malloc(host_len + 1));
+  memcpy(*host, host_start, host_len);
+  (*host)[host_len] = '\0';
+
+  if (port_start != nullptr) {
+    *port = gpr_strdup(port_start);
+  }
+
+  return 1;
+}
diff --git a/third_party/grpc/src/core/lib/gpr/host_port.h b/third_party/grpc/src/core/lib/gpr/host_port.h
new file mode 100644
index 0000000..0bf0960
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/host_port.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_HOST_PORT_H
+#define GRPC_CORE_LIB_GPR_HOST_PORT_H
+
+#include <grpc/support/port_platform.h>
+
+/** Given a host and port, creates a newly-allocated string of the form
+   "host:port" or "[ho:st]:port", depending on whether the host contains colons
+   like an IPv6 literal.  If the host is already bracketed, then additional
+   brackets will not be added.
+
+   Usage is similar to gpr_asprintf: returns the number of bytes written
+   (excluding the final '\0'), and *out points to a string which must later be
+   destroyed using gpr_free().
+
+   In the unlikely event of an error, returns -1 and sets *out to NULL. */
+int gpr_join_host_port(char** out, const char* host, int port);
+
+/** Given a name in the form "host:port" or "[ho:st]:port", split into hostname
+   and port number, into newly allocated strings, which must later be
+   destroyed using gpr_free().
+   Return 1 on success, 0 on failure. Guarantees *host and *port == NULL on
+   failure. */
+int gpr_split_host_port(const char* name, char** host, char** port);
+
+#endif /* GRPC_CORE_LIB_GPR_HOST_PORT_H */
diff --git a/third_party/grpc/src/core/lib/gpr/log.cc b/third_party/grpc/src/core/lib/gpr/log.cc
new file mode 100644
index 0000000..01ef112
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/log.cc
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+
+#include <stdio.h>
+#include <string.h>
+
+void gpr_default_log(gpr_log_func_args* args);
+static gpr_atm g_log_func = (gpr_atm)gpr_default_log;
+static gpr_atm g_min_severity_to_print = GPR_LOG_VERBOSITY_UNSET;
+
+const char* gpr_log_severity_string(gpr_log_severity severity) {
+  switch (severity) {
+    case GPR_LOG_SEVERITY_DEBUG:
+      return "D";
+    case GPR_LOG_SEVERITY_INFO:
+      return "I";
+    case GPR_LOG_SEVERITY_ERROR:
+      return "E";
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
+
+int gpr_should_log(gpr_log_severity severity) {
+  return static_cast<gpr_atm>(severity) >=
+                 gpr_atm_no_barrier_load(&g_min_severity_to_print)
+             ? 1
+             : 0;
+}
+
+void gpr_log_message(const char* file, int line, gpr_log_severity severity,
+                     const char* message) {
+  if (gpr_should_log(severity) == 0) {
+    return;
+  }
+
+  gpr_log_func_args lfargs;
+  memset(&lfargs, 0, sizeof(lfargs));
+  lfargs.file = file;
+  lfargs.line = line;
+  lfargs.severity = severity;
+  lfargs.message = message;
+  ((gpr_log_func)gpr_atm_no_barrier_load(&g_log_func))(&lfargs);
+}
+
+void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print) {
+  gpr_atm_no_barrier_store(&g_min_severity_to_print,
+                           (gpr_atm)min_severity_to_print);
+}
+
+void gpr_log_verbosity_init() {
+  char* verbosity = nullptr;
+  const char* insecure_getenv = gpr_getenv_silent("GRPC_VERBOSITY", &verbosity);
+
+  gpr_atm min_severity_to_print = GPR_LOG_SEVERITY_ERROR;
+  if (verbosity != nullptr) {
+    if (gpr_stricmp(verbosity, "DEBUG") == 0) {
+      min_severity_to_print = static_cast<gpr_atm>(GPR_LOG_SEVERITY_DEBUG);
+    } else if (gpr_stricmp(verbosity, "INFO") == 0) {
+      min_severity_to_print = static_cast<gpr_atm>(GPR_LOG_SEVERITY_INFO);
+    } else if (gpr_stricmp(verbosity, "ERROR") == 0) {
+      min_severity_to_print = static_cast<gpr_atm>(GPR_LOG_SEVERITY_ERROR);
+    }
+    gpr_free(verbosity);
+  }
+  if ((gpr_atm_no_barrier_load(&g_min_severity_to_print)) ==
+      GPR_LOG_VERBOSITY_UNSET) {
+    gpr_atm_no_barrier_store(&g_min_severity_to_print, min_severity_to_print);
+  }
+
+  if (insecure_getenv != nullptr) {
+    gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used",
+            insecure_getenv);
+  }
+}
+
+void gpr_set_log_function(gpr_log_func f) {
+  gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)(f ? f : gpr_default_log));
+}
diff --git a/third_party/grpc/src/core/lib/gpr/log_android.cc b/third_party/grpc/src/core/lib/gpr/log_android.cc
new file mode 100644
index 0000000..40ef4c6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/log_android.cc
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_ANDROID
+
+#include <android/log.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+static android_LogPriority severity_to_log_priority(gpr_log_severity severity) {
+  switch (severity) {
+    case GPR_LOG_SEVERITY_DEBUG:
+      return ANDROID_LOG_DEBUG;
+    case GPR_LOG_SEVERITY_INFO:
+      return ANDROID_LOG_INFO;
+    case GPR_LOG_SEVERITY_ERROR:
+      return ANDROID_LOG_ERROR;
+  }
+  return ANDROID_LOG_DEFAULT;
+}
+
+void gpr_log(const char* file, int line, gpr_log_severity severity,
+             const char* format, ...) {
+  /* Avoid message construction if gpr_log_message won't log */
+  if (gpr_should_log(severity) == 0) {
+    return;
+  }
+  char* message = NULL;
+  va_list args;
+  va_start(args, format);
+  vasprintf(&message, format, args);
+  va_end(args);
+  gpr_log_message(file, line, severity, message);
+  free(message);
+}
+
+void gpr_default_log(gpr_log_func_args* args) {
+  const char* final_slash;
+  const char* display_file;
+  char* output = NULL;
+
+  final_slash = strrchr(args->file, '/');
+  if (final_slash == NULL)
+    display_file = args->file;
+  else
+    display_file = final_slash + 1;
+
+  asprintf(&output, "%s:%d] %s", display_file, args->line, args->message);
+
+  __android_log_write(severity_to_log_priority(args->severity), "GRPC", output);
+
+  /* allocated by asprintf => use free, not gpr_free */
+  free(output);
+}
+
+#endif /* GPR_ANDROID */
diff --git a/third_party/grpc/src/core/lib/gpr/log_linux.cc b/third_party/grpc/src/core/lib/gpr/log_linux.cc
new file mode 100644
index 0000000..8b597b4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/log_linux.cc
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX_LOG
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+static long sys_gettid(void) { return syscall(__NR_gettid); }
+
+void gpr_log(const char* file, int line, gpr_log_severity severity,
+             const char* format, ...) {
+  /* Avoid message construction if gpr_log_message won't log */
+  if (gpr_should_log(severity) == 0) {
+    return;
+  }
+  char* message = nullptr;
+  va_list args;
+  va_start(args, format);
+  if (vasprintf(&message, format, args) == -1) {
+    va_end(args);
+    return;
+  }
+  va_end(args);
+  gpr_log_message(file, line, severity, message);
+  /* message has been allocated by vasprintf above, and needs free */
+  free(message);
+}
+
+void gpr_default_log(gpr_log_func_args* args) {
+  const char* final_slash;
+  char* prefix;
+  const char* display_file;
+  char time_buffer[64];
+  time_t timer;
+  gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
+  struct tm tm;
+  static __thread long tid = 0;
+  if (tid == 0) tid = sys_gettid();
+
+  timer = static_cast<time_t>(now.tv_sec);
+  final_slash = strrchr(args->file, '/');
+  if (final_slash == nullptr)
+    display_file = args->file;
+  else
+    display_file = final_slash + 1;
+
+  if (!localtime_r(&timer, &tm)) {
+    strcpy(time_buffer, "error:localtime");
+  } else if (0 ==
+             strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) {
+    strcpy(time_buffer, "error:strftime");
+  }
+
+  gpr_asprintf(&prefix, "%s%s.%09" PRId32 " %7ld %s:%d]",
+               gpr_log_severity_string(args->severity), time_buffer,
+               now.tv_nsec, tid, display_file, args->line);
+
+  fprintf(stderr, "%-60s %s\n", prefix, args->message);
+  gpr_free(prefix);
+}
+
+#endif /* GPR_LINUX_LOG */
diff --git a/third_party/grpc/src/core/lib/gpr/log_posix.cc b/third_party/grpc/src/core/lib/gpr/log_posix.cc
new file mode 100644
index 0000000..cd0b702
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/log_posix.cc
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_LOG
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+static intptr_t sys_gettid(void) { return (intptr_t)pthread_self(); }
+
+void gpr_log(const char* file, int line, gpr_log_severity severity,
+             const char* format, ...) {
+  /* Avoid message construction if gpr_log_message won't log */
+  if (gpr_should_log(severity) == 0) {
+    return;
+  }
+  char buf[64];
+  char* allocated = nullptr;
+  char* message = nullptr;
+  int ret;
+  va_list args;
+  va_start(args, format);
+  ret = vsnprintf(buf, sizeof(buf), format, args);
+  va_end(args);
+  if (ret < 0) {
+    message = nullptr;
+  } else if ((size_t)ret <= sizeof(buf) - 1) {
+    message = buf;
+  } else {
+    message = allocated = (char*)gpr_malloc((size_t)ret + 1);
+    va_start(args, format);
+    vsnprintf(message, (size_t)(ret + 1), format, args);
+    va_end(args);
+  }
+  gpr_log_message(file, line, severity, message);
+  gpr_free(allocated);
+}
+
+void gpr_default_log(gpr_log_func_args* args) {
+  const char* final_slash;
+  const char* display_file;
+  char time_buffer[64];
+  time_t timer;
+  gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
+  struct tm tm;
+
+  timer = (time_t)now.tv_sec;
+  final_slash = strrchr(args->file, '/');
+  if (final_slash == nullptr)
+    display_file = args->file;
+  else
+    display_file = final_slash + 1;
+
+  if (!localtime_r(&timer, &tm)) {
+    strcpy(time_buffer, "error:localtime");
+  } else if (0 ==
+             strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) {
+    strcpy(time_buffer, "error:strftime");
+  }
+
+  char* prefix;
+  gpr_asprintf(&prefix, "%s%s.%09d %7tu %s:%d]",
+               gpr_log_severity_string(args->severity), time_buffer,
+               (int)(now.tv_nsec), sys_gettid(), display_file, args->line);
+
+  fprintf(stderr, "%-70s %s\n", prefix, args->message);
+  gpr_free(prefix);
+}
+
+#endif /* defined(GPR_POSIX_LOG) */
diff --git a/third_party/grpc/src/core/lib/gpr/log_windows.cc b/third_party/grpc/src/core/lib/gpr/log_windows.cc
new file mode 100644
index 0000000..060be57
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/log_windows.cc
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS_LOG
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_windows.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/string_windows.h"
+
+void gpr_log(const char* file, int line, gpr_log_severity severity,
+             const char* format, ...) {
+  /* Avoid message construction if gpr_log_message won't log */
+  if (gpr_should_log(severity) == 0) {
+    return;
+  }
+
+  char* message = NULL;
+  va_list args;
+  int ret;
+
+  /* Determine the length. */
+  va_start(args, format);
+  ret = _vscprintf(format, args);
+  va_end(args);
+  if (ret < 0) {
+    message = NULL;
+  } else {
+    /* Allocate a new buffer, with space for the NUL terminator. */
+    size_t strp_buflen = (size_t)ret + 1;
+    message = (char*)gpr_malloc(strp_buflen);
+
+    /* Print to the buffer. */
+    va_start(args, format);
+    ret = vsnprintf_s(message, strp_buflen, _TRUNCATE, format, args);
+    va_end(args);
+    if ((size_t)ret != strp_buflen - 1) {
+      /* This should never happen. */
+      gpr_free(message);
+      message = NULL;
+    }
+  }
+
+  gpr_log_message(file, line, severity, message);
+  gpr_free(message);
+}
+
+/* Simple starter implementation */
+void gpr_default_log(gpr_log_func_args* args) {
+  const char* final_slash;
+  const char* display_file;
+  char time_buffer[64];
+  time_t timer;
+  gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
+  struct tm tm;
+
+  timer = (time_t)now.tv_sec;
+  final_slash = strrchr(args->file, '\\');
+  if (final_slash == NULL)
+    display_file = args->file;
+  else
+    display_file = final_slash + 1;
+
+  if (localtime_s(&tm, &timer)) {
+    strcpy(time_buffer, "error:localtime");
+  } else if (0 ==
+             strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) {
+    strcpy(time_buffer, "error:strftime");
+  }
+
+  fprintf(stderr, "%s%s.%09u %5lu %s:%d] %s\n",
+          gpr_log_severity_string(args->severity), time_buffer,
+          (int)(now.tv_nsec), GetCurrentThreadId(), display_file, args->line,
+          args->message);
+  fflush(stderr);
+}
+
+#endif /* GPR_WINDOWS_LOG */
diff --git a/third_party/grpc/src/core/lib/gpr/mpscq.cc b/third_party/grpc/src/core/lib/gpr/mpscq.cc
new file mode 100644
index 0000000..076a6bb
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/mpscq.cc
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/gpr/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);
+}
+
+bool 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);
+  return prev == &q->stub;
+}
+
+gpr_mpscq_node* gpr_mpscq_pop(gpr_mpscq* q) {
+  bool empty;
+  return gpr_mpscq_pop_and_check_end(q, &empty);
+}
+
+gpr_mpscq_node* gpr_mpscq_pop_and_check_end(gpr_mpscq* q, bool* empty) {
+  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 == nullptr) {
+      *empty = true;
+      return nullptr;
+    }
+    q->tail = next;
+    tail = next;
+    next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next);
+  }
+  if (next != nullptr) {
+    *empty = false;
+    q->tail = next;
+    return tail;
+  }
+  gpr_mpscq_node* head = (gpr_mpscq_node*)gpr_atm_acq_load(&q->head);
+  if (tail != head) {
+    *empty = false;
+    // indicates a retry is in order: we're still adding
+    return nullptr;
+  }
+  gpr_mpscq_push(q, &q->stub);
+  next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next);
+  if (next != nullptr) {
+    *empty = false;
+    q->tail = next;
+    return tail;
+  }
+  // indicates a retry is in order: we're still adding
+  *empty = false;
+  return nullptr;
+}
+
+void gpr_locked_mpscq_init(gpr_locked_mpscq* q) {
+  gpr_mpscq_init(&q->queue);
+  gpr_mu_init(&q->mu);
+}
+
+void gpr_locked_mpscq_destroy(gpr_locked_mpscq* q) {
+  gpr_mpscq_destroy(&q->queue);
+  gpr_mu_destroy(&q->mu);
+}
+
+bool gpr_locked_mpscq_push(gpr_locked_mpscq* q, gpr_mpscq_node* n) {
+  return gpr_mpscq_push(&q->queue, n);
+}
+
+gpr_mpscq_node* gpr_locked_mpscq_try_pop(gpr_locked_mpscq* q) {
+  if (gpr_mu_trylock(&q->mu)) {
+    gpr_mpscq_node* n = gpr_mpscq_pop(&q->queue);
+    gpr_mu_unlock(&q->mu);
+    return n;
+  }
+  return nullptr;
+}
+
+gpr_mpscq_node* gpr_locked_mpscq_pop(gpr_locked_mpscq* q) {
+  gpr_mu_lock(&q->mu);
+  bool empty = false;
+  gpr_mpscq_node* n;
+  do {
+    n = gpr_mpscq_pop_and_check_end(&q->queue, &empty);
+  } while (n == nullptr && !empty);
+  gpr_mu_unlock(&q->mu);
+  return n;
+}
diff --git a/third_party/grpc/src/core/lib/gpr/mpscq.h b/third_party/grpc/src/core/lib/gpr/mpscq.h
new file mode 100644
index 0000000..5ded252
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/mpscq.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_MPSCQ_H
+#define GRPC_CORE_LIB_GPR_MPSCQ_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+#include <grpc/support/sync.h>
+#include <stdbool.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 {
+  // make sure head & tail don't share a cacheline
+  union {
+    char padding[GPR_CACHELINE_SIZE];
+    gpr_atm head;
+  };
+  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
+// Thread safe - can be called from multiple threads concurrently
+// Returns true if this was possibly the first node (may return true
+// sporadically, will not return false sporadically)
+bool 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!!)
+// Thread compatible - can only be called from one thread at a time
+gpr_mpscq_node* gpr_mpscq_pop(gpr_mpscq* q);
+// Pop a node; sets *empty to true if the queue is empty, or false if it is not
+gpr_mpscq_node* gpr_mpscq_pop_and_check_end(gpr_mpscq* q, bool* empty);
+
+// An mpscq with a lock: it's safe to pop from multiple threads, but doing
+// only one thread will succeed concurrently
+typedef struct gpr_locked_mpscq {
+  gpr_mpscq queue;
+  gpr_mu mu;
+} gpr_locked_mpscq;
+
+void gpr_locked_mpscq_init(gpr_locked_mpscq* q);
+void gpr_locked_mpscq_destroy(gpr_locked_mpscq* q);
+// Push a node
+// Thread safe - can be called from multiple threads concurrently
+// Returns true if this was possibly the first node (may return true
+// sporadically, will not return false sporadically)
+bool gpr_locked_mpscq_push(gpr_locked_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!!)
+// Thread safe - can be called from multiple threads concurrently
+gpr_mpscq_node* gpr_locked_mpscq_try_pop(gpr_locked_mpscq* q);
+
+// Pop a node.  Returns NULL only if the queue was empty at some point after
+// calling this function
+gpr_mpscq_node* gpr_locked_mpscq_pop(gpr_locked_mpscq* q);
+
+#endif /* GRPC_CORE_LIB_GPR_MPSCQ_H */
diff --git a/third_party/grpc/src/core/lib/gpr/murmur_hash.cc b/third_party/grpc/src/core/lib/gpr/murmur_hash.cc
new file mode 100644
index 0000000..cf25abf
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/murmur_hash.cc
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/gpr/murmur_hash.h"
+
+#include <string.h>
+
+#define ROTL32(x, r) ((x) << (r)) | ((x) >> (32 - (r)))
+
+#define FMIX32(h)    \
+  (h) ^= (h) >> 16;  \
+  (h) *= 0x85ebca6b; \
+  (h) ^= (h) >> 13;  \
+  (h) *= 0xc2b2ae35; \
+  (h) ^= (h) >> 16;
+
+uint32_t gpr_murmur_hash3(const void* key, size_t len, uint32_t seed) {
+  uint32_t h1 = seed;
+  uint32_t k1;
+
+  const uint32_t c1 = 0xcc9e2d51;
+  const uint32_t c2 = 0x1b873593;
+
+  const uint8_t* keyptr = static_cast<const uint8_t*>(key);
+  const size_t bsize = sizeof(k1);
+  const size_t nblocks = len / bsize;
+
+  /* body */
+  for (size_t i = 0; i < nblocks; i++, keyptr += bsize) {
+    memcpy(&k1, keyptr, bsize);
+
+    k1 *= c1;
+    k1 = ROTL32(k1, 15);
+    k1 *= c2;
+
+    h1 ^= k1;
+    h1 = ROTL32(h1, 13);
+    h1 = h1 * 5 + 0xe6546b64;
+  }
+
+  k1 = 0;
+
+  /* tail */
+  switch (len & 3) {
+    case 3:
+      k1 ^= (static_cast<uint32_t>(keyptr[2])) << 16;
+    /* fallthrough */
+    case 2:
+      k1 ^= (static_cast<uint32_t>(keyptr[1])) << 8;
+    /* fallthrough */
+    case 1:
+      k1 ^= keyptr[0];
+      k1 *= c1;
+      k1 = ROTL32(k1, 15);
+      k1 *= c2;
+      h1 ^= k1;
+  };
+
+  /* finalization */
+  h1 ^= static_cast<uint32_t>(len);
+  FMIX32(h1);
+  return h1;
+}
diff --git a/third_party/grpc/src/core/lib/gpr/murmur_hash.h b/third_party/grpc/src/core/lib/gpr/murmur_hash.h
new file mode 100644
index 0000000..8004889
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/murmur_hash.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_MURMUR_HASH_H
+#define GRPC_CORE_LIB_GPR_MURMUR_HASH_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+/* compute the hash of key (length len) */
+uint32_t gpr_murmur_hash3(const void* key, size_t len, uint32_t seed);
+
+#endif /* GRPC_CORE_LIB_GPR_MURMUR_HASH_H */
diff --git a/third_party/grpc/src/core/lib/gpr/spinlock.h b/third_party/grpc/src/core/lib/gpr/spinlock.h
new file mode 100644
index 0000000..9f35530
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/spinlock.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_SPINLOCK_H
+#define GRPC_CORE_LIB_GPR_SPINLOCK_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+
+/* Simple spinlock. No backoff strategy, gpr_spinlock_lock is almost always
+   a concurrency code smell. */
+typedef struct {
+  gpr_atm atm;
+} gpr_spinlock;
+
+#ifdef __cplusplus
+#define GPR_SPINLOCK_INITIALIZER (gpr_spinlock{0})
+#else
+#define GPR_SPINLOCK_INITIALIZER ((gpr_spinlock){0})
+#endif
+#define GPR_SPINLOCK_STATIC_INITIALIZER \
+  { 0 }
+
+#define gpr_spinlock_trylock(lock) (gpr_atm_acq_cas(&(lock)->atm, 0, 1))
+#define gpr_spinlock_unlock(lock) (gpr_atm_rel_store(&(lock)->atm, 0))
+#define gpr_spinlock_lock(lock) \
+  do {                          \
+  } while (!gpr_spinlock_trylock((lock)))
+
+#endif /* GRPC_CORE_LIB_GPR_SPINLOCK_H */
diff --git a/third_party/grpc/src/core/lib/gpr/string.cc b/third_party/grpc/src/core/lib/gpr/string.cc
new file mode 100644
index 0000000..0a76fc1f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/string.cc
@@ -0,0 +1,347 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/gpr/string.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+char* gpr_strdup(const char* src) {
+  char* dst;
+  size_t len;
+
+  if (!src) {
+    return nullptr;
+  }
+
+  len = strlen(src) + 1;
+  dst = static_cast<char*>(gpr_malloc(len));
+
+  memcpy(dst, src, len);
+
+  return dst;
+}
+
+typedef struct {
+  size_t capacity;
+  size_t length;
+  char* data;
+} dump_out;
+
+char* gpr_format_timespec(gpr_timespec tm) {
+  char time_buffer[35];
+  char ns_buffer[11];  // '.' + 9 digits of precision
+  struct tm* tm_info = localtime((const time_t*)&tm.tv_sec);
+  strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%H:%M:%S", tm_info);
+  snprintf(ns_buffer, 11, ".%09d", tm.tv_nsec);
+  // This loop trims off trailing zeros by inserting a null character that the
+  // right point. We iterate in chunks of three because we want 0, 3, 6, or 9
+  // fractional digits.
+  for (int i = 7; i >= 1; i -= 3) {
+    if (ns_buffer[i] == '0' && ns_buffer[i + 1] == '0' &&
+        ns_buffer[i + 2] == '0') {
+      ns_buffer[i] = '\0';
+      // Edge case in which all fractional digits were 0.
+      if (i == 1) {
+        ns_buffer[0] = '\0';
+      }
+    } else {
+      break;
+    }
+  }
+  char* full_time_str;
+  gpr_asprintf(&full_time_str, "%s%sZ", time_buffer, ns_buffer);
+  return full_time_str;
+}
+
+static dump_out dump_out_create(void) {
+  dump_out r = {0, 0, nullptr};
+  return r;
+}
+
+static void dump_out_append(dump_out* out, char c) {
+  if (out->length == out->capacity) {
+    out->capacity = GPR_MAX(8, 2 * out->capacity);
+    out->data = static_cast<char*>(gpr_realloc(out->data, out->capacity));
+  }
+  out->data[out->length++] = c;
+}
+
+static void hexdump(dump_out* out, const char* buf, size_t len) {
+  static const char* hex = "0123456789abcdef";
+
+  const uint8_t* const beg = reinterpret_cast<const uint8_t*>(buf);
+  const uint8_t* const end = beg + len;
+  const uint8_t* cur;
+
+  for (cur = beg; cur != end; ++cur) {
+    if (cur != beg) dump_out_append(out, ' ');
+    dump_out_append(out, hex[*cur >> 4]);
+    dump_out_append(out, hex[*cur & 0xf]);
+  }
+}
+
+static void asciidump(dump_out* out, const char* buf, size_t len) {
+  const uint8_t* const beg = reinterpret_cast<const uint8_t*>(buf);
+  const uint8_t* const end = beg + len;
+  const uint8_t* cur;
+  int out_was_empty = (out->length == 0);
+  if (!out_was_empty) {
+    dump_out_append(out, ' ');
+    dump_out_append(out, '\'');
+  }
+  for (cur = beg; cur != end; ++cur) {
+    dump_out_append(out, (isprint(*cur) ? *(char*)cur : '.'));
+  }
+  if (!out_was_empty) {
+    dump_out_append(out, '\'');
+  }
+}
+
+char* gpr_dump(const char* buf, size_t len, uint32_t flags) {
+  dump_out out = dump_out_create();
+  if (flags & GPR_DUMP_HEX) {
+    hexdump(&out, buf, len);
+  }
+  if (flags & GPR_DUMP_ASCII) {
+    asciidump(&out, buf, len);
+  }
+  dump_out_append(&out, 0);
+  return out.data;
+}
+
+int gpr_parse_bytes_to_uint32(const char* buf, size_t len, uint32_t* result) {
+  uint32_t out = 0;
+  uint32_t new_val;
+  size_t i;
+
+  if (len == 0) return 0; /* must have some bytes */
+
+  for (i = 0; i < len; i++) {
+    if (buf[i] < '0' || buf[i] > '9') return 0; /* bad char */
+    new_val = 10 * out + static_cast<uint32_t>(buf[i] - '0');
+    if (new_val < out) return 0; /* overflow */
+    out = new_val;
+  }
+
+  *result = out;
+  return 1;
+}
+
+void gpr_reverse_bytes(char* str, int len) {
+  char *p1, *p2;
+  for (p1 = str, p2 = str + len - 1; p2 > p1; ++p1, --p2) {
+    char temp = *p1;
+    *p1 = *p2;
+    *p2 = temp;
+  }
+}
+
+int gpr_ltoa(long value, char* string) {
+  long sign;
+  int i = 0;
+
+  if (value == 0) {
+    string[0] = '0';
+    string[1] = 0;
+    return 1;
+  }
+
+  sign = value < 0 ? -1 : 1;
+  while (value) {
+    string[i++] = static_cast<char>('0' + sign * (value % 10));
+    value /= 10;
+  }
+  if (sign < 0) string[i++] = '-';
+  gpr_reverse_bytes(string, i);
+  string[i] = 0;
+  return i;
+}
+
+int int64_ttoa(int64_t value, char* string) {
+  int64_t sign;
+  int i = 0;
+
+  if (value == 0) {
+    string[0] = '0';
+    string[1] = 0;
+    return 1;
+  }
+
+  sign = value < 0 ? -1 : 1;
+  while (value) {
+    string[i++] = static_cast<char>('0' + sign * (value % 10));
+    value /= 10;
+  }
+  if (sign < 0) string[i++] = '-';
+  gpr_reverse_bytes(string, i);
+  string[i] = 0;
+  return i;
+}
+
+int gpr_parse_nonnegative_int(const char* value) {
+  char* end;
+  long result = strtol(value, &end, 0);
+  if (*end != '\0' || result < 0 || result > INT_MAX) return -1;
+  return static_cast<int>(result);
+}
+
+char* gpr_leftpad(const char* str, char flag, size_t length) {
+  const size_t str_length = strlen(str);
+  const size_t out_length = str_length > length ? str_length : length;
+  char* out = static_cast<char*>(gpr_malloc(out_length + 1));
+  memset(out, flag, out_length - str_length);
+  memcpy(out + out_length - str_length, str, str_length);
+  out[out_length] = 0;
+  return out;
+}
+
+char* gpr_strjoin(const char** strs, size_t nstrs, size_t* final_length) {
+  return gpr_strjoin_sep(strs, nstrs, "", final_length);
+}
+
+char* gpr_strjoin_sep(const char** strs, size_t nstrs, const char* sep,
+                      size_t* final_length) {
+  const size_t sep_len = strlen(sep);
+  size_t out_length = 0;
+  size_t i;
+  char* out;
+  for (i = 0; i < nstrs; i++) {
+    out_length += strlen(strs[i]);
+  }
+  out_length += 1; /* null terminator */
+  if (nstrs > 0) {
+    out_length += sep_len * (nstrs - 1); /* separators */
+  }
+  out = static_cast<char*>(gpr_malloc(out_length));
+  out_length = 0;
+  for (i = 0; i < nstrs; i++) {
+    const size_t slen = strlen(strs[i]);
+    if (i != 0) {
+      memcpy(out + out_length, sep, sep_len);
+      out_length += sep_len;
+    }
+    memcpy(out + out_length, strs[i], slen);
+    out_length += slen;
+  }
+  out[out_length] = 0;
+  if (final_length != nullptr) {
+    *final_length = out_length;
+  }
+  return out;
+}
+
+void gpr_strvec_init(gpr_strvec* sv) { memset(sv, 0, sizeof(*sv)); }
+
+void gpr_strvec_destroy(gpr_strvec* sv) {
+  size_t i;
+  for (i = 0; i < sv->count; i++) {
+    gpr_free(sv->strs[i]);
+  }
+  gpr_free(sv->strs);
+}
+
+void gpr_strvec_add(gpr_strvec* sv, char* str) {
+  if (sv->count == sv->capacity) {
+    sv->capacity = GPR_MAX(sv->capacity + 8, sv->capacity * 2);
+    sv->strs = static_cast<char**>(
+        gpr_realloc(sv->strs, sizeof(char*) * sv->capacity));
+  }
+  sv->strs[sv->count++] = str;
+}
+
+char* gpr_strvec_flatten(gpr_strvec* sv, size_t* final_length) {
+  return gpr_strjoin((const char**)sv->strs, sv->count, final_length);
+}
+
+int gpr_stricmp(const char* a, const char* b) {
+  int ca, cb;
+  do {
+    ca = tolower(*a);
+    cb = tolower(*b);
+    ++a;
+    ++b;
+  } while (ca == cb && ca && cb);
+  return ca - cb;
+}
+
+static void add_string_to_split(const char* beg, const char* end, char*** strs,
+                                size_t* nstrs, size_t* capstrs) {
+  char* out =
+      static_cast<char*>(gpr_malloc(static_cast<size_t>(end - beg) + 1));
+  memcpy(out, beg, static_cast<size_t>(end - beg));
+  out[end - beg] = 0;
+  if (*nstrs == *capstrs) {
+    *capstrs = GPR_MAX(8, 2 * *capstrs);
+    *strs = static_cast<char**>(gpr_realloc(*strs, sizeof(*strs) * *capstrs));
+  }
+  (*strs)[*nstrs] = out;
+  ++*nstrs;
+}
+
+void gpr_string_split(const char* input, const char* sep, char*** strs,
+                      size_t* nstrs) {
+  const char* next;
+  *strs = nullptr;
+  *nstrs = 0;
+  size_t capstrs = 0;
+  while ((next = strstr(input, sep))) {
+    add_string_to_split(input, next, strs, nstrs, &capstrs);
+    input = next + strlen(sep);
+  }
+  add_string_to_split(input, input + strlen(input), strs, nstrs, &capstrs);
+}
+
+void* gpr_memrchr(const void* s, int c, size_t n) {
+  if (s == nullptr) return nullptr;
+  char* b = (char*)s;
+  size_t i;
+  for (i = 0; i < n; i++) {
+    if (b[n - i - 1] == c) {
+      return &b[n - i - 1];
+    }
+  }
+  return nullptr;
+}
+
+bool gpr_is_true(const char* s) {
+  size_t i;
+  if (s == nullptr) {
+    return false;
+  }
+  static const char* truthy[] = {"yes", "true", "1"};
+  for (i = 0; i < GPR_ARRAY_SIZE(truthy); i++) {
+    if (0 == gpr_stricmp(s, truthy[i])) {
+      return true;
+    }
+  }
+  return false;
+}
diff --git a/third_party/grpc/src/core/lib/gpr/string.h b/third_party/grpc/src/core/lib/gpr/string.h
new file mode 100644
index 0000000..ce51fe4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/string.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_STRING_H
+#define GRPC_CORE_LIB_GPR_STRING_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/gpr_types.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/* String utility functions */
+
+/* Flags for gpr_dump function. */
+#define GPR_DUMP_HEX 0x00000001
+#define GPR_DUMP_ASCII 0x00000002
+
+/* Converts array buf, of length len, into a C string  according to the flags.
+   Result should be freed with gpr_free() */
+char* gpr_dump(const char* buf, size_t len, uint32_t flags);
+
+/* Parses an array of bytes into an integer (base 10). Returns 1 on success,
+   0 on failure. */
+int gpr_parse_bytes_to_uint32(const char* data, size_t length,
+                              uint32_t* result);
+
+/* Minimum buffer size for calling ltoa */
+#define GPR_LTOA_MIN_BUFSIZE (3 * sizeof(long))
+
+/* Convert a long to a string in base 10; returns the length of the
+   output string (or 0 on failure).
+   output must be at least GPR_LTOA_MIN_BUFSIZE bytes long. */
+int gpr_ltoa(long value, char* output);
+
+/* Minimum buffer size for calling int64toa */
+#define GPR_INT64TOA_MIN_BUFSIZE (3 * sizeof(int64_t))
+
+/* Convert an int64 to a string in base 10; returns the length of the
+output string (or 0 on failure).
+output must be at least GPR_INT64TOA_MIN_BUFSIZE bytes long.
+NOTE: This function ensures sufficient bit width even on Win x64,
+where long is 32bit is size.*/
+int int64_ttoa(int64_t value, char* output);
+
+// Parses a non-negative number from a value string.  Returns -1 on error.
+int gpr_parse_nonnegative_int(const char* value);
+
+/* Reverse a run of bytes */
+void gpr_reverse_bytes(char* str, int len);
+
+/* Pad a string with flag characters. The given length specifies the minimum
+   field width. The input string is never truncated. */
+char* gpr_leftpad(const char* str, char flag, size_t length);
+
+/* Join a set of strings, returning the resulting string.
+   Total combined length (excluding null terminator) is returned in total_length
+   if it is non-null. */
+char* gpr_strjoin(const char** strs, size_t nstrs, size_t* total_length);
+
+/* Join a set of strings using a separator, returning the resulting string.
+   Total combined length (excluding null terminator) is returned in total_length
+   if it is non-null. */
+char* gpr_strjoin_sep(const char** strs, size_t nstrs, const char* sep,
+                      size_t* total_length);
+
+void gpr_string_split(const char* input, const char* sep, char*** strs,
+                      size_t* nstrs);
+
+/* Returns an allocated string that represents tm according to RFC-3339, and,
+   more specifically, follows:
+   https://developers.google.com/protocol-buffers/docs/proto3#json
+
+   Uses RFC 3339, where generated output will always be Z-normalized and uses
+   0, 3, 6 or 9 fractional digits. */
+char* gpr_format_timespec(gpr_timespec);
+
+/* A vector of strings... for building up a final string one piece at a time */
+typedef struct {
+  char** strs;
+  size_t count;
+  size_t capacity;
+} gpr_strvec;
+
+/* Initialize/destroy */
+void gpr_strvec_init(gpr_strvec* strs);
+void gpr_strvec_destroy(gpr_strvec* strs);
+/* Add a string to a strvec, takes ownership of the string */
+void gpr_strvec_add(gpr_strvec* strs, char* add);
+/* Return a joined string with all added substrings, optionally setting
+   total_length as per gpr_strjoin */
+char* gpr_strvec_flatten(gpr_strvec* strs, size_t* total_length);
+
+/** Case insensitive string comparison... return <0 if lower(a)<lower(b), ==0 if
+    lower(a)==lower(b), >0 if lower(a)>lower(b) */
+int gpr_stricmp(const char* a, const char* b);
+
+void* gpr_memrchr(const void* s, int c, size_t n);
+
+/** Return true if lower(s) equals "true", "yes" or "1", otherwise false. */
+bool gpr_is_true(const char* s);
+
+#endif /* GRPC_CORE_LIB_GPR_STRING_H */
diff --git a/third_party/grpc/src/core/lib/gpr/string_posix.cc b/third_party/grpc/src/core/lib/gpr/string_posix.cc
new file mode 100644
index 0000000..d32775f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/string_posix.cc
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_STRING
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+int gpr_asprintf(char** strp, const char* format, ...) {
+  va_list args;
+  int ret;
+  char buf[64];
+  size_t strp_buflen;
+
+  /* Use a constant-sized buffer to determine the length. */
+  va_start(args, format);
+  ret = vsnprintf(buf, sizeof(buf), format, args);
+  va_end(args);
+  if (ret < 0) {
+    *strp = nullptr;
+    return -1;
+  }
+
+  /* Allocate a new buffer, with space for the NUL terminator. */
+  strp_buflen = static_cast<size_t>(ret) + 1;
+  if ((*strp = static_cast<char*>(gpr_malloc(strp_buflen))) == nullptr) {
+    /* This shouldn't happen, because gpr_malloc() calls abort(). */
+    return -1;
+  }
+
+  /* Return early if we have all the bytes. */
+  if (strp_buflen <= sizeof(buf)) {
+    memcpy(*strp, buf, strp_buflen);
+    return ret;
+  }
+
+  /* Try again using the larger buffer. */
+  va_start(args, format);
+  ret = vsnprintf(*strp, strp_buflen, format, args);
+  va_end(args);
+  if (static_cast<size_t>(ret) == strp_buflen - 1) {
+    return ret;
+  }
+
+  /* This should never happen. */
+  gpr_free(*strp);
+  *strp = nullptr;
+  return -1;
+}
+
+#endif /* GPR_POSIX_STRING */
diff --git a/third_party/grpc/src/core/lib/gpr/string_util_windows.cc b/third_party/grpc/src/core/lib/gpr/string_util_windows.cc
new file mode 100644
index 0000000..8c8c99c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/string_util_windows.cc
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* Posix code for gpr snprintf support. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+/* Some platforms (namely msys) need wchar to be included BEFORE
+   anything else, especially strsafe.h. */
+#include <wchar.h>
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <strsafe.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log_windows.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/string_windows.h"
+
+#if defined UNICODE || defined _UNICODE
+LPTSTR
+gpr_char_to_tchar(LPCSTR input) {
+  LPTSTR ret;
+  int needed = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);
+  if (needed <= 0) return NULL;
+  ret = (LPTSTR)gpr_malloc((unsigned)needed * sizeof(TCHAR));
+  MultiByteToWideChar(CP_UTF8, 0, input, -1, ret, needed);
+  return ret;
+}
+
+LPSTR
+gpr_tchar_to_char(LPCTSTR input) {
+  LPSTR ret;
+  int needed = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL);
+  if (needed <= 0) return NULL;
+  ret = (LPSTR)gpr_malloc((unsigned)needed);
+  WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, needed, NULL, NULL);
+  return ret;
+}
+#else
+LPSTR gpr_tchar_to_char(LPCTSTR input) { return (LPSTR)gpr_strdup(input); }
+
+LPTSTR gpr_char_to_tchar(LPCTSTR input) { return (LPTSTR)gpr_strdup(input); }
+#endif
+
+char* gpr_format_message(int messageid) {
+  LPTSTR tmessage;
+  char* message;
+  DWORD status = FormatMessage(
+      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+          FORMAT_MESSAGE_IGNORE_INSERTS,
+      NULL, (DWORD)messageid, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+      (LPTSTR)(&tmessage), 0, NULL);
+  if (status == 0) return gpr_strdup("Unable to retrieve error string");
+  message = gpr_tchar_to_char(tmessage);
+  LocalFree(tmessage);
+  return message;
+}
+
+#endif /* GPR_WINDOWS */
diff --git a/third_party/grpc/src/core/lib/gpr/string_windows.cc b/third_party/grpc/src/core/lib/gpr/string_windows.cc
new file mode 100644
index 0000000..25bfd412
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/string_windows.cc
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* Windows code for gpr snprintf support. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS_STRING
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+
+int gpr_asprintf(char** strp, const char* format, ...) {
+  va_list args;
+  int ret;
+  size_t strp_buflen;
+
+  /* Determine the length. */
+  va_start(args, format);
+  ret = _vscprintf(format, args);
+  va_end(args);
+  if (ret < 0) {
+    *strp = NULL;
+    return -1;
+  }
+
+  /* Allocate a new buffer, with space for the NUL terminator. */
+  strp_buflen = (size_t)ret + 1;
+  if ((*strp = (char*)gpr_malloc(strp_buflen)) == NULL) {
+    /* This shouldn't happen, because gpr_malloc() calls abort(). */
+    return -1;
+  }
+
+  /* Print to the buffer. */
+  va_start(args, format);
+  ret = vsnprintf_s(*strp, strp_buflen, _TRUNCATE, format, args);
+  va_end(args);
+  if ((size_t)ret == strp_buflen - 1) {
+    return ret;
+  }
+
+  /* This should never happen. */
+  gpr_free(*strp);
+  *strp = NULL;
+  return -1;
+}
+
+#endif /* GPR_WINDOWS_STRING */
diff --git a/third_party/grpc/src/core/lib/gpr/string_windows.h b/third_party/grpc/src/core/lib/gpr/string_windows.h
new file mode 100644
index 0000000..e370f80
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/string_windows.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_STRING_WINDOWS_H
+#define GRPC_CORE_LIB_GPR_STRING_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+/* These allocate new strings using gpr_malloc to convert from and to utf-8. */
+LPTSTR gpr_char_to_tchar(LPCSTR input);
+LPSTR gpr_tchar_to_char(LPCTSTR input);
+
+#endif /* GPR_WINDOWS */
+
+#endif /* GRPC_CORE_LIB_GPR_STRING_WINDOWS_H */
diff --git a/third_party/grpc/src/core/lib/gpr/sync.cc b/third_party/grpc/src/core/lib/gpr/sync.cc
new file mode 100644
index 0000000..2f18fc5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/sync.cc
@@ -0,0 +1,124 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* Generic implementation of synchronization primitives. */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include <assert.h>
+
+/* Number of mutexes to allocate for events, to avoid lock contention.
+   Should be a prime. */
+enum { event_sync_partitions = 31 };
+
+/* Events are partitioned by address to avoid lock contention. */
+static struct sync_array_s {
+  gpr_mu mu;
+  gpr_cv cv;
+} sync_array[event_sync_partitions];
+
+/* This routine is executed once on first use, via event_once */
+static gpr_once event_once = GPR_ONCE_INIT;
+static void event_initialize(void) {
+  int i;
+  for (i = 0; i != event_sync_partitions; i++) {
+    gpr_mu_init(&sync_array[i].mu);
+    gpr_cv_init(&sync_array[i].cv);
+  }
+}
+
+/* Hash ev into an element of sync_array[]. */
+static struct sync_array_s* hash(gpr_event* ev) {
+  return &sync_array[((uintptr_t)ev) % event_sync_partitions];
+}
+
+void gpr_event_init(gpr_event* ev) {
+  gpr_once_init(&event_once, &event_initialize);
+  ev->state = 0;
+}
+
+void gpr_event_set(gpr_event* ev, void* value) {
+  struct sync_array_s* s = hash(ev);
+  gpr_mu_lock(&s->mu);
+  GPR_ASSERT(gpr_atm_acq_load(&ev->state) == 0);
+  gpr_atm_rel_store(&ev->state, (gpr_atm)value);
+  gpr_cv_broadcast(&s->cv);
+  gpr_mu_unlock(&s->mu);
+  GPR_ASSERT(value != nullptr);
+}
+
+void* gpr_event_get(gpr_event* ev) {
+  return (void*)gpr_atm_acq_load(&ev->state);
+}
+
+void* gpr_event_wait(gpr_event* ev, gpr_timespec abs_deadline) {
+  void* result = (void*)gpr_atm_acq_load(&ev->state);
+  if (result == nullptr) {
+    struct sync_array_s* s = hash(ev);
+    gpr_mu_lock(&s->mu);
+    do {
+      result = (void*)gpr_atm_acq_load(&ev->state);
+    } while (result == nullptr && !gpr_cv_wait(&s->cv, &s->mu, abs_deadline));
+    gpr_mu_unlock(&s->mu);
+  }
+  return result;
+}
+
+void gpr_ref_init(gpr_refcount* r, int n) { gpr_atm_rel_store(&r->count, n); }
+
+void gpr_ref(gpr_refcount* r) { gpr_atm_no_barrier_fetch_add(&r->count, 1); }
+
+void gpr_ref_non_zero(gpr_refcount* r) {
+#ifndef NDEBUG
+  gpr_atm prior = gpr_atm_no_barrier_fetch_add(&r->count, 1);
+  assert(prior > 0);
+#else
+  gpr_ref(r);
+#endif
+}
+
+void gpr_refn(gpr_refcount* r, int n) {
+  gpr_atm_no_barrier_fetch_add(&r->count, n);
+}
+
+int gpr_unref(gpr_refcount* r) {
+  gpr_atm prior = gpr_atm_full_fetch_add(&r->count, -1);
+  GPR_ASSERT(prior > 0);
+  return prior == 1;
+}
+
+int gpr_ref_is_unique(gpr_refcount* r) {
+  return gpr_atm_acq_load(&r->count) == 1;
+}
+
+void gpr_stats_init(gpr_stats_counter* c, intptr_t n) {
+  gpr_atm_rel_store(&c->value, n);
+}
+
+void gpr_stats_inc(gpr_stats_counter* c, intptr_t inc) {
+  gpr_atm_no_barrier_fetch_add(&c->value, inc);
+}
+
+intptr_t gpr_stats_read(const gpr_stats_counter* c) {
+  /* don't need acquire-load, but we have no no-barrier load yet */
+  return gpr_atm_acq_load(&c->value);
+}
diff --git a/third_party/grpc/src/core/lib/gpr/sync_posix.cc b/third_party/grpc/src/core/lib/gpr/sync_posix.cc
new file mode 100644
index 0000000..c09a759
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/sync_posix.cc
@@ -0,0 +1,241 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SYNC
+
+#include <errno.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include <time.h>
+#include "src/core/lib/profiling/timers.h"
+
+// For debug of the timer manager crash only.
+// TODO (mxyan): remove after bug is fixed.
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+#include <string.h>
+void (*g_grpc_debug_timer_manager_stats)(
+    int64_t timer_manager_init_count, int64_t timer_manager_shutdown_count,
+    int64_t fork_count, int64_t timer_wait_err, int64_t timer_cv_value,
+    int64_t timer_mu_value, int64_t abstime_sec_value,
+    int64_t abstime_nsec_value, int64_t abs_deadline_sec_value,
+    int64_t abs_deadline_nsec_value, int64_t now1_sec_value,
+    int64_t now1_nsec_value, int64_t now2_sec_value, int64_t now2_nsec_value,
+    int64_t add_result_sec_value, int64_t add_result_nsec_value,
+    int64_t sub_result_sec_value, int64_t sub_result_nsec_value,
+    int64_t next_value, int64_t start_time_sec,
+    int64_t start_time_nsec) = nullptr;
+int64_t g_timer_manager_init_count = 0;
+int64_t g_timer_manager_shutdown_count = 0;
+int64_t g_fork_count = 0;
+int64_t g_timer_wait_err = 0;
+int64_t g_timer_cv_value = 0;
+int64_t g_timer_mu_value = 0;
+int64_t g_abstime_sec_value = -1;
+int64_t g_abstime_nsec_value = -1;
+int64_t g_abs_deadline_sec_value = -1;
+int64_t g_abs_deadline_nsec_value = -1;
+int64_t g_now1_sec_value = -1;
+int64_t g_now1_nsec_value = -1;
+int64_t g_now2_sec_value = -1;
+int64_t g_now2_nsec_value = -1;
+int64_t g_add_result_sec_value = -1;
+int64_t g_add_result_nsec_value = -1;
+int64_t g_sub_result_sec_value = -1;
+int64_t g_sub_result_nsec_value = -1;
+int64_t g_next_value = -1;
+int64_t g_start_time_sec = -1;
+int64_t g_start_time_nsec = -1;
+#endif  // GRPC_DEBUG_TIMER_MANAGER
+
+#ifdef GPR_LOW_LEVEL_COUNTERS
+gpr_atm gpr_mu_locks = 0;
+gpr_atm gpr_counter_atm_cas = 0;
+gpr_atm gpr_counter_atm_add = 0;
+#endif
+
+void gpr_mu_init(gpr_mu* mu) {
+  GPR_ASSERT(pthread_mutex_init(mu, nullptr) == 0);
+}
+
+void gpr_mu_destroy(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_destroy(mu) == 0); }
+
+void gpr_mu_lock(gpr_mu* mu) {
+#ifdef GPR_LOW_LEVEL_COUNTERS
+  GPR_ATM_INC_COUNTER(gpr_mu_locks);
+#endif
+  GPR_TIMER_SCOPE("gpr_mu_lock", 0);
+  GPR_ASSERT(pthread_mutex_lock(mu) == 0);
+}
+
+void gpr_mu_unlock(gpr_mu* mu) {
+  GPR_TIMER_SCOPE("gpr_mu_unlock", 0);
+  GPR_ASSERT(pthread_mutex_unlock(mu) == 0);
+}
+
+int gpr_mu_trylock(gpr_mu* mu) {
+  GPR_TIMER_SCOPE("gpr_mu_trylock", 0);
+  int err = pthread_mutex_trylock(mu);
+  GPR_ASSERT(err == 0 || err == EBUSY);
+  return err == 0;
+}
+
+/*----------------------------------------*/
+
+void gpr_cv_init(gpr_cv* cv) {
+  pthread_condattr_t attr;
+  GPR_ASSERT(pthread_condattr_init(&attr) == 0);
+#if GPR_LINUX
+  GPR_ASSERT(pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0);
+#endif  // GPR_LINUX
+  GPR_ASSERT(pthread_cond_init(cv, &attr) == 0);
+}
+
+void gpr_cv_destroy(gpr_cv* cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); }
+
+// For debug of the timer manager crash only.
+// TODO (mxyan): remove after bug is fixed.
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+static gpr_timespec gpr_convert_clock_type_debug_timespec(
+    gpr_timespec t, gpr_clock_type clock_type, gpr_timespec& now1,
+    gpr_timespec& now2, gpr_timespec& add_result, gpr_timespec& sub_result) {
+  if (t.clock_type == clock_type) {
+    return t;
+  }
+
+  if (t.tv_sec == INT64_MAX || t.tv_sec == INT64_MIN) {
+    t.clock_type = clock_type;
+    return t;
+  }
+
+  if (clock_type == GPR_TIMESPAN) {
+    return gpr_time_sub(t, gpr_now(t.clock_type));
+  }
+
+  if (t.clock_type == GPR_TIMESPAN) {
+    return gpr_time_add(gpr_now(clock_type), t);
+  }
+
+  now1 = gpr_now(t.clock_type);
+  sub_result = gpr_time_sub(t, now1);
+  now2 = gpr_now(clock_type);
+  add_result = gpr_time_add(now2, sub_result);
+  return add_result;
+}
+
+#define gpr_convert_clock_type_debug(t, clock_type, now1, now2, add_result, \
+                                     sub_result)                            \
+  gpr_convert_clock_type_debug_timespec((t), (clock_type), (now1), (now2),  \
+                                        (add_result), (sub_result))
+#else
+#define gpr_convert_clock_type_debug(t, clock_type, now1, now2, add_result, \
+                                     sub_result)                            \
+  gpr_convert_clock_type((t), (clock_type))
+#endif
+
+int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) {
+  int err = 0;
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+  // For debug of the timer manager crash only.
+  // TODO (mxyan): remove after bug is fixed.
+  gpr_timespec abs_deadline_copy;
+  abs_deadline_copy.tv_sec = abs_deadline.tv_sec;
+  abs_deadline_copy.tv_nsec = abs_deadline.tv_nsec;
+  gpr_timespec now1;
+  gpr_timespec now2;
+  gpr_timespec add_result;
+  gpr_timespec sub_result;
+  memset(&now1, 0, sizeof(now1));
+  memset(&now2, 0, sizeof(now2));
+  memset(&add_result, 0, sizeof(add_result));
+  memset(&sub_result, 0, sizeof(sub_result));
+#endif
+  if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) ==
+      0) {
+    err = pthread_cond_wait(cv, mu);
+  } else {
+    struct timespec abs_deadline_ts;
+#if GPR_LINUX
+    abs_deadline = gpr_convert_clock_type_debug(
+        abs_deadline, GPR_CLOCK_MONOTONIC, now1, now2, add_result, sub_result);
+#else
+    abs_deadline = gpr_convert_clock_type_debug(
+        abs_deadline, GPR_CLOCK_REALTIME, now1, now2, add_result, sub_result);
+#endif  // GPR_LINUX
+    abs_deadline_ts.tv_sec = static_cast<time_t>(abs_deadline.tv_sec);
+    abs_deadline_ts.tv_nsec = abs_deadline.tv_nsec;
+    err = pthread_cond_timedwait(cv, mu, &abs_deadline_ts);
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+    // For debug of the timer manager crash only.
+    // TODO (mxyan): remove after bug is fixed.
+    if (GPR_UNLIKELY(!(err == 0 || err == ETIMEDOUT || err == EAGAIN))) {
+      g_abstime_sec_value = abs_deadline_ts.tv_sec;
+      g_abstime_nsec_value = abs_deadline_ts.tv_nsec;
+    }
+#endif
+  }
+
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+  // For debug of the timer manager crash only.
+  // TODO (mxyan): remove after bug is fixed.
+  if (GPR_UNLIKELY(!(err == 0 || err == ETIMEDOUT || err == EAGAIN))) {
+    if (g_grpc_debug_timer_manager_stats) {
+      g_timer_wait_err = err;
+      g_timer_cv_value = (int64_t)cv;
+      g_timer_mu_value = (int64_t)mu;
+      g_abs_deadline_sec_value = abs_deadline_copy.tv_sec;
+      g_abs_deadline_nsec_value = abs_deadline_copy.tv_nsec;
+      g_now1_sec_value = now1.tv_sec;
+      g_now1_nsec_value = now1.tv_nsec;
+      g_now2_sec_value = now2.tv_sec;
+      g_now2_nsec_value = now2.tv_nsec;
+      g_add_result_sec_value = add_result.tv_sec;
+      g_add_result_nsec_value = add_result.tv_nsec;
+      g_sub_result_sec_value = sub_result.tv_sec;
+      g_sub_result_nsec_value = sub_result.tv_nsec;
+      g_grpc_debug_timer_manager_stats(
+          g_timer_manager_init_count, g_timer_manager_shutdown_count,
+          g_fork_count, g_timer_wait_err, g_timer_cv_value, g_timer_mu_value,
+          g_abstime_sec_value, g_abstime_nsec_value, g_abs_deadline_sec_value,
+          g_abs_deadline_nsec_value, g_now1_sec_value, g_now1_nsec_value,
+          g_now2_sec_value, g_now2_nsec_value, g_add_result_sec_value,
+          g_add_result_nsec_value, g_sub_result_sec_value,
+          g_sub_result_nsec_value, g_next_value, g_start_time_sec,
+          g_start_time_nsec);
+    }
+  }
+#endif
+  GPR_ASSERT(err == 0 || err == ETIMEDOUT || err == EAGAIN);
+  return err == ETIMEDOUT;
+}
+
+void gpr_cv_signal(gpr_cv* cv) { GPR_ASSERT(pthread_cond_signal(cv) == 0); }
+
+void gpr_cv_broadcast(gpr_cv* cv) {
+  GPR_ASSERT(pthread_cond_broadcast(cv) == 0);
+}
+
+/*----------------------------------------*/
+
+void gpr_once_init(gpr_once* once, void (*init_function)(void)) {
+  GPR_ASSERT(pthread_once(once, init_function) == 0);
+}
+
+#endif /* GRP_POSIX_SYNC */
diff --git a/third_party/grpc/src/core/lib/gpr/sync_windows.cc b/third_party/grpc/src/core/lib/gpr/sync_windows.cc
new file mode 100644
index 0000000..7cd4163
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/sync_windows.cc
@@ -0,0 +1,118 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* Win32 code for gpr synchronization support. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+void gpr_mu_init(gpr_mu* mu) {
+  InitializeCriticalSection(&mu->cs);
+  mu->locked = 0;
+}
+
+void gpr_mu_destroy(gpr_mu* mu) { DeleteCriticalSection(&mu->cs); }
+
+void gpr_mu_lock(gpr_mu* mu) {
+  EnterCriticalSection(&mu->cs);
+  GPR_ASSERT(!mu->locked);
+  mu->locked = 1;
+}
+
+void gpr_mu_unlock(gpr_mu* mu) {
+  mu->locked = 0;
+  LeaveCriticalSection(&mu->cs);
+}
+
+int gpr_mu_trylock(gpr_mu* mu) {
+  int result = TryEnterCriticalSection(&mu->cs);
+  if (result) {
+    if (mu->locked) {                /* This thread already holds the lock. */
+      LeaveCriticalSection(&mu->cs); /* Decrement lock count. */
+      result = 0;                    /* Indicate failure */
+    }
+    mu->locked = 1;
+  }
+  return result;
+}
+
+/*----------------------------------------*/
+
+void gpr_cv_init(gpr_cv* cv) { InitializeConditionVariable(cv); }
+
+void gpr_cv_destroy(gpr_cv* cv) {
+  /* Condition variables don't need destruction in Win32. */
+}
+
+int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) {
+  int timeout = 0;
+  DWORD timeout_max_ms;
+  mu->locked = 0;
+  if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) ==
+      0) {
+    SleepConditionVariableCS(cv, &mu->cs, INFINITE);
+  } else {
+    abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME);
+    gpr_timespec now = gpr_now(abs_deadline.clock_type);
+    int64_t now_ms = (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000;
+    int64_t deadline_ms =
+        (int64_t)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000;
+    if (now_ms >= deadline_ms) {
+      timeout = 1;
+    } else {
+      if ((deadline_ms - now_ms) >= INFINITE) {
+        timeout_max_ms = INFINITE - 1;
+      } else {
+        timeout_max_ms = (DWORD)(deadline_ms - now_ms);
+      }
+      timeout = (SleepConditionVariableCS(cv, &mu->cs, timeout_max_ms) == 0 &&
+                 GetLastError() == ERROR_TIMEOUT);
+    }
+  }
+  mu->locked = 1;
+  return timeout;
+}
+
+void gpr_cv_signal(gpr_cv* cv) { WakeConditionVariable(cv); }
+
+void gpr_cv_broadcast(gpr_cv* cv) { WakeAllConditionVariable(cv); }
+
+/*----------------------------------------*/
+
+static void* dummy;
+struct run_once_func_arg {
+  void (*init_function)(void);
+};
+static BOOL CALLBACK run_once_func(gpr_once* once, void* v, void** pv) {
+  struct run_once_func_arg* arg = (struct run_once_func_arg*)v;
+  (*arg->init_function)();
+  return 1;
+}
+
+void gpr_once_init(gpr_once* once, void (*init_function)(void)) {
+  struct run_once_func_arg arg;
+  arg.init_function = init_function;
+  InitOnceExecuteOnce(once, run_once_func, &arg, &dummy);
+}
+
+#endif /* GPR_WINDOWS */
diff --git a/third_party/grpc/src/core/lib/gpr/time.cc b/third_party/grpc/src/core/lib/gpr/time.cc
new file mode 100644
index 0000000..64c1c98
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/time.cc
@@ -0,0 +1,251 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* Generic implementation of time calls. */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+int gpr_time_cmp(gpr_timespec a, gpr_timespec b) {
+  int cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec);
+  GPR_ASSERT(a.clock_type == b.clock_type);
+  if (cmp == 0 && a.tv_sec != INT64_MAX && a.tv_sec != INT64_MIN) {
+    cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);
+  }
+  return cmp;
+}
+
+gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b) {
+  return gpr_time_cmp(a, b) < 0 ? a : b;
+}
+
+gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b) {
+  return gpr_time_cmp(a, b) > 0 ? a : b;
+}
+
+gpr_timespec gpr_time_0(gpr_clock_type type) {
+  gpr_timespec out;
+  out.tv_sec = 0;
+  out.tv_nsec = 0;
+  out.clock_type = type;
+  return out;
+}
+
+gpr_timespec gpr_inf_future(gpr_clock_type type) {
+  gpr_timespec out;
+  out.tv_sec = INT64_MAX;
+  out.tv_nsec = 0;
+  out.clock_type = type;
+  return out;
+}
+
+gpr_timespec gpr_inf_past(gpr_clock_type type) {
+  gpr_timespec out;
+  out.tv_sec = INT64_MIN;
+  out.tv_nsec = 0;
+  out.clock_type = type;
+  return out;
+}
+
+static gpr_timespec to_seconds_from_sub_second_time(int64_t time_in_units,
+                                                    int64_t units_per_sec,
+                                                    gpr_clock_type type) {
+  gpr_timespec out;
+  if (time_in_units == INT64_MAX) {
+    out = gpr_inf_future(type);
+  } else if (time_in_units == INT64_MIN) {
+    out = gpr_inf_past(type);
+  } else {
+    if (time_in_units >= 0) {
+      out.tv_sec = time_in_units / units_per_sec;
+    } else {
+      out.tv_sec = (-((units_per_sec - 1) - (time_in_units + units_per_sec)) /
+                    units_per_sec) -
+                   1;
+    }
+    out.tv_nsec =
+        static_cast<int32_t>((time_in_units - out.tv_sec * units_per_sec) *
+                             GPR_NS_PER_SEC / units_per_sec);
+    out.clock_type = type;
+  }
+  return out;
+}
+
+static gpr_timespec to_seconds_from_above_second_time(int64_t time_in_units,
+                                                      int64_t secs_per_unit,
+                                                      gpr_clock_type type) {
+  gpr_timespec out;
+  if (time_in_units >= INT64_MAX / secs_per_unit) {
+    out = gpr_inf_future(type);
+  } else if (time_in_units <= INT64_MIN / secs_per_unit) {
+    out = gpr_inf_past(type);
+  } else {
+    out.tv_sec = time_in_units * secs_per_unit;
+    out.tv_nsec = 0;
+    out.clock_type = type;
+  }
+  return out;
+}
+
+gpr_timespec gpr_time_from_nanos(int64_t ns, gpr_clock_type type) {
+  return to_seconds_from_sub_second_time(ns, GPR_NS_PER_SEC, type);
+}
+
+gpr_timespec gpr_time_from_micros(int64_t us, gpr_clock_type type) {
+  return to_seconds_from_sub_second_time(us, GPR_US_PER_SEC, type);
+}
+
+gpr_timespec gpr_time_from_millis(int64_t ms, gpr_clock_type type) {
+  return to_seconds_from_sub_second_time(ms, GPR_MS_PER_SEC, type);
+}
+
+gpr_timespec gpr_time_from_seconds(int64_t s, gpr_clock_type type) {
+  return to_seconds_from_sub_second_time(s, 1, type);
+}
+
+gpr_timespec gpr_time_from_minutes(int64_t m, gpr_clock_type type) {
+  return to_seconds_from_above_second_time(m, 60, type);
+}
+
+gpr_timespec gpr_time_from_hours(int64_t h, gpr_clock_type type) {
+  return to_seconds_from_above_second_time(h, 3600, type);
+}
+
+gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) {
+  gpr_timespec sum;
+  int64_t inc = 0;
+  GPR_ASSERT(b.clock_type == GPR_TIMESPAN);
+  sum.clock_type = a.clock_type;
+  sum.tv_nsec = a.tv_nsec + b.tv_nsec;
+  if (sum.tv_nsec >= GPR_NS_PER_SEC) {
+    sum.tv_nsec -= GPR_NS_PER_SEC;
+    inc++;
+  }
+  if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) {
+    sum = a;
+  } else if (b.tv_sec == INT64_MAX ||
+             (b.tv_sec >= 0 && a.tv_sec >= INT64_MAX - b.tv_sec)) {
+    sum = gpr_inf_future(sum.clock_type);
+  } else if (b.tv_sec == INT64_MIN ||
+             (b.tv_sec <= 0 && a.tv_sec <= INT64_MIN - b.tv_sec)) {
+    sum = gpr_inf_past(sum.clock_type);
+  } else {
+    sum.tv_sec = a.tv_sec + b.tv_sec;
+    if (inc != 0 && sum.tv_sec == INT64_MAX - 1) {
+      sum = gpr_inf_future(sum.clock_type);
+    } else {
+      sum.tv_sec += inc;
+    }
+  }
+  return sum;
+}
+
+gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) {
+  gpr_timespec diff;
+  int64_t dec = 0;
+  if (b.clock_type == GPR_TIMESPAN) {
+    diff.clock_type = a.clock_type;
+  } else {
+    GPR_ASSERT(a.clock_type == b.clock_type);
+    diff.clock_type = GPR_TIMESPAN;
+  }
+  diff.tv_nsec = a.tv_nsec - b.tv_nsec;
+  if (diff.tv_nsec < 0) {
+    diff.tv_nsec += GPR_NS_PER_SEC;
+    dec++;
+  }
+  if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) {
+    diff = a;
+  } else if (b.tv_sec == INT64_MIN ||
+             (b.tv_sec <= 0 && a.tv_sec >= INT64_MAX + b.tv_sec)) {
+    diff = gpr_inf_future(GPR_CLOCK_REALTIME);
+  } else if (b.tv_sec == INT64_MAX ||
+             (b.tv_sec >= 0 && a.tv_sec <= INT64_MIN + b.tv_sec)) {
+    diff = gpr_inf_past(GPR_CLOCK_REALTIME);
+  } else {
+    diff.tv_sec = a.tv_sec - b.tv_sec;
+    if (dec != 0 && diff.tv_sec == INT64_MIN + 1) {
+      diff = gpr_inf_past(GPR_CLOCK_REALTIME);
+    } else {
+      diff.tv_sec -= dec;
+    }
+  }
+  return diff;
+}
+
+int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold) {
+  int cmp_ab;
+
+  GPR_ASSERT(a.clock_type == b.clock_type);
+  GPR_ASSERT(threshold.clock_type == GPR_TIMESPAN);
+
+  cmp_ab = gpr_time_cmp(a, b);
+  if (cmp_ab == 0) return 1;
+  if (cmp_ab < 0) {
+    return gpr_time_cmp(gpr_time_sub(b, a), threshold) <= 0;
+  } else {
+    return gpr_time_cmp(gpr_time_sub(a, b), threshold) <= 0;
+  }
+}
+
+int32_t gpr_time_to_millis(gpr_timespec t) {
+  if (t.tv_sec >= 2147483) {
+    if (t.tv_sec == 2147483 && t.tv_nsec < 648 * GPR_NS_PER_MS) {
+      return 2147483 * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS;
+    }
+    return 2147483647;
+  } else if (t.tv_sec <= -2147483) {
+    /* TODO(ctiller): correct handling here (it's so far in the past do we
+       care?) */
+    return -2147483647;
+  } else {
+    return static_cast<int32_t>(t.tv_sec * GPR_MS_PER_SEC +
+                                t.tv_nsec / GPR_NS_PER_MS);
+  }
+}
+
+double gpr_timespec_to_micros(gpr_timespec t) {
+  return static_cast<double>(t.tv_sec) * GPR_US_PER_SEC + t.tv_nsec * 1e-3;
+}
+
+gpr_timespec gpr_convert_clock_type(gpr_timespec t, gpr_clock_type clock_type) {
+  if (t.clock_type == clock_type) {
+    return t;
+  }
+
+  if (t.tv_sec == INT64_MAX || t.tv_sec == INT64_MIN) {
+    t.clock_type = clock_type;
+    return t;
+  }
+
+  if (clock_type == GPR_TIMESPAN) {
+    return gpr_time_sub(t, gpr_now(t.clock_type));
+  }
+
+  if (t.clock_type == GPR_TIMESPAN) {
+    return gpr_time_add(gpr_now(clock_type), t);
+  }
+
+  return gpr_time_add(gpr_now(clock_type),
+                      gpr_time_sub(t, gpr_now(t.clock_type)));
+}
diff --git a/third_party/grpc/src/core/lib/gpr/time_posix.cc b/third_party/grpc/src/core/lib/gpr/time_posix.cc
new file mode 100644
index 0000000..28836bf
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/time_posix.cc
@@ -0,0 +1,167 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/gpr/time_precise.h"
+
+#ifdef GPR_POSIX_TIME
+
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __linux__
+#include <sys/syscall.h>
+#endif
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
+static struct timespec timespec_from_gpr(gpr_timespec gts) {
+  struct timespec rv;
+  if (sizeof(time_t) < sizeof(int64_t)) {
+    /* fine to assert, as this is only used in gpr_sleep_until */
+    GPR_ASSERT(gts.tv_sec <= INT32_MAX && gts.tv_sec >= INT32_MIN);
+  }
+  rv.tv_sec = static_cast<time_t>(gts.tv_sec);
+  rv.tv_nsec = gts.tv_nsec;
+  return rv;
+}
+
+#if _POSIX_TIMERS > 0 || defined(__OpenBSD__)
+static gpr_timespec gpr_from_timespec(struct timespec ts,
+                                      gpr_clock_type clock_type) {
+  /*
+   * timespec.tv_sec can have smaller size than gpr_timespec.tv_sec,
+   * but we are only using this function to implement gpr_now
+   * so there's no need to handle "infinity" values.
+   */
+  gpr_timespec rv;
+  rv.tv_sec = ts.tv_sec;
+  rv.tv_nsec = static_cast<int32_t>(ts.tv_nsec);
+  rv.clock_type = clock_type;
+  return rv;
+}
+
+/** maps gpr_clock_type --> clockid_t for clock_gettime */
+static const clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC,
+                                                  CLOCK_REALTIME};
+
+void gpr_time_init(void) { gpr_precise_clock_init(); }
+
+static gpr_timespec now_impl(gpr_clock_type clock_type) {
+  struct timespec now;
+  GPR_ASSERT(clock_type != GPR_TIMESPAN);
+  if (clock_type == GPR_CLOCK_PRECISE) {
+    gpr_timespec ret;
+    gpr_precise_clock_now(&ret);
+    return ret;
+  } else {
+#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) && defined(__linux__)
+    /* avoid ABI problems by invoking syscalls directly */
+    syscall(SYS_clock_gettime, clockid_for_gpr_clock[clock_type], &now);
+#else
+    clock_gettime(clockid_for_gpr_clock[clock_type], &now);
+#endif
+    return gpr_from_timespec(now, clock_type);
+  }
+}
+#else
+  /* For some reason Apple's OSes haven't implemented clock_gettime. */
+
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+
+static double g_time_scale;
+static uint64_t g_time_start;
+
+void gpr_time_init(void) {
+  mach_timebase_info_data_t tb = {0, 1};
+  gpr_precise_clock_init();
+  mach_timebase_info(&tb);
+  g_time_scale = tb.numer;
+  g_time_scale /= tb.denom;
+  g_time_start = mach_absolute_time();
+}
+
+static gpr_timespec now_impl(gpr_clock_type clock) {
+  gpr_timespec now;
+  struct timeval now_tv;
+  double now_dbl;
+
+  now.clock_type = clock;
+  switch (clock) {
+    case GPR_CLOCK_REALTIME:
+      gettimeofday(&now_tv, nullptr);
+      now.tv_sec = now_tv.tv_sec;
+      now.tv_nsec = now_tv.tv_usec * 1000;
+      break;
+    case GPR_CLOCK_MONOTONIC:
+      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;
+    case GPR_CLOCK_PRECISE:
+      gpr_precise_clock_now(&now);
+      break;
+    case GPR_TIMESPAN:
+      abort();
+  }
+
+  return now;
+}
+#endif
+
+gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl;
+
+#ifdef GPR_LOW_LEVEL_COUNTERS
+gpr_atm gpr_now_call_count;
+#endif
+
+gpr_timespec gpr_now(gpr_clock_type clock_type) {
+#ifdef GPR_LOW_LEVEL_COUNTERS
+  __atomic_fetch_add(&gpr_now_call_count, 1, __ATOMIC_RELAXED);
+#endif
+  return gpr_now_impl(clock_type);
+}
+
+void gpr_sleep_until(gpr_timespec until) {
+  gpr_timespec now;
+  gpr_timespec delta;
+  struct timespec delta_ts;
+  int ns_result;
+
+  for (;;) {
+    /* We could simplify by using clock_nanosleep instead, but it might be
+     * slightly less portable. */
+    now = gpr_now(until.clock_type);
+    if (gpr_time_cmp(until, now) <= 0) {
+      return;
+    }
+
+    delta = gpr_time_sub(until, now);
+    delta_ts = timespec_from_gpr(delta);
+    ns_result = nanosleep(&delta_ts, nullptr);
+    if (ns_result == 0) {
+      break;
+    }
+  }
+}
+
+#endif /* GPR_POSIX_TIME */
diff --git a/third_party/grpc/src/core/lib/gpr/time_precise.cc b/third_party/grpc/src/core/lib/gpr/time_precise.cc
new file mode 100644
index 0000000..1b34fd7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/time_precise.cc
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <stdio.h>
+
+#include "src/core/lib/gpr/time_precise.h"
+
+#ifdef GRPC_TIMERS_RDTSC
+#if defined(__i386__)
+static void gpr_get_cycle_counter(int64_t int* clk) {
+  int64_t int ret;
+  __asm__ volatile("rdtsc" : "=A"(ret));
+  *clk = ret;
+}
+
+// ----------------------------------------------------------------
+#elif defined(__x86_64__) || defined(__amd64__)
+static void gpr_get_cycle_counter(int64_t* clk) {
+  uint64_t low, high;
+  __asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
+  *clk = (int64_t)(high << 32) | (int64_t)low;
+}
+#endif
+
+static double cycles_per_second = 0;
+static int64_t start_cycle;
+void gpr_precise_clock_init(void) {
+  time_t start;
+  int64_t end_cycle;
+  gpr_log(GPR_DEBUG, "Calibrating timers");
+  start = time(NULL);
+  while (time(NULL) == start)
+    ;
+  gpr_get_cycle_counter(&start_cycle);
+  while (time(NULL) <= start + 10)
+    ;
+  gpr_get_cycle_counter(&end_cycle);
+  cycles_per_second = (double)(end_cycle - start_cycle) / 10.0;
+  gpr_log(GPR_DEBUG, "... cycles_per_second = %f\n", cycles_per_second);
+}
+
+void gpr_precise_clock_now(gpr_timespec* clk) {
+  int64_t counter;
+  double secs;
+  gpr_get_cycle_counter(&counter);
+  secs = (double)(counter - start_cycle) / cycles_per_second;
+  clk->clock_type = GPR_CLOCK_PRECISE;
+  clk->tv_sec = (int64_t)secs;
+  clk->tv_nsec = (int32_t)(1e9 * (secs - (double)clk->tv_sec));
+}
+
+#else  /* GRPC_TIMERS_RDTSC */
+void gpr_precise_clock_init(void) {}
+
+void gpr_precise_clock_now(gpr_timespec* clk) {
+  *clk = gpr_now(GPR_CLOCK_REALTIME);
+  clk->clock_type = GPR_CLOCK_PRECISE;
+}
+#endif /* GRPC_TIMERS_RDTSC */
diff --git a/third_party/grpc/src/core/lib/gpr/time_precise.h b/third_party/grpc/src/core/lib/gpr/time_precise.h
new file mode 100644
index 0000000..a63ea9d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/time_precise.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_TIME_PRECISE_H
+#define GRPC_CORE_LIB_GPR_TIME_PRECISE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/time.h>
+
+void gpr_precise_clock_init(void);
+void gpr_precise_clock_now(gpr_timespec* clk);
+
+#endif /* GRPC_CORE_LIB_GPR_TIME_PRECISE_H */
diff --git a/third_party/grpc/src/core/lib/gpr/time_windows.cc b/third_party/grpc/src/core/lib/gpr/time_windows.cc
new file mode 100644
index 0000000..247cc16
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/time_windows.cc
@@ -0,0 +1,98 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* Win32 code for gpr time support. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS_TIME
+
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <limits.h>
+#include <process.h>
+#include <sys/timeb.h>
+
+#include "src/core/lib/gpr/time_precise.h"
+
+static LARGE_INTEGER g_start_time;
+static double g_time_scale;
+
+void gpr_time_init(void) {
+  LARGE_INTEGER frequency;
+  QueryPerformanceFrequency(&frequency);
+  QueryPerformanceCounter(&g_start_time);
+  g_time_scale = 1.0 / (double)frequency.QuadPart;
+}
+
+static gpr_timespec now_impl(gpr_clock_type clock) {
+  gpr_timespec now_tv;
+  LONGLONG diff;
+  struct _timeb now_tb;
+  LARGE_INTEGER timestamp;
+  double now_dbl;
+  now_tv.clock_type = clock;
+  switch (clock) {
+    case GPR_CLOCK_REALTIME:
+      _ftime_s(&now_tb);
+      now_tv.tv_sec = (int64_t)now_tb.time;
+      now_tv.tv_nsec = now_tb.millitm * 1000000;
+      break;
+    case GPR_CLOCK_MONOTONIC:
+    case GPR_CLOCK_PRECISE:
+      QueryPerformanceCounter(&timestamp);
+      diff = timestamp.QuadPart - g_start_time.QuadPart;
+      now_dbl = (double)diff * g_time_scale;
+      now_tv.tv_sec = (int64_t)now_dbl;
+      now_tv.tv_nsec = (int32_t)((now_dbl - (double)now_tv.tv_sec) * 1e9);
+      break;
+    case GPR_TIMESPAN:
+      abort();
+      break;
+  }
+  return now_tv;
+}
+
+gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl;
+
+gpr_timespec gpr_now(gpr_clock_type clock_type) {
+  return gpr_now_impl(clock_type);
+}
+
+void gpr_sleep_until(gpr_timespec until) {
+  gpr_timespec now;
+  gpr_timespec delta;
+  int64_t sleep_millis;
+
+  for (;;) {
+    /* We could simplify by using clock_nanosleep instead, but it might be
+     * slightly less portable. */
+    now = gpr_now(until.clock_type);
+    if (gpr_time_cmp(until, now) <= 0) {
+      return;
+    }
+
+    delta = gpr_time_sub(until, now);
+    sleep_millis =
+        delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS;
+    GPR_ASSERT((sleep_millis >= 0) && (sleep_millis <= INT_MAX));
+    Sleep((DWORD)sleep_millis);
+  }
+}
+
+#endif /* GPR_WINDOWS_TIME */
diff --git a/third_party/grpc/src/core/lib/gpr/tls.h b/third_party/grpc/src/core/lib/gpr/tls.h
new file mode 100644
index 0000000..aee8f4d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tls.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_TLS_H
+#define GRPC_CORE_LIB_GPR_TLS_H
+
+#include <grpc/support/port_platform.h>
+
+/** Thread local storage.
+
+   A minimal wrapper that should be implementable across many compilers,
+   and implementable efficiently across most modern compilers.
+
+   Thread locals have type intptr_t.
+
+   Declaring a thread local variable 'foo':
+     GPR_TLS_DECL(foo);
+   Thread locals always have static scope.
+
+   Declaring a thread local class variable 'foo':
+     GPR_TLS_CLASS_DECL(foo);
+
+   Defining the thread local class variable:
+     GPR_TLS_CLASS_DEF(foo);
+
+   Initializing a thread local (must be done at library initialization
+   time):
+     gpr_tls_init(&foo);
+
+   Destroying a thread local:
+     gpr_tls_destroy(&foo);
+
+   Setting a thread local (returns new_value):
+     gpr_tls_set(&foo, new_value);
+
+   Accessing a thread local:
+     current_value = gpr_tls_get(&foo);
+
+   ALL functions here may be implemented as macros. */
+
+#ifdef GPR_GCC_TLS
+#include "src/core/lib/gpr/tls_gcc.h"
+#endif
+
+#ifdef GPR_MSVC_TLS
+#include "src/core/lib/gpr/tls_msvc.h"
+#endif
+
+#ifdef GPR_PTHREAD_TLS
+#include "src/core/lib/gpr/tls_pthread.h"
+#endif
+
+#endif /* GRPC_CORE_LIB_GPR_TLS_H */
diff --git a/third_party/grpc/src/core/lib/gpr/tls_gcc.h b/third_party/grpc/src/core/lib/gpr/tls_gcc.h
new file mode 100644
index 0000000..72b360b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tls_gcc.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_TLS_GCC_H
+#define GRPC_CORE_LIB_GPR_TLS_GCC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include <grpc/support/log.h>
+
+/** Thread local storage based on gcc compiler primitives.
+   #include tls.h to use this - and see that file for documentation */
+
+struct gpr_gcc_thread_local {
+  intptr_t value;
+};
+
+#define GPR_TLS_DECL(name) \
+  static __thread struct gpr_gcc_thread_local name = {0}
+
+#define GPR_TLS_CLASS_DECL(name) \
+  static __thread struct gpr_gcc_thread_local name
+
+#define GPR_TLS_CLASS_DEF(name) __thread struct gpr_gcc_thread_local name = {0}
+
+#define gpr_tls_init(tls) \
+  do {                    \
+  } while (0)
+#define gpr_tls_destroy(tls) \
+  do {                       \
+  } while (0)
+#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value))
+#define gpr_tls_get(tls) ((tls)->value)
+
+#endif /* GRPC_CORE_LIB_GPR_TLS_GCC_H */
diff --git a/third_party/grpc/src/core/lib/gpr/tls_msvc.h b/third_party/grpc/src/core/lib/gpr/tls_msvc.h
new file mode 100644
index 0000000..f4b3f0f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tls_msvc.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_TLS_MSVC_H
+#define GRPC_CORE_LIB_GPR_TLS_MSVC_H
+
+/** Thread local storage based on ms visual c compiler primitives.
+#include <grpc/support/port_platform.h>
+
+   #include tls.h to use this - and see that file for documentation */
+
+struct gpr_msvc_thread_local {
+  intptr_t value;
+};
+
+/** Use GPR_TLS_DECL to declare tls static variables outside a class */
+#define GPR_TLS_DECL(name) \
+  static __declspec(thread) struct gpr_msvc_thread_local name = {0}
+
+/** Use GPR_TLS_CLASS_DECL to declare tls static variable members of a class.
+ *  GPR_TLS_CLASS_DEF needs to be called to define this member. */
+#define GPR_TLS_CLASS_DECL(name) \
+  static __declspec(thread) struct gpr_msvc_thread_local name
+
+#define GPR_TLS_CLASS_DEF(name) \
+  __declspec(thread) struct gpr_msvc_thread_local name = {0}
+
+#define gpr_tls_init(tls) \
+  do {                    \
+  } while (0)
+#define gpr_tls_destroy(tls) \
+  do {                       \
+  } while (0)
+#define gpr_tls_set(tls, new_value) (((tls)->value) = (new_value))
+#define gpr_tls_get(tls) ((tls)->value)
+
+#endif /* GRPC_CORE_LIB_GPR_TLS_MSVC_H */
diff --git a/third_party/grpc/src/core/lib/gpr/tls_pthread.cc b/third_party/grpc/src/core/lib/gpr/tls_pthread.cc
new file mode 100644
index 0000000..2e5b306
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tls_pthread.cc
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_PTHREAD_TLS
+
+#include "src/core/lib/gpr/tls.h"
+
+intptr_t gpr_tls_set(struct gpr_pthread_thread_local* tls, intptr_t value) {
+  GPR_ASSERT(0 == pthread_setspecific(tls->key, (void*)value));
+  return value;
+}
+
+#endif /* GPR_PTHREAD_TLS */
diff --git a/third_party/grpc/src/core/lib/gpr/tls_pthread.h b/third_party/grpc/src/core/lib/gpr/tls_pthread.h
new file mode 100644
index 0000000..a15f2f3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tls_pthread.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_TLS_PTHREAD_H
+#define GRPC_CORE_LIB_GPR_TLS_PTHREAD_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h> /* for GPR_ASSERT */
+#include <pthread.h>
+
+/** Thread local storage based on pthread library calls.
+   #include tls.h to use this - and see that file for documentation */
+
+struct gpr_pthread_thread_local {
+  pthread_key_t key;
+};
+
+/** Use GPR_TLS_DECL to declare tls static variables outside a class */
+#define GPR_TLS_DECL(name) static struct gpr_pthread_thread_local name = {0}
+
+/** Use GPR_TLS_CLASS_DECL to declare tls static variable members of a class.
+ *  GPR_TLS_CLASS_DEF needs to be called to define this member. */
+#define GPR_TLS_CLASS_DECL(name) static struct gpr_pthread_thread_local name
+
+/** Use GPR_TLS_CLASS_DEF to declare tls static variable members of a class.
+ *  GPR_TLS_CLASS_DEF needs to be called to define this member. */
+#define GPR_TLS_CLASS_DEF(name) struct gpr_pthread_thread_local name = {0}
+
+#define gpr_tls_init(tls) GPR_ASSERT(0 == pthread_key_create(&(tls)->key, NULL))
+#define gpr_tls_destroy(tls) pthread_key_delete((tls)->key)
+#define gpr_tls_get(tls) ((intptr_t)pthread_getspecific((tls)->key))
+#ifdef __cplusplus
+extern "C" {
+#endif
+intptr_t gpr_tls_set(struct gpr_pthread_thread_local* tls, intptr_t value);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_GPR_TLS_PTHREAD_H */
diff --git a/third_party/grpc/src/core/lib/gpr/tmpfile.h b/third_party/grpc/src/core/lib/gpr/tmpfile.h
new file mode 100644
index 0000000..3ce3ff5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tmpfile.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_TMPFILE_H
+#define GRPC_CORE_LIB_GPR_TMPFILE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdio.h>
+
+/* Creates a temporary file from a prefix.
+   If tmp_filename is not NULL, *tmp_filename is assigned the name of the
+   created file and it is the responsibility of the caller to gpr_free it
+   unless an error occurs in which case it will be set to NULL. */
+FILE* gpr_tmpfile(const char* prefix, char** tmp_filename);
+
+#endif /* GRPC_CORE_LIB_GPR_TMPFILE_H */
diff --git a/third_party/grpc/src/core/lib/gpr/tmpfile_msys.cc b/third_party/grpc/src/core/lib/gpr/tmpfile_msys.cc
new file mode 100644
index 0000000..76cd886
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tmpfile_msys.cc
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_MSYS_TMPFILE
+
+#include <io.h>
+#include <stdio.h>
+#include <string.h>
+#include <tchar.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string_windows.h"
+#include "src/core/lib/gpr/tmpfile.h"
+
+FILE* gpr_tmpfile(const char* prefix, char** tmp_filename_out) {
+  FILE* result = NULL;
+  char tmp_filename[MAX_PATH];
+  UINT success;
+
+  if (tmp_filename_out != NULL) *tmp_filename_out = NULL;
+
+  /* Generate a unique filename with our template + temporary path. */
+  success = GetTempFileNameA(".", prefix, 0, tmp_filename);
+  fprintf(stderr, "success = %d\n", success);
+
+  if (success) {
+    /* Open a file there. */
+    result = fopen(tmp_filename, "wb+");
+    fprintf(stderr, "result = %p\n", result);
+  }
+  if (result != NULL && tmp_filename_out) {
+    *tmp_filename_out = gpr_strdup(tmp_filename);
+  }
+
+  return result;
+}
+
+#endif /* GPR_MSYS_TMPFILE */
diff --git a/third_party/grpc/src/core/lib/gpr/tmpfile_posix.cc b/third_party/grpc/src/core/lib/gpr/tmpfile_posix.cc
new file mode 100644
index 0000000..ffdad33
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tmpfile_posix.cc
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_TMPFILE
+
+#include "src/core/lib/gpr/tmpfile.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+
+FILE* gpr_tmpfile(const char* prefix, char** tmp_filename) {
+  FILE* result = nullptr;
+  char* filename_template;
+  int fd;
+
+  if (tmp_filename != nullptr) *tmp_filename = nullptr;
+
+  gpr_asprintf(&filename_template, "/tmp/%s_XXXXXX", prefix);
+  GPR_ASSERT(filename_template != nullptr);
+
+  fd = mkstemp(filename_template);
+  if (fd == -1) {
+    gpr_log(GPR_ERROR, "mkstemp failed for filename_template %s with error %s.",
+            filename_template, strerror(errno));
+    goto end;
+  }
+  result = fdopen(fd, "w+");
+  if (result == nullptr) {
+    gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).",
+            filename_template, fd, strerror(errno));
+    unlink(filename_template);
+    close(fd);
+    goto end;
+  }
+
+end:
+  if (result != nullptr && tmp_filename != nullptr) {
+    *tmp_filename = filename_template;
+  } else {
+    gpr_free(filename_template);
+  }
+  return result;
+}
+
+#endif /* GPR_POSIX_TMPFILE */
diff --git a/third_party/grpc/src/core/lib/gpr/tmpfile_windows.cc b/third_party/grpc/src/core/lib/gpr/tmpfile_windows.cc
new file mode 100644
index 0000000..d486808
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/tmpfile_windows.cc
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS_TMPFILE
+
+#include <io.h>
+#include <stdio.h>
+#include <string.h>
+#include <tchar.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string_windows.h"
+#include "src/core/lib/gpr/tmpfile.h"
+
+FILE* gpr_tmpfile(const char* prefix, char** tmp_filename_out) {
+  FILE* result = NULL;
+  LPTSTR template_string = NULL;
+  TCHAR tmp_path[MAX_PATH];
+  TCHAR tmp_filename[MAX_PATH];
+  DWORD status;
+  UINT success;
+
+  if (tmp_filename_out != NULL) *tmp_filename_out = NULL;
+
+  /* Convert our prefix to TCHAR. */
+  template_string = gpr_char_to_tchar(prefix);
+  GPR_ASSERT(template_string);
+
+  /* Get the path to the best temporary folder available. */
+  status = GetTempPath(MAX_PATH, tmp_path);
+  if (status == 0 || status > MAX_PATH) goto end;
+
+  /* Generate a unique filename with our template + temporary path. */
+  success = GetTempFileName(tmp_path, template_string, 0, tmp_filename);
+  if (!success) goto end;
+
+  /* Open a file there. */
+  if (_tfopen_s(&result, tmp_filename, TEXT("wb+")) != 0) goto end;
+
+end:
+  if (result && tmp_filename_out) {
+    *tmp_filename_out = gpr_tchar_to_char(tmp_filename);
+  }
+
+  gpr_free(template_string);
+  return result;
+}
+
+#endif /* GPR_WINDOWS_TMPFILE */
diff --git a/third_party/grpc/src/core/lib/gpr/useful.h b/third_party/grpc/src/core/lib/gpr/useful.h
new file mode 100644
index 0000000..a4e73b9
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/useful.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPR_USEFUL_H
+#define GRPC_CORE_LIB_GPR_USEFUL_H
+
+/** useful macros that don't belong anywhere else */
+
+#define GPR_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define GPR_MAX(a, b) ((a) > (b) ? (a) : (b))
+#define GPR_CLAMP(a, min, max) ((a) < (min) ? (min) : (a) > (max) ? (max) : (a))
+/** rotl, rotr assume x is unsigned */
+#define GPR_ROTL(x, n) (((x) << (n)) | ((x) >> (sizeof(x) * 8 - (n))))
+#define GPR_ROTR(x, n) (((x) >> (n)) | ((x) << (sizeof(x) * 8 - (n))))
+
+#define GPR_ARRAY_SIZE(array) (sizeof(array) / sizeof(*(array)))
+
+#define GPR_SWAP(type, a, b) \
+  do {                       \
+    type x = a;              \
+    a = b;                   \
+    b = x;                   \
+  } while (0)
+
+/** Set the \a n-th bit of \a i (a mutable pointer). */
+#define GPR_BITSET(i, n) ((*(i)) |= (1u << (n)))
+
+/** Clear the \a n-th bit of \a i (a mutable pointer). */
+#define GPR_BITCLEAR(i, n) ((*(i)) &= ~(1u << (n)))
+
+/** Get the \a n-th bit of \a i */
+#define GPR_BITGET(i, n) (((i) & (1u << (n))) != 0)
+
+#define GPR_INTERNAL_HEXDIGIT_BITCOUNT(x)                        \
+  ((x) - (((x) >> 1) & 0x77777777) - (((x) >> 2) & 0x33333333) - \
+   (((x) >> 3) & 0x11111111))
+
+/** Returns number of bits set in bitset \a i */
+#define GPR_BITCOUNT(i)                          \
+  (((GPR_INTERNAL_HEXDIGIT_BITCOUNT(i) +         \
+     (GPR_INTERNAL_HEXDIGIT_BITCOUNT(i) >> 4)) & \
+    0x0f0f0f0f) %                                \
+   255)
+
+#define GPR_ICMP(a, b) ((a) < (b) ? -1 : ((a) > (b) ? 1 : 0))
+
+#define GPR_HASH_POINTER(x, range) \
+  ((((size_t)x) >> 4) ^ (((size_t)x) >> 9) ^ (((size_t)x) >> 14)) % (range)
+
+#endif /* GRPC_CORE_LIB_GPR_USEFUL_H */
diff --git a/third_party/grpc/src/core/lib/gpr/wrap_memcpy.cc b/third_party/grpc/src/core/lib/gpr/wrap_memcpy.cc
new file mode 100644
index 0000000..9b8608e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gpr/wrap_memcpy.cc
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <string.h>
+
+/* Provide a wrapped memcpy for targets that need to be backwards
+ * compatible with older libc's.
+ *
+ * Enable by setting LDFLAGS=-Wl,-wrap,memcpy when linking.
+ */
+
+extern "C" {
+#ifdef __linux__
+#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT)
+__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
+void* __wrap_memcpy(void* destination, const void* source, size_t num) {
+  return memcpy(destination, source, num);
+}
+#else /* !__x86_64__ */
+void* __wrap_memcpy(void* destination, const void* source, size_t num) {
+  return memmove(destination, source, num);
+}
+#endif
+#endif
+}
diff --git a/third_party/grpc/src/core/lib/gprpp/README.md b/third_party/grpc/src/core/lib/gprpp/README.md
new file mode 100644
index 0000000..eab018b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/README.md
@@ -0,0 +1,16 @@
+# GPR++ - Google Portable Runtime for C++
+
+The files in this directory contain various utility code for C++ code.
+None of this code is gRPC-specific; anything here may also be useful
+for other open source projects written in C++.
+
+Note that this is one of the few places in src/core where we allow
+the use of portability macros.
+
+Note that this is the only place in src/core where we allow
+use of the C++ standard library (i.e., anything in the `std::`
+namespace).  And for now, we require that any use of the
+standard library is build-time-only -- i.e., we do not allow
+run-time dependencies on libstdc++.  For more details, see
+[gRFC L6](/grpc/proposal/blob/master/L6-allow-c%2B%2B-in-grpc-core.md) and
+[Moving gRPC core to C++](/grpc/grpc/blob/master/doc/core/moving-to-c%2B%2B.md).
diff --git a/third_party/grpc/src/core/lib/gprpp/abstract.h b/third_party/grpc/src/core/lib/gprpp/abstract.h
new file mode 100644
index 0000000..5b7018e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/abstract.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_ABSTRACT_H
+#define GRPC_CORE_LIB_GPRPP_ABSTRACT_H
+
+// This is needed to support abstract base classes in the c core. Since gRPC
+// doesn't have a c++ runtime, it will hit a linker error on delete unless
+// we define a virtual operator delete. See this blog for more info:
+// https://eli.thegreenplace.net/2015/c-deleting-destructors-and-virtual-operator-delete/
+#define GRPC_ABSTRACT_BASE_CLASS \
+  static void operator delete(void* p) { abort(); }
+
+// gRPC currently can't depend on libstdc++, so we can't use "= 0" for
+// pure virtual methods.  Instead, we use this macro.
+#define GRPC_ABSTRACT                                                        \
+  {                                                                          \
+    gpr_log(GPR_ERROR, "Function marked GRPC_ABSTRACT was not implemented"); \
+    GPR_ASSERT(false);                                                       \
+  }
+
+#endif /* GRPC_CORE_LIB_GPRPP_ABSTRACT_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/atomic.h b/third_party/grpc/src/core/lib/gprpp/atomic.h
new file mode 100644
index 0000000..8b08fc4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/atomic.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_H
+#define GRPC_CORE_LIB_GPRPP_ATOMIC_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_HAS_CXX11_ATOMIC
+#include "src/core/lib/gprpp/atomic_with_std.h"
+#else
+#include "src/core/lib/gprpp/atomic_with_atm.h"
+#endif
+
+#endif /* GRPC_CORE_LIB_GPRPP_ATOMIC_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/atomic_with_atm.h b/third_party/grpc/src/core/lib/gprpp/atomic_with_atm.h
new file mode 100644
index 0000000..3d0021b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/atomic_with_atm.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H
+#define GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+
+namespace grpc_core {
+
+enum MemoryOrderRelaxed { memory_order_relaxed };
+
+template <class T>
+class atomic;
+
+template <>
+class atomic<bool> {
+ public:
+  atomic() { gpr_atm_no_barrier_store(&x_, static_cast<gpr_atm>(false)); }
+  explicit atomic(bool x) {
+    gpr_atm_no_barrier_store(&x_, static_cast<gpr_atm>(x));
+  }
+
+  bool compare_exchange_strong(bool& expected, bool update, MemoryOrderRelaxed,
+                               MemoryOrderRelaxed) {
+    if (!gpr_atm_no_barrier_cas(&x_, static_cast<gpr_atm>(expected),
+                                static_cast<gpr_atm>(update))) {
+      expected = gpr_atm_no_barrier_load(&x_) != 0;
+      return false;
+    }
+    return true;
+  }
+
+ private:
+  gpr_atm x_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/atomic_with_std.h b/third_party/grpc/src/core/lib/gprpp/atomic_with_std.h
new file mode 100644
index 0000000..a4ad16e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/atomic_with_std.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H
+#define GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H
+
+#include <grpc/support/port_platform.h>
+
+#include <atomic>
+
+namespace grpc_core {
+
+template <class T>
+using atomic = std::atomic<T>;
+
+typedef std::memory_order memory_order;
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/debug_location.h b/third_party/grpc/src/core/lib/gprpp/debug_location.h
new file mode 100644
index 0000000..287761b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/debug_location.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_DEBUG_LOCATION_H
+#define GRPC_CORE_LIB_GPRPP_DEBUG_LOCATION_H
+
+namespace grpc_core {
+
+// Used for tracking file and line where a call is made for debug builds.
+// No-op for non-debug builds.
+// Callers can use the DEBUG_LOCATION macro in either case.
+#ifndef NDEBUG
+class DebugLocation {
+ public:
+  DebugLocation(const char* file, int line) : file_(file), line_(line) {}
+  bool Log() const { return true; }
+  const char* file() const { return file_; }
+  int line() const { return line_; }
+
+ private:
+  const char* file_;
+  const int line_;
+};
+#define DEBUG_LOCATION ::grpc_core::DebugLocation(__FILE__, __LINE__)
+#else
+class DebugLocation {
+ public:
+  bool Log() const { return false; }
+  const char* file() const { return nullptr; }
+  int line() const { return -1; }
+};
+#define DEBUG_LOCATION ::grpc_core::DebugLocation()
+#endif
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_DEBUG_LOCATION_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/fork.cc b/third_party/grpc/src/core/lib/gprpp/fork.cc
new file mode 100644
index 0000000..3b9c165
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/fork.cc
@@ -0,0 +1,268 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/fork.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
+
+/*
+ * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK
+ *       AROUND VERY SPECIFIC USE CASES.
+ */
+
+namespace grpc_core {
+namespace internal {
+// The exec_ctx_count has 2 modes, blocked and unblocked.
+// When unblocked, the count is 2-indexed; exec_ctx_count=2 indicates
+// 0 active ExecCtxs, exex_ctx_count=3 indicates 1 active ExecCtxs...
+
+// When blocked, the exec_ctx_count is 0-indexed.  Note that ExecCtx
+// creation can only be blocked if there is exactly 1 outstanding ExecCtx,
+// meaning that BLOCKED and UNBLOCKED counts partition the integers
+#define UNBLOCKED(n) (n + 2)
+#define BLOCKED(n) (n)
+
+class ExecCtxState {
+ public:
+  ExecCtxState() : fork_complete_(true) {
+    gpr_mu_init(&mu_);
+    gpr_cv_init(&cv_);
+    gpr_atm_no_barrier_store(&count_, UNBLOCKED(0));
+  }
+
+  void IncExecCtxCount() {
+    gpr_atm count = gpr_atm_no_barrier_load(&count_);
+    while (true) {
+      if (count <= BLOCKED(1)) {
+        // This only occurs if we are trying to fork.  Wait until the fork()
+        // operation completes before allowing new ExecCtxs.
+        gpr_mu_lock(&mu_);
+        if (gpr_atm_no_barrier_load(&count_) <= BLOCKED(1)) {
+          while (!fork_complete_) {
+            gpr_cv_wait(&cv_, &mu_, gpr_inf_future(GPR_CLOCK_REALTIME));
+          }
+        }
+        gpr_mu_unlock(&mu_);
+      } else if (gpr_atm_no_barrier_cas(&count_, count, count + 1)) {
+        break;
+      }
+      count = gpr_atm_no_barrier_load(&count_);
+    }
+  }
+
+  void DecExecCtxCount() { gpr_atm_no_barrier_fetch_add(&count_, -1); }
+
+  bool BlockExecCtx() {
+    // Assumes there is an active ExecCtx when this function is called
+    if (gpr_atm_no_barrier_cas(&count_, UNBLOCKED(1), BLOCKED(1))) {
+      gpr_mu_lock(&mu_);
+      fork_complete_ = false;
+      gpr_mu_unlock(&mu_);
+      return true;
+    }
+    return false;
+  }
+
+  void AllowExecCtx() {
+    gpr_mu_lock(&mu_);
+    gpr_atm_no_barrier_store(&count_, UNBLOCKED(0));
+    fork_complete_ = true;
+    gpr_cv_broadcast(&cv_);
+    gpr_mu_unlock(&mu_);
+  }
+
+  ~ExecCtxState() {
+    gpr_mu_destroy(&mu_);
+    gpr_cv_destroy(&cv_);
+  }
+
+ private:
+  bool fork_complete_;
+  gpr_mu mu_;
+  gpr_cv cv_;
+  gpr_atm count_;
+};
+
+class ThreadState {
+ public:
+  ThreadState() : awaiting_threads_(false), threads_done_(false), count_(0) {
+    gpr_mu_init(&mu_);
+    gpr_cv_init(&cv_);
+  }
+
+  void IncThreadCount() {
+    gpr_mu_lock(&mu_);
+    count_++;
+    gpr_mu_unlock(&mu_);
+  }
+
+  void DecThreadCount() {
+    gpr_mu_lock(&mu_);
+    count_--;
+    if (awaiting_threads_ && count_ == 0) {
+      threads_done_ = true;
+      gpr_cv_signal(&cv_);
+    }
+    gpr_mu_unlock(&mu_);
+  }
+  void AwaitThreads() {
+    gpr_mu_lock(&mu_);
+    awaiting_threads_ = true;
+    threads_done_ = (count_ == 0);
+    while (!threads_done_) {
+      gpr_cv_wait(&cv_, &mu_, gpr_inf_future(GPR_CLOCK_REALTIME));
+    }
+    awaiting_threads_ = true;
+    gpr_mu_unlock(&mu_);
+  }
+
+  ~ThreadState() {
+    gpr_mu_destroy(&mu_);
+    gpr_cv_destroy(&cv_);
+  }
+
+ private:
+  bool awaiting_threads_;
+  bool threads_done_;
+  gpr_mu mu_;
+  gpr_cv cv_;
+  int count_;
+};
+
+}  // namespace
+
+void Fork::GlobalInit() {
+  if (!override_enabled_) {
+#ifdef GRPC_ENABLE_FORK_SUPPORT
+    support_enabled_ = true;
+#else
+    support_enabled_ = false;
+#endif
+    bool env_var_set = false;
+    char* env = gpr_getenv("GRPC_ENABLE_FORK_SUPPORT");
+    if (env != nullptr) {
+      static const char* truthy[] = {"yes",  "Yes",  "YES", "true",
+                                     "True", "TRUE", "1"};
+      static const char* falsey[] = {"no",    "No",    "NO", "false",
+                                     "False", "FALSE", "0"};
+      for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) {
+        if (0 == strcmp(env, truthy[i])) {
+          support_enabled_ = true;
+          env_var_set = true;
+          break;
+        }
+      }
+      if (!env_var_set) {
+        for (size_t i = 0; i < GPR_ARRAY_SIZE(falsey); i++) {
+          if (0 == strcmp(env, falsey[i])) {
+            support_enabled_ = false;
+            env_var_set = true;
+            break;
+          }
+        }
+      }
+      gpr_free(env);
+    }
+  }
+  if (support_enabled_) {
+    exec_ctx_state_ = grpc_core::New<internal::ExecCtxState>();
+    thread_state_ = grpc_core::New<internal::ThreadState>();
+  }
+}
+
+void Fork::GlobalShutdown() {
+  if (support_enabled_) {
+    grpc_core::Delete(exec_ctx_state_);
+    grpc_core::Delete(thread_state_);
+  }
+}
+
+bool Fork::Enabled() { return support_enabled_; }
+
+// Testing Only
+void Fork::Enable(bool enable) {
+  override_enabled_ = true;
+  support_enabled_ = enable;
+}
+
+void Fork::IncExecCtxCount() {
+  if (support_enabled_) {
+    exec_ctx_state_->IncExecCtxCount();
+  }
+}
+
+void Fork::DecExecCtxCount() {
+  if (support_enabled_) {
+    exec_ctx_state_->DecExecCtxCount();
+  }
+}
+
+void Fork::SetResetChildPollingEngineFunc(
+    Fork::child_postfork_func reset_child_polling_engine) {
+  reset_child_polling_engine_ = reset_child_polling_engine;
+}
+Fork::child_postfork_func Fork::GetResetChildPollingEngineFunc() {
+  return reset_child_polling_engine_;
+}
+
+bool Fork::BlockExecCtx() {
+  if (support_enabled_) {
+    return exec_ctx_state_->BlockExecCtx();
+  }
+  return false;
+}
+
+void Fork::AllowExecCtx() {
+  if (support_enabled_) {
+    exec_ctx_state_->AllowExecCtx();
+  }
+}
+
+void Fork::IncThreadCount() {
+  if (support_enabled_) {
+    thread_state_->IncThreadCount();
+  }
+}
+
+void Fork::DecThreadCount() {
+  if (support_enabled_) {
+    thread_state_->DecThreadCount();
+  }
+}
+void Fork::AwaitThreads() {
+  if (support_enabled_) {
+    thread_state_->AwaitThreads();
+  }
+}
+
+internal::ExecCtxState* Fork::exec_ctx_state_ = nullptr;
+internal::ThreadState* Fork::thread_state_ = nullptr;
+bool Fork::support_enabled_ = false;
+bool Fork::override_enabled_ = false;
+Fork::child_postfork_func Fork::reset_child_polling_engine_ = nullptr;
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/gprpp/fork.h b/third_party/grpc/src/core/lib/gprpp/fork.h
new file mode 100644
index 0000000..5a7404f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/fork.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_FORK_H
+#define GRPC_CORE_LIB_GPRPP_FORK_H
+
+/*
+ * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK
+ *       AROUND VERY SPECIFIC USE CASES.
+ */
+
+namespace grpc_core {
+
+namespace internal {
+class ExecCtxState;
+class ThreadState;
+}  // namespace internal
+
+class Fork {
+ public:
+  typedef void (*child_postfork_func)(void);
+
+  static void GlobalInit();
+  static void GlobalShutdown();
+
+  // Returns true if fork suppport is enabled, false otherwise
+  static bool Enabled();
+
+  // Increment the count of active ExecCtxs.
+  // Will block until a pending fork is complete if one is in progress.
+  static void IncExecCtxCount();
+
+  // Decrement the count of active ExecCtxs
+  static void DecExecCtxCount();
+
+  // Provide a function that will be invoked in the child's postfork handler to
+  // reset the polling engine's internal state.
+  static void SetResetChildPollingEngineFunc(
+      child_postfork_func reset_child_polling_engine);
+  static child_postfork_func GetResetChildPollingEngineFunc();
+
+  // Check if there is a single active ExecCtx
+  // (the one used to invoke this function).  If there are more,
+  // return false.  Otherwise, return true and block creation of
+  // more ExecCtx s until AlloWExecCtx() is called
+  //
+  static bool BlockExecCtx();
+  static void AllowExecCtx();
+
+  // Increment the count of active threads.
+  static void IncThreadCount();
+
+  // Decrement the count of active threads.
+  static void DecThreadCount();
+
+  // Await all core threads to be joined.
+  static void AwaitThreads();
+
+  // Test only: overrides environment variables/compile flags
+  // Must be called before grpc_init()
+  static void Enable(bool enable);
+
+ private:
+  static internal::ExecCtxState* exec_ctx_state_;
+  static internal::ThreadState* thread_state_;
+  static bool support_enabled_;
+  static bool override_enabled_;
+  static child_postfork_func reset_child_polling_engine_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_FORK_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/inlined_vector.h b/third_party/grpc/src/core/lib/gprpp/inlined_vector.h
new file mode 100644
index 0000000..66dc751
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/inlined_vector.h
@@ -0,0 +1,200 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H
+#define GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <cassert>
+#include <cstring>
+
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+
+// NOTE: We eventually want to use absl::InlinedVector here.  However,
+// there are currently build problems that prevent us from using absl.
+// In the interim, we define a custom implementation as a place-holder,
+// with the intent to eventually replace this with the absl
+// implementation.
+//
+// This place-holder implementation does not implement the full set of
+// functionality from the absl version; it has just the methods that we
+// currently happen to need in gRPC.  If additional functionality is
+// needed before this gets replaced with the absl version, it can be
+// added, with the following proviso:
+//
+// ANY METHOD ADDED HERE MUST COMPLY WITH THE INTERFACE IN THE absl
+// IMPLEMENTATION!
+//
+// TODO(nnoble, roth): Replace this with absl::InlinedVector once we
+// integrate absl into the gRPC build system in a usable way.
+template <typename T, size_t N>
+class InlinedVector {
+ public:
+  InlinedVector() { init_data(); }
+  ~InlinedVector() { destroy_elements(); }
+
+  // copy constructor
+  InlinedVector(const InlinedVector& v) {
+    init_data();
+    copy_from(v);
+  }
+
+  InlinedVector& operator=(const InlinedVector& v) {
+    if (this != &v) {
+      clear();
+      copy_from(v);
+    }
+    return *this;
+  }
+
+  // move constructor
+  InlinedVector(InlinedVector&& v) {
+    init_data();
+    move_from(v);
+  }
+
+  InlinedVector& operator=(InlinedVector&& v) {
+    if (this != &v) {
+      clear();
+      move_from(v);
+    }
+    return *this;
+  }
+
+  T* data() {
+    return dynamic_ != nullptr ? dynamic_ : reinterpret_cast<T*>(inline_);
+  }
+
+  const T* data() const {
+    return dynamic_ != nullptr ? dynamic_ : reinterpret_cast<const T*>(inline_);
+  }
+
+  T& operator[](size_t offset) {
+    assert(offset < size_);
+    return data()[offset];
+  }
+
+  const T& operator[](size_t offset) const {
+    assert(offset < size_);
+    return data()[offset];
+  }
+
+  void reserve(size_t capacity) {
+    if (capacity > capacity_) {
+      T* new_dynamic = static_cast<T*>(gpr_malloc(sizeof(T) * capacity));
+      move_elements(data(), new_dynamic, size_);
+      gpr_free(dynamic_);
+      dynamic_ = new_dynamic;
+      capacity_ = capacity;
+    }
+  }
+
+  template <typename... Args>
+  void emplace_back(Args&&... args) {
+    if (size_ == capacity_) {
+      reserve(capacity_ * 2);
+    }
+    new (&(data()[size_])) T(std::forward<Args>(args)...);
+    ++size_;
+  }
+
+  void push_back(const T& value) { emplace_back(value); }
+
+  void push_back(T&& value) { emplace_back(std::move(value)); }
+
+  void pop_back() {
+    assert(!empty());
+    size_t s = size();
+    T& value = data()[s - 1];
+    value.~T();
+    size_--;
+  }
+
+  size_t size() const { return size_; }
+  bool empty() const { return size_ == 0; }
+
+  size_t capacity() const { return capacity_; }
+
+  void clear() {
+    destroy_elements();
+    init_data();
+  }
+
+ private:
+  void copy_from(const InlinedVector& v) {
+    // if v is allocated, make sure we have enough capacity.
+    if (v.dynamic_ != nullptr) {
+      reserve(v.capacity_);
+    }
+    // copy over elements
+    for (size_t i = 0; i < v.size_; ++i) {
+      new (&(data()[i])) T(v[i]);
+    }
+    // copy over metadata
+    size_ = v.size_;
+    capacity_ = v.capacity_;
+  }
+
+  void move_from(InlinedVector& v) {
+    // if v is allocated, then we steal its dynamic array; otherwise, we
+    // move the elements individually.
+    if (v.dynamic_ != nullptr) {
+      dynamic_ = v.dynamic_;
+    } else {
+      move_elements(v.data(), data(), v.size_);
+    }
+    // copy over metadata
+    size_ = v.size_;
+    capacity_ = v.capacity_;
+    // null out the original
+    v.init_data();
+  }
+
+  static void move_elements(T* src, T* dst, size_t num_elements) {
+    for (size_t i = 0; i < num_elements; ++i) {
+      new (&dst[i]) T(std::move(src[i]));
+      src[i].~T();
+    }
+  }
+
+  void init_data() {
+    dynamic_ = nullptr;
+    size_ = 0;
+    capacity_ = N;
+  }
+
+  void destroy_elements() {
+    for (size_t i = 0; i < size_; ++i) {
+      T& value = data()[i];
+      value.~T();
+    }
+    gpr_free(dynamic_);
+  }
+
+  typename std::aligned_storage<sizeof(T)>::type inline_[N];
+  T* dynamic_;
+  size_t size_;
+  size_t capacity_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/manual_constructor.h b/third_party/grpc/src/core/lib/gprpp/manual_constructor.h
new file mode 100644
index 0000000..7f827ca
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/manual_constructor.h
@@ -0,0 +1,213 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_MANUAL_CONSTRUCTOR_H
+#define GRPC_CORE_LIB_GPRPP_MANUAL_CONSTRUCTOR_H
+
+// manually construct a region of memory with some type
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+#include <grpc/support/log.h>
+
+namespace grpc_core {
+
+// this contains templated helpers needed to implement the ManualConstructors
+// in this file.
+namespace manual_ctor_impl {
+
+// is_one_of returns true it a class, Member, is present in a variadic list of
+// classes, List.
+template <class Member, class... List>
+class is_one_of;
+
+template <class Member, class... List>
+class is_one_of<Member, Member, List...> {
+ public:
+  static constexpr const bool value = true;
+};
+
+template <class Member, class A, class... List>
+class is_one_of<Member, A, List...> {
+ public:
+  static constexpr const bool value = is_one_of<Member, List...>::value;
+};
+
+template <class Member>
+class is_one_of<Member> {
+ public:
+  static constexpr const bool value = false;
+};
+
+// max_size_of returns sizeof(Type) for the largest type in the variadic list
+// of classes, Types.
+template <class... Types>
+class max_size_of;
+
+template <class A>
+class max_size_of<A> {
+ public:
+  static constexpr const size_t value = sizeof(A);
+};
+
+template <class A, class... B>
+class max_size_of<A, B...> {
+ public:
+  static constexpr const size_t value = sizeof(A) > max_size_of<B...>::value
+                                            ? sizeof(A)
+                                            : max_size_of<B...>::value;
+};
+
+// max_size_of returns alignof(Type) for the largest type in the variadic list
+// of classes, Types.
+template <class... Types>
+class max_align_of;
+
+template <class A>
+class max_align_of<A> {
+ public:
+  static constexpr const size_t value = alignof(A);
+};
+
+template <class A, class... B>
+class max_align_of<A, B...> {
+ public:
+  static constexpr const size_t value = alignof(A) > max_align_of<B...>::value
+                                            ? alignof(A)
+                                            : max_align_of<B...>::value;
+};
+
+}  // namespace manual_ctor_impl
+
+template <class BaseType, class... DerivedTypes>
+class PolymorphicManualConstructor {
+ public:
+  // No constructor or destructor because one of the most useful uses of
+  // this class is as part of a union, and members of a union could not have
+  // constructors or destructors till C++11.  And, anyway, the whole point of
+  // this class is to bypass constructor and destructor.
+
+  BaseType* get() { return reinterpret_cast<BaseType*>(&space_); }
+  const BaseType* get() const {
+    return reinterpret_cast<const BaseType*>(&space_);
+  }
+
+  BaseType* operator->() { return get(); }
+  const BaseType* operator->() const { return get(); }
+
+  BaseType& operator*() { return *get(); }
+  const BaseType& operator*() const { return *get(); }
+
+  template <class DerivedType>
+  void Init() {
+    FinishInit(new (&space_) DerivedType);
+  }
+
+  // Init() constructs the Type instance using the given arguments
+  // (which are forwarded to Type's constructor).
+  //
+  // Note that Init() with no arguments performs default-initialization,
+  // not zero-initialization (i.e it behaves the same as "new Type;", not
+  // "new Type();"), so it will leave non-class types uninitialized.
+  template <class DerivedType, typename... Ts>
+  void Init(Ts&&... args) {
+    FinishInit(new (&space_) DerivedType(std::forward<Ts>(args)...));
+  }
+
+  // Init() that is equivalent to copy and move construction.
+  // Enables usage like this:
+  //   ManualConstructor<std::vector<int>> v;
+  //   v.Init({1, 2, 3});
+  template <class DerivedType>
+  void Init(const DerivedType& x) {
+    FinishInit(new (&space_) DerivedType(x));
+  }
+  template <class DerivedType>
+  void Init(DerivedType&& x) {
+    FinishInit(new (&space_) DerivedType(std::move(x)));
+  }
+
+  void Destroy() { get()->~BaseType(); }
+
+ private:
+  template <class DerivedType>
+  void FinishInit(DerivedType* p) {
+    static_assert(
+        manual_ctor_impl::is_one_of<DerivedType, DerivedTypes...>::value,
+        "DerivedType must be one of the predeclared DerivedTypes");
+    GPR_ASSERT(static_cast<BaseType*>(p) == p);
+  }
+
+  typename std::aligned_storage<
+      grpc_core::manual_ctor_impl::max_size_of<DerivedTypes...>::value,
+      grpc_core::manual_ctor_impl::max_align_of<DerivedTypes...>::value>::type
+      space_;
+};
+
+template <typename Type>
+class ManualConstructor {
+ public:
+  // No constructor or destructor because one of the most useful uses of
+  // this class is as part of a union, and members of a union could not have
+  // constructors or destructors till C++11.  And, anyway, the whole point of
+  // this class is to bypass constructor and destructor.
+
+  Type* get() { return reinterpret_cast<Type*>(&space_); }
+  const Type* get() const { return reinterpret_cast<const Type*>(&space_); }
+
+  Type* operator->() { return get(); }
+  const Type* operator->() const { return get(); }
+
+  Type& operator*() { return *get(); }
+  const Type& operator*() const { return *get(); }
+
+  void Init() { new (&space_) Type; }
+
+  // Init() constructs the Type instance using the given arguments
+  // (which are forwarded to Type's constructor).
+  //
+  // Note that Init() with no arguments performs default-initialization,
+  // not zero-initialization (i.e it behaves the same as "new Type;", not
+  // "new Type();"), so it will leave non-class types uninitialized.
+  template <typename... Ts>
+  void Init(Ts&&... args) {
+    new (&space_) Type(std::forward<Ts>(args)...);
+  }
+
+  // Init() that is equivalent to copy and move construction.
+  // Enables usage like this:
+  //   ManualConstructor<std::vector<int>> v;
+  //   v.Init({1, 2, 3});
+  void Init(const Type& x) { new (&space_) Type(x); }
+  void Init(Type&& x) { new (&space_) Type(std::move(x)); }
+
+  void Destroy() { get()->~Type(); }
+
+ private:
+  typename std::aligned_storage<sizeof(Type), alignof(Type)>::type space_;
+};
+
+}  // namespace grpc_core
+
+#endif
diff --git a/third_party/grpc/src/core/lib/gprpp/memory.h b/third_party/grpc/src/core/lib/gprpp/memory.h
new file mode 100644
index 0000000..b4b63ae
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/memory.h
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_MEMORY_H
+#define GRPC_CORE_LIB_GPRPP_MEMORY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+
+#include <limits>
+#include <memory>
+#include <utility>
+
+// Add this to a class that want to use Delete(), but has a private or
+// protected destructor.
+#define GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE \
+  template <typename T>                           \
+  friend void grpc_core::Delete(T*);
+// Add this to a class that want to use New(), but has a private or
+// protected constructor.
+#define GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW \
+  template <typename T, typename... Args>      \
+  friend T* grpc_core::New(Args&&...);
+
+namespace grpc_core {
+
+// Alternative to new, since we cannot use it (for fear of libstdc++)
+template <typename T, typename... Args>
+inline T* New(Args&&... args) {
+  void* p = gpr_malloc(sizeof(T));
+  return new (p) T(std::forward<Args>(args)...);
+}
+
+// Alternative to delete, since we cannot use it (for fear of libstdc++)
+template <typename T>
+inline void Delete(T* p) {
+  if (p == nullptr) return;
+  p->~T();
+  gpr_free(p);
+}
+
+template <typename T>
+class DefaultDelete {
+ public:
+  void operator()(T* p) { Delete(p); }
+};
+
+template <typename T, typename Deleter = DefaultDelete<T>>
+using UniquePtr = std::unique_ptr<T, Deleter>;
+
+template <typename T, typename... Args>
+inline UniquePtr<T> MakeUnique(Args&&... args) {
+  return UniquePtr<T>(New<T>(std::forward<Args>(args)...));
+}
+
+// an allocator that uses gpr_malloc/gpr_free
+template <class T>
+class Allocator {
+ public:
+  typedef T value_type;
+  typedef T* pointer;
+  typedef const T* const_pointer;
+  typedef T& reference;
+  typedef const T& const_reference;
+  typedef std::size_t size_type;
+  typedef std::ptrdiff_t difference_type;
+  typedef std::false_type propagate_on_container_move_assignment;
+  template <class U>
+  struct rebind {
+    typedef Allocator<U> other;
+  };
+  typedef std::true_type is_always_equal;
+
+  pointer address(reference x) const { return &x; }
+  const_pointer address(const_reference x) const { return &x; }
+  pointer allocate(std::size_t n,
+                   std::allocator<void>::const_pointer hint = nullptr) {
+    return static_cast<pointer>(gpr_malloc(n * sizeof(T)));
+  }
+  void deallocate(T* p, std::size_t n) { gpr_free(p); }
+  size_t max_size() const {
+    return std::numeric_limits<size_type>::max() / sizeof(value_type);
+  }
+  void construct(pointer p, const_reference val) { new ((void*)p) T(val); }
+  template <class U, class... Args>
+  void construct(U* p, Args&&... args) {
+    ::new ((void*)p) U(std::forward<Args>(args)...);
+  }
+  void destroy(pointer p) { p->~T(); }
+  template <class U>
+  void destroy(U* p) {
+    p->~U();
+  }
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_MEMORY_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/mutex_lock.h b/third_party/grpc/src/core/lib/gprpp/mutex_lock.h
new file mode 100644
index 0000000..54751d5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/mutex_lock.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H
+#define GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/sync.h>
+
+namespace grpc_core {
+
+class MutexLock {
+ public:
+  explicit MutexLock(gpr_mu* mu) : mu_(mu) { gpr_mu_lock(mu); }
+  ~MutexLock() { gpr_mu_unlock(mu_); }
+
+  MutexLock(const MutexLock&) = delete;
+  MutexLock& operator=(const MutexLock&) = delete;
+
+ private:
+  gpr_mu* const mu_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/orphanable.h b/third_party/grpc/src/core/lib/gprpp/orphanable.h
new file mode 100644
index 0000000..9053c60
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/orphanable.h
@@ -0,0 +1,133 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_ORPHANABLE_H
+#define GRPC_CORE_LIB_GPRPP_ORPHANABLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include <cinttypes>
+#include <memory>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/debug_location.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+
+namespace grpc_core {
+
+// A base class for orphanable objects, which have one external owner
+// but are not necessarily destroyed immediately when the external owner
+// gives up ownership.  Instead, the owner calls the object's Orphan()
+// method, and the object then takes responsibility for its own cleanup
+// and destruction.
+class Orphanable {
+ public:
+  // Gives up ownership of the object.  The implementation must arrange
+  // to eventually destroy the object without further interaction from the
+  // caller.
+  virtual void Orphan() GRPC_ABSTRACT;
+
+  // Not copyable or movable.
+  Orphanable(const Orphanable&) = delete;
+  Orphanable& operator=(const Orphanable&) = delete;
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  Orphanable() {}
+  virtual ~Orphanable() {}
+};
+
+template <typename T>
+class OrphanableDelete {
+ public:
+  void operator()(T* p) { p->Orphan(); }
+};
+
+template <typename T, typename Deleter = OrphanableDelete<T>>
+using OrphanablePtr = std::unique_ptr<T, Deleter>;
+
+template <typename T, typename... Args>
+inline OrphanablePtr<T> MakeOrphanable(Args&&... args) {
+  return OrphanablePtr<T>(New<T>(std::forward<Args>(args)...));
+}
+
+// A type of Orphanable with internal ref-counting.
+template <typename Child>
+class InternallyRefCounted : public Orphanable {
+ public:
+  // Not copyable nor movable.
+  InternallyRefCounted(const InternallyRefCounted&) = delete;
+  InternallyRefCounted& operator=(const InternallyRefCounted&) = delete;
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+  // Allow RefCountedPtr<> to access Unref() and IncrementRefCount().
+  template <typename T>
+  friend class RefCountedPtr;
+
+  // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
+  // Note: RefCount tracing is only enabled on debug builds, even when a
+  //       TraceFlag is used.
+  template <typename TraceFlagT = TraceFlag>
+  explicit InternallyRefCounted(TraceFlagT* trace_flag = nullptr)
+      : refs_(1, trace_flag) {}
+  virtual ~InternallyRefCounted() = default;
+
+  RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
+    IncrementRefCount();
+    return RefCountedPtr<Child>(static_cast<Child*>(this));
+  }
+  RefCountedPtr<Child> Ref(const DebugLocation& location,
+                           const char* reason) GRPC_MUST_USE_RESULT {
+    IncrementRefCount(location, reason);
+    return RefCountedPtr<Child>(static_cast<Child*>(this));
+  }
+
+  void Unref() {
+    if (refs_.Unref()) {
+      Delete(static_cast<Child*>(this));
+    }
+  }
+  void Unref(const DebugLocation& location, const char* reason) {
+    if (refs_.Unref(location, reason)) {
+      Delete(static_cast<Child*>(this));
+    }
+  }
+
+ private:
+  void IncrementRefCount() { refs_.Ref(); }
+  void IncrementRefCount(const DebugLocation& location, const char* reason) {
+    refs_.Ref(location, reason);
+  }
+
+  grpc_core::RefCount refs_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_ORPHANABLE_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/ref_counted.h b/third_party/grpc/src/core/lib/gprpp/ref_counted.h
new file mode 100644
index 0000000..fa97ffc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/ref_counted.h
@@ -0,0 +1,245 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
+#define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include <atomic>
+#include <cassert>
+#include <cinttypes>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/debug_location.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+
+namespace grpc_core {
+
+// PolymorphicRefCount enforces polymorphic destruction of RefCounted.
+class PolymorphicRefCount {
+ public:
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+  virtual ~PolymorphicRefCount() = default;
+};
+
+// NonPolymorphicRefCount does not enforce polymorphic destruction of
+// RefCounted. Please refer to grpc_core::RefCounted for more details, and
+// when in doubt use PolymorphicRefCount.
+class NonPolymorphicRefCount {
+ public:
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+  ~NonPolymorphicRefCount() = default;
+};
+
+// RefCount is a simple atomic ref-count.
+//
+// This is a C++ implementation of gpr_refcount, with inline functions. Due to
+// inline functions, this class is significantly more efficient than
+// gpr_refcount and should be preferred over gpr_refcount whenever possible.
+//
+// TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph
+//               above.
+class RefCount {
+ public:
+  using Value = intptr_t;
+
+  // `init` is the initial refcount stored in this object.
+  //
+  // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
+  // Note: RefCount tracing is only enabled on debug builds, even when a
+  //       TraceFlag is used.
+  template <typename TraceFlagT = TraceFlag>
+  constexpr explicit RefCount(Value init = 1, TraceFlagT* trace_flag = nullptr)
+      :
+#ifndef NDEBUG
+        trace_flag_(trace_flag),
+#endif
+        value_(init) {
+  }
+
+  // Increases the ref-count by `n`.
+  void Ref(Value n = 1) {
+    GPR_ATM_INC_ADD_THEN(value_.fetch_add(n, std::memory_order_relaxed));
+  }
+  void Ref(const DebugLocation& location, const char* reason, Value n = 1) {
+#ifndef NDEBUG
+    if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
+      const RefCount::Value old_refs = get();
+      gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
+              trace_flag_->name(), this, location.file(), location.line(),
+              old_refs, old_refs + n, reason);
+    }
+#endif
+    Ref(n);
+  }
+
+  // Similar to Ref() with an assert on the ref-count being non-zero.
+  void RefNonZero() {
+#ifndef NDEBUG
+    const Value prior =
+        GPR_ATM_INC_ADD_THEN(value_.fetch_add(1, std::memory_order_relaxed));
+    assert(prior > 0);
+#else
+    Ref();
+#endif
+  }
+  void RefNonZero(const DebugLocation& location, const char* reason) {
+#ifndef NDEBUG
+    if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
+      const RefCount::Value old_refs = get();
+      gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
+              trace_flag_->name(), this, location.file(), location.line(),
+              old_refs, old_refs + 1, reason);
+    }
+#endif
+    RefNonZero();
+  }
+
+  // Decrements the ref-count and returns true if the ref-count reaches 0.
+  bool Unref() {
+    const Value prior =
+        GPR_ATM_INC_ADD_THEN(value_.fetch_sub(1, std::memory_order_acq_rel));
+    GPR_DEBUG_ASSERT(prior > 0);
+    return prior == 1;
+  }
+  bool Unref(const DebugLocation& location, const char* reason) {
+#ifndef NDEBUG
+    if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
+      const RefCount::Value old_refs = get();
+      gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s",
+              trace_flag_->name(), this, location.file(), location.line(),
+              old_refs, old_refs - 1, reason);
+    }
+#endif
+    return Unref();
+  }
+
+ private:
+  Value get() const { return value_.load(std::memory_order_relaxed); }
+
+#ifndef NDEBUG
+  TraceFlag* trace_flag_;
+#endif
+  std::atomic<Value> value_;
+};
+
+// A base class for reference-counted objects.
+// New objects should be created via New() and start with a refcount of 1.
+// When the refcount reaches 0, the object will be deleted via Delete().
+//
+// This will commonly be used by CRTP (curiously-recurring template pattern)
+// e.g., class MyClass : public RefCounted<MyClass>
+//
+// Use PolymorphicRefCount and NonPolymorphicRefCount to select between
+// different implementations of RefCounted.
+//
+// Note that NonPolymorphicRefCount does not support polymorphic destruction.
+// So, use NonPolymorphicRefCount only when both of the following conditions
+// are guaranteed to hold:
+// (a) Child is a concrete leaf class in RefCounted<Child>, and
+// (b) you are gauranteed to call Unref only on concrete leaf classes and not
+//     their parents.
+//
+// The following example is illegal, because calling Unref() will not call
+// the dtor of Child.
+//
+//    class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
+//    class Child : public Parent {}
+//
+//    Child* ch;
+//    ch->Unref();
+//
+template <typename Child, typename Impl = PolymorphicRefCount>
+class RefCounted : public Impl {
+ public:
+  RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
+    IncrementRefCount();
+    return RefCountedPtr<Child>(static_cast<Child*>(this));
+  }
+
+  RefCountedPtr<Child> Ref(const DebugLocation& location,
+                           const char* reason) GRPC_MUST_USE_RESULT {
+    IncrementRefCount(location, reason);
+    return RefCountedPtr<Child>(static_cast<Child*>(this));
+  }
+
+  // TODO(roth): Once all of our code is converted to C++ and can use
+  // RefCountedPtr<> instead of manual ref-counting, make this method
+  // private, since it will only be used by RefCountedPtr<>, which is a
+  // friend of this class.
+  void Unref() {
+    if (refs_.Unref()) {
+      Delete(static_cast<Child*>(this));
+    }
+  }
+  void Unref(const DebugLocation& location, const char* reason) {
+    if (refs_.Unref(location, reason)) {
+      Delete(static_cast<Child*>(this));
+    }
+  }
+
+  // Not copyable nor movable.
+  RefCounted(const RefCounted&) = delete;
+  RefCounted& operator=(const RefCounted&) = delete;
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+
+  // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
+  // Note: RefCount tracing is only enabled on debug builds, even when a
+  //       TraceFlag is used.
+  template <typename TraceFlagT = TraceFlag>
+  explicit RefCounted(TraceFlagT* trace_flag = nullptr)
+      : refs_(1, trace_flag) {}
+
+  // Note: Depending on the Impl used, this dtor can be implicitly virtual.
+  ~RefCounted() = default;
+
+ private:
+  // Allow RefCountedPtr<> to access IncrementRefCount().
+  template <typename T>
+  friend class RefCountedPtr;
+
+  void IncrementRefCount() { refs_.Ref(); }
+  void IncrementRefCount(const DebugLocation& location, const char* reason) {
+    refs_.Ref(location, reason);
+  }
+
+  RefCount refs_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/ref_counted_ptr.h b/third_party/grpc/src/core/lib/gprpp/ref_counted_ptr.h
new file mode 100644
index 0000000..19f38d7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/ref_counted_ptr.h
@@ -0,0 +1,185 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H
+#define GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <type_traits>
+#include <utility>
+
+#include "src/core/lib/gprpp/debug_location.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+
+// A smart pointer class for objects that provide IncrementRefCount() and
+// Unref() methods, such as those provided by the RefCounted base class.
+template <typename T>
+class RefCountedPtr {
+ public:
+  RefCountedPtr() {}
+  RefCountedPtr(std::nullptr_t) {}
+
+  // If value is non-null, we take ownership of a ref to it.
+  template <typename Y>
+  explicit RefCountedPtr(Y* value) {
+    value_ = value;
+  }
+
+  // Move ctors.
+  RefCountedPtr(RefCountedPtr&& other) {
+    value_ = other.value_;
+    other.value_ = nullptr;
+  }
+  template <typename Y>
+  RefCountedPtr(RefCountedPtr<Y>&& other) {
+    value_ = static_cast<T*>(other.value_);
+    other.value_ = nullptr;
+  }
+
+  // Move assignment.
+  RefCountedPtr& operator=(RefCountedPtr&& other) {
+    reset(other.value_);
+    other.value_ = nullptr;
+    return *this;
+  }
+  template <typename Y>
+  RefCountedPtr& operator=(RefCountedPtr<Y>&& other) {
+    reset(other.value_);
+    other.value_ = nullptr;
+    return *this;
+  }
+
+  // Copy ctors.
+  RefCountedPtr(const RefCountedPtr& other) {
+    if (other.value_ != nullptr) other.value_->IncrementRefCount();
+    value_ = other.value_;
+  }
+  template <typename Y>
+  RefCountedPtr(const RefCountedPtr<Y>& other) {
+    static_assert(std::has_virtual_destructor<T>::value,
+                  "T does not have a virtual dtor");
+    if (other.value_ != nullptr) other.value_->IncrementRefCount();
+    value_ = static_cast<T*>(other.value_);
+  }
+
+  // Copy assignment.
+  RefCountedPtr& operator=(const RefCountedPtr& other) {
+    // Note: Order of reffing and unreffing is important here in case value_
+    // and other.value_ are the same object.
+    if (other.value_ != nullptr) other.value_->IncrementRefCount();
+    reset(other.value_);
+    return *this;
+  }
+  template <typename Y>
+  RefCountedPtr& operator=(const RefCountedPtr<Y>& other) {
+    static_assert(std::has_virtual_destructor<T>::value,
+                  "T does not have a virtual dtor");
+    // Note: Order of reffing and unreffing is important here in case value_
+    // and other.value_ are the same object.
+    if (other.value_ != nullptr) other.value_->IncrementRefCount();
+    reset(other.value_);
+    return *this;
+  }
+
+  ~RefCountedPtr() {
+    if (value_ != nullptr) value_->Unref();
+  }
+
+  // If value is non-null, we take ownership of a ref to it.
+  void reset(T* value = nullptr) {
+    if (value_ != nullptr) value_->Unref();
+    value_ = value;
+  }
+  void reset(const DebugLocation& location, const char* reason,
+             T* value = nullptr) {
+    if (value_ != nullptr) value_->Unref(location, reason);
+    value_ = value;
+  }
+  template <typename Y>
+  void reset(Y* value = nullptr) {
+    static_assert(std::has_virtual_destructor<T>::value,
+                  "T does not have a virtual dtor");
+    if (value_ != nullptr) value_->Unref();
+    value_ = static_cast<T*>(value);
+  }
+  template <typename Y>
+  void reset(const DebugLocation& location, const char* reason,
+             Y* value = nullptr) {
+    static_assert(std::has_virtual_destructor<T>::value,
+                  "T does not have a virtual dtor");
+    if (value_ != nullptr) value_->Unref(location, reason);
+    value_ = static_cast<T*>(value);
+  }
+
+  // TODO(roth): This method exists solely as a transition mechanism to allow
+  // us to pass a ref to idiomatic C code that does not use RefCountedPtr<>.
+  // Once all of our code has been converted to idiomatic C++, this
+  // method should go away.
+  T* release() {
+    T* value = value_;
+    value_ = nullptr;
+    return value;
+  }
+
+  T* get() const { return value_; }
+
+  T& operator*() const { return *value_; }
+  T* operator->() const { return value_; }
+
+  template <typename Y>
+  bool operator==(const RefCountedPtr<Y>& other) const {
+    return value_ == other.value_;
+  }
+
+  template <typename Y>
+  bool operator==(const Y* other) const {
+    return value_ == other;
+  }
+
+  bool operator==(std::nullptr_t) const { return value_ == nullptr; }
+
+  template <typename Y>
+  bool operator!=(const RefCountedPtr<Y>& other) const {
+    return value_ != other.value_;
+  }
+
+  template <typename Y>
+  bool operator!=(const Y* other) const {
+    return value_ != other;
+  }
+
+  bool operator!=(std::nullptr_t) const { return value_ != nullptr; }
+
+ private:
+  template <typename Y>
+  friend class RefCountedPtr;
+
+  T* value_ = nullptr;
+};
+
+template <typename T, typename... Args>
+inline RefCountedPtr<T> MakeRefCounted(Args&&... args) {
+  return RefCountedPtr<T>(New<T>(std::forward<Args>(args)...));
+}
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/thd.h b/third_party/grpc/src/core/lib/gprpp/thd.h
new file mode 100644
index 0000000..caf0652
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/thd.h
@@ -0,0 +1,132 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_GPRPP_THD_H
+#define GRPC_CORE_LIB_GPRPP_THD_H
+
+/** Internal thread interface. */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd_id.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+namespace internal {
+
+/// Base class for platform-specific thread-state
+class ThreadInternalsInterface {
+ public:
+  virtual ~ThreadInternalsInterface() {}
+  virtual void Start() GRPC_ABSTRACT;
+  virtual void Join() GRPC_ABSTRACT;
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+}  // namespace internal
+
+class Thread {
+ public:
+  /// Default constructor only to allow use in structs that lack constructors
+  /// Does not produce a validly-constructed thread; must later
+  /// use placement new to construct a real thread. Does not init mu_ and cv_
+  Thread() : state_(FAKE), impl_(nullptr) {}
+
+  /// Normal constructor to create a thread with name \a thd_name,
+  /// which will execute a thread based on function \a thd_body
+  /// with argument \a arg once it is started.
+  /// The optional \a success argument indicates whether the thread
+  /// is successfully created.
+  Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
+         bool* success = nullptr);
+
+  /// Move constructor for thread. After this is called, the other thread
+  /// no longer represents a living thread object
+  Thread(Thread&& other) : state_(other.state_), impl_(other.impl_) {
+    other.state_ = MOVED;
+    other.impl_ = nullptr;
+  }
+
+  /// Move assignment operator for thread. After this is called, the other
+  /// thread no longer represents a living thread object. Not allowed if this
+  /// thread actually exists
+  Thread& operator=(Thread&& other) {
+    if (this != &other) {
+      // TODO(vjpai): if we can be sure that all Thread's are actually
+      // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here.
+      // However, as long as threads come in structures that are
+      // allocated via gpr_malloc, this will not be the case, so we cannot
+      // assert it for the time being.
+      state_ = other.state_;
+      impl_ = other.impl_;
+      other.state_ = MOVED;
+      other.impl_ = nullptr;
+    }
+    return *this;
+  }
+
+  /// The destructor is strictly optional; either the thread never came to life
+  /// and the constructor itself killed it or it has already been joined and
+  /// the Join function kills it. The destructor shouldn't have to do anything.
+  ~Thread() { GPR_ASSERT(impl_ == nullptr); }
+
+  void Start() {
+    if (impl_ != nullptr) {
+      GPR_ASSERT(state_ == ALIVE);
+      state_ = STARTED;
+      impl_->Start();
+    } else {
+      GPR_ASSERT(state_ == FAILED);
+    }
+  };
+
+  void Join() {
+    if (impl_ != nullptr) {
+      impl_->Join();
+      grpc_core::Delete(impl_);
+      state_ = DONE;
+      impl_ = nullptr;
+    } else {
+      GPR_ASSERT(state_ == FAILED);
+    }
+  };
+
+ private:
+  Thread(const Thread&) = delete;
+  Thread& operator=(const Thread&) = delete;
+
+  /// The thread states are as follows:
+  /// FAKE -- just a dummy placeholder Thread created by the default constructor
+  /// ALIVE -- an actual thread of control exists associated with this thread
+  /// STARTED -- the thread of control has been started
+  /// DONE -- the thread of control has completed and been joined
+  /// FAILED -- the thread of control never came alive
+  /// MOVED -- contents were moved out and we're no longer tracking them
+  enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED };
+  ThreadState state_;
+  internal::ThreadInternalsInterface* impl_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_GPRPP_THD_H */
diff --git a/third_party/grpc/src/core/lib/gprpp/thd_posix.cc b/third_party/grpc/src/core/lib/gprpp/thd_posix.cc
new file mode 100644
index 0000000..2751b22
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/thd_posix.cc
@@ -0,0 +1,160 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* Posix implementation for gpr threads. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SYNC
+
+#include "src/core/lib/gprpp/thd.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd_id.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/fork.h"
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+namespace {
+class ThreadInternalsPosix;
+struct thd_arg {
+  ThreadInternalsPosix* thread;
+  void (*body)(void* arg); /* body of a thread */
+  void* arg;               /* argument to a thread */
+  const char* name;        /* name of thread. Can be nullptr. */
+};
+
+class ThreadInternalsPosix
+    : public grpc_core::internal::ThreadInternalsInterface {
+ public:
+  ThreadInternalsPosix(const char* thd_name, void (*thd_body)(void* arg),
+                       void* arg, bool* success)
+      : started_(false) {
+    gpr_mu_init(&mu_);
+    gpr_cv_init(&ready_);
+    pthread_attr_t attr;
+    /* don't use gpr_malloc as we may cause an infinite recursion with
+     * the profiling code */
+    thd_arg* info = static_cast<thd_arg*>(malloc(sizeof(*info)));
+    GPR_ASSERT(info != nullptr);
+    info->thread = this;
+    info->body = thd_body;
+    info->arg = arg;
+    info->name = thd_name;
+    grpc_core::Fork::IncThreadCount();
+
+    GPR_ASSERT(pthread_attr_init(&attr) == 0);
+    GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) ==
+               0);
+
+    *success =
+        (pthread_create(&pthread_id_, &attr,
+                        [](void* v) -> void* {
+                          thd_arg arg = *static_cast<thd_arg*>(v);
+                          free(v);
+                          if (arg.name != nullptr) {
+#if GPR_APPLE_PTHREAD_NAME
+                            /* Apple supports 64 characters, and will
+                             * truncate if it's longer. */
+                            pthread_setname_np(arg.name);
+#elif GPR_LINUX_PTHREAD_NAME
+                            /* Linux supports 16 characters max, and will
+                             * error if it's longer. */
+                            char buf[16];
+                            size_t buf_len = GPR_ARRAY_SIZE(buf) - 1;
+                            strncpy(buf, arg.name, buf_len);
+                            buf[buf_len] = '\0';
+                            pthread_setname_np(pthread_self(), buf);
+#endif  // GPR_APPLE_PTHREAD_NAME
+                          }
+
+                          gpr_mu_lock(&arg.thread->mu_);
+                          while (!arg.thread->started_) {
+                            gpr_cv_wait(&arg.thread->ready_, &arg.thread->mu_,
+                                        gpr_inf_future(GPR_CLOCK_MONOTONIC));
+                          }
+                          gpr_mu_unlock(&arg.thread->mu_);
+
+                          (*arg.body)(arg.arg);
+                          grpc_core::Fork::DecThreadCount();
+                          return nullptr;
+                        },
+                        info) == 0);
+
+    GPR_ASSERT(pthread_attr_destroy(&attr) == 0);
+
+    if (!(*success)) {
+      /* don't use gpr_free, as this was allocated using malloc (see above) */
+      free(info);
+      grpc_core::Fork::DecThreadCount();
+    }
+  };
+
+  ~ThreadInternalsPosix() override {
+    gpr_mu_destroy(&mu_);
+    gpr_cv_destroy(&ready_);
+  }
+
+  void Start() override {
+    gpr_mu_lock(&mu_);
+    started_ = true;
+    gpr_cv_signal(&ready_);
+    gpr_mu_unlock(&mu_);
+  }
+
+  void Join() override { pthread_join(pthread_id_, nullptr); }
+
+ private:
+  gpr_mu mu_;
+  gpr_cv ready_;
+  bool started_;
+  pthread_t pthread_id_;
+};
+
+}  // namespace
+
+Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
+               bool* success) {
+  bool outcome = false;
+  impl_ =
+      grpc_core::New<ThreadInternalsPosix>(thd_name, thd_body, arg, &outcome);
+  if (outcome) {
+    state_ = ALIVE;
+  } else {
+    state_ = FAILED;
+    grpc_core::Delete(impl_);
+    impl_ = nullptr;
+  }
+
+  if (success != nullptr) {
+    *success = outcome;
+  }
+}
+}  // namespace grpc_core
+
+// The following is in the external namespace as it is exposed as C89 API
+gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); }
+
+#endif /* GPR_POSIX_SYNC */
diff --git a/third_party/grpc/src/core/lib/gprpp/thd_windows.cc b/third_party/grpc/src/core/lib/gprpp/thd_windows.cc
new file mode 100644
index 0000000..71584fd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/gprpp/thd_windows.cc
@@ -0,0 +1,155 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* Windows implementation for gpr threads. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+#include "src/core/lib/gprpp/thd.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd_id.h>
+#include <string.h>
+
+#include "src/core/lib/gprpp/memory.h"
+
+#if defined(_MSC_VER)
+#define thread_local __declspec(thread)
+#define WIN_LAMBDA
+#elif defined(__GNUC__)
+#define thread_local __thread
+#define WIN_LAMBDA WINAPI
+#else
+#error "Unknown compiler - please file a bug report"
+#endif
+
+namespace {
+class ThreadInternalsWindows;
+struct thd_info {
+  ThreadInternalsWindows* thread;
+  void (*body)(void* arg); /* body of a thread */
+  void* arg;               /* argument to a thread */
+  HANDLE join_event;       /* the join event */
+};
+
+thread_local struct thd_info* g_thd_info;
+
+class ThreadInternalsWindows
+    : public grpc_core::internal::ThreadInternalsInterface {
+ public:
+  ThreadInternalsWindows(void (*thd_body)(void* arg), void* arg, bool* success)
+      : started_(false) {
+    gpr_mu_init(&mu_);
+    gpr_cv_init(&ready_);
+
+    HANDLE handle;
+    info_ = (struct thd_info*)gpr_malloc(sizeof(*info_));
+    info_->thread = this;
+    info_->body = thd_body;
+    info_->arg = arg;
+
+    info_->join_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    if (info_->join_event == nullptr) {
+      gpr_free(info_);
+      *success = false;
+    } else {
+      handle = CreateThread(
+          nullptr, 64 * 1024,
+          [](void* v) WIN_LAMBDA -> DWORD {
+            g_thd_info = static_cast<thd_info*>(v);
+            gpr_mu_lock(&g_thd_info->thread->mu_);
+            while (!g_thd_info->thread->started_) {
+              gpr_cv_wait(&g_thd_info->thread->ready_, &g_thd_info->thread->mu_,
+                          gpr_inf_future(GPR_CLOCK_MONOTONIC));
+            }
+            gpr_mu_unlock(&g_thd_info->thread->mu_);
+            g_thd_info->body(g_thd_info->arg);
+            BOOL ret = SetEvent(g_thd_info->join_event);
+            GPR_ASSERT(ret);
+            return 0;
+          },
+          info_, 0, nullptr);
+      if (handle == nullptr) {
+        destroy_thread();
+        *success = false;
+      } else {
+        CloseHandle(handle);
+        *success = true;
+      }
+    }
+  }
+
+  ~ThreadInternalsWindows() override {
+    gpr_mu_destroy(&mu_);
+    gpr_cv_destroy(&ready_);
+  }
+
+  void Start() override {
+    gpr_mu_lock(&mu_);
+    started_ = true;
+    gpr_cv_signal(&ready_);
+    gpr_mu_unlock(&mu_);
+  }
+
+  void Join() override {
+    DWORD ret = WaitForSingleObject(info_->join_event, INFINITE);
+    GPR_ASSERT(ret == WAIT_OBJECT_0);
+    destroy_thread();
+  }
+
+ private:
+  void destroy_thread() {
+    CloseHandle(info_->join_event);
+    gpr_free(info_);
+  }
+
+  gpr_mu mu_;
+  gpr_cv ready_;
+  bool started_;
+  thd_info* info_;
+};
+
+}  // namespace
+
+namespace grpc_core {
+
+Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
+               bool* success) {
+  bool outcome = false;
+  impl_ = grpc_core::New<ThreadInternalsWindows>(thd_body, arg, &outcome);
+  if (outcome) {
+    state_ = ALIVE;
+  } else {
+    state_ = FAILED;
+    grpc_core::Delete(impl_);
+    impl_ = nullptr;
+  }
+
+  if (success != nullptr) {
+    *success = outcome;
+  }
+}
+
+}  // namespace grpc_core
+
+gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; }
+
+#endif /* GPR_WINDOWS */
diff --git a/third_party/grpc/src/core/lib/http/format_request.cc b/third_party/grpc/src/core/lib/http/format_request.cc
new file mode 100644
index 0000000..1712344
--- /dev/null
+++ b/third_party/grpc/src/core/lib/http/format_request.cc
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/http/format_request.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/gpr/string.h"
+
+static void fill_common_header(const grpc_httpcli_request* request,
+                               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"));
+  /* just in case some crazy server really expects HTTP/1.1 */
+  gpr_strvec_add(buf, gpr_strdup("Host: "));
+  gpr_strvec_add(buf, gpr_strdup(request->host));
+  gpr_strvec_add(buf, gpr_strdup("\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 */
+  for (i = 0; i < request->http.hdr_count; i++) {
+    gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].key));
+    gpr_strvec_add(buf, gpr_strdup(": "));
+    gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].value));
+    gpr_strvec_add(buf, gpr_strdup("\r\n"));
+  }
+}
+
+grpc_slice grpc_httpcli_format_get_request(
+    const grpc_httpcli_request* request) {
+  gpr_strvec out;
+  char* flat;
+  size_t flat_len;
+
+  gpr_strvec_init(&out);
+  gpr_strvec_add(&out, gpr_strdup("GET "));
+  fill_common_header(request, &out, true);
+  gpr_strvec_add(&out, gpr_strdup("\r\n"));
+
+  flat = gpr_strvec_flatten(&out, &flat_len);
+  gpr_strvec_destroy(&out);
+
+  return grpc_slice_new(flat, flat_len, gpr_free);
+}
+
+grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request* request,
+                                            const char* body_bytes,
+                                            size_t body_size) {
+  gpr_strvec out;
+  char* tmp;
+  size_t out_len;
+  size_t i;
+
+  gpr_strvec_init(&out);
+
+  gpr_strvec_add(&out, gpr_strdup("POST "));
+  fill_common_header(request, &out, true);
+  if (body_bytes) {
+    uint8_t has_content_type = 0;
+    for (i = 0; i < request->http.hdr_count; i++) {
+      if (strcmp(request->http.hdrs[i].key, "Content-Type") == 0) {
+        has_content_type = 1;
+        break;
+      }
+    }
+    if (!has_content_type) {
+      gpr_strvec_add(&out, gpr_strdup("Content-Type: text/plain\r\n"));
+    }
+    gpr_asprintf(&tmp, "Content-Length: %lu\r\n",
+                 static_cast<unsigned long>(body_size));
+    gpr_strvec_add(&out, tmp);
+  }
+  gpr_strvec_add(&out, gpr_strdup("\r\n"));
+  tmp = gpr_strvec_flatten(&out, &out_len);
+  gpr_strvec_destroy(&out);
+
+  if (body_bytes) {
+    tmp = static_cast<char*>(gpr_realloc(tmp, out_len + body_size));
+    memcpy(tmp + out_len, body_bytes, body_size);
+    out_len += body_size;
+  }
+
+  return grpc_slice_new(tmp, out_len, gpr_free);
+}
+
+grpc_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 grpc_slice_new(flat, flat_len, gpr_free);
+}
diff --git a/third_party/grpc/src/core/lib/http/format_request.h b/third_party/grpc/src/core/lib/http/format_request.h
new file mode 100644
index 0000000..bcc332f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/http/format_request.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H
+#define GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/lib/http/httpcli.h"
+
+grpc_slice grpc_httpcli_format_get_request(const grpc_httpcli_request* request);
+grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request* request,
+                                            const char* body_bytes,
+                                            size_t body_size);
+grpc_slice grpc_httpcli_format_connect_request(
+    const grpc_httpcli_request* request);
+
+#endif /* GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H */
diff --git a/third_party/grpc/src/core/lib/http/httpcli.cc b/third_party/grpc/src/core/lib/http/httpcli.cc
new file mode 100644
index 0000000..3bd7a2c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/http/httpcli.cc
@@ -0,0 +1,303 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/http/httpcli.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"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/http/format_request.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/iomgr_internal.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/slice/slice_internal.h"
+
+typedef struct {
+  grpc_slice request_text;
+  grpc_http_parser parser;
+  grpc_resolved_addresses* addresses;
+  size_t next_address;
+  grpc_endpoint* ep;
+  char* host;
+  char* ssl_host_override;
+  grpc_millis deadline;
+  int have_read_byte;
+  const grpc_httpcli_handshaker* handshaker;
+  grpc_closure* on_done;
+  grpc_httpcli_context* context;
+  grpc_polling_entity* pollent;
+  grpc_iomgr_object iomgr_obj;
+  grpc_slice_buffer incoming;
+  grpc_slice_buffer outgoing;
+  grpc_closure on_read;
+  grpc_closure done_write;
+  grpc_closure connected;
+  grpc_error* overall_error;
+  grpc_resource_quota* resource_quota;
+} internal_request;
+
+static grpc_httpcli_get_override g_get_override = nullptr;
+static grpc_httpcli_post_override g_post_override = nullptr;
+
+static void plaintext_handshake(void* arg, grpc_endpoint* endpoint,
+                                const char* host, grpc_millis deadline,
+                                void (*on_done)(void* arg,
+                                                grpc_endpoint* endpoint)) {
+  on_done(arg, endpoint);
+}
+
+const grpc_httpcli_handshaker grpc_httpcli_plaintext = {"http",
+                                                        plaintext_handshake};
+
+void grpc_httpcli_context_init(grpc_httpcli_context* context) {
+  context->pollset_set = grpc_pollset_set_create();
+}
+
+void grpc_httpcli_context_destroy(grpc_httpcli_context* context) {
+  grpc_pollset_set_destroy(context->pollset_set);
+}
+
+static void next_address(internal_request* req, grpc_error* due_to_error);
+
+static void finish(internal_request* req, grpc_error* error) {
+  grpc_polling_entity_del_from_pollset_set(req->pollent,
+                                           req->context->pollset_set);
+  GRPC_CLOSURE_SCHED(req->on_done, error);
+  grpc_http_parser_destroy(&req->parser);
+  if (req->addresses != nullptr) {
+    grpc_resolved_addresses_destroy(req->addresses);
+  }
+  if (req->ep != nullptr) {
+    grpc_endpoint_destroy(req->ep);
+  }
+  grpc_slice_unref_internal(req->request_text);
+  gpr_free(req->host);
+  gpr_free(req->ssl_host_override);
+  grpc_iomgr_unregister_object(&req->iomgr_obj);
+  grpc_slice_buffer_destroy_internal(&req->incoming);
+  grpc_slice_buffer_destroy_internal(&req->outgoing);
+  GRPC_ERROR_UNREF(req->overall_error);
+  grpc_resource_quota_unref_internal(req->resource_quota);
+  gpr_free(req);
+}
+
+static void append_error(internal_request* req, grpc_error* error) {
+  if (req->overall_error == GRPC_ERROR_NONE) {
+    req->overall_error =
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed HTTP/1 client request");
+  }
+  grpc_resolved_address* addr = &req->addresses->addrs[req->next_address - 1];
+  char* addr_text = grpc_sockaddr_to_uri(addr);
+  req->overall_error = grpc_error_add_child(
+      req->overall_error,
+      grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS,
+                         grpc_slice_from_copied_string(addr_text)));
+  gpr_free(addr_text);
+}
+
+static void do_read(internal_request* req) {
+  grpc_endpoint_read(req->ep, &req->incoming, &req->on_read);
+}
+
+static void on_read(void* user_data, grpc_error* error) {
+  internal_request* req = static_cast<internal_request*>(user_data);
+  size_t i;
+
+  for (i = 0; i < req->incoming.count; i++) {
+    if (GRPC_SLICE_LENGTH(req->incoming.slices[i])) {
+      req->have_read_byte = 1;
+      grpc_error* err = grpc_http_parser_parse(
+          &req->parser, req->incoming.slices[i], nullptr);
+      if (err != GRPC_ERROR_NONE) {
+        finish(req, err);
+        return;
+      }
+    }
+  }
+
+  if (error == GRPC_ERROR_NONE) {
+    do_read(req);
+  } else if (!req->have_read_byte) {
+    next_address(req, GRPC_ERROR_REF(error));
+  } else {
+    finish(req, grpc_http_parser_eof(&req->parser));
+  }
+}
+
+static void on_written(internal_request* req) { do_read(req); }
+
+static void done_write(void* arg, grpc_error* error) {
+  internal_request* req = static_cast<internal_request*>(arg);
+  if (error == GRPC_ERROR_NONE) {
+    on_written(req);
+  } else {
+    next_address(req, GRPC_ERROR_REF(error));
+  }
+}
+
+static void start_write(internal_request* req) {
+  grpc_slice_ref_internal(req->request_text);
+  grpc_slice_buffer_add(&req->outgoing, req->request_text);
+  grpc_endpoint_write(req->ep, &req->outgoing, &req->done_write, nullptr);
+}
+
+static void on_handshake_done(void* arg, grpc_endpoint* ep) {
+  internal_request* req = static_cast<internal_request*>(arg);
+
+  if (!ep) {
+    next_address(req, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                          "Unexplained handshake failure"));
+    return;
+  }
+
+  req->ep = ep;
+  start_write(req);
+}
+
+static void on_connected(void* arg, grpc_error* error) {
+  internal_request* req = static_cast<internal_request*>(arg);
+
+  if (!req->ep) {
+    next_address(req, GRPC_ERROR_REF(error));
+    return;
+  }
+  req->handshaker->handshake(
+      req, req->ep, req->ssl_host_override ? req->ssl_host_override : req->host,
+      req->deadline, on_handshake_done);
+}
+
+static void next_address(internal_request* req, grpc_error* error) {
+  grpc_resolved_address* addr;
+  if (error != GRPC_ERROR_NONE) {
+    append_error(req, error);
+  }
+  if (req->next_address == req->addresses->naddrs) {
+    finish(req,
+           GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+               "Failed HTTP requests to all targets", &req->overall_error, 1));
+    return;
+  }
+  addr = &req->addresses->addrs[req->next_address++];
+  GRPC_CLOSURE_INIT(&req->connected, on_connected, req,
+                    grpc_schedule_on_exec_ctx);
+  grpc_arg arg = grpc_channel_arg_pointer_create(
+      (char*)GRPC_ARG_RESOURCE_QUOTA, req->resource_quota,
+      grpc_resource_quota_arg_vtable());
+  grpc_channel_args args = {1, &arg};
+  grpc_tcp_client_connect(&req->connected, &req->ep, req->context->pollset_set,
+                          &args, addr, req->deadline);
+}
+
+static void on_resolved(void* arg, grpc_error* error) {
+  internal_request* req = static_cast<internal_request*>(arg);
+  if (error != GRPC_ERROR_NONE) {
+    finish(req, GRPC_ERROR_REF(error));
+    return;
+  }
+  req->next_address = 0;
+  next_address(req, GRPC_ERROR_NONE);
+}
+
+static void internal_request_begin(grpc_httpcli_context* context,
+                                   grpc_polling_entity* pollent,
+                                   grpc_resource_quota* resource_quota,
+                                   const grpc_httpcli_request* request,
+                                   grpc_millis deadline, grpc_closure* on_done,
+                                   grpc_httpcli_response* response,
+                                   const char* name, grpc_slice request_text) {
+  internal_request* req =
+      static_cast<internal_request*>(gpr_malloc(sizeof(internal_request)));
+  memset(req, 0, sizeof(*req));
+  req->request_text = request_text;
+  grpc_http_parser_init(&req->parser, GRPC_HTTP_RESPONSE, response);
+  req->on_done = on_done;
+  req->deadline = deadline;
+  req->handshaker =
+      request->handshaker ? request->handshaker : &grpc_httpcli_plaintext;
+  req->context = context;
+  req->pollent = pollent;
+  req->overall_error = GRPC_ERROR_NONE;
+  req->resource_quota = grpc_resource_quota_ref_internal(resource_quota);
+  GRPC_CLOSURE_INIT(&req->on_read, on_read, req, grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&req->done_write, done_write, req,
+                    grpc_schedule_on_exec_ctx);
+  grpc_slice_buffer_init(&req->incoming);
+  grpc_slice_buffer_init(&req->outgoing);
+  grpc_iomgr_register_object(&req->iomgr_obj, name);
+  req->host = gpr_strdup(request->host);
+  req->ssl_host_override = gpr_strdup(request->ssl_host_override);
+
+  GPR_ASSERT(pollent);
+  grpc_polling_entity_add_to_pollset_set(req->pollent,
+                                         req->context->pollset_set);
+  grpc_resolve_address(
+      request->host, req->handshaker->default_port, req->context->pollset_set,
+      GRPC_CLOSURE_CREATE(on_resolved, req, grpc_schedule_on_exec_ctx),
+      &req->addresses);
+}
+
+void grpc_httpcli_get(grpc_httpcli_context* context,
+                      grpc_polling_entity* pollent,
+                      grpc_resource_quota* resource_quota,
+                      const grpc_httpcli_request* request, grpc_millis deadline,
+                      grpc_closure* on_done, grpc_httpcli_response* response) {
+  char* name;
+  if (g_get_override && g_get_override(request, deadline, on_done, response)) {
+    return;
+  }
+  gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path);
+  internal_request_begin(context, pollent, resource_quota, request, deadline,
+                         on_done, response, name,
+                         grpc_httpcli_format_get_request(request));
+  gpr_free(name);
+}
+
+void grpc_httpcli_post(grpc_httpcli_context* context,
+                       grpc_polling_entity* pollent,
+                       grpc_resource_quota* resource_quota,
+                       const grpc_httpcli_request* request,
+                       const char* body_bytes, size_t body_size,
+                       grpc_millis deadline, grpc_closure* on_done,
+                       grpc_httpcli_response* response) {
+  char* name;
+  if (g_post_override && g_post_override(request, body_bytes, body_size,
+                                         deadline, on_done, response)) {
+    return;
+  }
+  gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path);
+  internal_request_begin(
+      context, pollent, resource_quota, request, deadline, on_done, response,
+      name, grpc_httpcli_format_post_request(request, body_bytes, body_size));
+  gpr_free(name);
+}
+
+void grpc_httpcli_set_override(grpc_httpcli_get_override get,
+                               grpc_httpcli_post_override post) {
+  g_get_override = get;
+  g_post_override = post;
+}
diff --git a/third_party/grpc/src/core/lib/http/httpcli.h b/third_party/grpc/src/core/lib/http/httpcli.h
new file mode 100644
index 0000000..b073508
--- /dev/null
+++ b/third_party/grpc/src/core/lib/http/httpcli.h
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_HTTP_HTTPCLI_H
+#define GRPC_CORE_LIB_HTTP_HTTPCLI_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include <grpc/support/time.h>
+
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+/* User agent this library reports */
+#define GRPC_HTTPCLI_USER_AGENT "grpc-httpcli/0.0"
+
+/* Tracks in-progress http requests
+   TODO(ctiller): allow caching and capturing multiple requests for the
+                  same content and combining them */
+typedef struct grpc_httpcli_context {
+  grpc_pollset_set* pollset_set;
+} grpc_httpcli_context;
+
+typedef struct {
+  const char* default_port;
+  void (*handshake)(void* arg, grpc_endpoint* endpoint, const char* host,
+                    grpc_millis deadline,
+                    void (*on_done)(void* arg, grpc_endpoint* endpoint));
+} grpc_httpcli_handshaker;
+
+extern const grpc_httpcli_handshaker grpc_httpcli_plaintext;
+extern const grpc_httpcli_handshaker grpc_httpcli_ssl;
+
+/* A request */
+typedef struct grpc_httpcli_request {
+  /* The host name to connect to */
+  char* host;
+  /* The host to verify in the SSL handshake (or NULL) */
+  char* ssl_host_override;
+  /* The main part of the request
+     The following headers are supplied automatically and MUST NOT be set here:
+     Host, Connection, User-Agent */
+  grpc_http_request http;
+  /* handshaker to use ssl for the request */
+  const grpc_httpcli_handshaker* handshaker;
+} grpc_httpcli_request;
+
+/* Expose the parser response type as a httpcli response too */
+typedef struct grpc_http_response grpc_httpcli_response;
+
+void grpc_httpcli_context_init(grpc_httpcli_context* context);
+void grpc_httpcli_context_destroy(grpc_httpcli_context* context);
+
+/* Asynchronously perform a HTTP GET.
+   'context' specifies the http context under which to do the get
+   'pollset' indicates a grpc_pollset that is interested in the result
+     of the get - work on this pollset may be used to progress the get
+     operation
+   '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 */
+void grpc_httpcli_get(grpc_httpcli_context* context,
+                      grpc_polling_entity* pollent,
+                      grpc_resource_quota* resource_quota,
+                      const grpc_httpcli_request* request, grpc_millis deadline,
+                      grpc_closure* on_complete,
+                      grpc_httpcli_response* response);
+
+/* Asynchronously perform a HTTP POST.
+   'context' specifies the http context under which to do the post
+   'pollset' indicates a grpc_pollset that is interested in the result
+     of the post - work on this pollset may be used to progress the post
+     operation
+   'request' contains request parameters - these are caller owned and can be
+     destroyed once the call returns
+   'body_bytes' and 'body_size' specify the payload for the post.
+     When there is no body, pass in NULL as body_bytes.
+   '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
+   Does not support ?var1=val1&var2=val2 in the path. */
+void grpc_httpcli_post(grpc_httpcli_context* context,
+                       grpc_polling_entity* pollent,
+                       grpc_resource_quota* resource_quota,
+                       const grpc_httpcli_request* request,
+                       const char* body_bytes, size_t body_size,
+                       grpc_millis deadline, grpc_closure* on_complete,
+                       grpc_httpcli_response* response);
+
+/* override functions return 1 if they handled the request, 0 otherwise */
+typedef int (*grpc_httpcli_get_override)(const grpc_httpcli_request* request,
+                                         grpc_millis deadline,
+                                         grpc_closure* on_complete,
+                                         grpc_httpcli_response* response);
+typedef int (*grpc_httpcli_post_override)(const grpc_httpcli_request* request,
+                                          const char* body_bytes,
+                                          size_t body_size,
+                                          grpc_millis deadline,
+                                          grpc_closure* on_complete,
+                                          grpc_httpcli_response* response);
+
+void grpc_httpcli_set_override(grpc_httpcli_get_override get,
+                               grpc_httpcli_post_override post);
+
+#endif /* GRPC_CORE_LIB_HTTP_HTTPCLI_H */
diff --git a/third_party/grpc/src/core/lib/http/httpcli_security_connector.cc b/third_party/grpc/src/core/lib/http/httpcli_security_connector.cc
new file mode 100644
index 0000000..fdea751
--- /dev/null
+++ b/third_party/grpc/src/core/lib/http/httpcli_security_connector.cc
@@ -0,0 +1,210 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/http/httpcli.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"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/ssl_transport_security.h"
+
+class grpc_httpcli_ssl_channel_security_connector final
+    : public grpc_channel_security_connector {
+ public:
+  explicit grpc_httpcli_ssl_channel_security_connector(char* secure_peer_name)
+      : grpc_channel_security_connector(
+            /*url_scheme=*/nullptr,
+            /*channel_creds=*/nullptr,
+            /*request_metadata_creds=*/nullptr),
+        secure_peer_name_(secure_peer_name) {}
+
+  ~grpc_httpcli_ssl_channel_security_connector() override {
+    if (handshaker_factory_ != nullptr) {
+      tsi_ssl_client_handshaker_factory_unref(handshaker_factory_);
+    }
+    if (secure_peer_name_ != nullptr) {
+      gpr_free(secure_peer_name_);
+    }
+  }
+
+  tsi_result InitHandshakerFactory(const char* pem_root_certs,
+                                   const tsi_ssl_root_certs_store* root_store) {
+    tsi_ssl_client_handshaker_options options;
+    memset(&options, 0, sizeof(options));
+    options.pem_root_certs = pem_root_certs;
+    options.root_store = root_store;
+    return tsi_create_ssl_client_handshaker_factory_with_options(
+        &options, &handshaker_factory_);
+  }
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_mgr) override {
+    tsi_handshaker* handshaker = nullptr;
+    if (handshaker_factory_ != nullptr) {
+      tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker(
+          handshaker_factory_, secure_peer_name_, &handshaker);
+      if (result != TSI_OK) {
+        gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
+                tsi_result_to_string(result));
+      }
+    }
+    grpc_handshake_manager_add(
+        handshake_mgr, grpc_security_handshaker_create(handshaker, this));
+  }
+
+  tsi_ssl_client_handshaker_factory* handshaker_factory() const {
+    return handshaker_factory_;
+  }
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* /*auth_context*/,
+                  grpc_closure* on_peer_checked) override {
+    grpc_error* error = GRPC_ERROR_NONE;
+
+    /* Check the peer name. */
+    if (secure_peer_name_ != nullptr &&
+        !tsi_ssl_peer_matches_name(&peer, secure_peer_name_)) {
+      char* msg;
+      gpr_asprintf(&msg, "Peer name %s is not in peer certificate",
+                   secure_peer_name_);
+      error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      gpr_free(msg);
+    }
+    GRPC_CLOSURE_SCHED(on_peer_checked, error);
+    tsi_peer_destruct(&peer);
+  }
+
+  int cmp(const grpc_security_connector* other_sc) const override {
+    auto* other =
+        reinterpret_cast<const grpc_httpcli_ssl_channel_security_connector*>(
+            other_sc);
+    return strcmp(secure_peer_name_, other->secure_peer_name_);
+  }
+
+  bool check_call_host(const char* host, grpc_auth_context* auth_context,
+                       grpc_closure* on_call_host_checked,
+                       grpc_error** error) override {
+    *error = GRPC_ERROR_NONE;
+    return true;
+  }
+
+  void cancel_check_call_host(grpc_closure* on_call_host_checked,
+                              grpc_error* error) override {
+    GRPC_ERROR_UNREF(error);
+  }
+
+  const char* secure_peer_name() const { return secure_peer_name_; }
+
+ private:
+  tsi_ssl_client_handshaker_factory* handshaker_factory_ = nullptr;
+  char* secure_peer_name_;
+};
+
+static grpc_core::RefCountedPtr<grpc_channel_security_connector>
+httpcli_ssl_channel_security_connector_create(
+    const char* pem_root_certs, const tsi_ssl_root_certs_store* root_store,
+    const char* secure_peer_name) {
+  if (secure_peer_name != nullptr && pem_root_certs == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Cannot assert a secure peer name without a trust root.");
+    return nullptr;
+  }
+  grpc_core::RefCountedPtr<grpc_httpcli_ssl_channel_security_connector> c =
+      grpc_core::MakeRefCounted<grpc_httpcli_ssl_channel_security_connector>(
+          secure_peer_name == nullptr ? nullptr : gpr_strdup(secure_peer_name));
+  tsi_result result = c->InitHandshakerFactory(pem_root_certs, root_store);
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
+            tsi_result_to_string(result));
+    return nullptr;
+  }
+  return c;
+}
+
+/* handshaker */
+
+typedef struct {
+  void (*func)(void* arg, grpc_endpoint* endpoint);
+  void* arg;
+  grpc_handshake_manager* handshake_mgr;
+} on_done_closure;
+
+static void on_handshake_done(void* arg, grpc_error* error) {
+  grpc_handshaker_args* args = static_cast<grpc_handshaker_args*>(arg);
+  on_done_closure* c = static_cast<on_done_closure*>(args->user_data);
+  if (error != GRPC_ERROR_NONE) {
+    const char* msg = grpc_error_string(error);
+    gpr_log(GPR_ERROR, "Secure transport setup failed: %s", msg);
+
+    c->func(c->arg, nullptr);
+  } else {
+    grpc_channel_args_destroy(args->args);
+    grpc_slice_buffer_destroy_internal(args->read_buffer);
+    gpr_free(args->read_buffer);
+    c->func(c->arg, args->endpoint);
+  }
+  grpc_handshake_manager_destroy(c->handshake_mgr);
+  gpr_free(c);
+}
+
+static void ssl_handshake(void* arg, grpc_endpoint* tcp, const char* host,
+                          grpc_millis deadline,
+                          void (*on_done)(void* arg, grpc_endpoint* endpoint)) {
+  on_done_closure* c = static_cast<on_done_closure*>(gpr_malloc(sizeof(*c)));
+  const char* pem_root_certs =
+      grpc_core::DefaultSslRootStore::GetPemRootCerts();
+  const tsi_ssl_root_certs_store* root_store =
+      grpc_core::DefaultSslRootStore::GetRootStore();
+  if (root_store == nullptr) {
+    gpr_log(GPR_ERROR, "Could not get default pem root certs.");
+    on_done(arg, nullptr);
+    gpr_free(c);
+    return;
+  }
+  c->func = on_done;
+  c->arg = arg;
+  grpc_core::RefCountedPtr<grpc_channel_security_connector> sc =
+      httpcli_ssl_channel_security_connector_create(pem_root_certs, root_store,
+                                                    host);
+  GPR_ASSERT(sc != nullptr);
+  grpc_arg channel_arg = grpc_security_connector_to_arg(sc.get());
+  grpc_channel_args args = {1, &channel_arg};
+  c->handshake_mgr = grpc_handshake_manager_create();
+  grpc_handshakers_add(HANDSHAKER_CLIENT, &args,
+                       nullptr /* interested_parties */, c->handshake_mgr);
+  grpc_handshake_manager_do_handshake(
+      c->handshake_mgr, tcp, nullptr /* channel_args */, deadline,
+      nullptr /* acceptor */, on_handshake_done, c /* user_data */);
+  sc.reset(DEBUG_LOCATION, "httpcli");
+}
+
+const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake};
diff --git a/third_party/grpc/src/core/lib/http/parser.cc b/third_party/grpc/src/core/lib/http/parser.cc
new file mode 100644
index 0000000..a37fdda
--- /dev/null
+++ b/third_party/grpc/src/core/lib/http/parser.cc
@@ -0,0 +1,371 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/http/parser.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+grpc_core::TraceFlag grpc_http1_trace(false, "http1");
+
+static char* buf2str(void* buffer, size_t length) {
+  char* out = static_cast<char*>(gpr_malloc(length + 1));
+  memcpy(out, buffer, length);
+  out[length] = 0;
+  return out;
+}
+
+static grpc_error* handle_response_line(grpc_http_parser* parser) {
+  uint8_t* beg = parser->cur_line;
+  uint8_t* cur = beg;
+  uint8_t* end = beg + parser->cur_line_length;
+
+  if (cur == end || *cur++ != 'H')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'H'");
+  if (cur == end || *cur++ != 'T')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'");
+  if (cur == end || *cur++ != 'T')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'");
+  if (cur == end || *cur++ != 'P')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'P'");
+  if (cur == end || *cur++ != '/')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '/'");
+  if (cur == end || *cur++ != '1')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '1'");
+  if (cur == end || *cur++ != '.')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '.'");
+  if (cur == end || *cur < '0' || *cur++ > '1') {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Expected HTTP/1.0 or HTTP/1.1");
+  }
+  if (cur == end || *cur++ != ' ')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected ' '");
+  if (cur == end || *cur < '1' || *cur++ > '9')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code");
+  if (cur == end || *cur < '0' || *cur++ > '9')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code");
+  if (cur == end || *cur < '0' || *cur++ > '9')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code");
+  parser->http.response->status =
+      (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
+  if (cur == end || *cur++ != ' ')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected ' '");
+
+  /* we don't really care about the status code message */
+
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* handle_request_line(grpc_http_parser* parser) {
+  uint8_t* beg = parser->cur_line;
+  uint8_t* cur = beg;
+  uint8_t* end = beg + parser->cur_line_length;
+  uint8_t vers_major = 0;
+  uint8_t vers_minor = 0;
+
+  while (cur != end && *cur++ != ' ')
+    ;
+  if (cur == end)
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "No method on HTTP request line");
+  parser->http.request->method =
+      buf2str(beg, static_cast<size_t>(cur - beg - 1));
+
+  beg = cur;
+  while (cur != end && *cur++ != ' ')
+    ;
+  if (cur == end)
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No path on HTTP request line");
+  parser->http.request->path = buf2str(beg, static_cast<size_t>(cur - beg - 1));
+
+  if (cur == end || *cur++ != 'H')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'H'");
+  if (cur == end || *cur++ != 'T')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'");
+  if (cur == end || *cur++ != 'T')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'");
+  if (cur == end || *cur++ != 'P')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'P'");
+  if (cur == end || *cur++ != '/')
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '/'");
+  vers_major = static_cast<uint8_t>(*cur++ - '1' + 1);
+  ++cur;
+  if (cur == end)
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "End of line in HTTP version string");
+  vers_minor = static_cast<uint8_t>(*cur++ - '1' + 1);
+
+  if (vers_major == 1) {
+    if (vers_minor == 0) {
+      parser->http.request->version = GRPC_HTTP_HTTP10;
+    } else if (vers_minor == 1) {
+      parser->http.request->version = GRPC_HTTP_HTTP11;
+    } else {
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+    }
+  } else if (vers_major == 2) {
+    if (vers_minor == 0) {
+      parser->http.request->version = GRPC_HTTP_HTTP20;
+    } else {
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+    }
+  } else {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* handle_first_line(grpc_http_parser* parser) {
+  switch (parser->type) {
+    case GRPC_HTTP_REQUEST:
+      return handle_request_line(parser);
+    case GRPC_HTTP_RESPONSE:
+      return handle_response_line(parser);
+  }
+  GPR_UNREACHABLE_CODE(
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here"));
+}
+
+static grpc_error* add_header(grpc_http_parser* parser) {
+  uint8_t* beg = parser->cur_line;
+  uint8_t* cur = beg;
+  uint8_t* end = beg + parser->cur_line_length;
+  size_t* hdr_count = nullptr;
+  grpc_http_header** hdrs = nullptr;
+  grpc_http_header hdr = {nullptr, nullptr};
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  GPR_ASSERT(cur != end);
+
+  if (*cur == ' ' || *cur == '\t') {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Continued header lines not supported yet");
+    goto done;
+  }
+
+  while (cur != end && *cur != ':') {
+    cur++;
+  }
+  if (cur == end) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Didn't find ':' in header string");
+    goto done;
+  }
+  GPR_ASSERT(cur >= beg);
+  hdr.key = buf2str(beg, static_cast<size_t>(cur - beg));
+  cur++; /* skip : */
+
+  while (cur != end && (*cur == ' ' || *cur == '\t')) {
+    cur++;
+  }
+  GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length);
+  hdr.value = buf2str(
+      cur, static_cast<size_t>(end - cur) - parser->cur_line_end_length);
+
+  switch (parser->type) {
+    case GRPC_HTTP_RESPONSE:
+      hdr_count = &parser->http.response->hdr_count;
+      hdrs = &parser->http.response->hdrs;
+      break;
+    case GRPC_HTTP_REQUEST:
+      hdr_count = &parser->http.request->hdr_count;
+      hdrs = &parser->http.request->hdrs;
+      break;
+  }
+
+  if (*hdr_count == parser->hdr_capacity) {
+    parser->hdr_capacity =
+        GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2);
+    *hdrs = static_cast<grpc_http_header*>(
+        gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs)));
+  }
+  (*hdrs)[(*hdr_count)++] = hdr;
+
+done:
+  if (error != GRPC_ERROR_NONE) {
+    gpr_free(hdr.key);
+    gpr_free(hdr.value);
+  }
+  return error;
+}
+
+static grpc_error* finish_line(grpc_http_parser* parser,
+                               bool* found_body_start) {
+  grpc_error* err;
+  switch (parser->state) {
+    case GRPC_HTTP_FIRST_LINE:
+      err = handle_first_line(parser);
+      if (err != GRPC_ERROR_NONE) return err;
+      parser->state = GRPC_HTTP_HEADERS;
+      break;
+    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);
+      if (err != GRPC_ERROR_NONE) {
+        return err;
+      }
+      break;
+    case GRPC_HTTP_BODY:
+      GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "Should never reach here"));
+  }
+
+  parser->cur_line_length = 0;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* addbyte_body(grpc_http_parser* parser, uint8_t byte) {
+  size_t* body_length = nullptr;
+  char** body = nullptr;
+
+  if (parser->type == GRPC_HTTP_RESPONSE) {
+    body_length = &parser->http.response->body_length;
+    body = &parser->http.response->body;
+  } else if (parser->type == GRPC_HTTP_REQUEST) {
+    body_length = &parser->http.request->body_length;
+    body = &parser->http.request->body;
+  } else {
+    GPR_UNREACHABLE_CODE(
+        return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here"));
+  }
+
+  if (*body_length == parser->body_capacity) {
+    parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2);
+    *body =
+        static_cast<char*>(gpr_realloc((void*)*body, parser->body_capacity));
+  }
+  (*body)[*body_length] = static_cast<char>(byte);
+  (*body_length)++;
+
+  return GRPC_ERROR_NONE;
+}
+
+static bool check_line(grpc_http_parser* parser) {
+  if (parser->cur_line_length >= 2 &&
+      parser->cur_line[parser->cur_line_length - 2] == '\r' &&
+      parser->cur_line[parser->cur_line_length - 1] == '\n') {
+    return true;
+  }
+
+  // HTTP request with \n\r line termiantors.
+  else if (parser->cur_line_length >= 2 &&
+           parser->cur_line[parser->cur_line_length - 2] == '\n' &&
+           parser->cur_line[parser->cur_line_length - 1] == '\r') {
+    return true;
+  }
+
+  // HTTP request with only \n line terminators.
+  else if (parser->cur_line_length >= 1 &&
+           parser->cur_line[parser->cur_line_length - 1] == '\n') {
+    parser->cur_line_end_length = 1;
+    return true;
+  }
+
+  return false;
+}
+
+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:
+      if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) {
+        if (grpc_http1_trace.enabled())
+          gpr_log(GPR_ERROR, "HTTP header max line length (%d) exceeded",
+                  GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
+        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "HTTP header max line length exceeded");
+      }
+      parser->cur_line[parser->cur_line_length] = byte;
+      parser->cur_line_length++;
+      if (check_line(parser)) {
+        return finish_line(parser, found_body_start);
+      }
+      return GRPC_ERROR_NONE;
+    case GRPC_HTTP_BODY:
+      return addbyte_body(parser, byte);
+  }
+  GPR_UNREACHABLE_CODE(return GRPC_ERROR_NONE);
+}
+
+void grpc_http_parser_init(grpc_http_parser* parser, grpc_http_type type,
+                           void* request_or_response) {
+  memset(parser, 0, sizeof(*parser));
+  parser->state = GRPC_HTTP_FIRST_LINE;
+  parser->type = type;
+  parser->http.request_or_response = request_or_response;
+  parser->cur_line_end_length = 2;
+}
+
+void grpc_http_parser_destroy(grpc_http_parser* parser) {}
+
+void grpc_http_request_destroy(grpc_http_request* request) {
+  size_t i;
+  gpr_free(request->body);
+  for (i = 0; i < request->hdr_count; i++) {
+    gpr_free(request->hdrs[i].key);
+    gpr_free(request->hdrs[i].value);
+  }
+  gpr_free(request->hdrs);
+  gpr_free(request->method);
+  gpr_free(request->path);
+}
+
+void grpc_http_response_destroy(grpc_http_response* response) {
+  size_t i;
+  gpr_free(response->body);
+  for (i = 0; i < response->hdr_count; i++) {
+    gpr_free(response->hdrs[i].key);
+    gpr_free(response->hdrs[i].value);
+  }
+  gpr_free(response->hdrs);
+}
+
+grpc_error* grpc_http_parser_parse(grpc_http_parser* parser, grpc_slice slice,
+                                   size_t* start_of_body) {
+  for (size_t i = 0; i < GRPC_SLICE_LENGTH(slice); i++) {
+    bool found_body_start = false;
+    grpc_error* err =
+        addbyte(parser, GRPC_SLICE_START_PTR(slice)[i], &found_body_start);
+    if (err != GRPC_ERROR_NONE) return err;
+    if (found_body_start && start_of_body != nullptr) *start_of_body = i + 1;
+  }
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_http_parser_eof(grpc_http_parser* parser) {
+  if (parser->state != GRPC_HTTP_BODY) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Did not finish headers");
+  }
+  return GRPC_ERROR_NONE;
+}
diff --git a/third_party/grpc/src/core/lib/http/parser.h b/third_party/grpc/src/core/lib/http/parser.h
new file mode 100644
index 0000000..a8f47c9
--- /dev/null
+++ b/third_party/grpc/src/core/lib/http/parser.h
@@ -0,0 +1,113 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_HTTP_PARSER_H
+#define GRPC_CORE_LIB_HTTP_PARSER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/error.h"
+
+/* Maximum length of a header string of the form 'Key: Value\r\n' */
+#define GRPC_HTTP_PARSER_MAX_HEADER_LENGTH 4096
+
+/* A single header to be passed in a request */
+typedef struct grpc_http_header {
+  char* key;
+  char* value;
+} grpc_http_header;
+
+typedef enum {
+  GRPC_HTTP_FIRST_LINE,
+  GRPC_HTTP_HEADERS,
+  GRPC_HTTP_BODY
+} grpc_http_parser_state;
+
+typedef enum {
+  GRPC_HTTP_HTTP10,
+  GRPC_HTTP_HTTP11,
+  GRPC_HTTP_HTTP20,
+} grpc_http_version;
+
+typedef enum {
+  GRPC_HTTP_RESPONSE,
+  GRPC_HTTP_REQUEST,
+} grpc_http_type;
+
+/* A request */
+typedef struct grpc_http_request {
+  /* Method of the request (e.g. GET, POST) */
+  char* method;
+  /* The path of the resource to fetch */
+  char* path;
+  /* HTTP version to use */
+  grpc_http_version version;
+  /* Headers attached to the request */
+  size_t hdr_count;
+  grpc_http_header* hdrs;
+  /* Body: length and contents; contents are NOT null-terminated */
+  size_t body_length;
+  char* body;
+} grpc_http_request;
+
+/* A response */
+typedef struct grpc_http_response {
+  /* HTTP status code */
+  int status = 0;
+  /* Headers: count and key/values */
+  size_t hdr_count = 0;
+  grpc_http_header* hdrs = nullptr;
+  /* Body: length and contents; contents are NOT null-terminated */
+  size_t body_length = 0;
+  char* body = nullptr;
+} grpc_http_response;
+
+typedef struct {
+  grpc_http_parser_state state;
+  grpc_http_type type;
+
+  union {
+    grpc_http_response* response;
+    grpc_http_request* request;
+    void* request_or_response;
+  } http;
+  size_t body_capacity;
+  size_t hdr_capacity;
+
+  uint8_t cur_line[GRPC_HTTP_PARSER_MAX_HEADER_LENGTH];
+  size_t cur_line_length;
+  size_t cur_line_end_length;
+} grpc_http_parser;
+
+void grpc_http_parser_init(grpc_http_parser* parser, grpc_http_type type,
+                           void* request_or_response);
+void grpc_http_parser_destroy(grpc_http_parser* parser);
+
+/* 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, grpc_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);
+void grpc_http_response_destroy(grpc_http_response* response);
+
+extern grpc_core::TraceFlag grpc_http1_trace;
+
+#endif /* GRPC_CORE_LIB_HTTP_PARSER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/README.md b/third_party/grpc/src/core/lib/iomgr/README.md
new file mode 100644
index 0000000..9b22b76
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/README.md
@@ -0,0 +1,6 @@
+# iomgr
+
+Platform abstractions for I/O (mostly network).
+
+Provides abstractions over TCP/UDP I/O, file loading, polling, and concurrency
+management for various operating systems.
diff --git a/third_party/grpc/src/core/lib/iomgr/block_annotate.h b/third_party/grpc/src/core/lib/iomgr/block_annotate.h
new file mode 100644
index 0000000..a57873a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/block_annotate.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H
+#define GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H
+
+void gpr_thd_start_blocking_region();
+void gpr_thd_end_blocking_region();
+
+/* These annotations identify the beginning and end of regions where
+   the code may block for reasons other than synchronization functions.
+   These include poll, epoll, and getaddrinfo. */
+
+#ifdef GRPC_SCHEDULING_MARK_BLOCKING_REGION
+#define GRPC_SCHEDULING_START_BLOCKING_REGION \
+  do {                                        \
+    gpr_thd_start_blocking_region();          \
+  } while (0)
+#define GRPC_SCHEDULING_END_BLOCKING_REGION     \
+  do {                                          \
+    gpr_thd_end_blocking_region();              \
+    grpc_core::ExecCtx::Get()->InvalidateNow(); \
+  } while (0)
+#define GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX \
+  do {                                                  \
+    gpr_thd_end_blocking_region();                      \
+  } while (0)
+
+#else
+#define GRPC_SCHEDULING_START_BLOCKING_REGION \
+  do {                                        \
+  } while (0)
+#define GRPC_SCHEDULING_END_BLOCKING_REGION     \
+  do {                                          \
+    grpc_core::ExecCtx::Get()->InvalidateNow(); \
+  } while (0)
+#define GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX \
+  do {                                                  \
+  } while (0)
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/buffer_list.cc b/third_party/grpc/src/core/lib/iomgr/buffer_list.cc
new file mode 100644
index 0000000..ace17a1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/buffer_list.cc
@@ -0,0 +1,145 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/buffer_list.h"
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/log.h>
+
+#ifdef GRPC_LINUX_ERRQUEUE
+#include <time.h>
+
+#include "src/core/lib/gprpp/memory.h"
+
+namespace grpc_core {
+void TracedBuffer::AddNewEntry(TracedBuffer** head, uint32_t seq_no,
+                               void* arg) {
+  GPR_DEBUG_ASSERT(head != nullptr);
+  TracedBuffer* new_elem = New<TracedBuffer>(seq_no, arg);
+  /* Store the current time as the sendmsg time. */
+  new_elem->ts_.sendmsg_time = gpr_now(GPR_CLOCK_REALTIME);
+  new_elem->ts_.scheduled_time = gpr_inf_past(GPR_CLOCK_REALTIME);
+  new_elem->ts_.sent_time = gpr_inf_past(GPR_CLOCK_REALTIME);
+  new_elem->ts_.acked_time = gpr_inf_past(GPR_CLOCK_REALTIME);
+  if (*head == nullptr) {
+    *head = new_elem;
+    return;
+  }
+  /* Append at the end. */
+  TracedBuffer* ptr = *head;
+  while (ptr->next_ != nullptr) {
+    ptr = ptr->next_;
+  }
+  ptr->next_ = new_elem;
+}
+
+namespace {
+/** Fills gpr_timespec gts based on values from timespec ts */
+void fill_gpr_from_timestamp(gpr_timespec* gts, const struct timespec* ts) {
+  gts->tv_sec = ts->tv_sec;
+  gts->tv_nsec = static_cast<int32_t>(ts->tv_nsec);
+  gts->clock_type = GPR_CLOCK_REALTIME;
+}
+
+void default_timestamps_callback(void* arg, grpc_core::Timestamps* ts,
+                                 grpc_error* shudown_err) {
+  gpr_log(GPR_DEBUG, "Timestamps callback has not been registered");
+}
+
+/** The saved callback function that will be invoked when we get all the
+ * timestamps that we are going to get for a TracedBuffer. */
+void (*timestamps_callback)(void*, grpc_core::Timestamps*,
+                            grpc_error* shutdown_err) =
+    default_timestamps_callback;
+} /* namespace */
+
+void TracedBuffer::ProcessTimestamp(TracedBuffer** head,
+                                    struct sock_extended_err* serr,
+                                    struct scm_timestamping* tss) {
+  GPR_DEBUG_ASSERT(head != nullptr);
+  TracedBuffer* elem = *head;
+  TracedBuffer* next = nullptr;
+  while (elem != nullptr) {
+    /* The byte number refers to the sequence number of the last byte which this
+     * timestamp relates to. */
+    if (serr->ee_data >= elem->seq_no_) {
+      switch (serr->ee_info) {
+        case SCM_TSTAMP_SCHED:
+          fill_gpr_from_timestamp(&(elem->ts_.scheduled_time), &(tss->ts[0]));
+          elem = elem->next_;
+          break;
+        case SCM_TSTAMP_SND:
+          fill_gpr_from_timestamp(&(elem->ts_.sent_time), &(tss->ts[0]));
+          elem = elem->next_;
+          break;
+        case SCM_TSTAMP_ACK:
+          fill_gpr_from_timestamp(&(elem->ts_.acked_time), &(tss->ts[0]));
+          /* Got all timestamps. Do the callback and free this TracedBuffer.
+           * The thing below can be passed by value if we don't want the
+           * restriction on the lifetime. */
+          timestamps_callback(elem->arg_, &(elem->ts_), GRPC_ERROR_NONE);
+          next = elem->next_;
+          Delete<TracedBuffer>(elem);
+          *head = elem = next;
+          break;
+        default:
+          abort();
+      }
+    } else {
+      break;
+    }
+  }
+}
+
+void TracedBuffer::Shutdown(TracedBuffer** head, void* remaining,
+                            grpc_error* shutdown_err) {
+  GPR_DEBUG_ASSERT(head != nullptr);
+  TracedBuffer* elem = *head;
+  while (elem != nullptr) {
+    timestamps_callback(elem->arg_, &(elem->ts_), shutdown_err);
+    auto* next = elem->next_;
+    Delete<TracedBuffer>(elem);
+    elem = next;
+  }
+  *head = nullptr;
+  if (remaining != nullptr) {
+    timestamps_callback(remaining, nullptr, shutdown_err);
+  }
+  GRPC_ERROR_UNREF(shutdown_err);
+}
+
+void grpc_tcp_set_write_timestamps_callback(void (*fn)(void*,
+                                                       grpc_core::Timestamps*,
+                                                       grpc_error* error)) {
+  timestamps_callback = fn;
+}
+} /* namespace grpc_core */
+
+#else /* GRPC_LINUX_ERRQUEUE */
+
+namespace grpc_core {
+void grpc_tcp_set_write_timestamps_callback(void (*fn)(void*,
+                                                       grpc_core::Timestamps*,
+                                                       grpc_error* error)) {
+  gpr_log(GPR_DEBUG, "Timestamps callback is not enabled for this platform");
+}
+} /* namespace grpc_core */
+
+#endif /* GRPC_LINUX_ERRQUEUE */
diff --git a/third_party/grpc/src/core/lib/iomgr/buffer_list.h b/third_party/grpc/src/core/lib/iomgr/buffer_list.h
new file mode 100644
index 0000000..627f1bd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/buffer_list.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_BUFFER_LIST_H
+#define GRPC_CORE_LIB_IOMGR_BUFFER_LIST_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/internal_errqueue.h"
+
+namespace grpc_core {
+struct Timestamps {
+  /* TODO(yashykt): This would also need to store OPTSTAT once support is added
+   */
+  gpr_timespec sendmsg_time;
+  gpr_timespec scheduled_time;
+  gpr_timespec sent_time;
+  gpr_timespec acked_time;
+
+  uint32_t byte_offset; /* byte offset relative to the start of the RPC */
+};
+
+/** TracedBuffer is a class to keep track of timestamps for a specific buffer in
+ * the TCP layer. We are only tracking timestamps for Linux kernels and hence
+ * this class would only be used by Linux platforms. For all other platforms,
+ * TracedBuffer would be an empty class.
+ *
+ * The timestamps collected are according to grpc_core::Timestamps declared
+ * above.
+ *
+ * A TracedBuffer list is kept track of using the head element of the list. If
+ * the head element of the list is nullptr, then the list is empty.
+ */
+#ifdef GRPC_LINUX_ERRQUEUE
+class TracedBuffer {
+ public:
+  /** Add a new entry in the TracedBuffer list pointed to by head. Also saves
+   * sendmsg_time with the current timestamp. */
+  static void AddNewEntry(grpc_core::TracedBuffer** head, uint32_t seq_no,
+                          void* arg);
+
+  /** Processes a received timestamp based on sock_extended_err and
+   * scm_timestamping structures. It will invoke the timestamps callback if the
+   * timestamp type is SCM_TSTAMP_ACK. */
+  static void ProcessTimestamp(grpc_core::TracedBuffer** head,
+                               struct sock_extended_err* serr,
+                               struct scm_timestamping* tss);
+
+  /** Cleans the list by calling the callback for each traced buffer in the list
+   * with timestamps that it has. */
+  static void Shutdown(grpc_core::TracedBuffer** head, void* remaining,
+                       grpc_error* shutdown_err);
+
+ private:
+  GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
+
+  TracedBuffer(uint32_t seq_no, void* arg)
+      : seq_no_(seq_no), arg_(arg), next_(nullptr) {}
+
+  uint32_t seq_no_; /* The sequence number for the last byte in the buffer */
+  void* arg_;       /* The arg to pass to timestamps_callback */
+  grpc_core::Timestamps ts_; /* The timestamps corresponding to this buffer */
+  grpc_core::TracedBuffer* next_; /* The next TracedBuffer in the list */
+};
+#else  /* GRPC_LINUX_ERRQUEUE */
+class TracedBuffer {
+ public:
+  /* Dummy shutdown function */
+  static void Shutdown(grpc_core::TracedBuffer** head, void* remaining,
+                       grpc_error* shutdown_err) {}
+};
+#endif /* GRPC_LINUX_ERRQUEUE */
+
+/** Sets the callback function to call when timestamps for a write are
+ *  collected. The callback does not own a reference to error. */
+void grpc_tcp_set_write_timestamps_callback(void (*fn)(void*,
+                                                       grpc_core::Timestamps*,
+                                                       grpc_error* error));
+
+}; /* namespace grpc_core */
+
+#endif /* GRPC_CORE_LIB_IOMGR_BUFFER_LIST_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/call_combiner.cc b/third_party/grpc/src/core/lib/iomgr/call_combiner.cc
new file mode 100644
index 0000000..6b5759a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/call_combiner.cc
@@ -0,0 +1,262 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/call_combiner.h"
+
+#include <inttypes.h>
+
+#include <grpc/support/log.h>
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/profiling/timers.h"
+
+grpc_core::TraceFlag grpc_call_combiner_trace(false, "call_combiner");
+
+static grpc_error* decode_cancel_state_error(gpr_atm cancel_state) {
+  if (cancel_state & 1) {
+    return (grpc_error*)(cancel_state & ~static_cast<gpr_atm>(1));
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static gpr_atm encode_cancel_state_error(grpc_error* error) {
+  return static_cast<gpr_atm>(1) | (gpr_atm)error;
+}
+
+#ifdef GRPC_TSAN_ENABLED
+static void tsan_closure(void* user_data, grpc_error* error) {
+  grpc_call_combiner* call_combiner =
+      static_cast<grpc_call_combiner*>(user_data);
+  // We ref-count the lock, and check if it's already taken.
+  // If it was taken, we should do nothing. Otherwise, we will mark it as
+  // locked. Note that if two different threads try to do this, only one of
+  // them will be able to mark the lock as acquired, while they both run their
+  // callbacks. In such cases (which should never happen for call_combiner),
+  // TSAN will correctly produce an error.
+  //
+  // TODO(soheil): This only covers the callbacks scheduled by
+  //               grpc_call_combiner_(start|finish). If in the future, a
+  //               callback gets scheduled using other mechanisms, we will need
+  //               to add APIs to externally lock call combiners.
+  grpc_core::RefCountedPtr<grpc_call_combiner::TsanLock> lock =
+      call_combiner->tsan_lock;
+  bool prev = false;
+  if (lock->taken.compare_exchange_strong(prev, true)) {
+    TSAN_ANNOTATE_RWLOCK_ACQUIRED(&lock->taken, true);
+  } else {
+    lock.reset();
+  }
+  GRPC_CLOSURE_RUN(call_combiner->original_closure, GRPC_ERROR_REF(error));
+  if (lock != nullptr) {
+    TSAN_ANNOTATE_RWLOCK_RELEASED(&lock->taken, true);
+    bool prev = true;
+    GPR_ASSERT(lock->taken.compare_exchange_strong(prev, false));
+  }
+}
+#endif
+
+static void call_combiner_sched_closure(grpc_call_combiner* call_combiner,
+                                        grpc_closure* closure,
+                                        grpc_error* error) {
+#ifdef GRPC_TSAN_ENABLED
+  call_combiner->original_closure = closure;
+  GRPC_CLOSURE_SCHED(&call_combiner->tsan_closure, error);
+#else
+  GRPC_CLOSURE_SCHED(closure, error);
+#endif
+}
+
+void grpc_call_combiner_init(grpc_call_combiner* call_combiner) {
+  gpr_atm_no_barrier_store(&call_combiner->cancel_state, 0);
+  gpr_atm_no_barrier_store(&call_combiner->size, 0);
+  gpr_mpscq_init(&call_combiner->queue);
+#ifdef GRPC_TSAN_ENABLED
+  GRPC_CLOSURE_INIT(&call_combiner->tsan_closure, tsan_closure, call_combiner,
+                    grpc_schedule_on_exec_ctx);
+#endif
+}
+
+void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner) {
+  gpr_mpscq_destroy(&call_combiner->queue);
+  GRPC_ERROR_UNREF(decode_cancel_state_error(call_combiner->cancel_state));
+}
+
+#ifndef NDEBUG
+#define DEBUG_ARGS , const char *file, int line
+#define DEBUG_FMT_STR "%s:%d: "
+#define DEBUG_FMT_ARGS , file, line
+#else
+#define DEBUG_ARGS
+#define DEBUG_FMT_STR
+#define DEBUG_FMT_ARGS
+#endif
+
+void grpc_call_combiner_start(grpc_call_combiner* call_combiner,
+                              grpc_closure* closure,
+                              grpc_error* error DEBUG_ARGS,
+                              const char* reason) {
+  GPR_TIMER_SCOPE("call_combiner_start", 0);
+  if (grpc_call_combiner_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "==> grpc_call_combiner_start() [%p] closure=%p [" DEBUG_FMT_STR
+            "%s] error=%s",
+            call_combiner, closure DEBUG_FMT_ARGS, reason,
+            grpc_error_string(error));
+  }
+  size_t prev_size = static_cast<size_t>(
+      gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)1));
+  if (grpc_call_combiner_trace.enabled()) {
+    gpr_log(GPR_INFO, "  size: %" PRIdPTR " -> %" PRIdPTR, prev_size,
+            prev_size + 1);
+  }
+  GRPC_STATS_INC_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS();
+  if (prev_size == 0) {
+    GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED();
+
+    GPR_TIMER_MARK("call_combiner_initiate", 0);
+    if (grpc_call_combiner_trace.enabled()) {
+      gpr_log(GPR_INFO, "  EXECUTING IMMEDIATELY");
+    }
+    // Queue was empty, so execute this closure immediately.
+    call_combiner_sched_closure(call_combiner, closure, error);
+  } else {
+    if (grpc_call_combiner_trace.enabled()) {
+      gpr_log(GPR_INFO, "  QUEUING");
+    }
+    // Queue was not empty, so add closure to queue.
+    closure->error_data.error = error;
+    gpr_mpscq_push(&call_combiner->queue,
+                   reinterpret_cast<gpr_mpscq_node*>(closure));
+  }
+}
+
+void grpc_call_combiner_stop(grpc_call_combiner* call_combiner DEBUG_ARGS,
+                             const char* reason) {
+  GPR_TIMER_SCOPE("call_combiner_stop", 0);
+  if (grpc_call_combiner_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "==> grpc_call_combiner_stop() [%p] [" DEBUG_FMT_STR "%s]",
+            call_combiner DEBUG_FMT_ARGS, reason);
+  }
+  size_t prev_size = static_cast<size_t>(
+      gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)-1));
+  if (grpc_call_combiner_trace.enabled()) {
+    gpr_log(GPR_INFO, "  size: %" PRIdPTR " -> %" PRIdPTR, prev_size,
+            prev_size - 1);
+  }
+  GPR_ASSERT(prev_size >= 1);
+  if (prev_size > 1) {
+    while (true) {
+      if (grpc_call_combiner_trace.enabled()) {
+        gpr_log(GPR_INFO, "  checking queue");
+      }
+      bool empty;
+      grpc_closure* closure = reinterpret_cast<grpc_closure*>(
+          gpr_mpscq_pop_and_check_end(&call_combiner->queue, &empty));
+      if (closure == nullptr) {
+        // This can happen either due to a race condition within the mpscq
+        // code or because of a race with grpc_call_combiner_start().
+        if (grpc_call_combiner_trace.enabled()) {
+          gpr_log(GPR_INFO, "  queue returned no result; checking again");
+        }
+        continue;
+      }
+      if (grpc_call_combiner_trace.enabled()) {
+        gpr_log(GPR_INFO, "  EXECUTING FROM QUEUE: closure=%p error=%s",
+                closure, grpc_error_string(closure->error_data.error));
+      }
+      call_combiner_sched_closure(call_combiner, closure,
+                                  closure->error_data.error);
+      break;
+    }
+  } else if (grpc_call_combiner_trace.enabled()) {
+    gpr_log(GPR_INFO, "  queue empty");
+  }
+}
+
+void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner,
+                                             grpc_closure* closure) {
+  GRPC_STATS_INC_CALL_COMBINER_SET_NOTIFY_ON_CANCEL();
+  while (true) {
+    // Decode original state.
+    gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state);
+    grpc_error* original_error = decode_cancel_state_error(original_state);
+    // If error is set, invoke the cancellation closure immediately.
+    // Otherwise, store the new closure.
+    if (original_error != GRPC_ERROR_NONE) {
+      if (grpc_call_combiner_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "call_combiner=%p: scheduling notify_on_cancel callback=%p "
+                "for pre-existing cancellation",
+                call_combiner, closure);
+      }
+      GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_REF(original_error));
+      break;
+    } else {
+      if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state,
+                           (gpr_atm)closure)) {
+        if (grpc_call_combiner_trace.enabled()) {
+          gpr_log(GPR_INFO, "call_combiner=%p: setting notify_on_cancel=%p",
+                  call_combiner, closure);
+        }
+        // If we replaced an earlier closure, invoke the original
+        // closure with GRPC_ERROR_NONE.  This allows callers to clean
+        // up any resources they may be holding for the callback.
+        if (original_state != 0) {
+          closure = (grpc_closure*)original_state;
+          if (grpc_call_combiner_trace.enabled()) {
+            gpr_log(GPR_INFO,
+                    "call_combiner=%p: scheduling old cancel callback=%p",
+                    call_combiner, closure);
+          }
+          GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
+        }
+        break;
+      }
+    }
+    // cas failed, try again.
+  }
+}
+
+void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner,
+                               grpc_error* error) {
+  GRPC_STATS_INC_CALL_COMBINER_CANCELLED();
+  while (true) {
+    gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state);
+    grpc_error* original_error = decode_cancel_state_error(original_state);
+    if (original_error != GRPC_ERROR_NONE) {
+      GRPC_ERROR_UNREF(error);
+      break;
+    }
+    if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state,
+                         encode_cancel_state_error(error))) {
+      if (original_state != 0) {
+        grpc_closure* notify_on_cancel = (grpc_closure*)original_state;
+        if (grpc_call_combiner_trace.enabled()) {
+          gpr_log(GPR_INFO,
+                  "call_combiner=%p: scheduling notify_on_cancel callback=%p",
+                  call_combiner, notify_on_cancel);
+        }
+        GRPC_CLOSURE_SCHED(notify_on_cancel, GRPC_ERROR_REF(error));
+      }
+      break;
+    }
+    // cas failed, try again.
+  }
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/call_combiner.h b/third_party/grpc/src/core/lib/iomgr/call_combiner.h
new file mode 100644
index 0000000..4ec0044
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/call_combiner.h
@@ -0,0 +1,222 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H
+#define GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include <grpc/support/atm.h>
+
+#include "src/core/lib/gpr/mpscq.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/dynamic_annotations.h"
+
+// A simple, lock-free mechanism for serializing activity related to a
+// single call.  This is similar to a combiner but is more lightweight.
+//
+// It requires the callback (or, in the common case where the callback
+// actually kicks off a chain of callbacks, the last callback in that
+// chain) to explicitly indicate (by calling GRPC_CALL_COMBINER_STOP())
+// when it is done with the action that was kicked off by the original
+// callback.
+
+extern grpc_core::TraceFlag grpc_call_combiner_trace;
+
+struct grpc_call_combiner {
+  gpr_atm size = 0;  // size_t, num closures in queue or currently executing
+  gpr_mpscq queue;
+  // Either 0 (if not cancelled and no cancellation closure set),
+  // a grpc_closure* (if the lowest bit is 0),
+  // or a grpc_error* (if the lowest bit is 1).
+  gpr_atm cancel_state = 0;
+#ifdef GRPC_TSAN_ENABLED
+  // A fake ref-counted lock that is kept alive after the destruction of
+  // grpc_call_combiner, when we are running the original closure.
+  //
+  // Ideally we want to lock and unlock the call combiner as a pointer, when the
+  // callback is called. However, original_closure is free to trigger
+  // anything on the call combiner (including destruction of grpc_call).
+  // Thus, we need a ref-counted structure that can outlive the call combiner.
+  struct TsanLock
+      : public grpc_core::RefCounted<TsanLock,
+                                     grpc_core::NonPolymorphicRefCount> {
+    TsanLock() { TSAN_ANNOTATE_RWLOCK_CREATE(&taken); }
+    ~TsanLock() { TSAN_ANNOTATE_RWLOCK_DESTROY(&taken); }
+
+    // To avoid double-locking by the same thread, we should acquire/release
+    // the lock only when taken is false. On each acquire taken must be set to
+    // true.
+    std::atomic<bool> taken{false};
+  };
+  grpc_core::RefCountedPtr<TsanLock> tsan_lock =
+      grpc_core::MakeRefCounted<TsanLock>();
+  grpc_closure tsan_closure;
+  grpc_closure* original_closure;
+#endif
+};
+
+// Assumes memory was initialized to zero.
+void grpc_call_combiner_init(grpc_call_combiner* call_combiner);
+
+void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner);
+
+#ifndef NDEBUG
+#define GRPC_CALL_COMBINER_START(call_combiner, closure, error, reason)   \
+  grpc_call_combiner_start((call_combiner), (closure), (error), __FILE__, \
+                           __LINE__, (reason))
+#define GRPC_CALL_COMBINER_STOP(call_combiner, reason) \
+  grpc_call_combiner_stop((call_combiner), __FILE__, __LINE__, (reason))
+/// Starts processing \a closure on \a call_combiner.
+void grpc_call_combiner_start(grpc_call_combiner* call_combiner,
+                              grpc_closure* closure, grpc_error* error,
+                              const char* file, int line, const char* reason);
+/// Yields the call combiner to the next closure in the queue, if any.
+void grpc_call_combiner_stop(grpc_call_combiner* call_combiner,
+                             const char* file, int line, const char* reason);
+#else
+#define GRPC_CALL_COMBINER_START(call_combiner, closure, error, reason) \
+  grpc_call_combiner_start((call_combiner), (closure), (error), (reason))
+#define GRPC_CALL_COMBINER_STOP(call_combiner, reason) \
+  grpc_call_combiner_stop((call_combiner), (reason))
+/// Starts processing \a closure on \a call_combiner.
+void grpc_call_combiner_start(grpc_call_combiner* call_combiner,
+                              grpc_closure* closure, grpc_error* error,
+                              const char* reason);
+/// Yields the call combiner to the next closure in the queue, if any.
+void grpc_call_combiner_stop(grpc_call_combiner* call_combiner,
+                             const char* reason);
+#endif
+
+/// Registers \a closure to be invoked by \a call_combiner when
+/// grpc_call_combiner_cancel() is called.
+///
+/// Once a closure is registered, it will always be scheduled exactly
+/// once; this allows the closure to hold references that will be freed
+/// regardless of whether or not the call was cancelled.  If a cancellation
+/// does occur, the closure will be scheduled with the cancellation error;
+/// otherwise, it will be scheduled with GRPC_ERROR_NONE.
+///
+/// The closure will be scheduled in the following cases:
+/// - If grpc_call_combiner_cancel() was called prior to registering the
+///   closure, it will be scheduled immediately with the cancelation error.
+/// - If grpc_call_combiner_cancel() is called after registering the
+///   closure, the closure will be scheduled with the cancellation error.
+/// - If grpc_call_combiner_set_notify_on_cancel() is called again to
+///   register a new cancellation closure, the previous cancellation
+///   closure will be scheduled with GRPC_ERROR_NONE.
+///
+/// If \a closure is NULL, then no closure will be invoked on
+/// cancellation; this effectively unregisters the previously set closure.
+/// However, most filters will not need to explicitly unregister their
+/// callbacks, as this is done automatically when the call is destroyed. Filters
+/// that schedule the cancellation closure on ExecCtx do not need to take a ref
+/// on the call stack to guarantee closure liveness. This is done by explicitly
+/// flushing ExecCtx after the unregistration during call destruction.
+void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner,
+                                             grpc_closure* closure);
+
+/// Indicates that the call has been cancelled.
+void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner,
+                               grpc_error* error);
+
+namespace grpc_core {
+
+// Helper for running a list of closures in a call combiner.
+//
+// Each callback running in the call combiner will eventually be
+// returned to the surface, at which point the surface will yield the
+// call combiner.  So when we are running in the call combiner and have
+// more than one callback to return to the surface, we need to re-enter
+// the call combiner for all but one of those callbacks.
+class CallCombinerClosureList {
+ public:
+  CallCombinerClosureList() {}
+
+  // Adds a closure to the list.  The closure must eventually result in
+  // the call combiner being yielded.
+  void Add(grpc_closure* closure, grpc_error* error, const char* reason) {
+    closures_.emplace_back(closure, error, reason);
+  }
+
+  // Runs all closures in the call combiner and yields the call combiner.
+  //
+  // All but one of the closures in the list will be scheduled via
+  // GRPC_CALL_COMBINER_START(), and the remaining closure will be
+  // scheduled via GRPC_CLOSURE_SCHED(), which will eventually result in
+  // yielding the call combiner.  If the list is empty, then the call
+  // combiner will be yielded immediately.
+  void RunClosures(grpc_call_combiner* call_combiner) {
+    if (closures_.empty()) {
+      GRPC_CALL_COMBINER_STOP(call_combiner, "no closures to schedule");
+      return;
+    }
+    for (size_t i = 1; i < closures_.size(); ++i) {
+      auto& closure = closures_[i];
+      GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error,
+                               closure.reason);
+    }
+    if (grpc_call_combiner_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "CallCombinerClosureList executing closure while already "
+              "holding call_combiner %p: closure=%p error=%s reason=%s",
+              call_combiner, closures_[0].closure,
+              grpc_error_string(closures_[0].error), closures_[0].reason);
+    }
+    // This will release the call combiner.
+    GRPC_CLOSURE_SCHED(closures_[0].closure, closures_[0].error);
+    closures_.clear();
+  }
+
+  // Runs all closures in the call combiner, but does NOT yield the call
+  // combiner.  All closures will be scheduled via GRPC_CALL_COMBINER_START().
+  void RunClosuresWithoutYielding(grpc_call_combiner* call_combiner) {
+    for (size_t i = 0; i < closures_.size(); ++i) {
+      auto& closure = closures_[i];
+      GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error,
+                               closure.reason);
+    }
+    closures_.clear();
+  }
+
+  size_t size() const { return closures_.size(); }
+
+ private:
+  struct CallCombinerClosure {
+    grpc_closure* closure;
+    grpc_error* error;
+    const char* reason;
+
+    CallCombinerClosure(grpc_closure* closure, grpc_error* error,
+                        const char* reason)
+        : closure(closure), error(error), reason(reason) {}
+  };
+
+  // There are generally a maximum of 6 closures to run in the call
+  // combiner, one for each pending op.
+  InlinedVector<CallCombinerClosure, 6> closures_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/cfstream_handle.cc b/third_party/grpc/src/core/lib/iomgr/cfstream_handle.cc
new file mode 100644
index 0000000..827fd24
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/cfstream_handle.cc
@@ -0,0 +1,185 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_CFSTREAM
+#import <CoreFoundation/CoreFoundation.h>
+#import "src/core/lib/iomgr/cfstream_handle.h"
+
+#include <grpc/support/atm.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+void* CFStreamHandle::Retain(void* info) {
+  CFStreamHandle* handle = static_cast<CFStreamHandle*>(info);
+  CFSTREAM_HANDLE_REF(handle, "retain");
+  return info;
+}
+
+void CFStreamHandle::Release(void* info) {
+  CFStreamHandle* handle = static_cast<CFStreamHandle*>(info);
+  CFSTREAM_HANDLE_UNREF(handle, "release");
+}
+
+CFStreamHandle* CFStreamHandle::CreateStreamHandle(
+    CFReadStreamRef read_stream, CFWriteStreamRef write_stream) {
+  return new CFStreamHandle(read_stream, write_stream);
+}
+
+void CFStreamHandle::ReadCallback(CFReadStreamRef stream,
+                                  CFStreamEventType type,
+                                  void* client_callback_info) {
+  CFStreamHandle* handle = static_cast<CFStreamHandle*>(client_callback_info);
+  CFSTREAM_HANDLE_REF(handle, "read callback");
+  dispatch_async(
+      dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        grpc_core::ExecCtx exec_ctx;
+        if (grpc_tcp_trace.enabled()) {
+          gpr_log(GPR_DEBUG, "CFStream ReadCallback (%p, %p, %lu, %p)", handle,
+                  stream, type, client_callback_info);
+        }
+        switch (type) {
+          case kCFStreamEventOpenCompleted:
+            handle->open_event_.SetReady();
+            break;
+          case kCFStreamEventHasBytesAvailable:
+          case kCFStreamEventEndEncountered:
+            handle->read_event_.SetReady();
+            break;
+          case kCFStreamEventErrorOccurred:
+            handle->open_event_.SetReady();
+            handle->read_event_.SetReady();
+            break;
+          default:
+            GPR_UNREACHABLE_CODE(return );
+        }
+        CFSTREAM_HANDLE_UNREF(handle, "read callback");
+      });
+}
+void CFStreamHandle::WriteCallback(CFWriteStreamRef stream,
+                                   CFStreamEventType type,
+                                   void* clientCallBackInfo) {
+  CFStreamHandle* handle = static_cast<CFStreamHandle*>(clientCallBackInfo);
+  CFSTREAM_HANDLE_REF(handle, "write callback");
+  dispatch_async(
+      dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        grpc_core::ExecCtx exec_ctx;
+        if (grpc_tcp_trace.enabled()) {
+          gpr_log(GPR_DEBUG, "CFStream WriteCallback (%p, %p, %lu, %p)", handle,
+                  stream, type, clientCallBackInfo);
+        }
+        switch (type) {
+          case kCFStreamEventOpenCompleted:
+            handle->open_event_.SetReady();
+            break;
+          case kCFStreamEventCanAcceptBytes:
+          case kCFStreamEventEndEncountered:
+            handle->write_event_.SetReady();
+            break;
+          case kCFStreamEventErrorOccurred:
+            handle->open_event_.SetReady();
+            handle->write_event_.SetReady();
+            break;
+          default:
+            GPR_UNREACHABLE_CODE(return );
+        }
+        CFSTREAM_HANDLE_UNREF(handle, "write callback");
+      });
+}
+
+CFStreamHandle::CFStreamHandle(CFReadStreamRef read_stream,
+                               CFWriteStreamRef write_stream) {
+  gpr_ref_init(&refcount_, 1);
+  open_event_.InitEvent();
+  read_event_.InitEvent();
+  write_event_.InitEvent();
+  CFStreamClientContext ctx = {0, static_cast<void*>(this),
+                               CFStreamHandle::Retain, CFStreamHandle::Release,
+                               nil};
+  CFReadStreamSetClient(
+      read_stream,
+      kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable |
+          kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
+      CFStreamHandle::ReadCallback, &ctx);
+  CFWriteStreamSetClient(
+      write_stream,
+      kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes |
+          kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
+      CFStreamHandle::WriteCallback, &ctx);
+  CFReadStreamScheduleWithRunLoop(read_stream, CFRunLoopGetMain(),
+                                  kCFRunLoopCommonModes);
+  CFWriteStreamScheduleWithRunLoop(write_stream, CFRunLoopGetMain(),
+                                   kCFRunLoopCommonModes);
+}
+
+CFStreamHandle::~CFStreamHandle() {
+  open_event_.DestroyEvent();
+  read_event_.DestroyEvent();
+  write_event_.DestroyEvent();
+}
+
+void CFStreamHandle::NotifyOnOpen(grpc_closure* closure) {
+  open_event_.NotifyOn(closure);
+}
+
+void CFStreamHandle::NotifyOnRead(grpc_closure* closure) {
+  read_event_.NotifyOn(closure);
+}
+
+void CFStreamHandle::NotifyOnWrite(grpc_closure* closure) {
+  write_event_.NotifyOn(closure);
+}
+
+void CFStreamHandle::Shutdown(grpc_error* error) {
+  open_event_.SetShutdown(GRPC_ERROR_REF(error));
+  read_event_.SetShutdown(GRPC_ERROR_REF(error));
+  write_event_.SetShutdown(GRPC_ERROR_REF(error));
+  GRPC_ERROR_UNREF(error);
+}
+
+void CFStreamHandle::Ref(const char* file, int line, const char* reason) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&refcount_.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "CFStream Handle ref %p : %s %" PRIdPTR " -> %" PRIdPTR, this,
+            reason, val, val + 1);
+  }
+  gpr_ref(&refcount_);
+}
+
+void CFStreamHandle::Unref(const char* file, int line, const char* reason) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&refcount_.count);
+    gpr_log(GPR_ERROR,
+            "CFStream Handle unref %p : %s %" PRIdPTR " -> %" PRIdPTR, this,
+            reason, val, val - 1);
+  }
+  if (gpr_unref(&refcount_)) {
+    delete this;
+  }
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/cfstream_handle.h b/third_party/grpc/src/core/lib/iomgr/cfstream_handle.h
new file mode 100644
index 0000000..4258e72
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/cfstream_handle.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* The CFStream handle acts as an event synchronization entity for
+ * read/write/open/error/eos events happening on CFStream streams. */
+
+#ifndef GRPC_CORE_LIB_IOMGR_CFSTREAM_HANDLE_H
+#define GRPC_CORE_LIB_IOMGR_CFSTREAM_HANDLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_CFSTREAM
+#import <CoreFoundation/CoreFoundation.h>
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/lockfree_event.h"
+
+class CFStreamHandle final {
+ public:
+  static CFStreamHandle* CreateStreamHandle(CFReadStreamRef read_stream,
+                                            CFWriteStreamRef write_stream);
+  ~CFStreamHandle();
+  CFStreamHandle(const CFReadStreamRef& ref) = delete;
+  CFStreamHandle(CFReadStreamRef&& ref) = delete;
+  CFStreamHandle& operator=(const CFStreamHandle& rhs) = delete;
+
+  void NotifyOnOpen(grpc_closure* closure);
+  void NotifyOnRead(grpc_closure* closure);
+  void NotifyOnWrite(grpc_closure* closure);
+  void Shutdown(grpc_error* error);
+
+  void Ref(const char* file = "", int line = 0, const char* reason = nullptr);
+  void Unref(const char* file = "", int line = 0, const char* reason = nullptr);
+
+ private:
+  CFStreamHandle(CFReadStreamRef read_stream, CFWriteStreamRef write_stream);
+  static void ReadCallback(CFReadStreamRef stream, CFStreamEventType type,
+                           void* client_callback_info);
+  static void WriteCallback(CFWriteStreamRef stream, CFStreamEventType type,
+                            void* client_callback_info);
+  static void* Retain(void* info);
+  static void Release(void* info);
+
+  grpc_core::LockfreeEvent open_event_;
+  grpc_core::LockfreeEvent read_event_;
+  grpc_core::LockfreeEvent write_event_;
+
+  gpr_refcount refcount_;
+};
+
+#ifdef DEBUG
+#define CFSTREAM_HANDLE_REF(handle, reason) \
+  (handle)->Ref(__FILE__, __LINE__, (reason))
+#define CFSTREAM_HANDLE_UNREF(handle, reason) \
+  (handle)->Unref(__FILE__, __LINE__, (reason))
+#else
+#define CFSTREAM_HANDLE_REF(handle, reason) (handle)->Ref()
+#define CFSTREAM_HANDLE_UNREF(handle, reason) (handle)->Unref()
+#endif
+
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_CFSTREAM_HANDLE_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/closure.h b/third_party/grpc/src/core/lib/iomgr/closure.h
new file mode 100644
index 0000000..bde3437
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/closure.h
@@ -0,0 +1,353 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_CLOSURE_H
+#define GRPC_CORE_LIB_IOMGR_CLOSURE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <assert.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <stdbool.h>
+#include "src/core/lib/gpr/mpscq.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/profiling/timers.h"
+
+struct grpc_closure;
+typedef struct grpc_closure grpc_closure;
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_closure;
+
+typedef struct grpc_closure_list {
+  grpc_closure* head;
+  grpc_closure* tail;
+} grpc_closure_list;
+
+/** gRPC Callback definition.
+ *
+ * \param arg Arbitrary input.
+ * \param error GRPC_ERROR_NONE if no error occurred, otherwise some grpc_error
+ *              describing what went wrong.
+ *              Error contract: it is not the cb's job to unref this error;
+ *              the closure scheduler will do that after the cb returns */
+typedef void (*grpc_iomgr_cb_func)(void* arg, grpc_error* error);
+
+typedef struct grpc_closure_scheduler grpc_closure_scheduler;
+
+typedef struct grpc_closure_scheduler_vtable {
+  /* NOTE: for all these functions, closure->scheduler == the scheduler that was
+           used to find this vtable */
+  void (*run)(grpc_closure* closure, grpc_error* error);
+  void (*sched)(grpc_closure* closure, grpc_error* error);
+  const char* name;
+} grpc_closure_scheduler_vtable;
+
+/** Abstract type that can schedule closures for execution */
+struct grpc_closure_scheduler {
+  const grpc_closure_scheduler_vtable* vtable;
+};
+
+/** 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;
+
+  /** Arguments to be passed to "cb". */
+  void* cb_arg;
+
+  /** Scheduler to schedule against: nullptr to schedule against current
+     execution context */
+  grpc_closure_scheduler* scheduler;
+
+  /** Once queued, the result of the closure. Before then: scratch space */
+  union {
+    grpc_error* error;
+    uintptr_t scratch;
+  } error_data;
+
+// extra tracing and debugging for grpc_closure. This incurs a decent amount of
+// overhead per closure, so it must be enabled at compile time.
+#ifndef NDEBUG
+  bool scheduled;
+  bool run;  // true = run, false = scheduled
+  const char* file_created;
+  int line_created;
+  const char* file_initiated;
+  int line_initiated;
+#endif
+};
+
+#ifndef NDEBUG
+inline grpc_closure* grpc_closure_init(const char* file, int line,
+                                       grpc_closure* closure,
+                                       grpc_iomgr_cb_func cb, void* cb_arg,
+                                       grpc_closure_scheduler* scheduler) {
+#else
+inline grpc_closure* grpc_closure_init(grpc_closure* closure,
+                                       grpc_iomgr_cb_func cb, void* cb_arg,
+                                       grpc_closure_scheduler* scheduler) {
+#endif
+  closure->cb = cb;
+  closure->cb_arg = cb_arg;
+  closure->scheduler = scheduler;
+  closure->error_data.error = GRPC_ERROR_NONE;
+#ifndef NDEBUG
+  closure->scheduled = false;
+  closure->file_initiated = nullptr;
+  closure->line_initiated = 0;
+  closure->run = false;
+  closure->file_created = file;
+  closure->line_created = line;
+#endif
+  return closure;
+}
+
+/** Initializes \a closure with \a cb and \a cb_arg. Returns \a closure. */
+#ifndef NDEBUG
+#define GRPC_CLOSURE_INIT(closure, cb, cb_arg, scheduler) \
+  grpc_closure_init(__FILE__, __LINE__, closure, cb, cb_arg, scheduler)
+#else
+#define GRPC_CLOSURE_INIT(closure, cb, cb_arg, scheduler) \
+  grpc_closure_init(closure, cb, cb_arg, scheduler)
+#endif
+
+namespace closure_impl {
+
+typedef struct {
+  grpc_iomgr_cb_func cb;
+  void* cb_arg;
+  grpc_closure wrapper;
+} wrapped_closure;
+
+inline void closure_wrapper(void* arg, grpc_error* error) {
+  wrapped_closure* wc = static_cast<wrapped_closure*>(arg);
+  grpc_iomgr_cb_func cb = wc->cb;
+  void* cb_arg = wc->cb_arg;
+  gpr_free(wc);
+  cb(cb_arg, error);
+}
+
+}  // namespace closure_impl
+
+#ifndef NDEBUG
+inline grpc_closure* grpc_closure_create(const char* file, int line,
+                                         grpc_iomgr_cb_func cb, void* cb_arg,
+                                         grpc_closure_scheduler* scheduler) {
+#else
+inline grpc_closure* grpc_closure_create(grpc_iomgr_cb_func cb, void* cb_arg,
+                                         grpc_closure_scheduler* scheduler) {
+#endif
+  closure_impl::wrapped_closure* wc =
+      static_cast<closure_impl::wrapped_closure*>(gpr_malloc(sizeof(*wc)));
+  wc->cb = cb;
+  wc->cb_arg = cb_arg;
+#ifndef NDEBUG
+  grpc_closure_init(file, line, &wc->wrapper, closure_impl::closure_wrapper, wc,
+                    scheduler);
+#else
+  grpc_closure_init(&wc->wrapper, closure_impl::closure_wrapper, wc, scheduler);
+#endif
+  return &wc->wrapper;
+}
+
+/* Create a heap allocated closure: try to avoid except for very rare events */
+#ifndef NDEBUG
+#define GRPC_CLOSURE_CREATE(cb, cb_arg, scheduler) \
+  grpc_closure_create(__FILE__, __LINE__, cb, cb_arg, scheduler)
+#else
+#define GRPC_CLOSURE_CREATE(cb, cb_arg, scheduler) \
+  grpc_closure_create(cb, cb_arg, scheduler)
+#endif
+
+#define GRPC_CLOSURE_LIST_INIT \
+  { nullptr, nullptr }
+
+inline void grpc_closure_list_init(grpc_closure_list* closure_list) {
+  closure_list->head = closure_list->tail = nullptr;
+}
+
+/** add \a closure to the end of \a list
+    and set \a closure's result to \a error
+    Returns true if \a list becomes non-empty */
+inline bool grpc_closure_list_append(grpc_closure_list* closure_list,
+                                     grpc_closure* closure, grpc_error* error) {
+  if (closure == nullptr) {
+    GRPC_ERROR_UNREF(error);
+    return false;
+  }
+  closure->error_data.error = error;
+  closure->next_data.next = nullptr;
+  bool was_empty = (closure_list->head == nullptr);
+  if (was_empty) {
+    closure_list->head = closure;
+  } else {
+    closure_list->tail->next_data.next = closure;
+  }
+  closure_list->tail = closure;
+  return was_empty;
+}
+
+/** force all success bits in \a list to false */
+inline void grpc_closure_list_fail_all(grpc_closure_list* list,
+                                       grpc_error* forced_failure) {
+  for (grpc_closure* c = list->head; c != nullptr; c = c->next_data.next) {
+    if (c->error_data.error == GRPC_ERROR_NONE) {
+      c->error_data.error = GRPC_ERROR_REF(forced_failure);
+    }
+  }
+  GRPC_ERROR_UNREF(forced_failure);
+}
+
+/** append all closures from \a src to \a dst and empty \a src. */
+inline void grpc_closure_list_move(grpc_closure_list* src,
+                                   grpc_closure_list* dst) {
+  if (src->head == nullptr) {
+    return;
+  }
+  if (dst->head == nullptr) {
+    *dst = *src;
+  } else {
+    dst->tail->next_data.next = src->head;
+    dst->tail = src->tail;
+  }
+  src->head = src->tail = nullptr;
+}
+
+/** return whether \a list is empty. */
+inline bool grpc_closure_list_empty(grpc_closure_list closure_list) {
+  return closure_list.head == nullptr;
+}
+
+#ifndef NDEBUG
+inline void grpc_closure_run(const char* file, int line, grpc_closure* c,
+                             grpc_error* error) {
+#else
+inline void grpc_closure_run(grpc_closure* c, grpc_error* error) {
+#endif
+  GPR_TIMER_SCOPE("grpc_closure_run", 0);
+  if (c != nullptr) {
+#ifndef NDEBUG
+    c->file_initiated = file;
+    c->line_initiated = line;
+    c->run = true;
+    GPR_ASSERT(c->cb != nullptr);
+#endif
+    c->scheduler->vtable->run(c, error);
+  } else {
+    GRPC_ERROR_UNREF(error);
+  }
+}
+
+/** Run a closure directly. Caller ensures that no locks are being held above.
+ *  Note that calling this at the end of a closure callback function itself is
+ *  by definition safe. */
+#ifndef NDEBUG
+#define GRPC_CLOSURE_RUN(closure, error) \
+  grpc_closure_run(__FILE__, __LINE__, closure, error)
+#else
+#define GRPC_CLOSURE_RUN(closure, error) grpc_closure_run(closure, error)
+#endif
+
+#ifndef NDEBUG
+inline void grpc_closure_sched(const char* file, int line, grpc_closure* c,
+                               grpc_error* error) {
+#else
+inline void grpc_closure_sched(grpc_closure* c, grpc_error* error) {
+#endif
+  GPR_TIMER_SCOPE("grpc_closure_sched", 0);
+  if (c != nullptr) {
+#ifndef NDEBUG
+    if (c->scheduled) {
+      gpr_log(GPR_ERROR,
+              "Closure already scheduled. (closure: %p, created: [%s:%d], "
+              "previously scheduled at: [%s: %d], newly scheduled at [%s: %d], "
+              "run?: %s",
+              c, c->file_created, c->line_created, c->file_initiated,
+              c->line_initiated, file, line, c->run ? "true" : "false");
+      abort();
+    }
+    c->scheduled = true;
+    c->file_initiated = file;
+    c->line_initiated = line;
+    c->run = false;
+    GPR_ASSERT(c->cb != nullptr);
+#endif
+    c->scheduler->vtable->sched(c, error);
+  } else {
+    GRPC_ERROR_UNREF(error);
+  }
+}
+
+/** Schedule a closure to be run. Does not need to be run from a safe point. */
+#ifndef NDEBUG
+#define GRPC_CLOSURE_SCHED(closure, error) \
+  grpc_closure_sched(__FILE__, __LINE__, closure, error)
+#else
+#define GRPC_CLOSURE_SCHED(closure, error) grpc_closure_sched(closure, error)
+#endif
+
+#ifndef NDEBUG
+inline void grpc_closure_list_sched(const char* file, int line,
+                                    grpc_closure_list* list) {
+#else
+inline void grpc_closure_list_sched(grpc_closure_list* list) {
+#endif
+  grpc_closure* c = list->head;
+  while (c != nullptr) {
+    grpc_closure* next = c->next_data.next;
+#ifndef NDEBUG
+    if (c->scheduled) {
+      gpr_log(GPR_ERROR,
+              "Closure already scheduled. (closure: %p, created: [%s:%d], "
+              "previously scheduled at: [%s: %d] run?: %s",
+              c, c->file_created, c->line_created, c->file_initiated,
+              c->line_initiated, c->run ? "true" : "false");
+      abort();
+    }
+    c->scheduled = true;
+    c->file_initiated = file;
+    c->line_initiated = line;
+    c->run = false;
+    GPR_ASSERT(c->cb != nullptr);
+#endif
+    c->scheduler->vtable->sched(c, c->error_data.error);
+    c = next;
+  }
+  list->head = list->tail = nullptr;
+}
+
+/** Schedule all closures in a list to be run. Does not need to be run from a
+ * safe point. */
+#ifndef NDEBUG
+#define GRPC_CLOSURE_LIST_SCHED(closure_list) \
+  grpc_closure_list_sched(__FILE__, __LINE__, closure_list)
+#else
+#define GRPC_CLOSURE_LIST_SCHED(closure_list) \
+  grpc_closure_list_sched(closure_list)
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_CLOSURE_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/combiner.cc b/third_party/grpc/src/core/lib/iomgr/combiner.cc
new file mode 100644
index 0000000..7c0062e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/combiner.cc
@@ -0,0 +1,375 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/combiner.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/profiling/timers.h"
+
+grpc_core::DebugOnlyTraceFlag grpc_combiner_trace(false, "combiner");
+
+#define GRPC_COMBINER_TRACE(fn)          \
+  do {                                   \
+    if (grpc_combiner_trace.enabled()) { \
+      fn;                                \
+    }                                    \
+  } while (0)
+
+#define STATE_UNORPHANED 1
+#define STATE_ELEM_COUNT_LOW_BIT 2
+
+struct grpc_combiner {
+  grpc_combiner* next_combiner_on_this_exec_ctx;
+  grpc_closure_scheduler scheduler;
+  grpc_closure_scheduler finally_scheduler;
+  gpr_mpscq queue;
+  // either:
+  // a pointer to the initiating exec ctx if that is the only exec_ctx that has
+  // ever queued to this combiner, or NULL. If this is non-null, it's not
+  // dereferencable (since the initiating exec_ctx may have gone out of scope)
+  gpr_atm initiating_exec_ctx_or_null;
+  // state is:
+  // lower bit - zero if orphaned (STATE_UNORPHANED)
+  // other bits - number of items queued on the lock (STATE_ELEM_COUNT_LOW_BIT)
+  gpr_atm state;
+  bool time_to_execute_final_list;
+  grpc_closure_list final_list;
+  grpc_closure offload;
+  gpr_refcount refs;
+};
+
+static void combiner_run(grpc_closure* closure, grpc_error* error);
+static void combiner_exec(grpc_closure* closure, grpc_error* error);
+static void combiner_finally_exec(grpc_closure* closure, grpc_error* error);
+
+static const grpc_closure_scheduler_vtable scheduler = {
+    combiner_run, combiner_exec, "combiner:immediately"};
+static const grpc_closure_scheduler_vtable finally_scheduler = {
+    combiner_finally_exec, combiner_finally_exec, "combiner:finally"};
+
+static void offload(void* arg, grpc_error* error);
+
+grpc_combiner* grpc_combiner_create(void) {
+  grpc_combiner* lock = static_cast<grpc_combiner*>(gpr_zalloc(sizeof(*lock)));
+  gpr_ref_init(&lock->refs, 1);
+  lock->scheduler.vtable = &scheduler;
+  lock->finally_scheduler.vtable = &finally_scheduler;
+  gpr_atm_no_barrier_store(&lock->state, STATE_UNORPHANED);
+  gpr_mpscq_init(&lock->queue);
+  grpc_closure_list_init(&lock->final_list);
+  GRPC_CLOSURE_INIT(&lock->offload, offload, lock,
+                    grpc_executor_scheduler(GRPC_EXECUTOR_SHORT));
+  GRPC_COMBINER_TRACE(gpr_log(GPR_INFO, "C:%p create", lock));
+  return lock;
+}
+
+static void really_destroy(grpc_combiner* lock) {
+  GRPC_COMBINER_TRACE(gpr_log(GPR_INFO, "C:%p really_destroy", lock));
+  GPR_ASSERT(gpr_atm_no_barrier_load(&lock->state) == 0);
+  gpr_mpscq_destroy(&lock->queue);
+  gpr_free(lock);
+}
+
+static void start_destroy(grpc_combiner* lock) {
+  gpr_atm old_state = gpr_atm_full_fetch_add(&lock->state, -STATE_UNORPHANED);
+  GRPC_COMBINER_TRACE(gpr_log(
+      GPR_INFO, "C:%p really_destroy old_state=%" PRIdPTR, lock, old_state));
+  if (old_state == 1) {
+    really_destroy(lock);
+  }
+}
+
+#ifndef NDEBUG
+#define GRPC_COMBINER_DEBUG_SPAM(op, delta)                                \
+  if (grpc_combiner_trace.enabled()) {                                     \
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,                            \
+            "C:%p %s %" PRIdPTR " --> %" PRIdPTR " %s", lock, (op),        \
+            gpr_atm_no_barrier_load(&lock->refs.count),                    \
+            gpr_atm_no_barrier_load(&lock->refs.count) + (delta), reason); \
+  }
+#else
+#define GRPC_COMBINER_DEBUG_SPAM(op, delta)
+#endif
+
+void grpc_combiner_unref(grpc_combiner* lock GRPC_COMBINER_DEBUG_ARGS) {
+  GRPC_COMBINER_DEBUG_SPAM("UNREF", -1);
+  if (gpr_unref(&lock->refs)) {
+    start_destroy(lock);
+  }
+}
+
+grpc_combiner* grpc_combiner_ref(grpc_combiner* lock GRPC_COMBINER_DEBUG_ARGS) {
+  GRPC_COMBINER_DEBUG_SPAM("  REF", 1);
+  gpr_ref(&lock->refs);
+  return lock;
+}
+
+static void push_last_on_exec_ctx(grpc_combiner* lock) {
+  lock->next_combiner_on_this_exec_ctx = nullptr;
+  if (grpc_core::ExecCtx::Get()->combiner_data()->active_combiner == nullptr) {
+    grpc_core::ExecCtx::Get()->combiner_data()->active_combiner =
+        grpc_core::ExecCtx::Get()->combiner_data()->last_combiner = lock;
+  } else {
+    grpc_core::ExecCtx::Get()
+        ->combiner_data()
+        ->last_combiner->next_combiner_on_this_exec_ctx = lock;
+    grpc_core::ExecCtx::Get()->combiner_data()->last_combiner = lock;
+  }
+}
+
+static void push_first_on_exec_ctx(grpc_combiner* lock) {
+  lock->next_combiner_on_this_exec_ctx =
+      grpc_core::ExecCtx::Get()->combiner_data()->active_combiner;
+  grpc_core::ExecCtx::Get()->combiner_data()->active_combiner = lock;
+  if (lock->next_combiner_on_this_exec_ctx == nullptr) {
+    grpc_core::ExecCtx::Get()->combiner_data()->last_combiner = lock;
+  }
+}
+
+#define COMBINER_FROM_CLOSURE_SCHEDULER(closure, scheduler_name) \
+  ((grpc_combiner*)(((char*)((closure)->scheduler)) -            \
+                    offsetof(grpc_combiner, scheduler_name)))
+
+static void combiner_exec(grpc_closure* cl, grpc_error* error) {
+  GPR_TIMER_SCOPE("combiner.execute", 0);
+  GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_ITEMS();
+  grpc_combiner* lock = COMBINER_FROM_CLOSURE_SCHEDULER(cl, scheduler);
+  gpr_atm last = gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT);
+  GRPC_COMBINER_TRACE(gpr_log(GPR_INFO,
+                              "C:%p grpc_combiner_execute c=%p last=%" PRIdPTR,
+                              lock, cl, last));
+  if (last == 1) {
+    GRPC_STATS_INC_COMBINER_LOCKS_INITIATED();
+    GPR_TIMER_MARK("combiner.initiated", 0);
+    gpr_atm_no_barrier_store(&lock->initiating_exec_ctx_or_null,
+                             (gpr_atm)grpc_core::ExecCtx::Get());
+    // first element on this list: add it to the list of combiner locks
+    // executing within this exec_ctx
+    push_last_on_exec_ctx(lock);
+  } else {
+    // there may be a race with setting here: if that happens, we may delay
+    // offload for one or two actions, and that's fine
+    gpr_atm initiator =
+        gpr_atm_no_barrier_load(&lock->initiating_exec_ctx_or_null);
+    if (initiator != 0 && initiator != (gpr_atm)grpc_core::ExecCtx::Get()) {
+      gpr_atm_no_barrier_store(&lock->initiating_exec_ctx_or_null, 0);
+    }
+  }
+  GPR_ASSERT(last & STATE_UNORPHANED);  // ensure lock has not been destroyed
+  assert(cl->cb);
+  cl->error_data.error = error;
+  gpr_mpscq_push(&lock->queue, &cl->next_data.atm_next);
+}
+
+static void move_next() {
+  grpc_core::ExecCtx::Get()->combiner_data()->active_combiner =
+      grpc_core::ExecCtx::Get()
+          ->combiner_data()
+          ->active_combiner->next_combiner_on_this_exec_ctx;
+  if (grpc_core::ExecCtx::Get()->combiner_data()->active_combiner == nullptr) {
+    grpc_core::ExecCtx::Get()->combiner_data()->last_combiner = nullptr;
+  }
+}
+
+static void offload(void* arg, grpc_error* error) {
+  grpc_combiner* lock = static_cast<grpc_combiner*>(arg);
+  push_last_on_exec_ctx(lock);
+}
+
+static void queue_offload(grpc_combiner* lock) {
+  GRPC_STATS_INC_COMBINER_LOCKS_OFFLOADED();
+  move_next();
+  GRPC_COMBINER_TRACE(gpr_log(GPR_INFO, "C:%p queue_offload", lock));
+  GRPC_CLOSURE_SCHED(&lock->offload, GRPC_ERROR_NONE);
+}
+
+bool grpc_combiner_continue_exec_ctx() {
+  GPR_TIMER_SCOPE("combiner.continue_exec_ctx", 0);
+  grpc_combiner* lock =
+      grpc_core::ExecCtx::Get()->combiner_data()->active_combiner;
+  if (lock == nullptr) {
+    return false;
+  }
+
+  bool contended =
+      gpr_atm_no_barrier_load(&lock->initiating_exec_ctx_or_null) == 0;
+
+  GRPC_COMBINER_TRACE(gpr_log(GPR_INFO,
+                              "C:%p grpc_combiner_continue_exec_ctx "
+                              "contended=%d "
+                              "exec_ctx_ready_to_finish=%d "
+                              "time_to_execute_final_list=%d",
+                              lock, contended,
+                              grpc_core::ExecCtx::Get()->IsReadyToFinish(),
+                              lock->time_to_execute_final_list));
+
+  if (contended && grpc_core::ExecCtx::Get()->IsReadyToFinish() &&
+      grpc_executor_is_threaded()) {
+    GPR_TIMER_MARK("offload_from_finished_exec_ctx", 0);
+    // this execution context wants to move on: schedule remaining work to be
+    // picked up on the executor
+    queue_offload(lock);
+    return true;
+  }
+
+  if (!lock->time_to_execute_final_list ||
+      // peek to see if something new has shown up, and execute that with
+      // priority
+      (gpr_atm_acq_load(&lock->state) >> 1) > 1) {
+    gpr_mpscq_node* n = gpr_mpscq_pop(&lock->queue);
+    GRPC_COMBINER_TRACE(
+        gpr_log(GPR_INFO, "C:%p maybe_finish_one n=%p", lock, n));
+    if (n == nullptr) {
+      // queue is in an inconsistent state: use this as a cue that we should
+      // go off and do something else for a while (and come back later)
+      GPR_TIMER_MARK("delay_busy", 0);
+      queue_offload(lock);
+      return true;
+    }
+    GPR_TIMER_SCOPE("combiner.exec1", 0);
+    grpc_closure* cl = reinterpret_cast<grpc_closure*>(n);
+    grpc_error* cl_err = cl->error_data.error;
+#ifndef NDEBUG
+    cl->scheduled = false;
+#endif
+    cl->cb(cl->cb_arg, cl_err);
+    GRPC_ERROR_UNREF(cl_err);
+  } else {
+    grpc_closure* c = lock->final_list.head;
+    GPR_ASSERT(c != nullptr);
+    grpc_closure_list_init(&lock->final_list);
+    int loops = 0;
+    while (c != nullptr) {
+      GPR_TIMER_SCOPE("combiner.exec_1final", 0);
+      GRPC_COMBINER_TRACE(
+          gpr_log(GPR_INFO, "C:%p execute_final[%d] c=%p", lock, loops, c));
+      grpc_closure* next = c->next_data.next;
+      grpc_error* error = c->error_data.error;
+#ifndef NDEBUG
+      c->scheduled = false;
+#endif
+      c->cb(c->cb_arg, error);
+      GRPC_ERROR_UNREF(error);
+      c = next;
+    }
+  }
+
+  GPR_TIMER_MARK("unref", 0);
+  move_next();
+  lock->time_to_execute_final_list = false;
+  gpr_atm old_state =
+      gpr_atm_full_fetch_add(&lock->state, -STATE_ELEM_COUNT_LOW_BIT);
+  GRPC_COMBINER_TRACE(
+      gpr_log(GPR_INFO, "C:%p finish old_state=%" PRIdPTR, lock, old_state));
+// Define a macro to ease readability of the following switch statement.
+#define OLD_STATE_WAS(orphaned, elem_count) \
+  (((orphaned) ? 0 : STATE_UNORPHANED) |    \
+   ((elem_count)*STATE_ELEM_COUNT_LOW_BIT))
+  // Depending on what the previous state was, we need to perform different
+  // actions.
+  switch (old_state) {
+    default:
+      // we have multiple queued work items: just continue executing them
+      break;
+    case OLD_STATE_WAS(false, 2):
+    case OLD_STATE_WAS(true, 2):
+      // we're down to one queued item: if it's the final list we should do that
+      if (!grpc_closure_list_empty(lock->final_list)) {
+        lock->time_to_execute_final_list = true;
+      }
+      break;
+    case OLD_STATE_WAS(false, 1):
+      // had one count, one unorphaned --> unlocked unorphaned
+      return true;
+    case OLD_STATE_WAS(true, 1):
+      // and one count, one orphaned --> unlocked and orphaned
+      really_destroy(lock);
+      return true;
+    case OLD_STATE_WAS(false, 0):
+    case OLD_STATE_WAS(true, 0):
+      // these values are illegal - representing an already unlocked or
+      // deleted lock
+      GPR_UNREACHABLE_CODE(return true);
+  }
+  push_first_on_exec_ctx(lock);
+  return true;
+}
+
+static void enqueue_finally(void* closure, grpc_error* error);
+
+static void combiner_finally_exec(grpc_closure* closure, grpc_error* error) {
+  GPR_TIMER_SCOPE("combiner.execute_finally", 0);
+  GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS();
+  grpc_combiner* lock =
+      COMBINER_FROM_CLOSURE_SCHEDULER(closure, finally_scheduler);
+  GRPC_COMBINER_TRACE(gpr_log(
+      GPR_INFO, "C:%p grpc_combiner_execute_finally c=%p; ac=%p", lock, closure,
+      grpc_core::ExecCtx::Get()->combiner_data()->active_combiner));
+  if (grpc_core::ExecCtx::Get()->combiner_data()->active_combiner != lock) {
+    GPR_TIMER_MARK("slowpath", 0);
+    GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(enqueue_finally, closure,
+                                           grpc_combiner_scheduler(lock)),
+                       error);
+    return;
+  }
+
+  if (grpc_closure_list_empty(lock->final_list)) {
+    gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT);
+  }
+  grpc_closure_list_append(&lock->final_list, closure, error);
+}
+
+static void combiner_run(grpc_closure* closure, grpc_error* error) {
+  grpc_combiner* lock = COMBINER_FROM_CLOSURE_SCHEDULER(closure, scheduler);
+#ifndef NDEBUG
+  closure->scheduled = false;
+  GRPC_COMBINER_TRACE(gpr_log(
+      GPR_DEBUG,
+      "Combiner:%p grpc_combiner_run closure:%p created [%s:%d] run [%s:%d]",
+      lock, closure, closure->file_created, closure->line_created,
+      closure->file_initiated, closure->line_initiated));
+#endif
+  GPR_ASSERT(grpc_core::ExecCtx::Get()->combiner_data()->active_combiner ==
+             lock);
+  closure->cb(closure->cb_arg, error);
+  GRPC_ERROR_UNREF(error);
+}
+
+static void enqueue_finally(void* closure, grpc_error* error) {
+  combiner_finally_exec(static_cast<grpc_closure*>(closure),
+                        GRPC_ERROR_REF(error));
+}
+
+grpc_closure_scheduler* grpc_combiner_scheduler(grpc_combiner* combiner) {
+  return &combiner->scheduler;
+}
+
+grpc_closure_scheduler* grpc_combiner_finally_scheduler(
+    grpc_combiner* combiner) {
+  return &combiner->finally_scheduler;
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/combiner.h b/third_party/grpc/src/core/lib/iomgr/combiner.h
new file mode 100644
index 0000000..3c947bf
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/combiner.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_COMBINER_H
+#define GRPC_CORE_LIB_IOMGR_COMBINER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include <grpc/support/atm.h>
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/mpscq.h"
+#include "src/core/lib/iomgr/exec_ctx.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(void);
+
+#ifndef NDEBUG
+#define GRPC_COMBINER_DEBUG_ARGS \
+  , const char *file, int line, const char *reason
+#define GRPC_COMBINER_REF(combiner, reason) \
+  grpc_combiner_ref((combiner), __FILE__, __LINE__, (reason))
+#define GRPC_COMBINER_UNREF(combiner, reason) \
+  grpc_combiner_unref((combiner), __FILE__, __LINE__, (reason))
+#else
+#define GRPC_COMBINER_DEBUG_ARGS
+#define GRPC_COMBINER_REF(combiner, reason) grpc_combiner_ref((combiner))
+#define GRPC_COMBINER_UNREF(combiner, reason) grpc_combiner_unref((combiner))
+#endif
+
+// Ref/unref the lock, for when we're sharing the lock ownership
+// Prefer to use the macros above
+grpc_combiner* grpc_combiner_ref(grpc_combiner* lock GRPC_COMBINER_DEBUG_ARGS);
+void grpc_combiner_unref(grpc_combiner* lock GRPC_COMBINER_DEBUG_ARGS);
+// Fetch a scheduler to schedule closures against
+grpc_closure_scheduler* grpc_combiner_scheduler(grpc_combiner* lock);
+// Scheduler to execute \a action within the lock just prior to unlocking.
+grpc_closure_scheduler* grpc_combiner_finally_scheduler(grpc_combiner* lock);
+
+bool grpc_combiner_continue_exec_ctx();
+
+extern grpc_core::DebugOnlyTraceFlag grpc_combiner_trace;
+
+#endif /* GRPC_CORE_LIB_IOMGR_COMBINER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/dynamic_annotations.h b/third_party/grpc/src/core/lib/iomgr/dynamic_annotations.h
new file mode 100644
index 0000000..7139280
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/dynamic_annotations.h
@@ -0,0 +1,67 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_DYNAMIC_ANNOTATIONS_H
+#define GRPC_CORE_LIB_IOMGR_DYNAMIC_ANNOTATIONS_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GRPC_TSAN_ENABLED
+
+#define TSAN_ANNOTATE_HAPPENS_BEFORE(addr) \
+  AnnotateHappensBefore(__FILE__, __LINE__, (void*)(addr))
+#define TSAN_ANNOTATE_HAPPENS_AFTER(addr) \
+  AnnotateHappensAfter(__FILE__, __LINE__, (void*)(addr))
+#define TSAN_ANNOTATE_RWLOCK_CREATE(addr) \
+  AnnotateRWLockCreate(__FILE__, __LINE__, (void*)(addr))
+#define TSAN_ANNOTATE_RWLOCK_DESTROY(addr) \
+  AnnotateRWLockDestroy(__FILE__, __LINE__, (void*)(addr))
+#define TSAN_ANNOTATE_RWLOCK_ACQUIRED(addr, is_w) \
+  AnnotateRWLockAcquired(__FILE__, __LINE__, (void*)(addr), (is_w))
+#define TSAN_ANNOTATE_RWLOCK_RELEASED(addr, is_w) \
+  AnnotateRWLockReleased(__FILE__, __LINE__, (void*)(addr), (is_w))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void AnnotateHappensBefore(const char* file, int line, const volatile void* cv);
+void AnnotateHappensAfter(const char* file, int line, const volatile void* cv);
+void AnnotateRWLockCreate(const char* file, int line,
+                          const volatile void* lock);
+void AnnotateRWLockDestroy(const char* file, int line,
+                           const volatile void* lock);
+void AnnotateRWLockAcquired(const char* file, int line,
+                            const volatile void* lock, long is_w);
+void AnnotateRWLockReleased(const char* file, int line,
+                            const volatile void* lock, long is_w);
+#ifdef __cplusplus
+}
+#endif
+
+#else /* GRPC_TSAN_ENABLED */
+
+#define TSAN_ANNOTATE_HAPPENS_BEFORE(addr)
+#define TSAN_ANNOTATE_HAPPENS_AFTER(addr)
+#define TSAN_ANNOTATE_RWLOCK_CREATE(addr)
+#define TSAN_ANNOTATE_RWLOCK_DESTROY(addr)
+#define TSAN_ANNOTATE_RWLOCK_ACQUIRED(addr, is_w)
+#define TSAN_ANNOTATE_RWLOCK_RELEASED(addr, is_w)
+
+#endif /* GRPC_TSAN_ENABLED */
+
+#endif /* GRPC_CORE_LIB_IOMGR_DYNAMIC_ANNOTATIONS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint.cc b/third_party/grpc/src/core/lib/iomgr/endpoint.cc
new file mode 100644
index 0000000..06316c6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/endpoint.cc
@@ -0,0 +1,67 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+
+grpc_core::TraceFlag grpc_tcp_trace(false, "tcp");
+
+void grpc_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                        grpc_closure* cb) {
+  ep->vtable->read(ep, slices, cb);
+}
+
+void grpc_endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                         grpc_closure* cb, void* arg) {
+  ep->vtable->write(ep, slices, cb, arg);
+}
+
+void grpc_endpoint_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) {
+  ep->vtable->add_to_pollset(ep, pollset);
+}
+
+void grpc_endpoint_add_to_pollset_set(grpc_endpoint* ep,
+                                      grpc_pollset_set* pollset_set) {
+  ep->vtable->add_to_pollset_set(ep, pollset_set);
+}
+
+void grpc_endpoint_delete_from_pollset_set(grpc_endpoint* ep,
+                                           grpc_pollset_set* pollset_set) {
+  ep->vtable->delete_from_pollset_set(ep, pollset_set);
+}
+
+void grpc_endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) {
+  ep->vtable->shutdown(ep, why);
+}
+
+void grpc_endpoint_destroy(grpc_endpoint* ep) { ep->vtable->destroy(ep); }
+
+char* grpc_endpoint_get_peer(grpc_endpoint* ep) {
+  return ep->vtable->get_peer(ep);
+}
+
+int grpc_endpoint_get_fd(grpc_endpoint* ep) { return ep->vtable->get_fd(ep); }
+
+grpc_resource_user* grpc_endpoint_get_resource_user(grpc_endpoint* ep) {
+  return ep->vtable->get_resource_user(ep);
+}
+
+bool grpc_endpoint_can_track_err(grpc_endpoint* ep) {
+  return ep->vtable->can_track_err(ep);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint.h b/third_party/grpc/src/core/lib/iomgr/endpoint.h
new file mode 100644
index 0000000..79c8ece
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/endpoint.h
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_H
+#define GRPC_CORE_LIB_IOMGR_ENDPOINT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/iomgr/resource_quota.h"
+
+/* An endpoint caps a streaming channel between two communicating processes.
+   Examples may be: a tcp socket, <stdin+stdout>, or some shared memory. */
+
+typedef struct grpc_endpoint grpc_endpoint;
+typedef struct grpc_endpoint_vtable grpc_endpoint_vtable;
+class Timestamps;
+
+struct grpc_endpoint_vtable {
+  void (*read)(grpc_endpoint* ep, grpc_slice_buffer* slices, grpc_closure* cb);
+  void (*write)(grpc_endpoint* ep, grpc_slice_buffer* slices, grpc_closure* cb,
+                void* arg);
+  void (*add_to_pollset)(grpc_endpoint* ep, grpc_pollset* pollset);
+  void (*add_to_pollset_set)(grpc_endpoint* ep, grpc_pollset_set* pollset);
+  void (*delete_from_pollset_set)(grpc_endpoint* ep, grpc_pollset_set* pollset);
+  void (*shutdown)(grpc_endpoint* ep, grpc_error* why);
+  void (*destroy)(grpc_endpoint* ep);
+  grpc_resource_user* (*get_resource_user)(grpc_endpoint* ep);
+  char* (*get_peer)(grpc_endpoint* ep);
+  int (*get_fd)(grpc_endpoint* ep);
+  bool (*can_track_err)(grpc_endpoint* ep);
+};
+
+/* 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 when the callback is
+   invoked with error != GRPC_ERROR_NONE. */
+void grpc_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                        grpc_closure* cb);
+
+char* grpc_endpoint_get_peer(grpc_endpoint* ep);
+
+/* Get the file descriptor used by \a ep. Return -1 if \a ep is not using an fd.
+ */
+int grpc_endpoint_get_fd(grpc_endpoint* ep);
+
+/* Write slices out to the socket.
+
+   If the connection is ready for more data after the end of the call, it
+   returns GRPC_ENDPOINT_DONE.
+   Otherwise it returns GRPC_ENDPOINT_PENDING and calls cb when the
+   connection is ready for more data.
+   \a slices may be mutated at will by the endpoint until cb is called.
+   No guarantee is made to the content of slices after a write EXCEPT that
+   it is a valid slice buffer.
+   \a arg is platform specific. It is currently only used by TCP on linux
+   platforms as an argument that would be forwarded to the timestamps callback.
+   */
+void grpc_endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                         grpc_closure* cb, void* arg);
+
+/* Causes any pending and future read/write callbacks to run immediately with
+   success==0 */
+void grpc_endpoint_shutdown(grpc_endpoint* ep, grpc_error* why);
+void grpc_endpoint_destroy(grpc_endpoint* ep);
+
+/* Add an endpoint to a pollset or pollset_set, so that when the pollset is
+   polled, events from this endpoint are considered */
+void grpc_endpoint_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset);
+void grpc_endpoint_add_to_pollset_set(grpc_endpoint* ep,
+                                      grpc_pollset_set* pollset_set);
+
+/* Delete an endpoint from a pollset_set */
+void grpc_endpoint_delete_from_pollset_set(grpc_endpoint* ep,
+                                           grpc_pollset_set* pollset_set);
+
+grpc_resource_user* grpc_endpoint_get_resource_user(grpc_endpoint* endpoint);
+
+bool grpc_endpoint_can_track_err(grpc_endpoint* ep);
+
+struct grpc_endpoint {
+  const grpc_endpoint_vtable* vtable;
+};
+
+#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.cc b/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.cc
new file mode 100644
index 0000000..7c4bc1a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.cc
@@ -0,0 +1,375 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_CFSTREAM_ENDPOINT
+
+#import <CoreFoundation/CoreFoundation.h>
+#import "src/core/lib/iomgr/endpoint_cfstream.h"
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/cfstream_handle.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/error_cfstream.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+typedef struct {
+  grpc_endpoint base;
+  gpr_refcount refcount;
+
+  CFReadStreamRef read_stream;
+  CFWriteStreamRef write_stream;
+  CFStreamHandle* stream_sync;
+
+  grpc_closure* read_cb;
+  grpc_closure* write_cb;
+  grpc_slice_buffer* read_slices;
+  grpc_slice_buffer* write_slices;
+
+  grpc_closure read_action;
+  grpc_closure write_action;
+
+  char* peer_string;
+  grpc_resource_user* resource_user;
+  grpc_resource_user_slice_allocator slice_allocator;
+} CFStreamEndpoint;
+
+static void CFStreamFree(CFStreamEndpoint* ep) {
+  grpc_resource_user_unref(ep->resource_user);
+  CFRelease(ep->read_stream);
+  CFRelease(ep->write_stream);
+  CFSTREAM_HANDLE_UNREF(ep->stream_sync, "free");
+  gpr_free(ep->peer_string);
+  gpr_free(ep);
+}
+
+#ifndef NDEBUG
+#define EP_REF(ep, reason) CFStreamRef((ep), (reason), __FILE__, __LINE__)
+#define EP_UNREF(ep, reason) CFStreamUnref((ep), (reason), __FILE__, __LINE__)
+static void CFStreamUnref(CFStreamEndpoint* ep, const char* reason,
+                          const char* file, int line) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&ep->refcount.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "CFStream endpoint unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep,
+            reason, val, val - 1);
+  }
+  if (gpr_unref(&ep->refcount)) {
+    CFStreamFree(ep);
+  }
+}
+static void CFStreamRef(CFStreamEndpoint* ep, const char* reason,
+                        const char* file, int line) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&ep->refcount.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "CFStream endpoint ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep,
+            reason, val, val + 1);
+  }
+  gpr_ref(&ep->refcount);
+}
+#else
+#define EP_REF(ep, reason) CFStreamRef((ep))
+#define EP_UNREF(ep, reason) CFStreamUnref((ep))
+static void CFStreamUnref(CFStreamEndpoint* ep) {
+  if (gpr_unref(&ep->refcount)) {
+    CFStreamFree(ep);
+  }
+}
+static void CFStreamRef(CFStreamEndpoint* ep) { gpr_ref(&ep->refcount); }
+#endif
+
+static grpc_error* CFStreamAnnotateError(grpc_error* src_error,
+                                         CFStreamEndpoint* ep) {
+  return grpc_error_set_str(
+      grpc_error_set_int(src_error, GRPC_ERROR_INT_GRPC_STATUS,
+                         GRPC_STATUS_UNAVAILABLE),
+      GRPC_ERROR_STR_TARGET_ADDRESS,
+      grpc_slice_from_copied_string(ep->peer_string));
+}
+
+static void CallReadCb(CFStreamEndpoint* ep, grpc_error* error) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CFStream endpoint:%p call_read_cb %p %p:%p", ep,
+            ep->read_cb, ep->read_cb->cb, ep->read_cb->cb_arg);
+    size_t i;
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "read: error=%s", str);
+
+    for (i = 0; i < ep->read_slices->count; i++) {
+      char* dump = grpc_dump_slice(ep->read_slices->slices[i],
+                                   GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", ep, ep->peer_string, dump);
+      gpr_free(dump);
+    }
+  }
+  grpc_closure* cb = ep->read_cb;
+  ep->read_cb = nullptr;
+  ep->read_slices = nullptr;
+  GRPC_CLOSURE_SCHED(cb, error);
+}
+
+static void CallWriteCb(CFStreamEndpoint* ep, grpc_error* error) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CFStream endpoint:%p call_write_cb %p %p:%p", ep,
+            ep->write_cb, ep->write_cb->cb, ep->write_cb->cb_arg);
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "write: error=%s", str);
+  }
+  grpc_closure* cb = ep->write_cb;
+  ep->write_cb = nullptr;
+  ep->write_slices = nullptr;
+  GRPC_CLOSURE_SCHED(cb, error);
+}
+
+static void ReadAction(void* arg, grpc_error* error) {
+  CFStreamEndpoint* ep = static_cast<CFStreamEndpoint*>(arg);
+  GPR_ASSERT(ep->read_cb != nullptr);
+  if (error) {
+    grpc_slice_buffer_reset_and_unref_internal(ep->read_slices);
+    CallReadCb(ep, GRPC_ERROR_REF(error));
+    EP_UNREF(ep, "read");
+    return;
+  }
+
+  GPR_ASSERT(ep->read_slices->count == 1);
+  grpc_slice slice = ep->read_slices->slices[0];
+  size_t len = GRPC_SLICE_LENGTH(slice);
+  CFIndex read_size =
+      CFReadStreamRead(ep->read_stream, GRPC_SLICE_START_PTR(slice), len);
+  if (read_size == -1) {
+    grpc_slice_buffer_reset_and_unref_internal(ep->read_slices);
+    CFErrorRef stream_error = CFReadStreamCopyError(ep->read_stream);
+    if (stream_error != nullptr) {
+      error = CFStreamAnnotateError(
+          GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "Read error"), ep);
+      CFRelease(stream_error);
+    } else {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Read error");
+    }
+    CallReadCb(ep, error);
+    EP_UNREF(ep, "read");
+  } else if (read_size == 0) {
+    grpc_slice_buffer_reset_and_unref_internal(ep->read_slices);
+    CallReadCb(ep,
+               CFStreamAnnotateError(
+                   GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), ep));
+    EP_UNREF(ep, "read");
+  } else {
+    if (read_size < len) {
+      grpc_slice_buffer_trim_end(ep->read_slices, len - read_size, nullptr);
+    }
+    CallReadCb(ep, GRPC_ERROR_NONE);
+    EP_UNREF(ep, "read");
+  }
+}
+
+static void WriteAction(void* arg, grpc_error* error) {
+  CFStreamEndpoint* ep = static_cast<CFStreamEndpoint*>(arg);
+  GPR_ASSERT(ep->write_cb != nullptr);
+  if (error) {
+    grpc_slice_buffer_reset_and_unref_internal(ep->write_slices);
+    CallWriteCb(ep, GRPC_ERROR_REF(error));
+    EP_UNREF(ep, "write");
+    return;
+  }
+
+  grpc_slice slice = grpc_slice_buffer_take_first(ep->write_slices);
+  size_t slice_len = GRPC_SLICE_LENGTH(slice);
+  CFIndex write_size = CFWriteStreamWrite(
+      ep->write_stream, GRPC_SLICE_START_PTR(slice), slice_len);
+  if (write_size == -1) {
+    grpc_slice_buffer_reset_and_unref_internal(ep->write_slices);
+    CFErrorRef stream_error = CFWriteStreamCopyError(ep->write_stream);
+    if (stream_error != nullptr) {
+      error = CFStreamAnnotateError(
+          GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "write failed."), ep);
+      CFRelease(stream_error);
+    } else {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("write failed.");
+    }
+    CallWriteCb(ep, error);
+    EP_UNREF(ep, "write");
+  } else {
+    if (write_size < GRPC_SLICE_LENGTH(slice)) {
+      grpc_slice_buffer_undo_take_first(
+          ep->write_slices, grpc_slice_sub(slice, write_size, slice_len));
+    }
+    if (ep->write_slices->length > 0) {
+      ep->stream_sync->NotifyOnWrite(&ep->write_action);
+    } else {
+      CallWriteCb(ep, GRPC_ERROR_NONE);
+      EP_UNREF(ep, "write");
+    }
+
+    if (grpc_tcp_trace.enabled()) {
+      grpc_slice trace_slice = grpc_slice_sub(slice, 0, write_size);
+      char* dump = grpc_dump_slice(trace_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", ep, ep->peer_string, dump);
+      gpr_free(dump);
+      grpc_slice_unref_internal(trace_slice);
+    }
+  }
+  grpc_slice_unref_internal(slice);
+}
+
+static void CFStreamReadAllocationDone(void* arg, grpc_error* error) {
+  CFStreamEndpoint* ep = static_cast<CFStreamEndpoint*>(arg);
+  if (error == GRPC_ERROR_NONE) {
+    ep->stream_sync->NotifyOnRead(&ep->read_action);
+  } else {
+    grpc_slice_buffer_reset_and_unref_internal(ep->read_slices);
+    CallReadCb(ep, error);
+    EP_UNREF(ep, "read");
+  }
+}
+
+static void CFStreamRead(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                         grpc_closure* cb) {
+  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CFStream endpoint:%p read (%p, %p) length:%zu", ep_impl,
+            slices, cb, slices->length);
+  }
+  GPR_ASSERT(ep_impl->read_cb == nullptr);
+  ep_impl->read_cb = cb;
+  ep_impl->read_slices = slices;
+  grpc_slice_buffer_reset_and_unref_internal(slices);
+  grpc_resource_user_alloc_slices(&ep_impl->slice_allocator,
+                                  GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1,
+                                  ep_impl->read_slices);
+  EP_REF(ep_impl, "read");
+}
+
+static void CFStreamWrite(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                          grpc_closure* cb, void* arg) {
+  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CFStream endpoint:%p write (%p, %p) length:%zu",
+            ep_impl, slices, cb, slices->length);
+  }
+  GPR_ASSERT(ep_impl->write_cb == nullptr);
+  ep_impl->write_cb = cb;
+  ep_impl->write_slices = slices;
+  EP_REF(ep_impl, "write");
+  ep_impl->stream_sync->NotifyOnWrite(&ep_impl->write_action);
+}
+
+void CFStreamShutdown(grpc_endpoint* ep, grpc_error* why) {
+  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CFStream endpoint:%p shutdown (%p)", ep_impl, why);
+  }
+  CFReadStreamClose(ep_impl->read_stream);
+  CFWriteStreamClose(ep_impl->write_stream);
+  ep_impl->stream_sync->Shutdown(why);
+  grpc_resource_user_shutdown(ep_impl->resource_user);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CFStream endpoint:%p shutdown DONE (%p)", ep_impl, why);
+  }
+}
+
+void CFStreamDestroy(grpc_endpoint* ep) {
+  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CFStream endpoint:%p destroy", ep_impl);
+  }
+  EP_UNREF(ep_impl, "destroy");
+}
+
+grpc_resource_user* CFStreamGetResourceUser(grpc_endpoint* ep) {
+  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
+  return ep_impl->resource_user;
+}
+
+char* CFStreamGetPeer(grpc_endpoint* ep) {
+  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
+  return gpr_strdup(ep_impl->peer_string);
+}
+
+int CFStreamGetFD(grpc_endpoint* ep) { return 0; }
+
+bool CFStreamCanTrackErr(grpc_endpoint* ep) { return false; }
+
+void CFStreamAddToPollset(grpc_endpoint* ep, grpc_pollset* pollset) {}
+void CFStreamAddToPollsetSet(grpc_endpoint* ep, grpc_pollset_set* pollset) {}
+void CFStreamDeleteFromPollsetSet(grpc_endpoint* ep,
+                                  grpc_pollset_set* pollset) {}
+
+static const grpc_endpoint_vtable vtable = {CFStreamRead,
+                                            CFStreamWrite,
+                                            CFStreamAddToPollset,
+                                            CFStreamAddToPollsetSet,
+                                            CFStreamDeleteFromPollsetSet,
+                                            CFStreamShutdown,
+                                            CFStreamDestroy,
+                                            CFStreamGetResourceUser,
+                                            CFStreamGetPeer,
+                                            CFStreamGetFD,
+                                            CFStreamCanTrackErr};
+
+grpc_endpoint* grpc_cfstream_endpoint_create(
+    CFReadStreamRef read_stream, CFWriteStreamRef write_stream,
+    const char* peer_string, grpc_resource_quota* resource_quota,
+    CFStreamHandle* stream_sync) {
+  CFStreamEndpoint* ep_impl =
+      static_cast<CFStreamEndpoint*>(gpr_malloc(sizeof(CFStreamEndpoint)));
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "CFStream endpoint:%p create readStream:%p writeStream: %p",
+            ep_impl, read_stream, write_stream);
+  }
+  ep_impl->base.vtable = &vtable;
+  gpr_ref_init(&ep_impl->refcount, 1);
+  ep_impl->read_stream = read_stream;
+  ep_impl->write_stream = write_stream;
+  CFRetain(read_stream);
+  CFRetain(write_stream);
+  ep_impl->stream_sync = stream_sync;
+  CFSTREAM_HANDLE_REF(ep_impl->stream_sync, "endpoint create");
+
+  ep_impl->peer_string = gpr_strdup(peer_string);
+  ep_impl->read_cb = nil;
+  ep_impl->write_cb = nil;
+  ep_impl->read_slices = nil;
+  ep_impl->write_slices = nil;
+  GRPC_CLOSURE_INIT(&ep_impl->read_action, ReadAction,
+                    static_cast<void*>(ep_impl), grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&ep_impl->write_action, WriteAction,
+                    static_cast<void*>(ep_impl), grpc_schedule_on_exec_ctx);
+  ep_impl->resource_user =
+      grpc_resource_user_create(resource_quota, peer_string);
+  grpc_resource_user_slice_allocator_init(&ep_impl->slice_allocator,
+                                          ep_impl->resource_user,
+                                          CFStreamReadAllocationDone, ep_impl);
+
+  return &ep_impl->base;
+}
+
+#endif /* GRPC_CFSTREAM_ENDPOINT */
diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.h b/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.h
new file mode 100644
index 0000000..ef957c1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_CFSTREAM_H
+#define GRPC_CORE_LIB_IOMGR_ENDPOINT_CFSTREAM_H
+/*
+   Low level TCP "bottom half" implementation, for use by transports built on
+   top of a TCP connection.
+
+   Note that this file does not (yet) include APIs for creating the socket in
+   the first place.
+
+   All calls passing slice transfer ownership of a slice refcount unless
+   otherwise specified.
+*/
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GRPC_CFSTREAM
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/cfstream_handle.h"
+#include "src/core/lib/iomgr/endpoint.h"
+
+grpc_endpoint* grpc_cfstream_endpoint_create(
+    CFReadStreamRef read_stream, CFWriteStreamRef write_stream,
+    const char* peer_string, grpc_resource_quota* resource_quota,
+    CFStreamHandle* stream_sync);
+
+#endif /* GRPC_CFSTREAM */
+
+#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_CFSTREAM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_pair.h b/third_party/grpc/src/core/lib/iomgr/endpoint_pair.h
new file mode 100644
index 0000000..08f9e3c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/endpoint_pair.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H
+#define GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+
+typedef struct {
+  grpc_endpoint* client;
+  grpc_endpoint* server;
+} grpc_endpoint_pair;
+
+grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char* name,
+                                                   grpc_channel_args* args);
+
+#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_pair_posix.cc b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_posix.cc
new file mode 100644
index 0000000..5c5c246
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_posix.cc
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/endpoint_pair.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+
+static void create_sockets(int sv[2]) {
+  int flags;
+  grpc_create_socketpair_if_unix(sv);
+  flags = fcntl(sv[0], F_GETFL, 0);
+  GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
+  flags = fcntl(sv[1], F_GETFL, 0);
+  GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
+  GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]) == GRPC_ERROR_NONE);
+  GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == GRPC_ERROR_NONE);
+}
+
+grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char* name,
+                                                   grpc_channel_args* args) {
+  int sv[2];
+  grpc_endpoint_pair p;
+  char* final_name;
+  create_sockets(sv);
+
+  grpc_core::ExecCtx exec_ctx;
+
+  gpr_asprintf(&final_name, "%s:client", name);
+  p.client = grpc_tcp_create(grpc_fd_create(sv[1], final_name, false), args,
+                             "socketpair-server");
+  gpr_free(final_name);
+  gpr_asprintf(&final_name, "%s:server", name);
+  p.server = grpc_tcp_create(grpc_fd_create(sv[0], final_name, false), args,
+                             "socketpair-client");
+  gpr_free(final_name);
+
+  return p;
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_pair_uv.cc b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_uv.cc
new file mode 100644
index 0000000..b99d178
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_uv.cc
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_UV
+
+#include <stdlib.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/endpoint_pair.h"
+
+grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char* name,
+                                                   grpc_channel_args* args) {
+  grpc_endpoint_pair endpoint_pair;
+  // TODO(mlumish): implement this properly under libuv
+  GPR_ASSERT(false &&
+             "grpc_iomgr_create_endpoint_pair is not suppoted with libuv");
+  return endpoint_pair;
+}
+
+#endif /* GRPC_UV */
diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_pair_windows.cc b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_windows.cc
new file mode 100644
index 0000000..177331d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_windows.cc
@@ -0,0 +1,87 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+#include "src/core/lib/iomgr/endpoint_pair.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include "src/core/lib/iomgr/socket_windows.h"
+#include "src/core/lib/iomgr/tcp_windows.h"
+
+static void create_sockets(SOCKET sv[2]) {
+  SOCKET svr_sock = INVALID_SOCKET;
+  SOCKET lst_sock = INVALID_SOCKET;
+  SOCKET cli_sock = INVALID_SOCKET;
+  SOCKADDR_IN addr;
+  int addr_len = sizeof(addr);
+
+  lst_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+                       WSA_FLAG_OVERLAPPED);
+  GPR_ASSERT(lst_sock != INVALID_SOCKET);
+
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  addr.sin_family = AF_INET;
+  GPR_ASSERT(bind(lst_sock, (grpc_sockaddr*)&addr, sizeof(addr)) !=
+             SOCKET_ERROR);
+  GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR);
+  GPR_ASSERT(getsockname(lst_sock, (grpc_sockaddr*)&addr, &addr_len) !=
+             SOCKET_ERROR);
+
+  cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+                       WSA_FLAG_OVERLAPPED);
+  GPR_ASSERT(cli_sock != INVALID_SOCKET);
+
+  GPR_ASSERT(WSAConnect(cli_sock, (grpc_sockaddr*)&addr, addr_len, NULL, NULL,
+                        NULL, NULL) == 0);
+  svr_sock = accept(lst_sock, (grpc_sockaddr*)&addr, &addr_len);
+  GPR_ASSERT(svr_sock != INVALID_SOCKET);
+
+  closesocket(lst_sock);
+  grpc_tcp_prepare_socket(cli_sock);
+  grpc_tcp_prepare_socket(svr_sock);
+
+  sv[1] = cli_sock;
+  sv[0] = svr_sock;
+}
+
+grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(
+    const char* name, grpc_channel_args* channel_args) {
+  SOCKET sv[2];
+  grpc_endpoint_pair p;
+  create_sockets(sv);
+  grpc_core::ExecCtx exec_ctx;
+  p.client = grpc_tcp_create(grpc_winsocket_create(sv[1], "endpoint:client"),
+                             channel_args, "endpoint:server");
+  p.server = grpc_tcp_create(grpc_winsocket_create(sv[0], "endpoint:server"),
+                             channel_args, "endpoint:client");
+
+  return p;
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/error.cc b/third_party/grpc/src/core/lib/iomgr/error.cc
new file mode 100644
index 0000000..6ae077f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/error.cc
@@ -0,0 +1,801 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/error.h"
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <grpc/status.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#ifdef GPR_WINDOWS
+#include <grpc/support/log_windows.h>
+#endif
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/error_internal.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount(false,
+                                                        "error_refcount");
+grpc_core::DebugOnlyTraceFlag grpc_trace_closure(false, "closure");
+
+static const char* error_int_name(grpc_error_ints key) {
+  switch (key) {
+    case GRPC_ERROR_INT_ERRNO:
+      return "errno";
+    case GRPC_ERROR_INT_FILE_LINE:
+      return "file_line";
+    case GRPC_ERROR_INT_STREAM_ID:
+      return "stream_id";
+    case GRPC_ERROR_INT_GRPC_STATUS:
+      return "grpc_status";
+    case GRPC_ERROR_INT_OFFSET:
+      return "offset";
+    case GRPC_ERROR_INT_INDEX:
+      return "index";
+    case GRPC_ERROR_INT_SIZE:
+      return "size";
+    case GRPC_ERROR_INT_HTTP2_ERROR:
+      return "http2_error";
+    case GRPC_ERROR_INT_TSI_CODE:
+      return "tsi_code";
+    case GRPC_ERROR_INT_SECURITY_STATUS:
+      return "security_status";
+    case GRPC_ERROR_INT_FD:
+      return "fd";
+    case GRPC_ERROR_INT_WSA_ERROR:
+      return "wsa_error";
+    case GRPC_ERROR_INT_HTTP_STATUS:
+      return "http_status";
+    case GRPC_ERROR_INT_LIMIT:
+      return "limit";
+    case GRPC_ERROR_INT_OCCURRED_DURING_WRITE:
+      return "occurred_during_write";
+    case GRPC_ERROR_INT_MAX:
+      GPR_UNREACHABLE_CODE(return "unknown");
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+static const char* error_str_name(grpc_error_strs key) {
+  switch (key) {
+    case GRPC_ERROR_STR_KEY:
+      return "key";
+    case GRPC_ERROR_STR_VALUE:
+      return "value";
+    case GRPC_ERROR_STR_DESCRIPTION:
+      return "description";
+    case GRPC_ERROR_STR_OS_ERROR:
+      return "os_error";
+    case GRPC_ERROR_STR_TARGET_ADDRESS:
+      return "target_address";
+    case GRPC_ERROR_STR_SYSCALL:
+      return "syscall";
+    case GRPC_ERROR_STR_FILE:
+      return "file";
+    case GRPC_ERROR_STR_GRPC_MESSAGE:
+      return "grpc_message";
+    case GRPC_ERROR_STR_RAW_BYTES:
+      return "raw_bytes";
+    case GRPC_ERROR_STR_TSI_ERROR:
+      return "tsi_error";
+    case GRPC_ERROR_STR_FILENAME:
+      return "filename";
+    case GRPC_ERROR_STR_QUEUED_BUFFERS:
+      return "queued_buffers";
+    case GRPC_ERROR_STR_MAX:
+      GPR_UNREACHABLE_CODE(return "unknown");
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+static const char* error_time_name(grpc_error_times key) {
+  switch (key) {
+    case GRPC_ERROR_TIME_CREATED:
+      return "created";
+    case GRPC_ERROR_TIME_MAX:
+      GPR_UNREACHABLE_CODE(return "unknown");
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+#ifndef NDEBUG
+grpc_error* grpc_error_do_ref(grpc_error* err, const char* file, int line) {
+  if (grpc_trace_error_refcount.enabled()) {
+    gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err,
+            gpr_atm_no_barrier_load(&err->atomics.refs.count),
+            gpr_atm_no_barrier_load(&err->atomics.refs.count) + 1, file, line);
+  }
+  gpr_ref(&err->atomics.refs);
+  return err;
+}
+#else
+grpc_error* grpc_error_do_ref(grpc_error* err) {
+  gpr_ref(&err->atomics.refs);
+  return err;
+}
+#endif
+
+static void unref_errs(grpc_error* err) {
+  uint8_t slot = err->first_err;
+  while (slot != UINT8_MAX) {
+    grpc_linked_error* lerr =
+        reinterpret_cast<grpc_linked_error*>(err->arena + slot);
+    GRPC_ERROR_UNREF(lerr->err);
+    GPR_ASSERT(err->last_err == slot ? lerr->next == UINT8_MAX
+                                     : lerr->next != UINT8_MAX);
+    slot = lerr->next;
+  }
+}
+
+static void unref_slice(grpc_slice slice) { grpc_slice_unref_internal(slice); }
+
+static void unref_strs(grpc_error* err) {
+  for (size_t which = 0; which < GRPC_ERROR_STR_MAX; ++which) {
+    uint8_t slot = err->strs[which];
+    if (slot != UINT8_MAX) {
+      unref_slice(*reinterpret_cast<grpc_slice*>(err->arena + slot));
+    }
+  }
+}
+
+static void error_destroy(grpc_error* err) {
+  GPR_ASSERT(!grpc_error_is_special(err));
+  unref_errs(err);
+  unref_strs(err);
+  gpr_free((void*)gpr_atm_acq_load(&err->atomics.error_string));
+  gpr_free(err);
+}
+
+#ifndef NDEBUG
+void grpc_error_do_unref(grpc_error* err, const char* file, int line) {
+  if (grpc_trace_error_refcount.enabled()) {
+    gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err,
+            gpr_atm_no_barrier_load(&err->atomics.refs.count),
+            gpr_atm_no_barrier_load(&err->atomics.refs.count) - 1, file, line);
+  }
+  if (gpr_unref(&err->atomics.refs)) {
+    error_destroy(err);
+  }
+}
+#else
+void grpc_error_do_unref(grpc_error* err) {
+  if (gpr_unref(&err->atomics.refs)) {
+    error_destroy(err);
+  }
+}
+#endif
+
+static uint8_t get_placement(grpc_error** err, size_t size) {
+  GPR_ASSERT(*err);
+  uint8_t slots = static_cast<uint8_t>(size / sizeof(intptr_t));
+  if ((*err)->arena_size + slots > (*err)->arena_capacity) {
+    (*err)->arena_capacity = static_cast<uint8_t> GPR_MIN(
+        UINT8_MAX - 1, (3 * (*err)->arena_capacity / 2));
+    if ((*err)->arena_size + slots > (*err)->arena_capacity) {
+      return UINT8_MAX;
+    }
+#ifndef NDEBUG
+    grpc_error* orig = *err;
+#endif
+    *err = static_cast<grpc_error*>(gpr_realloc(
+        *err, sizeof(grpc_error) + (*err)->arena_capacity * sizeof(intptr_t)));
+#ifndef NDEBUG
+    if (grpc_trace_error_refcount.enabled()) {
+      if (*err != orig) {
+        gpr_log(GPR_DEBUG, "realloc %p -> %p", orig, *err);
+      }
+    }
+#endif
+  }
+  uint8_t placement = (*err)->arena_size;
+  (*err)->arena_size = static_cast<uint8_t>((*err)->arena_size + slots);
+  return placement;
+}
+
+static void internal_set_int(grpc_error** err, grpc_error_ints which,
+                             intptr_t value) {
+  uint8_t slot = (*err)->ints[which];
+  if (slot == UINT8_MAX) {
+    slot = get_placement(err, sizeof(value));
+    if (slot == UINT8_MAX) {
+      gpr_log(GPR_ERROR, "Error %p is full, dropping int {\"%s\":%" PRIiPTR "}",
+              *err, error_int_name(which), value);
+      return;
+    }
+  }
+  (*err)->ints[which] = slot;
+  (*err)->arena[slot] = value;
+}
+
+static void internal_set_str(grpc_error** err, grpc_error_strs which,
+                             grpc_slice value) {
+  uint8_t slot = (*err)->strs[which];
+  if (slot == UINT8_MAX) {
+    slot = get_placement(err, sizeof(value));
+    if (slot == UINT8_MAX) {
+      const char* str = grpc_slice_to_c_string(value);
+      gpr_log(GPR_ERROR, "Error %p is full, dropping string {\"%s\":\"%s\"}",
+              *err, error_str_name(which), str);
+      gpr_free((void*)str);
+      return;
+    }
+  } else {
+    unref_slice(*reinterpret_cast<grpc_slice*>((*err)->arena + slot));
+  }
+  (*err)->strs[which] = slot;
+  memcpy((*err)->arena + slot, &value, sizeof(value));
+}
+
+static char* fmt_time(gpr_timespec tm);
+static void internal_set_time(grpc_error** err, grpc_error_times which,
+                              gpr_timespec value) {
+  uint8_t slot = (*err)->times[which];
+  if (slot == UINT8_MAX) {
+    slot = get_placement(err, sizeof(value));
+    if (slot == UINT8_MAX) {
+      const char* time_str = fmt_time(value);
+      gpr_log(GPR_ERROR, "Error %p is full, dropping \"%s\":\"%s\"}", *err,
+              error_time_name(which), time_str);
+      gpr_free((void*)time_str);
+      return;
+    }
+  }
+  (*err)->times[which] = slot;
+  memcpy((*err)->arena + slot, &value, sizeof(value));
+}
+
+static void internal_add_error(grpc_error** err, grpc_error* new_err) {
+  grpc_linked_error new_last = {new_err, UINT8_MAX};
+  uint8_t slot = get_placement(err, sizeof(grpc_linked_error));
+  if (slot == UINT8_MAX) {
+    gpr_log(GPR_ERROR, "Error %p is full, dropping error %p = %s", *err,
+            new_err, grpc_error_string(new_err));
+    GRPC_ERROR_UNREF(new_err);
+    return;
+  }
+  if ((*err)->first_err == UINT8_MAX) {
+    GPR_ASSERT((*err)->last_err == UINT8_MAX);
+    (*err)->last_err = slot;
+    (*err)->first_err = slot;
+  } else {
+    GPR_ASSERT((*err)->last_err != UINT8_MAX);
+    grpc_linked_error* old_last =
+        reinterpret_cast<grpc_linked_error*>((*err)->arena + (*err)->last_err);
+    old_last->next = slot;
+    (*err)->last_err = slot;
+  }
+  memcpy((*err)->arena + slot, &new_last, sizeof(grpc_linked_error));
+}
+
+#define SLOTS_PER_INT (sizeof(intptr_t) / sizeof(intptr_t))
+#define SLOTS_PER_STR (sizeof(grpc_slice) / sizeof(intptr_t))
+#define SLOTS_PER_TIME (sizeof(gpr_timespec) / sizeof(intptr_t))
+#define SLOTS_PER_LINKED_ERROR (sizeof(grpc_linked_error) / sizeof(intptr_t))
+
+// size of storing one int and two slices and a timespec. For line, desc, file,
+// and time created
+#define DEFAULT_ERROR_CAPACITY \
+  (SLOTS_PER_INT + (SLOTS_PER_STR * 2) + SLOTS_PER_TIME)
+
+// It is very common to include and extra int and string in an error
+#define SURPLUS_CAPACITY (2 * SLOTS_PER_INT + SLOTS_PER_TIME)
+
+static bool g_error_creation_allowed = true;
+
+void grpc_disable_error_creation() { g_error_creation_allowed = false; }
+
+void grpc_enable_error_creation() { g_error_creation_allowed = true; }
+
+grpc_error* grpc_error_create(const char* file, int line, grpc_slice desc,
+                              grpc_error** referencing,
+                              size_t num_referencing) {
+  GPR_TIMER_SCOPE("grpc_error_create", 0);
+  uint8_t initial_arena_capacity = static_cast<uint8_t>(
+      DEFAULT_ERROR_CAPACITY +
+      static_cast<uint8_t>(num_referencing * SLOTS_PER_LINKED_ERROR) +
+      SURPLUS_CAPACITY);
+  grpc_error* err = static_cast<grpc_error*>(
+      gpr_malloc(sizeof(*err) + initial_arena_capacity * sizeof(intptr_t)));
+  if (err == nullptr) {  // TODO(ctiller): make gpr_malloc return NULL
+    return GRPC_ERROR_OOM;
+  }
+#ifndef NDEBUG
+  if (!g_error_creation_allowed) {
+    gpr_log(GPR_ERROR,
+            "Error creation occurred when error creation was disabled [%s:%d]",
+            file, line);
+    abort();
+  }
+  if (grpc_trace_error_refcount.enabled()) {
+    gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line);
+  }
+#endif
+
+  err->arena_size = 0;
+  err->arena_capacity = initial_arena_capacity;
+  err->first_err = UINT8_MAX;
+  err->last_err = UINT8_MAX;
+
+  memset(err->ints, UINT8_MAX, GRPC_ERROR_INT_MAX);
+  memset(err->strs, UINT8_MAX, GRPC_ERROR_STR_MAX);
+  memset(err->times, UINT8_MAX, GRPC_ERROR_TIME_MAX);
+
+  internal_set_int(&err, GRPC_ERROR_INT_FILE_LINE, line);
+  internal_set_str(&err, GRPC_ERROR_STR_FILE,
+                   grpc_slice_from_static_string(file));
+  internal_set_str(&err, GRPC_ERROR_STR_DESCRIPTION, desc);
+
+  for (size_t i = 0; i < num_referencing; ++i) {
+    if (referencing[i] == GRPC_ERROR_NONE) continue;
+    internal_add_error(
+        &err,
+        GRPC_ERROR_REF(
+            referencing[i]));  // TODO(ncteisen), change ownership semantics
+  }
+
+  internal_set_time(&err, GRPC_ERROR_TIME_CREATED, gpr_now(GPR_CLOCK_REALTIME));
+
+  gpr_atm_no_barrier_store(&err->atomics.error_string, 0);
+  gpr_ref_init(&err->atomics.refs, 1);
+  return err;
+}
+
+static void ref_strs(grpc_error* err) {
+  for (size_t i = 0; i < GRPC_ERROR_STR_MAX; ++i) {
+    uint8_t slot = err->strs[i];
+    if (slot != UINT8_MAX) {
+      grpc_slice_ref_internal(
+          *reinterpret_cast<grpc_slice*>(err->arena + slot));
+    }
+  }
+}
+
+static void ref_errs(grpc_error* err) {
+  uint8_t slot = err->first_err;
+  while (slot != UINT8_MAX) {
+    grpc_linked_error* lerr =
+        reinterpret_cast<grpc_linked_error*>(err->arena + slot);
+    GRPC_ERROR_REF(lerr->err);
+    slot = lerr->next;
+  }
+}
+
+static grpc_error* copy_error_and_unref(grpc_error* in) {
+  GPR_TIMER_SCOPE("copy_error_and_unref", 0);
+  grpc_error* out;
+  if (grpc_error_is_special(in)) {
+    out = GRPC_ERROR_CREATE_FROM_STATIC_STRING("unknown");
+    if (in == GRPC_ERROR_NONE) {
+      internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION,
+                       grpc_slice_from_static_string("no error"));
+      internal_set_int(&out, GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK);
+    } else if (in == GRPC_ERROR_OOM) {
+      internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION,
+                       grpc_slice_from_static_string("oom"));
+    } else if (in == GRPC_ERROR_CANCELLED) {
+      internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION,
+                       grpc_slice_from_static_string("cancelled"));
+      internal_set_int(&out, GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED);
+    }
+  } else if (gpr_ref_is_unique(&in->atomics.refs)) {
+    out = in;
+  } else {
+    uint8_t new_arena_capacity = in->arena_capacity;
+    // the returned err will be added to, so we ensure this is room to avoid
+    // unneeded allocations.
+    if (in->arena_capacity - in->arena_size <
+        static_cast<uint8_t> SLOTS_PER_STR) {
+      new_arena_capacity = static_cast<uint8_t>(3 * new_arena_capacity / 2);
+    }
+    out = static_cast<grpc_error*>(
+        gpr_malloc(sizeof(*in) + new_arena_capacity * sizeof(intptr_t)));
+#ifndef NDEBUG
+    if (grpc_trace_error_refcount.enabled()) {
+      gpr_log(GPR_DEBUG, "%p create copying %p", out, in);
+    }
+#endif
+    // bulk memcpy of the rest of the struct.
+    size_t skip = sizeof(&out->atomics);
+    memcpy((void*)((uintptr_t)out + skip), (void*)((uintptr_t)in + skip),
+           sizeof(*in) + (in->arena_size * sizeof(intptr_t)) - skip);
+    // manually set the atomics and the new capacity
+    gpr_atm_no_barrier_store(&out->atomics.error_string, 0);
+    gpr_ref_init(&out->atomics.refs, 1);
+    out->arena_capacity = new_arena_capacity;
+    ref_strs(out);
+    ref_errs(out);
+    GRPC_ERROR_UNREF(in);
+  }
+  return out;
+}
+
+grpc_error* grpc_error_set_int(grpc_error* src, grpc_error_ints which,
+                               intptr_t value) {
+  GPR_TIMER_SCOPE("grpc_error_set_int", 0);
+  grpc_error* new_err = copy_error_and_unref(src);
+  internal_set_int(&new_err, which, value);
+  return new_err;
+}
+
+typedef struct {
+  grpc_status_code code;
+  const char* msg;
+} special_error_status_map;
+static const special_error_status_map error_status_map[] = {
+    {GRPC_STATUS_OK, ""},                               // GRPC_ERROR_NONE
+    {GRPC_STATUS_INVALID_ARGUMENT, ""},                 // GRPC_ERROR_RESERVED_1
+    {GRPC_STATUS_RESOURCE_EXHAUSTED, "Out of memory"},  // GRPC_ERROR_OOM
+    {GRPC_STATUS_INVALID_ARGUMENT, ""},                 // GRPC_ERROR_RESERVED_2
+    {GRPC_STATUS_CANCELLED, "Cancelled"},               // GRPC_ERROR_CANCELLED
+};
+
+bool grpc_error_get_int(grpc_error* err, grpc_error_ints which, intptr_t* p) {
+  GPR_TIMER_SCOPE("grpc_error_get_int", 0);
+  if (grpc_error_is_special(err)) {
+    if (which != GRPC_ERROR_INT_GRPC_STATUS) return false;
+    *p = error_status_map[reinterpret_cast<size_t>(err)].code;
+    return true;
+  }
+  uint8_t slot = err->ints[which];
+  if (slot != UINT8_MAX) {
+    if (p != nullptr) *p = err->arena[slot];
+    return true;
+  }
+  return false;
+}
+
+grpc_error* grpc_error_set_str(grpc_error* src, grpc_error_strs which,
+                               grpc_slice str) {
+  GPR_TIMER_SCOPE("grpc_error_set_str", 0);
+  grpc_error* new_err = copy_error_and_unref(src);
+  internal_set_str(&new_err, which, str);
+  return new_err;
+}
+
+bool grpc_error_get_str(grpc_error* err, grpc_error_strs which,
+                        grpc_slice* str) {
+  if (grpc_error_is_special(err)) {
+    if (which != GRPC_ERROR_STR_GRPC_MESSAGE) return false;
+    *str = grpc_slice_from_static_string(
+        error_status_map[reinterpret_cast<size_t>(err)].msg);
+    return true;
+  }
+  uint8_t slot = err->strs[which];
+  if (slot != UINT8_MAX) {
+    *str = *reinterpret_cast<grpc_slice*>(err->arena + slot);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+grpc_error* grpc_error_add_child(grpc_error* src, grpc_error* child) {
+  GPR_TIMER_SCOPE("grpc_error_add_child", 0);
+  if (src != GRPC_ERROR_NONE) {
+    if (child == GRPC_ERROR_NONE) {
+      /* \a child is empty. Simply return the ref to \a src */
+      return src;
+    } else if (child != src) {
+      grpc_error* new_err = copy_error_and_unref(src);
+      internal_add_error(&new_err, child);
+      return new_err;
+    } else {
+      /* \a src and \a child are the same. Drop one of the references and return
+       * the other */
+      GRPC_ERROR_UNREF(child);
+      return src;
+    }
+  } else {
+    /* \a src is empty. Simply return the ref to \a child */
+    return child;
+  }
+}
+
+static const char* no_error_string = "\"No Error\"";
+static const char* oom_error_string = "\"Out of memory\"";
+static const char* cancelled_error_string = "\"Cancelled\"";
+
+typedef struct {
+  char* key;
+  char* value;
+} kv_pair;
+
+typedef struct {
+  kv_pair* kvs;
+  size_t num_kvs;
+  size_t cap_kvs;
+} kv_pairs;
+
+static void append_chr(char c, char** s, size_t* sz, size_t* cap) {
+  if (*sz == *cap) {
+    *cap = GPR_MAX(8, 3 * *cap / 2);
+    *s = static_cast<char*>(gpr_realloc(*s, *cap));
+  }
+  (*s)[(*sz)++] = c;
+}
+
+static void append_str(const char* str, char** s, size_t* sz, size_t* cap) {
+  for (const char* c = str; *c; c++) {
+    append_chr(*c, s, sz, cap);
+  }
+}
+
+static void append_esc_str(const uint8_t* str, size_t len, char** s, size_t* sz,
+                           size_t* cap) {
+  static const char* hex = "0123456789abcdef";
+  append_chr('"', s, sz, cap);
+  for (size_t i = 0; i < len; i++, str++) {
+    if (*str < 32 || *str >= 127) {
+      append_chr('\\', s, sz, cap);
+      switch (*str) {
+        case '\b':
+          append_chr('b', s, sz, cap);
+          break;
+        case '\f':
+          append_chr('f', s, sz, cap);
+          break;
+        case '\n':
+          append_chr('n', s, sz, cap);
+          break;
+        case '\r':
+          append_chr('r', s, sz, cap);
+          break;
+        case '\t':
+          append_chr('t', s, sz, cap);
+          break;
+        default:
+          append_chr('u', s, sz, cap);
+          append_chr('0', s, sz, cap);
+          append_chr('0', s, sz, cap);
+          append_chr(hex[*str >> 4], s, sz, cap);
+          append_chr(hex[*str & 0x0f], s, sz, cap);
+          break;
+      }
+    } else {
+      append_chr(static_cast<char>(*str), s, sz, cap);
+    }
+  }
+  append_chr('"', s, sz, cap);
+}
+
+static void append_kv(kv_pairs* kvs, char* key, char* value) {
+  if (kvs->num_kvs == kvs->cap_kvs) {
+    kvs->cap_kvs = GPR_MAX(3 * kvs->cap_kvs / 2, 4);
+    kvs->kvs = static_cast<kv_pair*>(
+        gpr_realloc(kvs->kvs, sizeof(*kvs->kvs) * kvs->cap_kvs));
+  }
+  kvs->kvs[kvs->num_kvs].key = key;
+  kvs->kvs[kvs->num_kvs].value = value;
+  kvs->num_kvs++;
+}
+
+static char* key_int(grpc_error_ints which) {
+  return gpr_strdup(error_int_name(which));
+}
+
+static char* fmt_int(intptr_t p) {
+  char* s;
+  gpr_asprintf(&s, "%" PRIdPTR, p);
+  return s;
+}
+
+static void collect_ints_kvs(grpc_error* err, kv_pairs* kvs) {
+  for (size_t which = 0; which < GRPC_ERROR_INT_MAX; ++which) {
+    uint8_t slot = err->ints[which];
+    if (slot != UINT8_MAX) {
+      append_kv(kvs, key_int(static_cast<grpc_error_ints>(which)),
+                fmt_int(err->arena[slot]));
+    }
+  }
+}
+
+static char* key_str(grpc_error_strs which) {
+  return gpr_strdup(error_str_name(which));
+}
+
+static char* fmt_str(grpc_slice slice) {
+  char* s = nullptr;
+  size_t sz = 0;
+  size_t cap = 0;
+  append_esc_str((const uint8_t*)GRPC_SLICE_START_PTR(slice),
+                 GRPC_SLICE_LENGTH(slice), &s, &sz, &cap);
+  append_chr(0, &s, &sz, &cap);
+  return s;
+}
+
+static void collect_strs_kvs(grpc_error* err, kv_pairs* kvs) {
+  for (size_t which = 0; which < GRPC_ERROR_STR_MAX; ++which) {
+    uint8_t slot = err->strs[which];
+    if (slot != UINT8_MAX) {
+      append_kv(kvs, key_str(static_cast<grpc_error_strs>(which)),
+                fmt_str(*reinterpret_cast<grpc_slice*>(err->arena + slot)));
+    }
+  }
+}
+
+static char* key_time(grpc_error_times which) {
+  return gpr_strdup(error_time_name(which));
+}
+
+static char* fmt_time(gpr_timespec tm) {
+  char* out;
+  const char* pfx = "!!";
+  switch (tm.clock_type) {
+    case GPR_CLOCK_MONOTONIC:
+      pfx = "@monotonic:";
+      break;
+    case GPR_CLOCK_REALTIME:
+      pfx = "@";
+      break;
+    case GPR_CLOCK_PRECISE:
+      pfx = "@precise:";
+      break;
+    case GPR_TIMESPAN:
+      pfx = "";
+      break;
+  }
+  gpr_asprintf(&out, "\"%s%" PRId64 ".%09d\"", pfx, tm.tv_sec, tm.tv_nsec);
+  return out;
+}
+
+static void collect_times_kvs(grpc_error* err, kv_pairs* kvs) {
+  for (size_t which = 0; which < GRPC_ERROR_TIME_MAX; ++which) {
+    uint8_t slot = err->times[which];
+    if (slot != UINT8_MAX) {
+      append_kv(kvs, key_time(static_cast<grpc_error_times>(which)),
+                fmt_time(*reinterpret_cast<gpr_timespec*>(err->arena + slot)));
+    }
+  }
+}
+
+static void add_errs(grpc_error* err, char** s, size_t* sz, size_t* cap) {
+  uint8_t slot = err->first_err;
+  bool first = true;
+  while (slot != UINT8_MAX) {
+    grpc_linked_error* lerr =
+        reinterpret_cast<grpc_linked_error*>(err->arena + slot);
+    if (!first) append_chr(',', s, sz, cap);
+    first = false;
+    const char* e = grpc_error_string(lerr->err);
+    append_str(e, s, sz, cap);
+    GPR_ASSERT(err->last_err == slot ? lerr->next == UINT8_MAX
+                                     : lerr->next != UINT8_MAX);
+    slot = lerr->next;
+  }
+}
+
+static char* errs_string(grpc_error* err) {
+  char* s = nullptr;
+  size_t sz = 0;
+  size_t cap = 0;
+  append_chr('[', &s, &sz, &cap);
+  add_errs(err, &s, &sz, &cap);
+  append_chr(']', &s, &sz, &cap);
+  append_chr(0, &s, &sz, &cap);
+  return s;
+}
+
+static int cmp_kvs(const void* a, const void* b) {
+  const kv_pair* ka = static_cast<const kv_pair*>(a);
+  const kv_pair* kb = static_cast<const kv_pair*>(b);
+  return strcmp(ka->key, kb->key);
+}
+
+static char* finish_kvs(kv_pairs* kvs) {
+  char* s = nullptr;
+  size_t sz = 0;
+  size_t cap = 0;
+
+  append_chr('{', &s, &sz, &cap);
+  for (size_t i = 0; i < kvs->num_kvs; i++) {
+    if (i != 0) append_chr(',', &s, &sz, &cap);
+    append_esc_str(reinterpret_cast<const uint8_t*>(kvs->kvs[i].key),
+                   strlen(kvs->kvs[i].key), &s, &sz, &cap);
+    gpr_free(kvs->kvs[i].key);
+    append_chr(':', &s, &sz, &cap);
+    append_str(kvs->kvs[i].value, &s, &sz, &cap);
+    gpr_free(kvs->kvs[i].value);
+  }
+  append_chr('}', &s, &sz, &cap);
+  append_chr(0, &s, &sz, &cap);
+
+  gpr_free(kvs->kvs);
+  return s;
+}
+
+const char* grpc_error_string(grpc_error* err) {
+  GPR_TIMER_SCOPE("grpc_error_string", 0);
+  if (err == GRPC_ERROR_NONE) return no_error_string;
+  if (err == GRPC_ERROR_OOM) return oom_error_string;
+  if (err == GRPC_ERROR_CANCELLED) return cancelled_error_string;
+
+  void* p = (void*)gpr_atm_acq_load(&err->atomics.error_string);
+  if (p != nullptr) {
+    return static_cast<const char*>(p);
+  }
+
+  kv_pairs kvs;
+  memset(&kvs, 0, sizeof(kvs));
+
+  collect_ints_kvs(err, &kvs);
+  collect_strs_kvs(err, &kvs);
+  collect_times_kvs(err, &kvs);
+  if (err->first_err != UINT8_MAX) {
+    append_kv(&kvs, gpr_strdup("referenced_errors"), errs_string(err));
+  }
+
+  qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs);
+
+  char* out = finish_kvs(&kvs);
+
+  if (!gpr_atm_rel_cas(&err->atomics.error_string, 0, (gpr_atm)out)) {
+    gpr_free(out);
+    out = (char*)gpr_atm_acq_load(&err->atomics.error_string);
+  }
+
+  return out;
+}
+
+grpc_error* grpc_os_error(const char* file, int line, int err,
+                          const char* call_name) {
+  return grpc_error_set_str(
+      grpc_error_set_str(
+          grpc_error_set_int(
+              grpc_error_create(file, line,
+                                grpc_slice_from_static_string("OS Error"),
+                                nullptr, 0),
+              GRPC_ERROR_INT_ERRNO, err),
+          GRPC_ERROR_STR_OS_ERROR,
+          grpc_slice_from_static_string(strerror(err))),
+      GRPC_ERROR_STR_SYSCALL, grpc_slice_from_copied_string(call_name));
+}
+
+#ifdef GPR_WINDOWS
+grpc_error* grpc_wsa_error(const char* file, int line, int err,
+                           const char* call_name) {
+  char* utf8_message = gpr_format_message(err);
+  grpc_error* error = grpc_error_set_str(
+      grpc_error_set_str(
+          grpc_error_set_int(
+              grpc_error_create(file, line,
+                                grpc_slice_from_static_string("OS Error"), NULL,
+                                0),
+              GRPC_ERROR_INT_WSA_ERROR, err),
+          GRPC_ERROR_STR_OS_ERROR, grpc_slice_from_copied_string(utf8_message)),
+      GRPC_ERROR_STR_SYSCALL, grpc_slice_from_static_string(call_name));
+  gpr_free(utf8_message);
+  return error;
+}
+#endif
+
+bool grpc_log_if_error(const char* what, grpc_error* error, const char* file,
+                       int line) {
+  if (error == GRPC_ERROR_NONE) return true;
+  const char* msg = grpc_error_string(error);
+  gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "%s: %s", what, msg);
+  GRPC_ERROR_UNREF(error);
+  return false;
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/error.h b/third_party/grpc/src/core/lib/iomgr/error.h
new file mode 100644
index 0000000..cb740d5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/error.h
@@ -0,0 +1,245 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_ERROR_H
+#define GRPC_CORE_LIB_IOMGR_ERROR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <grpc/slice.h>
+#include <grpc/status.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/debug/trace.h"
+
+/// Opaque representation of an error.
+/// See https://github.com/grpc/grpc/blob/master/doc/core/grpc-error.md for a
+/// full write up of this object.
+
+typedef struct grpc_error grpc_error;
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount;
+
+typedef enum {
+  /// 'errno' from the operating system
+  GRPC_ERROR_INT_ERRNO,
+  /// __LINE__ from the call site creating the error
+  GRPC_ERROR_INT_FILE_LINE,
+  /// stream identifier: for errors that are associated with an individual
+  /// wire stream
+  GRPC_ERROR_INT_STREAM_ID,
+  /// grpc status code representing this error
+  GRPC_ERROR_INT_GRPC_STATUS,
+  /// offset into some binary blob (usually represented by
+  /// GRPC_ERROR_STR_RAW_BYTES) where the error occurred
+  GRPC_ERROR_INT_OFFSET,
+  /// context sensitive index associated with the error
+  GRPC_ERROR_INT_INDEX,
+  /// context sensitive size associated with the error
+  GRPC_ERROR_INT_SIZE,
+  /// http2 error code associated with the error (see the HTTP2 RFC)
+  GRPC_ERROR_INT_HTTP2_ERROR,
+  /// TSI status code associated with the error
+  GRPC_ERROR_INT_TSI_CODE,
+  /// grpc_security_status associated with the error
+  GRPC_ERROR_INT_SECURITY_STATUS,
+  /// WSAGetLastError() reported when this error occurred
+  GRPC_ERROR_INT_WSA_ERROR,
+  /// File descriptor associated with this error
+  GRPC_ERROR_INT_FD,
+  /// HTTP status (i.e. 404)
+  GRPC_ERROR_INT_HTTP_STATUS,
+  /// context sensitive limit associated with the error
+  GRPC_ERROR_INT_LIMIT,
+  /// chttp2: did the error occur while a write was in progress
+  GRPC_ERROR_INT_OCCURRED_DURING_WRITE,
+
+  /// Must always be last
+  GRPC_ERROR_INT_MAX,
+} grpc_error_ints;
+
+typedef enum {
+  /// top-level textual description of this error
+  GRPC_ERROR_STR_DESCRIPTION,
+  /// source file in which this error occurred
+  GRPC_ERROR_STR_FILE,
+  /// operating system description of this error
+  GRPC_ERROR_STR_OS_ERROR,
+  /// syscall that generated this error
+  GRPC_ERROR_STR_SYSCALL,
+  /// peer that we were trying to communicate when this error occurred
+  GRPC_ERROR_STR_TARGET_ADDRESS,
+  /// grpc status message associated with this error
+  GRPC_ERROR_STR_GRPC_MESSAGE,
+  /// hex dump (or similar) with the data that generated this error
+  GRPC_ERROR_STR_RAW_BYTES,
+  /// tsi error string associated with this error
+  GRPC_ERROR_STR_TSI_ERROR,
+  /// filename that we were trying to read/write when this error occurred
+  GRPC_ERROR_STR_FILENAME,
+  /// which data was queued for writing when the error occurred
+  GRPC_ERROR_STR_QUEUED_BUFFERS,
+  /// key associated with the error
+  GRPC_ERROR_STR_KEY,
+  /// value associated with the error
+  GRPC_ERROR_STR_VALUE,
+
+  /// Must always be last
+  GRPC_ERROR_STR_MAX,
+} grpc_error_strs;
+
+typedef enum {
+  /// timestamp of error creation
+  GRPC_ERROR_TIME_CREATED,
+
+  /// Must always be last
+  GRPC_ERROR_TIME_MAX,
+} grpc_error_times;
+
+/// The following "special" errors can be propagated without allocating memory.
+/// They are always even so that other code (particularly combiner locks,
+/// polling engines) can safely use the lower bit for themselves.
+
+#define GRPC_ERROR_NONE ((grpc_error*)NULL)
+#define GRPC_ERROR_RESERVED_1 ((grpc_error*)1)
+#define GRPC_ERROR_OOM ((grpc_error*)2)
+#define GRPC_ERROR_RESERVED_2 ((grpc_error*)3)
+#define GRPC_ERROR_CANCELLED ((grpc_error*)4)
+#define GRPC_ERROR_SPECIAL_MAX GRPC_ERROR_CANCELLED
+
+inline bool grpc_error_is_special(struct grpc_error* err) {
+  return err <= GRPC_ERROR_SPECIAL_MAX;
+}
+
+// debug only toggles that allow for a sanity to check that ensures we will
+// never create any errors in the per-RPC hotpath.
+void grpc_disable_error_creation();
+void grpc_enable_error_creation();
+
+const char* grpc_error_string(grpc_error* error);
+
+/// Create an error - but use GRPC_ERROR_CREATE instead
+grpc_error* grpc_error_create(const char* file, int line, grpc_slice desc,
+                              grpc_error** referencing, size_t num_referencing);
+/// Create an error (this is the preferred way of generating an error that is
+///   not due to a system call - for system calls, use GRPC_OS_ERROR or
+///   GRPC_WSA_ERROR as appropriate)
+/// \a referencing is an array of num_referencing elements indicating one or
+/// more errors that are believed to have contributed to this one
+/// err = grpc_error_create(x, y, z, r, nr) is equivalent to:
+///   err = grpc_error_create(x, y, z, NULL, 0);
+///   for (i=0; i<nr; i++) err = grpc_error_add_child(err, r[i]);
+#define GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc)                           \
+  grpc_error_create(__FILE__, __LINE__, grpc_slice_from_static_string(desc), \
+                    NULL, 0)
+#define GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc)                           \
+  grpc_error_create(__FILE__, __LINE__, grpc_slice_from_copied_string(desc), \
+                    NULL, 0)
+
+// Create an error that references some other errors. This function adds a
+// reference to each error in errs - it does not consume an existing reference
+#define GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(desc, errs, count)  \
+  grpc_error_create(__FILE__, __LINE__, grpc_slice_from_static_string(desc), \
+                    errs, count)
+#define GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(desc, errs, count)  \
+  grpc_error_create(__FILE__, __LINE__, grpc_slice_from_copied_string(desc), \
+                    errs, count)
+
+#ifndef NDEBUG
+grpc_error* grpc_error_do_ref(grpc_error* err, const char* file, int line);
+void grpc_error_do_unref(grpc_error* err, const char* file, int line);
+inline grpc_error* grpc_error_ref(grpc_error* err, const char* file, int line) {
+  if (grpc_error_is_special(err)) return err;
+  return grpc_error_do_ref(err, file, line);
+}
+inline void grpc_error_unref(grpc_error* err, const char* file, int line) {
+  if (grpc_error_is_special(err)) return;
+  grpc_error_do_unref(err, file, line);
+}
+#define GRPC_ERROR_REF(err) grpc_error_ref(err, __FILE__, __LINE__)
+#define GRPC_ERROR_UNREF(err) grpc_error_unref(err, __FILE__, __LINE__)
+#else
+grpc_error* grpc_error_do_ref(grpc_error* err);
+void grpc_error_do_unref(grpc_error* err);
+inline grpc_error* grpc_error_ref(grpc_error* err) {
+  if (grpc_error_is_special(err)) return err;
+  return grpc_error_do_ref(err);
+}
+inline void grpc_error_unref(grpc_error* err) {
+  if (grpc_error_is_special(err)) return;
+  grpc_error_do_unref(err);
+}
+#define GRPC_ERROR_REF(err) grpc_error_ref(err)
+#define GRPC_ERROR_UNREF(err) grpc_error_unref(err)
+#endif
+
+grpc_error* grpc_error_set_int(grpc_error* src, grpc_error_ints which,
+                               intptr_t value) GRPC_MUST_USE_RESULT;
+/// It is an error to pass nullptr as `p`. Caller should allocate a dummy
+/// intptr_t for `p`, even if the value of `p` is not used.
+bool grpc_error_get_int(grpc_error* error, grpc_error_ints which, intptr_t* p);
+/// This call takes ownership of the slice; the error is responsible for
+/// eventually unref-ing it.
+grpc_error* grpc_error_set_str(grpc_error* src, grpc_error_strs which,
+                               grpc_slice str) GRPC_MUST_USE_RESULT;
+/// Returns false if the specified string is not set.
+/// Caller does NOT own the slice.
+bool grpc_error_get_str(grpc_error* error, grpc_error_strs which,
+                        grpc_slice* s);
+
+/// 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. The src error takes ownership of the
+/// child error.
+///
+/// Edge Conditions -
+/// 1) If either of \a src or \a child is GRPC_ERROR_NONE, returns a reference
+/// to the other argument. 2) If both \a src and \a child are GRPC_ERROR_NONE,
+/// returns GRPC_ERROR_NONE. 3) If \a src and \a child point to the same error,
+/// returns a single reference. (Note that, 2 references should have been
+/// received to the error in this case.)
+grpc_error* grpc_error_add_child(grpc_error* src,
+                                 grpc_error* child) GRPC_MUST_USE_RESULT;
+
+grpc_error* grpc_os_error(const char* file, int line, int err,
+                          const char* call_name) GRPC_MUST_USE_RESULT;
+
+inline grpc_error* grpc_assert_never_ok(grpc_error* error) {
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  return error;
+}
+
+/// create an error associated with errno!=0 (an 'operating system' error)
+#define GRPC_OS_ERROR(err, call_name) \
+  grpc_assert_never_ok(grpc_os_error(__FILE__, __LINE__, err, call_name))
+grpc_error* grpc_wsa_error(const char* file, int line, int err,
+                           const char* call_name) GRPC_MUST_USE_RESULT;
+/// windows only: create an error associated with WSAGetLastError()!=0
+#define GRPC_WSA_ERROR(err, call_name) \
+  grpc_wsa_error(__FILE__, __LINE__, err, call_name)
+
+bool grpc_log_if_error(const char* what, grpc_error* error, const char* file,
+                       int line);
+#define GRPC_LOG_IF_ERROR(what, error) \
+  grpc_log_if_error((what), (error), __FILE__, __LINE__)
+
+#endif /* GRPC_CORE_LIB_IOMGR_ERROR_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/error_cfstream.cc b/third_party/grpc/src/core/lib/iomgr/error_cfstream.cc
new file mode 100644
index 0000000..d7af8c3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/error_cfstream.cc
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_CFSTREAM
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/iomgr/error.h"
+
+#define MAX_ERROR_DESCRIPTION 256
+
+grpc_error* grpc_error_create_from_cferror(const char* file, int line,
+                                           void* arg, const char* custom_desc) {
+  CFErrorRef error = static_cast<CFErrorRef>(arg);
+  char buf_domain[MAX_ERROR_DESCRIPTION];
+  char buf_desc[MAX_ERROR_DESCRIPTION];
+  char* error_msg;
+  CFErrorDomain domain = CFErrorGetDomain((error));
+  CFIndex code = CFErrorGetCode((error));
+  CFStringRef desc = CFErrorCopyDescription((error));
+  CFStringGetCString(domain, buf_domain, MAX_ERROR_DESCRIPTION,
+                     kCFStringEncodingUTF8);
+  CFStringGetCString(desc, buf_desc, MAX_ERROR_DESCRIPTION,
+                     kCFStringEncodingUTF8);
+  gpr_asprintf(&error_msg, "%s (error domain:%s, code:%ld, description:%s)",
+               custom_desc, buf_domain, code, buf_desc);
+  CFRelease(desc);
+  grpc_error* return_error = grpc_error_create(
+      file, line, grpc_slice_from_copied_string(error_msg), NULL, 0);
+  gpr_free(error_msg);
+  return return_error;
+}
+#endif /* GRPC_CFSTREAM */
diff --git a/third_party/grpc/src/core/lib/iomgr/error_cfstream.h b/third_party/grpc/src/core/lib/iomgr/error_cfstream.h
new file mode 100644
index 0000000..06ab751
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/error_cfstream.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_ERROR_CFSTREAM_H
+#define GRPC_CORE_LIB_IOMGR_ERROR_CFSTREAM_H
+
+#ifdef GRPC_CFSTREAM
+// Create an error from Apple Core Foundation CFError object
+#define GRPC_ERROR_CREATE_FROM_CFERROR(error, desc)  \
+  grpc_error_create_from_cferror(__FILE__, __LINE__, \
+                                 static_cast<void*>((error)), (desc))
+grpc_error* grpc_error_create_from_cferror(const char* file, int line,
+                                           void* arg, const char* desc);
+#endif /* GRPC_CFSTREAM */
+
+#endif /* GRPC_CORE_LIB_IOMGR_ERROR_CFSTREAM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/error_internal.h b/third_party/grpc/src/core/lib/iomgr/error_internal.h
new file mode 100644
index 0000000..8027396
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/error_internal.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H
+#define GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <inttypes.h>
+#include <stdbool.h>  // TODO, do we need this?
+
+#include <grpc/support/sync.h>
+#include "src/core/lib/iomgr/error.h"
+
+typedef struct grpc_linked_error grpc_linked_error;
+
+struct grpc_linked_error {
+  grpc_error* err;
+  uint8_t next;
+};
+
+// c core representation of an error. See error.h for high level description of
+// this object.
+struct grpc_error {
+  // All atomics in grpc_error must be stored in this nested struct. The rest of
+  // the object is memcpy-ed in bulk in copy_and_unref.
+  struct atomics {
+    gpr_refcount refs;
+    gpr_atm error_string;
+  } atomics;
+  // These arrays index into dynamic arena at the bottom of the struct.
+  // UINT8_MAX is used as a sentinel value.
+  uint8_t ints[GRPC_ERROR_INT_MAX];
+  uint8_t strs[GRPC_ERROR_STR_MAX];
+  uint8_t times[GRPC_ERROR_TIME_MAX];
+  // The child errors are stored in the arena, but are effectively a linked list
+  // structure, since they are contained withing grpc_linked_error objects.
+  uint8_t first_err;
+  uint8_t last_err;
+  // The arena is dynamically reallocated with a grow factor of 1.5.
+  uint8_t arena_size;
+  uint8_t arena_capacity;
+  intptr_t arena[0];
+};
+
+#endif /* GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.cc b/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.cc
new file mode 100644
index 0000000..4b8c891
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.cc
@@ -0,0 +1,1347 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/log.h>
+
+/* This polling engine is only relevant on linux kernels supporting epoll
+   epoll_create() or epoll_create1() */
+#ifdef GRPC_LINUX_EPOLL
+#include "src/core/lib/iomgr/ev_epoll1_linux.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/tls.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/iomgr/block_annotate.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/lockfree_event.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/profiling/timers.h"
+
+static grpc_wakeup_fd global_wakeup_fd;
+
+/*******************************************************************************
+ * Singleton epoll set related fields
+ */
+
+#define MAX_EPOLL_EVENTS 100
+#define MAX_EPOLL_EVENTS_HANDLED_PER_ITERATION 1
+
+/* NOTE ON SYNCHRONIZATION:
+ * - Fields in this struct are only modified by the designated poller. Hence
+ *   there is no need for any locks to protect the struct.
+ * - num_events and cursor fields have to be of atomic type to provide memory
+ *   visibility guarantees only. i.e In case of multiple pollers, the designated
+ *   polling thread keeps changing; the thread that wrote these values may be
+ *   different from the thread reading the values
+ */
+typedef struct epoll_set {
+  int epfd;
+
+  /* The epoll_events after the last call to epoll_wait() */
+  struct epoll_event events[MAX_EPOLL_EVENTS];
+
+  /* The number of epoll_events after the last call to epoll_wait() */
+  gpr_atm num_events;
+
+  /* Index of the first event in epoll_events that has to be processed. This
+   * field is only valid if num_events > 0 */
+  gpr_atm cursor;
+} epoll_set;
+
+/* The global singleton epoll set */
+static epoll_set g_epoll_set;
+
+static int epoll_create_and_cloexec() {
+#ifdef GRPC_LINUX_EPOLL_CREATE1
+  int fd = epoll_create1(EPOLL_CLOEXEC);
+  if (fd < 0) {
+    gpr_log(GPR_ERROR, "epoll_create1 unavailable");
+  }
+#else
+  int fd = epoll_create(MAX_EPOLL_EVENTS);
+  if (fd < 0) {
+    gpr_log(GPR_ERROR, "epoll_create unavailable");
+  } else if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+    gpr_log(GPR_ERROR, "fcntl following epoll_create failed");
+    return -1;
+  }
+#endif
+  return fd;
+}
+
+/* Must be called *only* once */
+static bool epoll_set_init() {
+  g_epoll_set.epfd = epoll_create_and_cloexec();
+  if (g_epoll_set.epfd < 0) {
+    return false;
+  }
+
+  gpr_log(GPR_INFO, "grpc epoll fd: %d", g_epoll_set.epfd);
+  gpr_atm_no_barrier_store(&g_epoll_set.num_events, 0);
+  gpr_atm_no_barrier_store(&g_epoll_set.cursor, 0);
+  return true;
+}
+
+/* epoll_set_init() MUST be called before calling this. */
+static void epoll_set_shutdown() {
+  if (g_epoll_set.epfd >= 0) {
+    close(g_epoll_set.epfd);
+    g_epoll_set.epfd = -1;
+  }
+}
+
+/*******************************************************************************
+ * Fd Declarations
+ */
+
+/* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */
+struct grpc_fork_fd_list {
+  grpc_fd* fd;
+  grpc_fd* next;
+  grpc_fd* prev;
+};
+
+struct grpc_fd {
+  int fd;
+
+  grpc_core::ManualConstructor<grpc_core::LockfreeEvent> read_closure;
+  grpc_core::ManualConstructor<grpc_core::LockfreeEvent> write_closure;
+  grpc_core::ManualConstructor<grpc_core::LockfreeEvent> error_closure;
+
+  struct grpc_fd* freelist_next;
+
+  grpc_iomgr_object iomgr_object;
+
+  /* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */
+  grpc_fork_fd_list* fork_fd_list;
+};
+
+static void fd_global_init(void);
+static void fd_global_shutdown(void);
+
+/*******************************************************************************
+ * Pollset Declarations
+ */
+
+typedef enum { UNKICKED, KICKED, DESIGNATED_POLLER } kick_state;
+
+static const char* kick_state_string(kick_state st) {
+  switch (st) {
+    case UNKICKED:
+      return "UNKICKED";
+    case KICKED:
+      return "KICKED";
+    case DESIGNATED_POLLER:
+      return "DESIGNATED_POLLER";
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
+
+struct grpc_pollset_worker {
+  kick_state state;
+  int kick_state_mutator;  // which line of code last changed kick state
+  bool initialized_cv;
+  grpc_pollset_worker* next;
+  grpc_pollset_worker* prev;
+  gpr_cv cv;
+  grpc_closure_list schedule_on_end_work;
+};
+
+#define SET_KICK_STATE(worker, kick_state)   \
+  do {                                       \
+    (worker)->state = (kick_state);          \
+    (worker)->kick_state_mutator = __LINE__; \
+  } while (false)
+
+#define MAX_NEIGHBORHOODS 1024
+
+typedef struct pollset_neighborhood {
+  union {
+    char pad[GPR_CACHELINE_SIZE];
+    struct {
+      gpr_mu mu;
+      grpc_pollset* active_root;
+    };
+  };
+} pollset_neighborhood;
+
+struct grpc_pollset {
+  gpr_mu mu;
+  pollset_neighborhood* neighborhood;
+  bool reassigning_neighborhood;
+  grpc_pollset_worker* root_worker;
+  bool kicked_without_poller;
+
+  /* Set to true if the pollset is observed to have no workers available to
+     poll */
+  bool seen_inactive;
+  bool shutting_down;             /* Is the pollset shutting down ? */
+  grpc_closure* shutdown_closure; /* Called after after shutdown is complete */
+
+  /* Number of workers who are *about-to* attach themselves to the pollset
+   * worker list */
+  int begin_refs;
+
+  grpc_pollset* next;
+  grpc_pollset* prev;
+};
+
+/*******************************************************************************
+ * Pollset-set Declarations
+ */
+
+struct grpc_pollset_set {
+  char unused;
+};
+
+/*******************************************************************************
+ * Common helpers
+ */
+
+static bool append_error(grpc_error** composite, grpc_error* error,
+                         const char* desc) {
+  if (error == GRPC_ERROR_NONE) return true;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc);
+  }
+  *composite = grpc_error_add_child(*composite, error);
+  return false;
+}
+
+/*******************************************************************************
+ * Fd Definitions
+ */
+
+/* We need to keep a freelist not because of any concerns of malloc performance
+ * but instead so that implementations with multiple threads in (for example)
+ * epoll_wait deal with the race between pollset removal and incoming poll
+ * notifications.
+ *
+ * The problem is that the poller ultimately holds a reference to this
+ * object, so it is very difficult to know when is safe to free it, at least
+ * without some expensive synchronization.
+ *
+ * If we keep the object freelisted, in the worst case losing this race just
+ * becomes a spurious read notification on a reused fd.
+ */
+
+/* The alarm system needs to be able to wakeup 'some poller' sometimes
+ * (specifically when a new alarm needs to be triggered earlier than the next
+ * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a
+ * case occurs. */
+
+static grpc_fd* fd_freelist = nullptr;
+static gpr_mu fd_freelist_mu;
+
+/* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */
+static grpc_fd* fork_fd_list_head = nullptr;
+static gpr_mu fork_fd_list_mu;
+
+static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
+
+static void fd_global_shutdown(void) {
+  // TODO(guantaol): We don't have a reasonable explanation about this
+  // lock()/unlock() pattern. It can be a valid barrier if there is at most one
+  // pending lock() at this point. Otherwise, there is still a possibility of
+  // use-after-free race. Need to reason about the code and/or clean it up.
+  gpr_mu_lock(&fd_freelist_mu);
+  gpr_mu_unlock(&fd_freelist_mu);
+  while (fd_freelist != nullptr) {
+    grpc_fd* fd = fd_freelist;
+    fd_freelist = fd_freelist->freelist_next;
+    gpr_free(fd);
+  }
+  gpr_mu_destroy(&fd_freelist_mu);
+}
+
+static void fork_fd_list_add_grpc_fd(grpc_fd* fd) {
+  if (grpc_core::Fork::Enabled()) {
+    gpr_mu_lock(&fork_fd_list_mu);
+    fd->fork_fd_list =
+        static_cast<grpc_fork_fd_list*>(gpr_malloc(sizeof(grpc_fork_fd_list)));
+    fd->fork_fd_list->next = fork_fd_list_head;
+    fd->fork_fd_list->prev = nullptr;
+    if (fork_fd_list_head != nullptr) {
+      fork_fd_list_head->fork_fd_list->prev = fd;
+    }
+    fork_fd_list_head = fd;
+    gpr_mu_unlock(&fork_fd_list_mu);
+  }
+}
+
+static void fork_fd_list_remove_grpc_fd(grpc_fd* fd) {
+  if (grpc_core::Fork::Enabled()) {
+    gpr_mu_lock(&fork_fd_list_mu);
+    if (fork_fd_list_head == fd) {
+      fork_fd_list_head = fd->fork_fd_list->next;
+    }
+    if (fd->fork_fd_list->prev != nullptr) {
+      fd->fork_fd_list->prev->fork_fd_list->next = fd->fork_fd_list->next;
+    }
+    if (fd->fork_fd_list->next != nullptr) {
+      fd->fork_fd_list->next->fork_fd_list->prev = fd->fork_fd_list->prev;
+    }
+    gpr_free(fd->fork_fd_list);
+    gpr_mu_unlock(&fork_fd_list_mu);
+  }
+}
+
+static grpc_fd* fd_create(int fd, const char* name, bool track_err) {
+  grpc_fd* new_fd = nullptr;
+
+  gpr_mu_lock(&fd_freelist_mu);
+  if (fd_freelist != nullptr) {
+    new_fd = fd_freelist;
+    fd_freelist = fd_freelist->freelist_next;
+  }
+  gpr_mu_unlock(&fd_freelist_mu);
+
+  if (new_fd == nullptr) {
+    new_fd = static_cast<grpc_fd*>(gpr_malloc(sizeof(grpc_fd)));
+    new_fd->read_closure.Init();
+    new_fd->write_closure.Init();
+    new_fd->error_closure.Init();
+  }
+  new_fd->fd = fd;
+  new_fd->read_closure->InitEvent();
+  new_fd->write_closure->InitEvent();
+  new_fd->error_closure->InitEvent();
+
+  new_fd->freelist_next = nullptr;
+
+  char* fd_name;
+  gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
+  grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
+  fork_fd_list_add_grpc_fd(new_fd);
+#ifndef NDEBUG
+  if (grpc_trace_fd_refcount.enabled()) {
+    gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name);
+  }
+#endif
+  gpr_free(fd_name);
+
+  struct epoll_event ev;
+  ev.events = static_cast<uint32_t>(EPOLLIN | EPOLLOUT | EPOLLET);
+  /* Use the least significant bit of ev.data.ptr to store track_err. We expect
+   * the addresses to be word aligned. We need to store track_err to avoid
+   * synchronization issues when accessing it after receiving an event.
+   * Accessing fd would be a data race there because the fd might have been
+   * returned to the free list at that point. */
+  ev.data.ptr = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(new_fd) |
+                                        (track_err ? 1 : 0));
+  if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_ADD, fd, &ev) != 0) {
+    gpr_log(GPR_ERROR, "epoll_ctl failed: %s", strerror(errno));
+  }
+
+  return new_fd;
+}
+
+static int fd_wrapped_fd(grpc_fd* fd) { return fd->fd; }
+
+/* if 'releasing_fd' is true, it means that we are going to detach the internal
+ * fd from grpc_fd structure (i.e which means we should not be calling
+ * shutdown() syscall on that fd) */
+static void fd_shutdown_internal(grpc_fd* fd, grpc_error* why,
+                                 bool releasing_fd) {
+  if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) {
+    if (!releasing_fd) {
+      shutdown(fd->fd, SHUT_RDWR);
+    }
+    fd->write_closure->SetShutdown(GRPC_ERROR_REF(why));
+    fd->error_closure->SetShutdown(GRPC_ERROR_REF(why));
+  }
+  GRPC_ERROR_UNREF(why);
+}
+
+/* Might be called multiple times */
+static void fd_shutdown(grpc_fd* fd, grpc_error* why) {
+  fd_shutdown_internal(fd, why, false);
+}
+
+static void fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd,
+                      const char* reason) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  bool is_release_fd = (release_fd != nullptr);
+
+  if (!fd->read_closure->IsShutdown()) {
+    fd_shutdown_internal(fd, GRPC_ERROR_CREATE_FROM_COPIED_STRING(reason),
+                         is_release_fd);
+  }
+
+  /* If release_fd is not NULL, we should be relinquishing control of the file
+     descriptor fd->fd (but we still own the grpc_fd structure). */
+  if (is_release_fd) {
+    *release_fd = fd->fd;
+  } else {
+    close(fd->fd);
+  }
+
+  GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_REF(error));
+
+  grpc_iomgr_unregister_object(&fd->iomgr_object);
+  fork_fd_list_remove_grpc_fd(fd);
+  fd->read_closure->DestroyEvent();
+  fd->write_closure->DestroyEvent();
+  fd->error_closure->DestroyEvent();
+
+  gpr_mu_lock(&fd_freelist_mu);
+  fd->freelist_next = fd_freelist;
+  fd_freelist = fd;
+  gpr_mu_unlock(&fd_freelist_mu);
+}
+
+static bool fd_is_shutdown(grpc_fd* fd) {
+  return fd->read_closure->IsShutdown();
+}
+
+static void fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) {
+  fd->read_closure->NotifyOn(closure);
+}
+
+static void fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) {
+  fd->write_closure->NotifyOn(closure);
+}
+
+static void fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) {
+  fd->error_closure->NotifyOn(closure);
+}
+
+static void fd_become_readable(grpc_fd* fd) { fd->read_closure->SetReady(); }
+
+static void fd_become_writable(grpc_fd* fd) { fd->write_closure->SetReady(); }
+
+static void fd_has_errors(grpc_fd* fd) { fd->error_closure->SetReady(); }
+
+/*******************************************************************************
+ * Pollset Definitions
+ */
+
+GPR_TLS_DECL(g_current_thread_pollset);
+GPR_TLS_DECL(g_current_thread_worker);
+
+/* The designated poller */
+static gpr_atm g_active_poller;
+
+static pollset_neighborhood* g_neighborhoods;
+static size_t g_num_neighborhoods;
+
+/* Return true if first in list */
+static bool worker_insert(grpc_pollset* pollset, grpc_pollset_worker* worker) {
+  if (pollset->root_worker == nullptr) {
+    pollset->root_worker = worker;
+    worker->next = worker->prev = worker;
+    return true;
+  } else {
+    worker->next = pollset->root_worker;
+    worker->prev = worker->next->prev;
+    worker->next->prev = worker;
+    worker->prev->next = worker;
+    return false;
+  }
+}
+
+/* Return true if last in list */
+typedef enum { EMPTIED, NEW_ROOT, REMOVED } worker_remove_result;
+
+static worker_remove_result worker_remove(grpc_pollset* pollset,
+                                          grpc_pollset_worker* worker) {
+  if (worker == pollset->root_worker) {
+    if (worker == worker->next) {
+      pollset->root_worker = nullptr;
+      return EMPTIED;
+    } else {
+      pollset->root_worker = worker->next;
+      worker->prev->next = worker->next;
+      worker->next->prev = worker->prev;
+      return NEW_ROOT;
+    }
+  } else {
+    worker->prev->next = worker->next;
+    worker->next->prev = worker->prev;
+    return REMOVED;
+  }
+}
+
+static size_t choose_neighborhood(void) {
+  return static_cast<size_t>(gpr_cpu_current_cpu()) % g_num_neighborhoods;
+}
+
+static grpc_error* pollset_global_init(void) {
+  gpr_tls_init(&g_current_thread_pollset);
+  gpr_tls_init(&g_current_thread_worker);
+  gpr_atm_no_barrier_store(&g_active_poller, 0);
+  global_wakeup_fd.read_fd = -1;
+  grpc_error* err = grpc_wakeup_fd_init(&global_wakeup_fd);
+  if (err != GRPC_ERROR_NONE) return err;
+  struct epoll_event ev;
+  ev.events = static_cast<uint32_t>(EPOLLIN | EPOLLET);
+  ev.data.ptr = &global_wakeup_fd;
+  if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_ADD, global_wakeup_fd.read_fd,
+                &ev) != 0) {
+    return GRPC_OS_ERROR(errno, "epoll_ctl");
+  }
+  g_num_neighborhoods = GPR_CLAMP(gpr_cpu_num_cores(), 1, MAX_NEIGHBORHOODS);
+  g_neighborhoods = static_cast<pollset_neighborhood*>(
+      gpr_zalloc(sizeof(*g_neighborhoods) * g_num_neighborhoods));
+  for (size_t i = 0; i < g_num_neighborhoods; i++) {
+    gpr_mu_init(&g_neighborhoods[i].mu);
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static void pollset_global_shutdown(void) {
+  gpr_tls_destroy(&g_current_thread_pollset);
+  gpr_tls_destroy(&g_current_thread_worker);
+  if (global_wakeup_fd.read_fd != -1) grpc_wakeup_fd_destroy(&global_wakeup_fd);
+  for (size_t i = 0; i < g_num_neighborhoods; i++) {
+    gpr_mu_destroy(&g_neighborhoods[i].mu);
+  }
+  gpr_free(g_neighborhoods);
+}
+
+static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
+  gpr_mu_init(&pollset->mu);
+  *mu = &pollset->mu;
+  pollset->neighborhood = &g_neighborhoods[choose_neighborhood()];
+  pollset->reassigning_neighborhood = false;
+  pollset->root_worker = nullptr;
+  pollset->kicked_without_poller = false;
+  pollset->seen_inactive = true;
+  pollset->shutting_down = false;
+  pollset->shutdown_closure = nullptr;
+  pollset->begin_refs = 0;
+  pollset->next = pollset->prev = nullptr;
+}
+
+static void pollset_destroy(grpc_pollset* pollset) {
+  gpr_mu_lock(&pollset->mu);
+  if (!pollset->seen_inactive) {
+    pollset_neighborhood* neighborhood = pollset->neighborhood;
+    gpr_mu_unlock(&pollset->mu);
+  retry_lock_neighborhood:
+    gpr_mu_lock(&neighborhood->mu);
+    gpr_mu_lock(&pollset->mu);
+    if (!pollset->seen_inactive) {
+      if (pollset->neighborhood != neighborhood) {
+        gpr_mu_unlock(&neighborhood->mu);
+        neighborhood = pollset->neighborhood;
+        gpr_mu_unlock(&pollset->mu);
+        goto retry_lock_neighborhood;
+      }
+      pollset->prev->next = pollset->next;
+      pollset->next->prev = pollset->prev;
+      if (pollset == pollset->neighborhood->active_root) {
+        pollset->neighborhood->active_root =
+            pollset->next == pollset ? nullptr : pollset->next;
+      }
+    }
+    gpr_mu_unlock(&pollset->neighborhood->mu);
+  }
+  gpr_mu_unlock(&pollset->mu);
+  gpr_mu_destroy(&pollset->mu);
+}
+
+static grpc_error* pollset_kick_all(grpc_pollset* pollset) {
+  GPR_TIMER_SCOPE("pollset_kick_all", 0);
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (pollset->root_worker != nullptr) {
+    grpc_pollset_worker* worker = pollset->root_worker;
+    do {
+      GRPC_STATS_INC_POLLSET_KICK();
+      switch (worker->state) {
+        case KICKED:
+          GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
+          break;
+        case UNKICKED:
+          SET_KICK_STATE(worker, KICKED);
+          if (worker->initialized_cv) {
+            GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
+            gpr_cv_signal(&worker->cv);
+          }
+          break;
+        case DESIGNATED_POLLER:
+          GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
+          SET_KICK_STATE(worker, KICKED);
+          append_error(&error, grpc_wakeup_fd_wakeup(&global_wakeup_fd),
+                       "pollset_kick_all");
+          break;
+      }
+
+      worker = worker->next;
+    } while (worker != pollset->root_worker);
+  }
+  // TODO: sreek.  Check if we need to set 'kicked_without_poller' to true here
+  // in the else case
+  return error;
+}
+
+static void pollset_maybe_finish_shutdown(grpc_pollset* pollset) {
+  if (pollset->shutdown_closure != nullptr && pollset->root_worker == nullptr &&
+      pollset->begin_refs == 0) {
+    GPR_TIMER_MARK("pollset_finish_shutdown", 0);
+    GRPC_CLOSURE_SCHED(pollset->shutdown_closure, GRPC_ERROR_NONE);
+    pollset->shutdown_closure = nullptr;
+  }
+}
+
+static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
+  GPR_TIMER_SCOPE("pollset_shutdown", 0);
+  GPR_ASSERT(pollset->shutdown_closure == nullptr);
+  GPR_ASSERT(!pollset->shutting_down);
+  pollset->shutdown_closure = closure;
+  pollset->shutting_down = true;
+  GRPC_LOG_IF_ERROR("pollset_shutdown", pollset_kick_all(pollset));
+  pollset_maybe_finish_shutdown(pollset);
+}
+
+static int poll_deadline_to_millis_timeout(grpc_millis millis) {
+  if (millis == GRPC_MILLIS_INF_FUTURE) return -1;
+  grpc_millis delta = millis - grpc_core::ExecCtx::Get()->Now();
+  if (delta > INT_MAX) {
+    return INT_MAX;
+  } else if (delta < 0) {
+    return 0;
+  } else {
+    return static_cast<int>(delta);
+  }
+}
+
+/* Process the epoll events found by do_epoll_wait() function.
+   - g_epoll_set.cursor points to the index of the first event to be processed
+   - This function then processes up-to MAX_EPOLL_EVENTS_PER_ITERATION and
+     updates the g_epoll_set.cursor
+
+   NOTE ON SYNCRHONIZATION: Similar to do_epoll_wait(), this function is only
+   called by g_active_poller thread. So there is no need for synchronization
+   when accessing fields in g_epoll_set */
+static grpc_error* process_epoll_events(grpc_pollset* pollset) {
+  GPR_TIMER_SCOPE("process_epoll_events", 0);
+
+  static const char* err_desc = "process_events";
+  grpc_error* error = GRPC_ERROR_NONE;
+  long num_events = gpr_atm_acq_load(&g_epoll_set.num_events);
+  long cursor = gpr_atm_acq_load(&g_epoll_set.cursor);
+  for (int idx = 0;
+       (idx < MAX_EPOLL_EVENTS_HANDLED_PER_ITERATION) && cursor != num_events;
+       idx++) {
+    long c = cursor++;
+    struct epoll_event* ev = &g_epoll_set.events[c];
+    void* data_ptr = ev->data.ptr;
+
+    if (data_ptr == &global_wakeup_fd) {
+      append_error(&error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd),
+                   err_desc);
+    } else {
+      grpc_fd* fd = reinterpret_cast<grpc_fd*>(
+          reinterpret_cast<intptr_t>(data_ptr) & ~static_cast<intptr_t>(1));
+      bool track_err =
+          reinterpret_cast<intptr_t>(data_ptr) & static_cast<intptr_t>(1);
+      bool cancel = (ev->events & EPOLLHUP) != 0;
+      bool error = (ev->events & EPOLLERR) != 0;
+      bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0;
+      bool write_ev = (ev->events & EPOLLOUT) != 0;
+      bool err_fallback = error && !track_err;
+
+      if (error && !err_fallback) {
+        fd_has_errors(fd);
+      }
+
+      if (read_ev || cancel || err_fallback) {
+        fd_become_readable(fd);
+      }
+
+      if (write_ev || cancel || err_fallback) {
+        fd_become_writable(fd);
+      }
+    }
+  }
+  gpr_atm_rel_store(&g_epoll_set.cursor, cursor);
+  return error;
+}
+
+/* Do epoll_wait and store the events in g_epoll_set.events field. This does not
+   "process" any of the events yet; that is done in process_epoll_events().
+   *See process_epoll_events() function for more details.
+
+   NOTE ON SYNCHRONIZATION: At any point of time, only the g_active_poller
+   (i.e the designated poller thread) will be calling this function. So there is
+   no need for any synchronization when accesing fields in g_epoll_set */
+static grpc_error* do_epoll_wait(grpc_pollset* ps, grpc_millis deadline) {
+  GPR_TIMER_SCOPE("do_epoll_wait", 0);
+
+  int r;
+  int timeout = poll_deadline_to_millis_timeout(deadline);
+  if (timeout != 0) {
+    GRPC_SCHEDULING_START_BLOCKING_REGION;
+  }
+  do {
+    GRPC_STATS_INC_SYSCALL_POLL();
+    r = epoll_wait(g_epoll_set.epfd, g_epoll_set.events, MAX_EPOLL_EVENTS,
+                   timeout);
+  } while (r < 0 && errno == EINTR);
+  if (timeout != 0) {
+    GRPC_SCHEDULING_END_BLOCKING_REGION;
+  }
+
+  if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait");
+
+  GRPC_STATS_INC_POLL_EVENTS_RETURNED(r);
+
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "ps: %p poll got %d events", ps, r);
+  }
+
+  gpr_atm_rel_store(&g_epoll_set.num_events, r);
+  gpr_atm_rel_store(&g_epoll_set.cursor, 0);
+
+  return GRPC_ERROR_NONE;
+}
+
+static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
+                         grpc_pollset_worker** worker_hdl,
+                         grpc_millis deadline) {
+  GPR_TIMER_SCOPE("begin_worker", 0);
+  if (worker_hdl != nullptr) *worker_hdl = worker;
+  worker->initialized_cv = false;
+  SET_KICK_STATE(worker, UNKICKED);
+  worker->schedule_on_end_work = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT;
+  pollset->begin_refs++;
+
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "PS:%p BEGIN_STARTS:%p", pollset, worker);
+  }
+
+  if (pollset->seen_inactive) {
+    // pollset has been observed to be inactive, we need to move back to the
+    // active list
+    bool is_reassigning = false;
+    if (!pollset->reassigning_neighborhood) {
+      is_reassigning = true;
+      pollset->reassigning_neighborhood = true;
+      pollset->neighborhood = &g_neighborhoods[choose_neighborhood()];
+    }
+    pollset_neighborhood* neighborhood = pollset->neighborhood;
+    gpr_mu_unlock(&pollset->mu);
+  // pollset unlocked: state may change (even worker->kick_state)
+  retry_lock_neighborhood:
+    gpr_mu_lock(&neighborhood->mu);
+    gpr_mu_lock(&pollset->mu);
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, "PS:%p BEGIN_REORG:%p kick_state=%s is_reassigning=%d",
+              pollset, worker, kick_state_string(worker->state),
+              is_reassigning);
+    }
+    if (pollset->seen_inactive) {
+      if (neighborhood != pollset->neighborhood) {
+        gpr_mu_unlock(&neighborhood->mu);
+        neighborhood = pollset->neighborhood;
+        gpr_mu_unlock(&pollset->mu);
+        goto retry_lock_neighborhood;
+      }
+
+      /* In the brief time we released the pollset locks above, the worker MAY
+         have been kicked. In this case, the worker should get out of this
+         pollset ASAP and hence this should neither add the pollset to
+         neighborhood nor mark the pollset as active.
+
+         On a side note, the only way a worker's kick state could have changed
+         at this point is if it were "kicked specifically". Since the worker has
+         not added itself to the pollset yet (by calling worker_insert()), it is
+         not visible in the "kick any" path yet */
+      if (worker->state == UNKICKED) {
+        pollset->seen_inactive = false;
+        if (neighborhood->active_root == nullptr) {
+          neighborhood->active_root = pollset->next = pollset->prev = pollset;
+          /* Make this the designated poller if there isn't one already */
+          if (worker->state == UNKICKED &&
+              gpr_atm_no_barrier_cas(&g_active_poller, 0, (gpr_atm)worker)) {
+            SET_KICK_STATE(worker, DESIGNATED_POLLER);
+          }
+        } else {
+          pollset->next = neighborhood->active_root;
+          pollset->prev = pollset->next->prev;
+          pollset->next->prev = pollset->prev->next = pollset;
+        }
+      }
+    }
+    if (is_reassigning) {
+      GPR_ASSERT(pollset->reassigning_neighborhood);
+      pollset->reassigning_neighborhood = false;
+    }
+    gpr_mu_unlock(&neighborhood->mu);
+  }
+
+  worker_insert(pollset, worker);
+  pollset->begin_refs--;
+  if (worker->state == UNKICKED && !pollset->kicked_without_poller) {
+    GPR_ASSERT(gpr_atm_no_barrier_load(&g_active_poller) != (gpr_atm)worker);
+    worker->initialized_cv = true;
+    gpr_cv_init(&worker->cv);
+    while (worker->state == UNKICKED && !pollset->shutting_down) {
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO, "PS:%p BEGIN_WAIT:%p kick_state=%s shutdown=%d",
+                pollset, worker, kick_state_string(worker->state),
+                pollset->shutting_down);
+      }
+
+      if (gpr_cv_wait(&worker->cv, &pollset->mu,
+                      grpc_millis_to_timespec(deadline, GPR_CLOCK_MONOTONIC)) &&
+          worker->state == UNKICKED) {
+        /* If gpr_cv_wait returns true (i.e a timeout), pretend that the worker
+           received a kick */
+        SET_KICK_STATE(worker, KICKED);
+      }
+    }
+    grpc_core::ExecCtx::Get()->InvalidateNow();
+  }
+
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "PS:%p BEGIN_DONE:%p kick_state=%s shutdown=%d "
+            "kicked_without_poller: %d",
+            pollset, worker, kick_state_string(worker->state),
+            pollset->shutting_down, pollset->kicked_without_poller);
+  }
+
+  /* We release pollset lock in this function at a couple of places:
+   *   1. Briefly when assigning pollset to a neighborhood
+   *   2. When doing gpr_cv_wait()
+   * It is possible that 'kicked_without_poller' was set to true during (1) and
+   * 'shutting_down' is set to true during (1) or (2). If either of them is
+   * true, this worker cannot do polling */
+  /* TODO(sreek): Perhaps there is a better way to handle kicked_without_poller
+   * case; especially when the worker is the DESIGNATED_POLLER */
+
+  if (pollset->kicked_without_poller) {
+    pollset->kicked_without_poller = false;
+    return false;
+  }
+
+  return worker->state == DESIGNATED_POLLER && !pollset->shutting_down;
+}
+
+static bool check_neighborhood_for_available_poller(
+    pollset_neighborhood* neighborhood) {
+  GPR_TIMER_SCOPE("check_neighborhood_for_available_poller", 0);
+  bool found_worker = false;
+  do {
+    grpc_pollset* inspect = neighborhood->active_root;
+    if (inspect == nullptr) {
+      break;
+    }
+    gpr_mu_lock(&inspect->mu);
+    GPR_ASSERT(!inspect->seen_inactive);
+    grpc_pollset_worker* inspect_worker = inspect->root_worker;
+    if (inspect_worker != nullptr) {
+      do {
+        switch (inspect_worker->state) {
+          case UNKICKED:
+            if (gpr_atm_no_barrier_cas(&g_active_poller, 0,
+                                       (gpr_atm)inspect_worker)) {
+              if (grpc_polling_trace.enabled()) {
+                gpr_log(GPR_INFO, " .. choose next poller to be %p",
+                        inspect_worker);
+              }
+              SET_KICK_STATE(inspect_worker, DESIGNATED_POLLER);
+              if (inspect_worker->initialized_cv) {
+                GPR_TIMER_MARK("signal worker", 0);
+                GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
+                gpr_cv_signal(&inspect_worker->cv);
+              }
+            } else {
+              if (grpc_polling_trace.enabled()) {
+                gpr_log(GPR_INFO, " .. beaten to choose next poller");
+              }
+            }
+            // even if we didn't win the cas, there's a worker, we can stop
+            found_worker = true;
+            break;
+          case KICKED:
+            break;
+          case DESIGNATED_POLLER:
+            found_worker = true;  // ok, so someone else found the worker, but
+                                  // we'll accept that
+            break;
+        }
+        inspect_worker = inspect_worker->next;
+      } while (!found_worker && inspect_worker != inspect->root_worker);
+    }
+    if (!found_worker) {
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO, " .. mark pollset %p inactive", inspect);
+      }
+      inspect->seen_inactive = true;
+      if (inspect == neighborhood->active_root) {
+        neighborhood->active_root =
+            inspect->next == inspect ? nullptr : inspect->next;
+      }
+      inspect->next->prev = inspect->prev;
+      inspect->prev->next = inspect->next;
+      inspect->next = inspect->prev = nullptr;
+    }
+    gpr_mu_unlock(&inspect->mu);
+  } while (!found_worker);
+  return found_worker;
+}
+
+static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
+                       grpc_pollset_worker** worker_hdl) {
+  GPR_TIMER_SCOPE("end_worker", 0);
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "PS:%p END_WORKER:%p", pollset, worker);
+  }
+  if (worker_hdl != nullptr) *worker_hdl = nullptr;
+  /* Make sure we appear kicked */
+  SET_KICK_STATE(worker, KICKED);
+  grpc_closure_list_move(&worker->schedule_on_end_work,
+                         grpc_core::ExecCtx::Get()->closure_list());
+  if (gpr_atm_no_barrier_load(&g_active_poller) == (gpr_atm)worker) {
+    if (worker->next != worker && worker->next->state == UNKICKED) {
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO, " .. choose next poller to be peer %p", worker);
+      }
+      GPR_ASSERT(worker->next->initialized_cv);
+      gpr_atm_no_barrier_store(&g_active_poller, (gpr_atm)worker->next);
+      SET_KICK_STATE(worker->next, DESIGNATED_POLLER);
+      GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
+      gpr_cv_signal(&worker->next->cv);
+      if (grpc_core::ExecCtx::Get()->HasWork()) {
+        gpr_mu_unlock(&pollset->mu);
+        grpc_core::ExecCtx::Get()->Flush();
+        gpr_mu_lock(&pollset->mu);
+      }
+    } else {
+      gpr_atm_no_barrier_store(&g_active_poller, 0);
+      size_t poller_neighborhood_idx =
+          static_cast<size_t>(pollset->neighborhood - g_neighborhoods);
+      gpr_mu_unlock(&pollset->mu);
+      bool found_worker = false;
+      bool scan_state[MAX_NEIGHBORHOODS];
+      for (size_t i = 0; !found_worker && i < g_num_neighborhoods; i++) {
+        pollset_neighborhood* neighborhood =
+            &g_neighborhoods[(poller_neighborhood_idx + i) %
+                             g_num_neighborhoods];
+        if (gpr_mu_trylock(&neighborhood->mu)) {
+          found_worker = check_neighborhood_for_available_poller(neighborhood);
+          gpr_mu_unlock(&neighborhood->mu);
+          scan_state[i] = true;
+        } else {
+          scan_state[i] = false;
+        }
+      }
+      for (size_t i = 0; !found_worker && i < g_num_neighborhoods; i++) {
+        if (scan_state[i]) continue;
+        pollset_neighborhood* neighborhood =
+            &g_neighborhoods[(poller_neighborhood_idx + i) %
+                             g_num_neighborhoods];
+        gpr_mu_lock(&neighborhood->mu);
+        found_worker = check_neighborhood_for_available_poller(neighborhood);
+        gpr_mu_unlock(&neighborhood->mu);
+      }
+      grpc_core::ExecCtx::Get()->Flush();
+      gpr_mu_lock(&pollset->mu);
+    }
+  } else if (grpc_core::ExecCtx::Get()->HasWork()) {
+    gpr_mu_unlock(&pollset->mu);
+    grpc_core::ExecCtx::Get()->Flush();
+    gpr_mu_lock(&pollset->mu);
+  }
+  if (worker->initialized_cv) {
+    gpr_cv_destroy(&worker->cv);
+  }
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, " .. remove worker");
+  }
+  if (EMPTIED == worker_remove(pollset, worker)) {
+    pollset_maybe_finish_shutdown(pollset);
+  }
+  GPR_ASSERT(gpr_atm_no_barrier_load(&g_active_poller) != (gpr_atm)worker);
+}
+
+/* pollset->po.mu lock must be held by the caller before calling this.
+   The function pollset_work() may temporarily release the lock (pollset->po.mu)
+   during the course of its execution but it will always re-acquire the lock and
+   ensure that it is held by the time the function returns */
+static grpc_error* pollset_work(grpc_pollset* ps,
+                                grpc_pollset_worker** worker_hdl,
+                                grpc_millis deadline) {
+  GPR_TIMER_SCOPE("pollset_work", 0);
+  grpc_pollset_worker worker;
+  grpc_error* error = GRPC_ERROR_NONE;
+  static const char* err_desc = "pollset_work";
+  if (ps->kicked_without_poller) {
+    ps->kicked_without_poller = false;
+    return GRPC_ERROR_NONE;
+  }
+
+  if (begin_worker(ps, &worker, worker_hdl, deadline)) {
+    gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps);
+    gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker);
+    GPR_ASSERT(!ps->shutting_down);
+    GPR_ASSERT(!ps->seen_inactive);
+
+    gpr_mu_unlock(&ps->mu); /* unlock */
+    /* This is the designated polling thread at this point and should ideally do
+       polling. However, if there are unprocessed events left from a previous
+       call to do_epoll_wait(), skip calling epoll_wait() in this iteration and
+       process the pending epoll events.
+
+       The reason for decoupling do_epoll_wait and process_epoll_events is to
+       better distrubute the work (i.e handling epoll events) across multiple
+       threads
+
+       process_epoll_events() returns very quickly: It just queues the work on
+       exec_ctx but does not execute it (the actual exectution or more
+       accurately grpc_core::ExecCtx::Get()->Flush() happens in end_worker()
+       AFTER selecting a designated poller). So we are not waiting long periods
+       without a designated poller */
+    if (gpr_atm_acq_load(&g_epoll_set.cursor) ==
+        gpr_atm_acq_load(&g_epoll_set.num_events)) {
+      append_error(&error, do_epoll_wait(ps, deadline), err_desc);
+    }
+    append_error(&error, process_epoll_events(ps), err_desc);
+
+    gpr_mu_lock(&ps->mu); /* lock */
+
+    gpr_tls_set(&g_current_thread_worker, 0);
+  } else {
+    gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps);
+  }
+  end_worker(ps, &worker, worker_hdl);
+
+  gpr_tls_set(&g_current_thread_pollset, 0);
+  return error;
+}
+
+static grpc_error* pollset_kick(grpc_pollset* pollset,
+                                grpc_pollset_worker* specific_worker) {
+  GPR_TIMER_SCOPE("pollset_kick", 0);
+  GRPC_STATS_INC_POLLSET_KICK();
+  grpc_error* ret_err = GRPC_ERROR_NONE;
+  if (grpc_polling_trace.enabled()) {
+    gpr_strvec log;
+    gpr_strvec_init(&log);
+    char* tmp;
+    gpr_asprintf(&tmp, "PS:%p KICK:%p curps=%p curworker=%p root=%p", pollset,
+                 specific_worker, (void*)gpr_tls_get(&g_current_thread_pollset),
+                 (void*)gpr_tls_get(&g_current_thread_worker),
+                 pollset->root_worker);
+    gpr_strvec_add(&log, tmp);
+    if (pollset->root_worker != nullptr) {
+      gpr_asprintf(&tmp, " {kick_state=%s next=%p {kick_state=%s}}",
+                   kick_state_string(pollset->root_worker->state),
+                   pollset->root_worker->next,
+                   kick_state_string(pollset->root_worker->next->state));
+      gpr_strvec_add(&log, tmp);
+    }
+    if (specific_worker != nullptr) {
+      gpr_asprintf(&tmp, " worker_kick_state=%s",
+                   kick_state_string(specific_worker->state));
+      gpr_strvec_add(&log, tmp);
+    }
+    tmp = gpr_strvec_flatten(&log, nullptr);
+    gpr_strvec_destroy(&log);
+    gpr_log(GPR_DEBUG, "%s", tmp);
+    gpr_free(tmp);
+  }
+
+  if (specific_worker == nullptr) {
+    if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) {
+      grpc_pollset_worker* root_worker = pollset->root_worker;
+      if (root_worker == nullptr) {
+        GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER();
+        pollset->kicked_without_poller = true;
+        if (grpc_polling_trace.enabled()) {
+          gpr_log(GPR_INFO, " .. kicked_without_poller");
+        }
+        goto done;
+      }
+      grpc_pollset_worker* next_worker = root_worker->next;
+      if (root_worker->state == KICKED) {
+        GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
+        if (grpc_polling_trace.enabled()) {
+          gpr_log(GPR_INFO, " .. already kicked %p", root_worker);
+        }
+        SET_KICK_STATE(root_worker, KICKED);
+        goto done;
+      } else if (next_worker->state == KICKED) {
+        GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
+        if (grpc_polling_trace.enabled()) {
+          gpr_log(GPR_INFO, " .. already kicked %p", next_worker);
+        }
+        SET_KICK_STATE(next_worker, KICKED);
+        goto done;
+      } else if (root_worker ==
+                     next_worker &&  // only try and wake up a poller if
+                                     // there is no next worker
+                 root_worker == (grpc_pollset_worker*)gpr_atm_no_barrier_load(
+                                    &g_active_poller)) {
+        GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
+        if (grpc_polling_trace.enabled()) {
+          gpr_log(GPR_INFO, " .. kicked %p", root_worker);
+        }
+        SET_KICK_STATE(root_worker, KICKED);
+        ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd);
+        goto done;
+      } else if (next_worker->state == UNKICKED) {
+        GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
+        if (grpc_polling_trace.enabled()) {
+          gpr_log(GPR_INFO, " .. kicked %p", next_worker);
+        }
+        GPR_ASSERT(next_worker->initialized_cv);
+        SET_KICK_STATE(next_worker, KICKED);
+        gpr_cv_signal(&next_worker->cv);
+        goto done;
+      } else if (next_worker->state == DESIGNATED_POLLER) {
+        if (root_worker->state != DESIGNATED_POLLER) {
+          if (grpc_polling_trace.enabled()) {
+            gpr_log(
+                GPR_INFO,
+                " .. kicked root non-poller %p (initialized_cv=%d) (poller=%p)",
+                root_worker, root_worker->initialized_cv, next_worker);
+          }
+          SET_KICK_STATE(root_worker, KICKED);
+          if (root_worker->initialized_cv) {
+            GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
+            gpr_cv_signal(&root_worker->cv);
+          }
+          goto done;
+        } else {
+          GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
+          if (grpc_polling_trace.enabled()) {
+            gpr_log(GPR_INFO, " .. non-root poller %p (root=%p)", next_worker,
+                    root_worker);
+          }
+          SET_KICK_STATE(next_worker, KICKED);
+          ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd);
+          goto done;
+        }
+      } else {
+        GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
+        GPR_ASSERT(next_worker->state == KICKED);
+        SET_KICK_STATE(next_worker, KICKED);
+        goto done;
+      }
+    } else {
+      GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD();
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO, " .. kicked while waking up");
+      }
+      goto done;
+    }
+
+    GPR_UNREACHABLE_CODE(goto done);
+  }
+
+  if (specific_worker->state == KICKED) {
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, " .. specific worker already kicked");
+    }
+    goto done;
+  } else if (gpr_tls_get(&g_current_thread_worker) ==
+             (intptr_t)specific_worker) {
+    GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD();
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, " .. mark %p kicked", specific_worker);
+    }
+    SET_KICK_STATE(specific_worker, KICKED);
+    goto done;
+  } else if (specific_worker ==
+             (grpc_pollset_worker*)gpr_atm_no_barrier_load(&g_active_poller)) {
+    GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, " .. kick active poller");
+    }
+    SET_KICK_STATE(specific_worker, KICKED);
+    ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd);
+    goto done;
+  } else if (specific_worker->initialized_cv) {
+    GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, " .. kick waiting worker");
+    }
+    SET_KICK_STATE(specific_worker, KICKED);
+    gpr_cv_signal(&specific_worker->cv);
+    goto done;
+  } else {
+    GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, " .. kick non-waiting worker");
+    }
+    SET_KICK_STATE(specific_worker, KICKED);
+    goto done;
+  }
+done:
+  return ret_err;
+}
+
+static void pollset_add_fd(grpc_pollset* pollset, grpc_fd* fd) {}
+
+/*******************************************************************************
+ * Pollset-set Definitions
+ */
+
+static grpc_pollset_set* pollset_set_create(void) {
+  return (grpc_pollset_set*)(static_cast<intptr_t>(0xdeafbeef));
+}
+
+static void pollset_set_destroy(grpc_pollset_set* pss) {}
+
+static void pollset_set_add_fd(grpc_pollset_set* pss, grpc_fd* fd) {}
+
+static void pollset_set_del_fd(grpc_pollset_set* pss, grpc_fd* fd) {}
+
+static void pollset_set_add_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {}
+
+static void pollset_set_del_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {}
+
+static void pollset_set_add_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {}
+
+static void pollset_set_del_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {}
+
+/*******************************************************************************
+ * Event engine binding
+ */
+
+static void shutdown_background_closure(void) {}
+
+static void shutdown_engine(void) {
+  fd_global_shutdown();
+  pollset_global_shutdown();
+  epoll_set_shutdown();
+  if (grpc_core::Fork::Enabled()) {
+    gpr_mu_destroy(&fork_fd_list_mu);
+    grpc_core::Fork::SetResetChildPollingEngineFunc(nullptr);
+  }
+}
+
+static const grpc_event_engine_vtable vtable = {
+    sizeof(grpc_pollset),
+    true,
+    false,
+
+    fd_create,
+    fd_wrapped_fd,
+    fd_orphan,
+    fd_shutdown,
+    fd_notify_on_read,
+    fd_notify_on_write,
+    fd_notify_on_error,
+    fd_become_readable,
+    fd_become_writable,
+    fd_has_errors,
+    fd_is_shutdown,
+
+    pollset_init,
+    pollset_shutdown,
+    pollset_destroy,
+    pollset_work,
+    pollset_kick,
+    pollset_add_fd,
+
+    pollset_set_create,
+    pollset_set_destroy,
+    pollset_set_add_pollset,
+    pollset_set_del_pollset,
+    pollset_set_add_pollset_set,
+    pollset_set_del_pollset_set,
+    pollset_set_add_fd,
+    pollset_set_del_fd,
+
+    shutdown_background_closure,
+    shutdown_engine,
+};
+
+/* Called by the child process's post-fork handler to close open fds, including
+ * the global epoll fd. This allows gRPC to shutdown in the child process
+ * without interfering with connections or RPCs ongoing in the parent. */
+static void reset_event_manager_on_fork() {
+  gpr_mu_lock(&fork_fd_list_mu);
+  while (fork_fd_list_head != nullptr) {
+    close(fork_fd_list_head->fd);
+    fork_fd_list_head->fd = -1;
+    fork_fd_list_head = fork_fd_list_head->fork_fd_list->next;
+  }
+  gpr_mu_unlock(&fork_fd_list_mu);
+  shutdown_engine();
+  grpc_init_epoll1_linux(true);
+}
+
+/* It is possible that GLIBC has epoll but the underlying kernel doesn't.
+ * Create epoll_fd (epoll_set_init() takes care of that) to make sure epoll
+ * support is available */
+const grpc_event_engine_vtable* grpc_init_epoll1_linux(bool explicit_request) {
+  if (!grpc_has_wakeup_fd()) {
+    gpr_log(GPR_ERROR, "Skipping epoll1 because of no wakeup fd.");
+    return nullptr;
+  }
+
+  if (!epoll_set_init()) {
+    return nullptr;
+  }
+
+  fd_global_init();
+
+  if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
+    fd_global_shutdown();
+    epoll_set_shutdown();
+    return nullptr;
+  }
+
+  if (grpc_core::Fork::Enabled()) {
+    gpr_mu_init(&fork_fd_list_mu);
+    grpc_core::Fork::SetResetChildPollingEngineFunc(
+        reset_event_manager_on_fork);
+  }
+  return &vtable;
+}
+
+#else /* defined(GRPC_LINUX_EPOLL) */
+#if defined(GRPC_POSIX_SOCKET_EV_EPOLL1)
+#include "src/core/lib/iomgr/ev_epoll1_linux.h"
+/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return
+ * NULL */
+const grpc_event_engine_vtable* grpc_init_epoll1_linux(bool explicit_request) {
+  return nullptr;
+}
+#endif /* defined(GRPC_POSIX_SOCKET_EV_EPOLL1) */
+#endif /* !defined(GRPC_LINUX_EPOLL) */
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.h b/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.h
new file mode 100644
index 0000000..ca0db72
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H
+#define GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/port.h"
+
+// a polling engine that utilizes a singleton epoll set and turnstile polling
+
+const grpc_event_engine_vtable* grpc_init_epoll1_linux(bool explicit_request);
+
+#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.cc b/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.cc
new file mode 100644
index 0000000..4258ded
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.cc
@@ -0,0 +1,1685 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/log.h>
+
+/* This polling engine is only relevant on linux kernels supporting epoll() */
+#ifdef GRPC_LINUX_EPOLL_CREATE1
+
+#include "src/core/lib/iomgr/ev_epollex_linux.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/spinlock.h"
+#include "src/core/lib/gpr/tls.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/iomgr/block_annotate.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/is_epollexclusive_available.h"
+#include "src/core/lib/iomgr/lockfree_event.h"
+#include "src/core/lib/iomgr/sys_epoll_wrapper.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/profiling/timers.h"
+
+// debug aid: create workers on the heap (allows asan to spot
+// use-after-destruction)
+//#define GRPC_EPOLLEX_CREATE_WORKERS_ON_HEAP 1
+
+#define MAX_EPOLL_EVENTS 100
+// TODO(juanlishen): We use a greater-than-one value here as a workaround fix to
+// a keepalive ping timeout issue. We may want to revert https://github
+// .com/grpc/grpc/pull/14943 once we figure out the root cause.
+#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 16
+#define MAX_FDS_IN_CACHE 32
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_pollable_refcount(false,
+                                                           "pollable_refcount");
+
+/*******************************************************************************
+ * pollable Declarations
+ */
+
+typedef enum { PO_MULTI, PO_FD, PO_EMPTY } pollable_type;
+
+typedef struct pollable pollable;
+
+typedef struct cached_fd {
+  // Set to the grpc_fd's salt value. See 'salt' variable' in grpc_fd for more
+  // details
+  intptr_t salt;
+
+  // The underlying fd
+  int fd;
+
+  // A recency time counter that helps to determine the LRU fd in the cache
+  uint64_t last_used;
+} cached_fd;
+
+/// A pollable is something that can be polled: it has an epoll set to poll on,
+/// and a wakeup fd for kicks
+/// There are three broad types:
+///  - PO_EMPTY - the empty pollable, used before file descriptors are added to
+///               a pollset
+///  - PO_FD - a pollable containing only one FD - used to optimize single-fd
+///            pollsets (which are common with synchronous api usage)
+///  - PO_MULTI - a pollable containing many fds
+struct pollable {
+  pollable_type type;  // immutable
+  gpr_refcount refs;
+
+  int epfd;
+  grpc_wakeup_fd wakeup;
+
+  // The following are relevant only for type PO_FD
+  grpc_fd* owner_fd;       // Set to the owner_fd if the type is PO_FD
+  gpr_mu owner_orphan_mu;  // Synchronizes access to owner_orphaned field
+  bool owner_orphaned;     // Is the owner fd orphaned
+
+  grpc_pollset_set* pollset_set;
+  pollable* next;
+  pollable* prev;
+
+  gpr_mu mu;
+  grpc_pollset_worker* root_worker;
+
+  int event_cursor;
+  int event_count;
+  struct epoll_event events[MAX_EPOLL_EVENTS];
+
+  // We may be calling pollable_add_fd() on the same (pollable, fd) multiple
+  // times. To prevent pollable_add_fd() from making multiple sys calls to
+  // epoll_ctl() to add the fd, we maintain a cache of what fds are already
+  // present in the underlying epoll-set.
+  //
+  // Since this is not a correctness issue, we do not need to maintain all the
+  // fds in the cache. Hence we just use an LRU cache of size 'MAX_FDS_IN_CACHE'
+  //
+  // NOTE: An ideal implementation of this should do the following:
+  //  1) Add fds to the cache in pollable_add_fd() function (i.e whenever the fd
+  //     is added to the pollable's epoll set)
+  //  2) Remove the fd from the cache whenever the fd is removed from the
+  //     underlying epoll set (i.e whenever fd_orphan() is called).
+  //
+  // Implementing (2) above (i.e removing fds from cache on fd_orphan) adds a
+  // lot of complexity since an fd can be present in multiple pollables. So our
+  // implementation ONLY DOES (1) and NOT (2).
+  //
+  // The cache_fd.salt variable helps here to maintain correctness (it serves as
+  // an epoch that differentiates one grpc_fd from the other even though both of
+  // them may have the same fd number)
+  //
+  // The following implements LRU-eviction cache of fds in this pollable
+  cached_fd fd_cache[MAX_FDS_IN_CACHE];
+  int fd_cache_size;
+  uint64_t fd_cache_counter;  // Recency timer tick counter
+};
+
+static const char* pollable_type_string(pollable_type t) {
+  switch (t) {
+    case PO_MULTI:
+      return "pollset";
+    case PO_FD:
+      return "fd";
+    case PO_EMPTY:
+      return "empty";
+  }
+  return "<invalid>";
+}
+
+static char* pollable_desc(pollable* p) {
+  char* out;
+  gpr_asprintf(&out, "type=%s epfd=%d wakeup=%d", pollable_type_string(p->type),
+               p->epfd, p->wakeup.read_fd);
+  return out;
+}
+
+/// Shared empty pollable - used by pollset to poll on until the first fd is
+/// added
+static pollable* g_empty_pollable;
+
+static grpc_error* pollable_create(pollable_type type, pollable** p);
+#ifdef NDEBUG
+static pollable* pollable_ref(pollable* p);
+static void pollable_unref(pollable* p);
+#define POLLABLE_REF(p, r) pollable_ref(p)
+#define POLLABLE_UNREF(p, r) pollable_unref(p)
+#else
+static pollable* pollable_ref(pollable* p, int line, const char* reason);
+static void pollable_unref(pollable* p, int line, const char* reason);
+#define POLLABLE_REF(p, r) pollable_ref((p), __LINE__, (r))
+#define POLLABLE_UNREF(p, r) pollable_unref((p), __LINE__, (r))
+#endif
+
+/*******************************************************************************
+ * Fd Declarations
+ */
+
+// Monotonically increasing Epoch counter that is assinged to each grpc_fd. See
+// the description of 'salt' variable in 'grpc_fd' for more details
+// TODO: (sreek/kpayson) gpr_atm is intptr_t which may not be wide-enough on
+// 32-bit systems. Change this to int_64 - atleast on 32-bit systems
+static gpr_atm g_fd_salt;
+
+struct grpc_fd {
+  int fd;
+
+  // Since fd numbers can be reused (after old fds are closed), this serves as
+  // an epoch that uniquely identifies this fd (i.e the pair (salt, fd) is
+  // unique (until the salt counter (i.e g_fd_salt) overflows)
+  intptr_t salt;
+
+  // refst format:
+  //     bit 0    : 1=Active / 0=Orphaned
+  //     bits 1-n : refcount
+  //  Ref/Unref by two to avoid altering the orphaned bit
+  gpr_atm refst;
+
+  gpr_mu orphan_mu;
+
+  gpr_mu pollable_mu;
+  pollable* pollable_obj;
+
+  grpc_core::ManualConstructor<grpc_core::LockfreeEvent> read_closure;
+  grpc_core::ManualConstructor<grpc_core::LockfreeEvent> write_closure;
+  grpc_core::ManualConstructor<grpc_core::LockfreeEvent> error_closure;
+
+  struct grpc_fd* freelist_next;
+  grpc_closure* on_done_closure;
+
+  grpc_iomgr_object iomgr_object;
+
+  // Do we need to track EPOLLERR events separately?
+  bool track_err;
+};
+
+static void fd_global_init(void);
+static void fd_global_shutdown(void);
+
+/*******************************************************************************
+ * Pollset Declarations
+ */
+
+typedef struct {
+  grpc_pollset_worker* next;
+  grpc_pollset_worker* prev;
+} pwlink;
+
+typedef enum { PWLINK_POLLABLE = 0, PWLINK_POLLSET, PWLINK_COUNT } pwlinks;
+
+struct grpc_pollset_worker {
+  bool kicked;
+  bool initialized_cv;
+#ifndef NDEBUG
+  // debug aid: which thread started this worker
+  pid_t originator;
+#endif
+  gpr_cv cv;
+  grpc_pollset* pollset;
+  pollable* pollable_obj;
+
+  pwlink links[PWLINK_COUNT];
+};
+
+struct grpc_pollset {
+  gpr_mu mu;
+  gpr_atm worker_count;
+  pollable* active_pollable;
+  bool kicked_without_poller;
+  grpc_closure* shutdown_closure;
+  bool already_shutdown;
+  grpc_pollset_worker* root_worker;
+  int containing_pollset_set_count;
+};
+
+/*******************************************************************************
+ * Pollset-set Declarations
+ */
+
+struct grpc_pollset_set {
+  gpr_refcount refs;
+  gpr_mu mu;
+  grpc_pollset_set* parent;
+
+  size_t pollset_count;
+  size_t pollset_capacity;
+  grpc_pollset** pollsets;
+
+  size_t fd_count;
+  size_t fd_capacity;
+  grpc_fd** fds;
+};
+
+/*******************************************************************************
+ * Common helpers
+ */
+
+static bool append_error(grpc_error** composite, grpc_error* error,
+                         const char* desc) {
+  if (error == GRPC_ERROR_NONE) return true;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc);
+  }
+  *composite = grpc_error_add_child(*composite, error);
+  return false;
+}
+
+/*******************************************************************************
+ * Fd Definitions
+ */
+
+/* We need to keep a freelist not because of any concerns of malloc performance
+ * but instead so that implementations with multiple threads in (for example)
+ * epoll_wait deal with the race between pollset removal and incoming poll
+ * notifications.
+ *
+ * The problem is that the poller ultimately holds a reference to this
+ * object, so it is very difficult to know when is safe to free it, at least
+ * without some expensive synchronization.
+ *
+ * If we keep the object freelisted, in the worst case losing this race just
+ * becomes a spurious read notification on a reused fd.
+ */
+
+static grpc_fd* fd_freelist = nullptr;
+static gpr_mu fd_freelist_mu;
+
+#ifndef NDEBUG
+#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__)
+#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
+static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
+                   int line) {
+  if (grpc_trace_fd_refcount.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
+            fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
+            gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line);
+  }
+#else
+#define REF_BY(fd, n, reason) ref_by(fd, n)
+#define UNREF_BY(fd, n, reason) unref_by(fd, n)
+static void ref_by(grpc_fd* fd, int n) {
+#endif
+  GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0);
+}
+
+#ifndef NDEBUG
+#define INVALIDATE_FD(fd) invalidate_fd(fd)
+/* Since an fd is never really destroyed (i.e gpr_free() is not called), it is
+ * hard to cases where fd fields are accessed even after calling fd_destroy().
+ * The following invalidates fd fields to make catching such errors easier */
+static void invalidate_fd(grpc_fd* fd) {
+  fd->fd = -1;
+  fd->salt = -1;
+  gpr_atm_no_barrier_store(&fd->refst, -1);
+  memset(&fd->orphan_mu, -1, sizeof(fd->orphan_mu));
+  memset(&fd->pollable_mu, -1, sizeof(fd->pollable_mu));
+  fd->pollable_obj = nullptr;
+  fd->on_done_closure = nullptr;
+  memset(&fd->iomgr_object, -1, sizeof(fd->iomgr_object));
+  fd->track_err = false;
+}
+#else
+#define INVALIDATE_FD(fd)
+#endif
+
+/* Uninitialize and add to the freelist */
+static void fd_destroy(void* arg, grpc_error* error) {
+  grpc_fd* fd = static_cast<grpc_fd*>(arg);
+  grpc_iomgr_unregister_object(&fd->iomgr_object);
+  POLLABLE_UNREF(fd->pollable_obj, "fd_pollable");
+  gpr_mu_destroy(&fd->pollable_mu);
+  gpr_mu_destroy(&fd->orphan_mu);
+
+  fd->read_closure->DestroyEvent();
+  fd->write_closure->DestroyEvent();
+  fd->error_closure->DestroyEvent();
+
+  INVALIDATE_FD(fd);
+
+  /* Add the fd to the freelist */
+  gpr_mu_lock(&fd_freelist_mu);
+  fd->freelist_next = fd_freelist;
+  fd_freelist = fd;
+  gpr_mu_unlock(&fd_freelist_mu);
+}
+
+#ifndef NDEBUG
+static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file,
+                     int line) {
+  if (grpc_trace_fd_refcount.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
+            fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
+            gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line);
+  }
+#else
+static void unref_by(grpc_fd* fd, int n) {
+#endif
+  gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n);
+  if (old == n) {
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_CREATE(fd_destroy, fd, grpc_schedule_on_exec_ctx),
+        GRPC_ERROR_NONE);
+  } else {
+    GPR_ASSERT(old > n);
+  }
+}
+
+static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
+
+static void fd_global_shutdown(void) {
+  // TODO(guantaol): We don't have a reasonable explanation about this
+  // lock()/unlock() pattern. It can be a valid barrier if there is at most one
+  // pending lock() at this point. Otherwise, there is still a possibility of
+  // use-after-free race. Need to reason about the code and/or clean it up.
+  gpr_mu_lock(&fd_freelist_mu);
+  gpr_mu_unlock(&fd_freelist_mu);
+  while (fd_freelist != nullptr) {
+    grpc_fd* fd = fd_freelist;
+    fd_freelist = fd_freelist->freelist_next;
+    gpr_free(fd);
+  }
+  gpr_mu_destroy(&fd_freelist_mu);
+}
+
+static grpc_fd* fd_create(int fd, const char* name, bool track_err) {
+  grpc_fd* new_fd = nullptr;
+
+  gpr_mu_lock(&fd_freelist_mu);
+  if (fd_freelist != nullptr) {
+    new_fd = fd_freelist;
+    fd_freelist = fd_freelist->freelist_next;
+  }
+  gpr_mu_unlock(&fd_freelist_mu);
+
+  if (new_fd == nullptr) {
+    new_fd = static_cast<grpc_fd*>(gpr_malloc(sizeof(grpc_fd)));
+    new_fd->read_closure.Init();
+    new_fd->write_closure.Init();
+    new_fd->error_closure.Init();
+  }
+
+  new_fd->fd = fd;
+  new_fd->salt = gpr_atm_no_barrier_fetch_add(&g_fd_salt, 1);
+  gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1);
+  gpr_mu_init(&new_fd->orphan_mu);
+  gpr_mu_init(&new_fd->pollable_mu);
+  new_fd->pollable_obj = nullptr;
+  new_fd->read_closure->InitEvent();
+  new_fd->write_closure->InitEvent();
+  new_fd->error_closure->InitEvent();
+  new_fd->freelist_next = nullptr;
+  new_fd->on_done_closure = nullptr;
+
+  char* fd_name;
+  gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
+  grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
+#ifndef NDEBUG
+  if (grpc_trace_fd_refcount.enabled()) {
+    gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name);
+  }
+#endif
+  gpr_free(fd_name);
+
+  new_fd->track_err = track_err;
+  return new_fd;
+}
+
+static int fd_wrapped_fd(grpc_fd* fd) {
+  int ret_fd = fd->fd;
+  return (gpr_atm_acq_load(&fd->refst) & 1) ? ret_fd : -1;
+}
+
+static void fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd,
+                      const char* reason) {
+  bool is_fd_closed = false;
+
+  gpr_mu_lock(&fd->orphan_mu);
+
+  // Get the fd->pollable_obj and set the owner_orphaned on that pollable to
+  // true so that the pollable will no longer access its owner_fd field.
+  gpr_mu_lock(&fd->pollable_mu);
+  pollable* pollable_obj = fd->pollable_obj;
+  gpr_mu_unlock(&fd->pollable_mu);
+
+  if (pollable_obj) {
+    gpr_mu_lock(&pollable_obj->owner_orphan_mu);
+    pollable_obj->owner_orphaned = true;
+  }
+
+  fd->on_done_closure = on_done;
+
+  /* If release_fd is not NULL, we should be relinquishing control of the file
+     descriptor fd->fd (but we still own the grpc_fd structure). */
+  if (release_fd != nullptr) {
+    *release_fd = fd->fd;
+  } else {
+    close(fd->fd);
+    is_fd_closed = true;
+  }
+
+  // TODO(sreek): handle fd removal (where is_fd_closed=false)
+  if (!is_fd_closed) {
+    GRPC_FD_TRACE("epoll_fd %p (%d) was orphaned but not closed.", fd, fd->fd);
+  }
+
+  /* Remove the active status but keep referenced. We want this grpc_fd struct
+     to be alive (and not added to freelist) until the end of this function */
+  REF_BY(fd, 1, reason);
+
+  GRPC_CLOSURE_SCHED(fd->on_done_closure, GRPC_ERROR_NONE);
+
+  if (pollable_obj) {
+    gpr_mu_unlock(&pollable_obj->owner_orphan_mu);
+  }
+
+  gpr_mu_unlock(&fd->orphan_mu);
+
+  UNREF_BY(fd, 2, reason); /* Drop the reference */
+}
+
+static bool fd_is_shutdown(grpc_fd* fd) {
+  return fd->read_closure->IsShutdown();
+}
+
+/* Might be called multiple times */
+static void fd_shutdown(grpc_fd* fd, grpc_error* why) {
+  if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) {
+    if (shutdown(fd->fd, SHUT_RDWR)) {
+      if (errno != ENOTCONN) {
+        gpr_log(GPR_ERROR, "Error shutting down fd %d. errno: %d",
+                grpc_fd_wrapped_fd(fd), errno);
+      }
+    }
+    fd->write_closure->SetShutdown(GRPC_ERROR_REF(why));
+    fd->error_closure->SetShutdown(GRPC_ERROR_REF(why));
+  }
+  GRPC_ERROR_UNREF(why);
+}
+
+static void fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) {
+  fd->read_closure->NotifyOn(closure);
+}
+
+static void fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) {
+  fd->write_closure->NotifyOn(closure);
+}
+
+static void fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) {
+  fd->error_closure->NotifyOn(closure);
+}
+
+/*******************************************************************************
+ * Pollable Definitions
+ */
+
+static grpc_error* pollable_create(pollable_type type, pollable** p) {
+  *p = nullptr;
+
+  int epfd = epoll_create1(EPOLL_CLOEXEC);
+  if (epfd == -1) {
+    return GRPC_OS_ERROR(errno, "epoll_create1");
+  }
+  GRPC_FD_TRACE("Pollable_create: created epfd: %d (type: %d)", epfd, type);
+  *p = static_cast<pollable*>(gpr_malloc(sizeof(**p)));
+  grpc_error* err = grpc_wakeup_fd_init(&(*p)->wakeup);
+  if (err != GRPC_ERROR_NONE) {
+    GRPC_FD_TRACE(
+        "Pollable_create: closed epfd: %d (type: %d). wakeupfd_init error",
+        epfd, type);
+    close(epfd);
+    gpr_free(*p);
+    *p = nullptr;
+    return err;
+  }
+  struct epoll_event ev;
+  ev.events = static_cast<uint32_t>(EPOLLIN | EPOLLET);
+  ev.data.ptr = (void*)(1 | (intptr_t) & (*p)->wakeup);
+  if (epoll_ctl(epfd, EPOLL_CTL_ADD, (*p)->wakeup.read_fd, &ev) != 0) {
+    err = GRPC_OS_ERROR(errno, "epoll_ctl");
+    GRPC_FD_TRACE(
+        "Pollable_create: closed epfd: %d (type: %d). epoll_ctl error", epfd,
+        type);
+    close(epfd);
+    grpc_wakeup_fd_destroy(&(*p)->wakeup);
+    gpr_free(*p);
+    *p = nullptr;
+    return err;
+  }
+
+  (*p)->type = type;
+  gpr_ref_init(&(*p)->refs, 1);
+  gpr_mu_init(&(*p)->mu);
+  (*p)->epfd = epfd;
+  (*p)->owner_fd = nullptr;
+  gpr_mu_init(&(*p)->owner_orphan_mu);
+  (*p)->owner_orphaned = false;
+  (*p)->pollset_set = nullptr;
+  (*p)->next = (*p)->prev = *p;
+  (*p)->root_worker = nullptr;
+  (*p)->event_cursor = 0;
+  (*p)->event_count = 0;
+  (*p)->fd_cache_size = 0;
+  (*p)->fd_cache_counter = 0;
+  return GRPC_ERROR_NONE;
+}
+
+#ifdef NDEBUG
+static pollable* pollable_ref(pollable* p) {
+#else
+static pollable* pollable_ref(pollable* p, int line, const char* reason) {
+  if (grpc_trace_pollable_refcount.enabled()) {
+    int r = static_cast<int> gpr_atm_no_barrier_load(&p->refs.count);
+    gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG,
+            "POLLABLE:%p   ref %d->%d %s", p, r, r + 1, reason);
+  }
+#endif
+  gpr_ref(&p->refs);
+  return p;
+}
+
+#ifdef NDEBUG
+static void pollable_unref(pollable* p) {
+#else
+static void pollable_unref(pollable* p, int line, const char* reason) {
+  if (p == nullptr) return;
+  if (grpc_trace_pollable_refcount.enabled()) {
+    int r = static_cast<int> gpr_atm_no_barrier_load(&p->refs.count);
+    gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG,
+            "POLLABLE:%p unref %d->%d %s", p, r, r - 1, reason);
+  }
+#endif
+  if (p != nullptr && gpr_unref(&p->refs)) {
+    GRPC_FD_TRACE("pollable_unref: Closing epfd: %d", p->epfd);
+    close(p->epfd);
+    grpc_wakeup_fd_destroy(&p->wakeup);
+    gpr_mu_destroy(&p->owner_orphan_mu);
+    gpr_free(p);
+  }
+}
+
+static grpc_error* pollable_add_fd(pollable* p, grpc_fd* fd) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  static const char* err_desc = "pollable_add_fd";
+  const int epfd = p->epfd;
+  gpr_mu_lock(&p->mu);
+  p->fd_cache_counter++;
+
+  // Handle the case of overflow for our cache counter by
+  // reseting the recency-counter on all cache objects
+  if (p->fd_cache_counter == 0) {
+    for (int i = 0; i < p->fd_cache_size; i++) {
+      p->fd_cache[i].last_used = 0;
+    }
+  }
+
+  int lru_idx = 0;
+  for (int i = 0; i < p->fd_cache_size; i++) {
+    if (p->fd_cache[i].fd == fd->fd && p->fd_cache[i].salt == fd->salt) {
+      GRPC_STATS_INC_POLLSET_FD_CACHE_HITS();
+      p->fd_cache[i].last_used = p->fd_cache_counter;
+      gpr_mu_unlock(&p->mu);
+      return GRPC_ERROR_NONE;
+    } else if (p->fd_cache[i].last_used < p->fd_cache[lru_idx].last_used) {
+      lru_idx = i;
+    }
+  }
+
+  // Add to cache
+  if (p->fd_cache_size < MAX_FDS_IN_CACHE) {
+    lru_idx = p->fd_cache_size;
+    p->fd_cache_size++;
+  }
+  p->fd_cache[lru_idx].fd = fd->fd;
+  p->fd_cache[lru_idx].salt = fd->salt;
+  p->fd_cache[lru_idx].last_used = p->fd_cache_counter;
+  gpr_mu_unlock(&p->mu);
+
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "add fd %p (%d) to pollable %p", fd, fd->fd, p);
+  }
+
+  struct epoll_event ev_fd;
+  ev_fd.events =
+      static_cast<uint32_t>(EPOLLET | EPOLLIN | EPOLLOUT | EPOLLEXCLUSIVE);
+  /* Use the second least significant bit of ev_fd.data.ptr to store track_err
+   * to avoid synchronization issues when accessing it after receiving an event.
+   * Accessing fd would be a data race there because the fd might have been
+   * returned to the free list at that point. */
+  ev_fd.data.ptr = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(fd) |
+                                           (fd->track_err ? 2 : 0));
+  GRPC_STATS_INC_SYSCALL_EPOLL_CTL();
+  if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd->fd, &ev_fd) != 0) {
+    switch (errno) {
+      case EEXIST:
+        break;
+      default:
+        append_error(&error, GRPC_OS_ERROR(errno, "epoll_ctl"), err_desc);
+    }
+  }
+
+  return error;
+}
+
+/*******************************************************************************
+ * Pollset Definitions
+ */
+
+GPR_TLS_DECL(g_current_thread_pollset);
+GPR_TLS_DECL(g_current_thread_worker);
+
+/* Global state management */
+static grpc_error* pollset_global_init(void) {
+  gpr_tls_init(&g_current_thread_pollset);
+  gpr_tls_init(&g_current_thread_worker);
+  return pollable_create(PO_EMPTY, &g_empty_pollable);
+}
+
+static void pollset_global_shutdown(void) {
+  POLLABLE_UNREF(g_empty_pollable, "g_empty_pollable");
+  gpr_tls_destroy(&g_current_thread_pollset);
+  gpr_tls_destroy(&g_current_thread_worker);
+}
+
+/* pollset->mu must be held while calling this function */
+static void pollset_maybe_finish_shutdown(grpc_pollset* pollset) {
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "PS:%p (pollable:%p) maybe_finish_shutdown sc=%p (target:!NULL) "
+            "rw=%p (target:NULL) cpsc=%d (target:0)",
+            pollset, pollset->active_pollable, pollset->shutdown_closure,
+            pollset->root_worker, pollset->containing_pollset_set_count);
+  }
+  if (pollset->shutdown_closure != nullptr && pollset->root_worker == nullptr &&
+      pollset->containing_pollset_set_count == 0) {
+    GPR_TIMER_MARK("pollset_finish_shutdown", 0);
+    GRPC_CLOSURE_SCHED(pollset->shutdown_closure, GRPC_ERROR_NONE);
+    pollset->shutdown_closure = nullptr;
+    pollset->already_shutdown = true;
+  }
+}
+
+/* pollset->mu must be held before calling this function,
+ * pollset->active_pollable->mu & specific_worker->pollable_obj->mu must not be
+ * held */
+static grpc_error* kick_one_worker(grpc_pollset_worker* specific_worker) {
+  GPR_TIMER_SCOPE("kick_one_worker", 0);
+  pollable* p = specific_worker->pollable_obj;
+  grpc_core::MutexLock lock(&p->mu);
+  GPR_ASSERT(specific_worker != nullptr);
+  if (specific_worker->kicked) {
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, "PS:%p kicked_specific_but_already_kicked", p);
+    }
+    GRPC_STATS_INC_POLLSET_KICKED_AGAIN();
+    return GRPC_ERROR_NONE;
+  }
+  if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) {
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, "PS:%p kicked_specific_but_awake", p);
+    }
+    GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD();
+    specific_worker->kicked = true;
+    return GRPC_ERROR_NONE;
+  }
+  if (specific_worker == p->root_worker) {
+    GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD();
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, "PS:%p kicked_specific_via_wakeup_fd", p);
+    }
+    specific_worker->kicked = true;
+    grpc_error* error = grpc_wakeup_fd_wakeup(&p->wakeup);
+    return error;
+  }
+  if (specific_worker->initialized_cv) {
+    GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV();
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_INFO, "PS:%p kicked_specific_via_cv", p);
+    }
+    specific_worker->kicked = true;
+    gpr_cv_signal(&specific_worker->cv);
+    return GRPC_ERROR_NONE;
+  }
+  // we can get here during end_worker after removing specific_worker from the
+  // pollable list but before removing it from the pollset list
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* pollset_kick(grpc_pollset* pollset,
+                                grpc_pollset_worker* specific_worker) {
+  GPR_TIMER_SCOPE("pollset_kick", 0);
+  GRPC_STATS_INC_POLLSET_KICK();
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "PS:%p kick %p tls_pollset=%p tls_worker=%p pollset.root_worker=%p",
+            pollset, specific_worker,
+            (void*)gpr_tls_get(&g_current_thread_pollset),
+            (void*)gpr_tls_get(&g_current_thread_worker), pollset->root_worker);
+  }
+  if (specific_worker == nullptr) {
+    if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) {
+      if (pollset->root_worker == nullptr) {
+        if (grpc_polling_trace.enabled()) {
+          gpr_log(GPR_INFO, "PS:%p kicked_any_without_poller", pollset);
+        }
+        GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER();
+        pollset->kicked_without_poller = true;
+        return GRPC_ERROR_NONE;
+      } else {
+        // We've been asked to kick a poller, but we haven't been told which one
+        // ... any will do
+        // We look at the pollset worker list because:
+        // 1. the pollable list may include workers from other pollers, so we'd
+        //    need to do an O(N) search
+        // 2. we'd additionally need to take the pollable lock, which we've so
+        //    far avoided
+        // Now, we would prefer to wake a poller in cv_wait, and not in
+        // epoll_wait (since the latter would imply the need to do an additional
+        // wakeup)
+        // We know that if a worker is at the root of a pollable, it's (likely)
+        // also the root of a pollset, and we know that if a worker is NOT at
+        // the root of a pollset, it's (likely) not at the root of a pollable,
+        // so we take our chances and choose the SECOND worker enqueued against
+        // the pollset as a worker that's likely to be in cv_wait
+        return kick_one_worker(
+            pollset->root_worker->links[PWLINK_POLLSET].next);
+      }
+    } else {
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO, "PS:%p kicked_any_but_awake", pollset);
+      }
+      GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD();
+      return GRPC_ERROR_NONE;
+    }
+  } else {
+    return kick_one_worker(specific_worker);
+  }
+}
+
+static grpc_error* pollset_kick_all(grpc_pollset* pollset) {
+  GPR_TIMER_SCOPE("pollset_kick_all", 0);
+  grpc_error* error = GRPC_ERROR_NONE;
+  const char* err_desc = "pollset_kick_all";
+  grpc_pollset_worker* w = pollset->root_worker;
+  if (w != nullptr) {
+    do {
+      GRPC_STATS_INC_POLLSET_KICK();
+      append_error(&error, kick_one_worker(w), err_desc);
+      w = w->links[PWLINK_POLLSET].next;
+    } while (w != pollset->root_worker);
+  }
+  return error;
+}
+
+static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
+  gpr_mu_init(&pollset->mu);
+  gpr_atm_no_barrier_store(&pollset->worker_count, 0);
+  pollset->active_pollable = POLLABLE_REF(g_empty_pollable, "pollset");
+  pollset->kicked_without_poller = false;
+  pollset->shutdown_closure = nullptr;
+  pollset->already_shutdown = false;
+  pollset->root_worker = nullptr;
+  pollset->containing_pollset_set_count = 0;
+  *mu = &pollset->mu;
+}
+
+static int poll_deadline_to_millis_timeout(grpc_millis millis) {
+  if (millis == GRPC_MILLIS_INF_FUTURE) return -1;
+  grpc_millis delta = millis - grpc_core::ExecCtx::Get()->Now();
+  if (delta > INT_MAX)
+    return INT_MAX;
+  else if (delta < 0)
+    return 0;
+  else
+    return static_cast<int>(delta);
+}
+
+static void fd_become_readable(grpc_fd* fd) { fd->read_closure->SetReady(); }
+
+static void fd_become_writable(grpc_fd* fd) { fd->write_closure->SetReady(); }
+
+static void fd_has_errors(grpc_fd* fd) { fd->error_closure->SetReady(); }
+
+/* Get the pollable_obj attached to this fd. If none is attached, create a new
+ * pollable object (of type PO_FD), attach it to the fd and return it
+ *
+ * Note that if a pollable object is already attached to the fd, it may be of
+ * either PO_FD or PO_MULTI type */
+static grpc_error* get_fd_pollable(grpc_fd* fd, pollable** p) {
+  gpr_mu_lock(&fd->pollable_mu);
+  grpc_error* error = GRPC_ERROR_NONE;
+  static const char* err_desc = "get_fd_pollable";
+  if (fd->pollable_obj == nullptr) {
+    if (append_error(&error, pollable_create(PO_FD, &fd->pollable_obj),
+                     err_desc)) {
+      fd->pollable_obj->owner_fd = fd;
+      if (!append_error(&error, pollable_add_fd(fd->pollable_obj, fd),
+                        err_desc)) {
+        POLLABLE_UNREF(fd->pollable_obj, "fd_pollable");
+        fd->pollable_obj = nullptr;
+      }
+    }
+  }
+  if (error == GRPC_ERROR_NONE) {
+    GPR_ASSERT(fd->pollable_obj != nullptr);
+    *p = POLLABLE_REF(fd->pollable_obj, "pollset");
+  } else {
+    GPR_ASSERT(fd->pollable_obj == nullptr);
+    *p = nullptr;
+  }
+  gpr_mu_unlock(&fd->pollable_mu);
+  return error;
+}
+
+/* pollset->po.mu lock must be held by the caller before calling this */
+static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
+  GPR_TIMER_SCOPE("pollset_shutdown", 0);
+  GPR_ASSERT(pollset->shutdown_closure == nullptr);
+  pollset->shutdown_closure = closure;
+  GRPC_LOG_IF_ERROR("pollset_shutdown", pollset_kick_all(pollset));
+  pollset_maybe_finish_shutdown(pollset);
+}
+
+static grpc_error* pollable_process_events(grpc_pollset* pollset,
+                                           pollable* pollable_obj, bool drain) {
+  GPR_TIMER_SCOPE("pollable_process_events", 0);
+  static const char* err_desc = "pollset_process_events";
+  // Use a simple heuristic to determine how many fd events to process
+  // per loop iteration.  (events/workers)
+  int handle_count = 1;
+  int worker_count = gpr_atm_no_barrier_load(&pollset->worker_count);
+  GPR_ASSERT(worker_count > 0);
+  handle_count =
+      (pollable_obj->event_count - pollable_obj->event_cursor) / worker_count;
+  if (handle_count == 0) {
+    handle_count = 1;
+  } else if (handle_count > MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) {
+    handle_count = MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL;
+  }
+  grpc_error* error = GRPC_ERROR_NONE;
+  for (int i = 0; (drain || i < handle_count) &&
+                  pollable_obj->event_cursor != pollable_obj->event_count;
+       i++) {
+    int n = pollable_obj->event_cursor++;
+    struct epoll_event* ev = &pollable_obj->events[n];
+    void* data_ptr = ev->data.ptr;
+    if (1 & (intptr_t)data_ptr) {
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO, "PS:%p got pollset_wakeup %p", pollset, data_ptr);
+      }
+      append_error(&error,
+                   grpc_wakeup_fd_consume_wakeup(
+                       (grpc_wakeup_fd*)((~static_cast<intptr_t>(1)) &
+                                         (intptr_t)data_ptr)),
+                   err_desc);
+    } else {
+      grpc_fd* fd =
+          reinterpret_cast<grpc_fd*>(reinterpret_cast<intptr_t>(data_ptr) & ~2);
+      bool track_err = reinterpret_cast<intptr_t>(data_ptr) & 2;
+      bool cancel = (ev->events & EPOLLHUP) != 0;
+      bool error = (ev->events & EPOLLERR) != 0;
+      bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0;
+      bool write_ev = (ev->events & EPOLLOUT) != 0;
+      bool err_fallback = error && !track_err;
+
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "PS:%p got fd %p: cancel=%d read=%d "
+                "write=%d",
+                pollset, fd, cancel, read_ev, write_ev);
+      }
+      if (error && !err_fallback) {
+        fd_has_errors(fd);
+      }
+      if (read_ev || cancel || err_fallback) {
+        fd_become_readable(fd);
+      }
+      if (write_ev || cancel || err_fallback) {
+        fd_become_writable(fd);
+      }
+    }
+  }
+
+  return error;
+}
+
+/* pollset_shutdown is guaranteed to be called before pollset_destroy. */
+static void pollset_destroy(grpc_pollset* pollset) {
+  POLLABLE_UNREF(pollset->active_pollable, "pollset");
+  pollset->active_pollable = nullptr;
+  gpr_mu_destroy(&pollset->mu);
+}
+
+static grpc_error* pollable_epoll(pollable* p, grpc_millis deadline) {
+  GPR_TIMER_SCOPE("pollable_epoll", 0);
+  int timeout = poll_deadline_to_millis_timeout(deadline);
+
+  if (grpc_polling_trace.enabled()) {
+    char* desc = pollable_desc(p);
+    gpr_log(GPR_INFO, "POLLABLE:%p[%s] poll for %dms", p, desc, timeout);
+    gpr_free(desc);
+  }
+
+  if (timeout != 0) {
+    GRPC_SCHEDULING_START_BLOCKING_REGION;
+  }
+  int r;
+  do {
+    GRPC_STATS_INC_SYSCALL_POLL();
+    r = epoll_wait(p->epfd, p->events, MAX_EPOLL_EVENTS, timeout);
+  } while (r < 0 && errno == EINTR);
+  if (timeout != 0) {
+    GRPC_SCHEDULING_END_BLOCKING_REGION;
+  }
+
+  if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait");
+
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "POLLABLE:%p got %d events", p, r);
+  }
+
+  p->event_cursor = 0;
+  p->event_count = r;
+
+  return GRPC_ERROR_NONE;
+}
+
+/* Return true if first in list */
+static bool worker_insert(grpc_pollset_worker** root_worker,
+                          grpc_pollset_worker* worker, pwlinks link) {
+  if (*root_worker == nullptr) {
+    *root_worker = worker;
+    worker->links[link].next = worker->links[link].prev = worker;
+    return true;
+  } else {
+    worker->links[link].next = *root_worker;
+    worker->links[link].prev = worker->links[link].next->links[link].prev;
+    worker->links[link].next->links[link].prev = worker;
+    worker->links[link].prev->links[link].next = worker;
+    return false;
+  }
+}
+
+/* returns the new root IFF the root changed */
+typedef enum { WRR_NEW_ROOT, WRR_EMPTIED, WRR_REMOVED } worker_remove_result;
+
+static worker_remove_result worker_remove(grpc_pollset_worker** root_worker,
+                                          grpc_pollset_worker* worker,
+                                          pwlinks link) {
+  if (worker == *root_worker) {
+    if (worker == worker->links[link].next) {
+      *root_worker = nullptr;
+      return WRR_EMPTIED;
+    } else {
+      *root_worker = worker->links[link].next;
+      worker->links[link].prev->links[link].next = worker->links[link].next;
+      worker->links[link].next->links[link].prev = worker->links[link].prev;
+      return WRR_NEW_ROOT;
+    }
+  } else {
+    worker->links[link].prev->links[link].next = worker->links[link].next;
+    worker->links[link].next->links[link].prev = worker->links[link].prev;
+    return WRR_REMOVED;
+  }
+}
+
+/* Return true if this thread should poll */
+static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
+                         grpc_pollset_worker** worker_hdl,
+                         grpc_millis deadline) {
+  GPR_TIMER_SCOPE("begin_worker", 0);
+  bool do_poll =
+      (pollset->shutdown_closure == nullptr && !pollset->already_shutdown);
+  gpr_atm_no_barrier_fetch_add(&pollset->worker_count, 1);
+  if (worker_hdl != nullptr) *worker_hdl = worker;
+  worker->initialized_cv = false;
+  worker->kicked = false;
+  worker->pollset = pollset;
+  worker->pollable_obj =
+      POLLABLE_REF(pollset->active_pollable, "pollset_worker");
+  worker_insert(&pollset->root_worker, worker, PWLINK_POLLSET);
+  gpr_mu_lock(&worker->pollable_obj->mu);
+  if (!worker_insert(&worker->pollable_obj->root_worker, worker,
+                     PWLINK_POLLABLE)) {
+    worker->initialized_cv = true;
+    gpr_cv_init(&worker->cv);
+    gpr_mu_unlock(&pollset->mu);
+    if (grpc_polling_trace.enabled() &&
+        worker->pollable_obj->root_worker != worker) {
+      gpr_log(GPR_INFO, "PS:%p wait %p w=%p for %dms", pollset,
+              worker->pollable_obj, worker,
+              poll_deadline_to_millis_timeout(deadline));
+    }
+    while (do_poll && worker->pollable_obj->root_worker != worker) {
+      if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->mu,
+                      grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) {
+        if (grpc_polling_trace.enabled()) {
+          gpr_log(GPR_INFO, "PS:%p timeout_wait %p w=%p", pollset,
+                  worker->pollable_obj, worker);
+        }
+        do_poll = false;
+      } else if (worker->kicked) {
+        if (grpc_polling_trace.enabled()) {
+          gpr_log(GPR_INFO, "PS:%p wakeup %p w=%p", pollset,
+                  worker->pollable_obj, worker);
+        }
+        do_poll = false;
+      } else if (grpc_polling_trace.enabled() &&
+                 worker->pollable_obj->root_worker != worker) {
+        gpr_log(GPR_INFO, "PS:%p spurious_wakeup %p w=%p", pollset,
+                worker->pollable_obj, worker);
+      }
+    }
+    grpc_core::ExecCtx::Get()->InvalidateNow();
+  } else {
+    gpr_mu_unlock(&pollset->mu);
+  }
+  gpr_mu_unlock(&worker->pollable_obj->mu);
+
+  return do_poll;
+}
+
+static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker,
+                       grpc_pollset_worker** worker_hdl) {
+  GPR_TIMER_SCOPE("end_worker", 0);
+  gpr_mu_lock(&pollset->mu);
+  gpr_mu_lock(&worker->pollable_obj->mu);
+  switch (worker_remove(&worker->pollable_obj->root_worker, worker,
+                        PWLINK_POLLABLE)) {
+    case WRR_NEW_ROOT: {
+      // wakeup new poller
+      grpc_pollset_worker* new_root = worker->pollable_obj->root_worker;
+      GPR_ASSERT(new_root->initialized_cv);
+      gpr_cv_signal(&new_root->cv);
+      break;
+    }
+    case WRR_EMPTIED:
+      if (pollset->active_pollable != worker->pollable_obj) {
+        // pollable no longer being polled: flush events
+        pollable_process_events(pollset, worker->pollable_obj, true);
+      }
+      break;
+    case WRR_REMOVED:
+      break;
+  }
+  gpr_mu_unlock(&worker->pollable_obj->mu);
+  POLLABLE_UNREF(worker->pollable_obj, "pollset_worker");
+  if (worker_remove(&pollset->root_worker, worker, PWLINK_POLLSET) ==
+      WRR_EMPTIED) {
+    pollset_maybe_finish_shutdown(pollset);
+  }
+  if (worker->initialized_cv) {
+    gpr_cv_destroy(&worker->cv);
+  }
+  gpr_atm_no_barrier_fetch_add(&pollset->worker_count, -1);
+}
+
+#ifndef NDEBUG
+static long sys_gettid(void) { return syscall(__NR_gettid); }
+#endif
+
+/* pollset->mu lock must be held by the caller before calling this.
+   The function pollset_work() may temporarily release the lock (pollset->po.mu)
+   during the course of its execution but it will always re-acquire the lock and
+   ensure that it is held by the time the function returns */
+static grpc_error* pollset_work(grpc_pollset* pollset,
+                                grpc_pollset_worker** worker_hdl,
+                                grpc_millis deadline) {
+  GPR_TIMER_SCOPE("pollset_work", 0);
+#ifdef GRPC_EPOLLEX_CREATE_WORKERS_ON_HEAP
+  grpc_pollset_worker* worker =
+      (grpc_pollset_worker*)gpr_malloc(sizeof(*worker));
+#define WORKER_PTR (worker)
+#else
+  grpc_pollset_worker worker;
+#define WORKER_PTR (&worker)
+#endif
+#ifndef NDEBUG
+  WORKER_PTR->originator = sys_gettid();
+#endif
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "PS:%p work hdl=%p worker=%p now=%" PRId64 " deadline=%" PRId64
+            " kwp=%d pollable=%p",
+            pollset, worker_hdl, WORKER_PTR, grpc_core::ExecCtx::Get()->Now(),
+            deadline, pollset->kicked_without_poller, pollset->active_pollable);
+  }
+  static const char* err_desc = "pollset_work";
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (pollset->kicked_without_poller) {
+    pollset->kicked_without_poller = false;
+  } else {
+    if (begin_worker(pollset, WORKER_PTR, worker_hdl, deadline)) {
+      gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset);
+      gpr_tls_set(&g_current_thread_worker, (intptr_t)WORKER_PTR);
+      if (WORKER_PTR->pollable_obj->event_cursor ==
+          WORKER_PTR->pollable_obj->event_count) {
+        append_error(&error, pollable_epoll(WORKER_PTR->pollable_obj, deadline),
+                     err_desc);
+      }
+      append_error(
+          &error,
+          pollable_process_events(pollset, WORKER_PTR->pollable_obj, false),
+          err_desc);
+      grpc_core::ExecCtx::Get()->Flush();
+      gpr_tls_set(&g_current_thread_pollset, 0);
+      gpr_tls_set(&g_current_thread_worker, 0);
+    }
+    end_worker(pollset, WORKER_PTR, worker_hdl);
+  }
+#ifdef GRPC_EPOLLEX_CREATE_WORKERS_ON_HEAP
+  gpr_free(worker);
+#endif
+#undef WORKER_PTR
+  return error;
+}
+
+static grpc_error* pollset_transition_pollable_from_empty_to_fd_locked(
+    grpc_pollset* pollset, grpc_fd* fd) {
+  static const char* err_desc = "pollset_transition_pollable_from_empty_to_fd";
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "PS:%p add fd %p (%d); transition pollable from empty to fd",
+            pollset, fd, fd->fd);
+  }
+  append_error(&error, pollset_kick_all(pollset), err_desc);
+  POLLABLE_UNREF(pollset->active_pollable, "pollset");
+  append_error(&error, get_fd_pollable(fd, &pollset->active_pollable),
+               err_desc);
+  return error;
+}
+
+static grpc_error* pollset_transition_pollable_from_fd_to_multi_locked(
+    grpc_pollset* pollset, grpc_fd* and_add_fd) {
+  static const char* err_desc = "pollset_transition_pollable_from_fd_to_multi";
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(
+        GPR_INFO,
+        "PS:%p add fd %p (%d); transition pollable from fd %p to multipoller",
+        pollset, and_add_fd, and_add_fd ? and_add_fd->fd : -1,
+        pollset->active_pollable->owner_fd);
+  }
+  append_error(&error, pollset_kick_all(pollset), err_desc);
+  grpc_fd* initial_fd = pollset->active_pollable->owner_fd;
+  POLLABLE_UNREF(pollset->active_pollable, "pollset");
+  pollset->active_pollable = nullptr;
+  if (append_error(&error, pollable_create(PO_MULTI, &pollset->active_pollable),
+                   err_desc)) {
+    append_error(&error, pollable_add_fd(pollset->active_pollable, initial_fd),
+                 err_desc);
+    if (and_add_fd != nullptr) {
+      append_error(&error,
+                   pollable_add_fd(pollset->active_pollable, and_add_fd),
+                   err_desc);
+    }
+  }
+  return error;
+}
+
+/* expects pollsets locked, flag whether fd is locked or not */
+static grpc_error* pollset_add_fd_locked(grpc_pollset* pollset, grpc_fd* fd) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  pollable* po_at_start =
+      POLLABLE_REF(pollset->active_pollable, "pollset_add_fd");
+  switch (pollset->active_pollable->type) {
+    case PO_EMPTY:
+      /* empty pollable --> single fd pollable */
+      error = pollset_transition_pollable_from_empty_to_fd_locked(pollset, fd);
+      break;
+    case PO_FD:
+      gpr_mu_lock(&po_at_start->owner_orphan_mu);
+      if (po_at_start->owner_orphaned) {
+        error =
+            pollset_transition_pollable_from_empty_to_fd_locked(pollset, fd);
+      } else {
+        /* fd --> multipoller */
+        error =
+            pollset_transition_pollable_from_fd_to_multi_locked(pollset, fd);
+      }
+      gpr_mu_unlock(&po_at_start->owner_orphan_mu);
+      break;
+    case PO_MULTI:
+      error = pollable_add_fd(pollset->active_pollable, fd);
+      break;
+  }
+  if (error != GRPC_ERROR_NONE) {
+    POLLABLE_UNREF(pollset->active_pollable, "pollset");
+    pollset->active_pollable = po_at_start;
+  } else {
+    POLLABLE_UNREF(po_at_start, "pollset_add_fd");
+  }
+  return error;
+}
+
+static grpc_error* pollset_as_multipollable_locked(grpc_pollset* pollset,
+                                                   pollable** pollable_obj) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  pollable* po_at_start =
+      POLLABLE_REF(pollset->active_pollable, "pollset_as_multipollable");
+  switch (pollset->active_pollable->type) {
+    case PO_EMPTY:
+      POLLABLE_UNREF(pollset->active_pollable, "pollset");
+      error = pollable_create(PO_MULTI, &pollset->active_pollable);
+      /* Any workers currently polling on this pollset must now be woked up so
+       * that they can pick up the new active_pollable */
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "PS:%p active pollable transition from empty to multi",
+                pollset);
+      }
+      static const char* err_desc =
+          "pollset_as_multipollable_locked: empty -> multi";
+      append_error(&error, pollset_kick_all(pollset), err_desc);
+      break;
+    case PO_FD:
+      gpr_mu_lock(&po_at_start->owner_orphan_mu);
+      if (po_at_start->owner_orphaned) {
+        // Unlock before Unref'ing the pollable
+        gpr_mu_unlock(&po_at_start->owner_orphan_mu);
+        POLLABLE_UNREF(pollset->active_pollable, "pollset");
+        error = pollable_create(PO_MULTI, &pollset->active_pollable);
+      } else {
+        error = pollset_transition_pollable_from_fd_to_multi_locked(pollset,
+                                                                    nullptr);
+        gpr_mu_unlock(&po_at_start->owner_orphan_mu);
+      }
+      break;
+    case PO_MULTI:
+      break;
+  }
+  if (error != GRPC_ERROR_NONE) {
+    POLLABLE_UNREF(pollset->active_pollable, "pollset");
+    pollset->active_pollable = po_at_start;
+    *pollable_obj = nullptr;
+  } else {
+    *pollable_obj = POLLABLE_REF(pollset->active_pollable, "pollset_set");
+    POLLABLE_UNREF(po_at_start, "pollset_as_multipollable");
+  }
+  return error;
+}
+
+static void pollset_add_fd(grpc_pollset* pollset, grpc_fd* fd) {
+  GPR_TIMER_SCOPE("pollset_add_fd", 0);
+  gpr_mu_lock(&pollset->mu);
+  grpc_error* error = pollset_add_fd_locked(pollset, fd);
+  gpr_mu_unlock(&pollset->mu);
+  GRPC_LOG_IF_ERROR("pollset_add_fd", error);
+}
+
+/*******************************************************************************
+ * Pollset-set Definitions
+ */
+
+static grpc_pollset_set* pss_lock_adam(grpc_pollset_set* pss) {
+  gpr_mu_lock(&pss->mu);
+  while (pss->parent != nullptr) {
+    gpr_mu_unlock(&pss->mu);
+    pss = pss->parent;
+    gpr_mu_lock(&pss->mu);
+  }
+  return pss;
+}
+
+static grpc_pollset_set* pollset_set_create(void) {
+  grpc_pollset_set* pss =
+      static_cast<grpc_pollset_set*>(gpr_zalloc(sizeof(*pss)));
+  gpr_mu_init(&pss->mu);
+  gpr_ref_init(&pss->refs, 1);
+  return pss;
+}
+
+static void pollset_set_unref(grpc_pollset_set* pss) {
+  if (pss == nullptr) return;
+  if (!gpr_unref(&pss->refs)) return;
+  pollset_set_unref(pss->parent);
+  gpr_mu_destroy(&pss->mu);
+  for (size_t i = 0; i < pss->pollset_count; i++) {
+    gpr_mu_lock(&pss->pollsets[i]->mu);
+    if (0 == --pss->pollsets[i]->containing_pollset_set_count) {
+      pollset_maybe_finish_shutdown(pss->pollsets[i]);
+    }
+    gpr_mu_unlock(&pss->pollsets[i]->mu);
+  }
+  for (size_t i = 0; i < pss->fd_count; i++) {
+    UNREF_BY(pss->fds[i], 2, "pollset_set");
+  }
+  gpr_free(pss->pollsets);
+  gpr_free(pss->fds);
+  gpr_free(pss);
+}
+
+static void pollset_set_add_fd(grpc_pollset_set* pss, grpc_fd* fd) {
+  GPR_TIMER_SCOPE("pollset_set_add_fd", 0);
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "PSS:%p: add fd %p (%d)", pss, fd, fd->fd);
+  }
+  grpc_error* error = GRPC_ERROR_NONE;
+  static const char* err_desc = "pollset_set_add_fd";
+  pss = pss_lock_adam(pss);
+  for (size_t i = 0; i < pss->pollset_count; i++) {
+    append_error(&error, pollable_add_fd(pss->pollsets[i]->active_pollable, fd),
+                 err_desc);
+  }
+  if (pss->fd_count == pss->fd_capacity) {
+    pss->fd_capacity = GPR_MAX(pss->fd_capacity * 2, 8);
+    pss->fds = static_cast<grpc_fd**>(
+        gpr_realloc(pss->fds, pss->fd_capacity * sizeof(*pss->fds)));
+  }
+  REF_BY(fd, 2, "pollset_set");
+  pss->fds[pss->fd_count++] = fd;
+  gpr_mu_unlock(&pss->mu);
+
+  GRPC_LOG_IF_ERROR(err_desc, error);
+}
+
+static void pollset_set_del_fd(grpc_pollset_set* pss, grpc_fd* fd) {
+  GPR_TIMER_SCOPE("pollset_set_del_fd", 0);
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "PSS:%p: del fd %p", pss, fd);
+  }
+  pss = pss_lock_adam(pss);
+  size_t i;
+  for (i = 0; i < pss->fd_count; i++) {
+    if (pss->fds[i] == fd) {
+      UNREF_BY(fd, 2, "pollset_set");
+      break;
+    }
+  }
+  GPR_ASSERT(i != pss->fd_count);
+  for (; i < pss->fd_count - 1; i++) {
+    pss->fds[i] = pss->fds[i + 1];
+  }
+  pss->fd_count--;
+  gpr_mu_unlock(&pss->mu);
+}
+
+static void pollset_set_del_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {
+  GPR_TIMER_SCOPE("pollset_set_del_pollset", 0);
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "PSS:%p: del pollset %p", pss, ps);
+  }
+  pss = pss_lock_adam(pss);
+  size_t i;
+  for (i = 0; i < pss->pollset_count; i++) {
+    if (pss->pollsets[i] == ps) {
+      break;
+    }
+  }
+  GPR_ASSERT(i != pss->pollset_count);
+  for (; i < pss->pollset_count - 1; i++) {
+    pss->pollsets[i] = pss->pollsets[i + 1];
+  }
+  pss->pollset_count--;
+  gpr_mu_unlock(&pss->mu);
+  gpr_mu_lock(&ps->mu);
+  if (0 == --ps->containing_pollset_set_count) {
+    pollset_maybe_finish_shutdown(ps);
+  }
+  gpr_mu_unlock(&ps->mu);
+}
+
+// add all fds to pollables, and output a new array of unorphaned out_fds
+// assumes pollsets are multipollable
+static grpc_error* add_fds_to_pollsets(grpc_fd** fds, size_t fd_count,
+                                       grpc_pollset** pollsets,
+                                       size_t pollset_count,
+                                       const char* err_desc, grpc_fd** out_fds,
+                                       size_t* out_fd_count) {
+  GPR_TIMER_SCOPE("add_fds_to_pollsets", 0);
+  grpc_error* error = GRPC_ERROR_NONE;
+  for (size_t i = 0; i < fd_count; i++) {
+    gpr_mu_lock(&fds[i]->orphan_mu);
+    if ((gpr_atm_no_barrier_load(&fds[i]->refst) & 1) == 0) {
+      gpr_mu_unlock(&fds[i]->orphan_mu);
+      UNREF_BY(fds[i], 2, "pollset_set");
+    } else {
+      for (size_t j = 0; j < pollset_count; j++) {
+        append_error(&error,
+                     pollable_add_fd(pollsets[j]->active_pollable, fds[i]),
+                     err_desc);
+      }
+      gpr_mu_unlock(&fds[i]->orphan_mu);
+      out_fds[(*out_fd_count)++] = fds[i];
+    }
+  }
+  return error;
+}
+
+static void pollset_set_add_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {
+  GPR_TIMER_SCOPE("pollset_set_add_pollset", 0);
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "PSS:%p: add pollset %p", pss, ps);
+  }
+  grpc_error* error = GRPC_ERROR_NONE;
+  static const char* err_desc = "pollset_set_add_pollset";
+  pollable* pollable_obj = nullptr;
+  gpr_mu_lock(&ps->mu);
+  if (!GRPC_LOG_IF_ERROR(err_desc,
+                         pollset_as_multipollable_locked(ps, &pollable_obj))) {
+    GPR_ASSERT(pollable_obj == nullptr);
+    gpr_mu_unlock(&ps->mu);
+    return;
+  }
+  ps->containing_pollset_set_count++;
+  gpr_mu_unlock(&ps->mu);
+  pss = pss_lock_adam(pss);
+  size_t initial_fd_count = pss->fd_count;
+  pss->fd_count = 0;
+  append_error(&error,
+               add_fds_to_pollsets(pss->fds, initial_fd_count, &ps, 1, err_desc,
+                                   pss->fds, &pss->fd_count),
+               err_desc);
+  if (pss->pollset_count == pss->pollset_capacity) {
+    pss->pollset_capacity = GPR_MAX(pss->pollset_capacity * 2, 8);
+    pss->pollsets = static_cast<grpc_pollset**>(gpr_realloc(
+        pss->pollsets, pss->pollset_capacity * sizeof(*pss->pollsets)));
+  }
+  pss->pollsets[pss->pollset_count++] = ps;
+  gpr_mu_unlock(&pss->mu);
+  POLLABLE_UNREF(pollable_obj, "pollset_set");
+
+  GRPC_LOG_IF_ERROR(err_desc, error);
+}
+
+static void pollset_set_add_pollset_set(grpc_pollset_set* a,
+                                        grpc_pollset_set* b) {
+  GPR_TIMER_SCOPE("pollset_set_add_pollset_set", 0);
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "PSS: merge (%p, %p)", a, b);
+  }
+  grpc_error* error = GRPC_ERROR_NONE;
+  static const char* err_desc = "pollset_set_add_fd";
+  for (;;) {
+    if (a == b) {
+      // pollset ancestors are the same: nothing to do
+      return;
+    }
+    if (a > b) {
+      GPR_SWAP(grpc_pollset_set*, a, b);
+    }
+    gpr_mu* a_mu = &a->mu;
+    gpr_mu* b_mu = &b->mu;
+    gpr_mu_lock(a_mu);
+    gpr_mu_lock(b_mu);
+    if (a->parent != nullptr) {
+      a = a->parent;
+    } else if (b->parent != nullptr) {
+      b = b->parent;
+    } else {
+      break;  // exit loop, both pollsets locked
+    }
+    gpr_mu_unlock(a_mu);
+    gpr_mu_unlock(b_mu);
+  }
+  // try to do the least copying possible
+  // TODO(sreek): there's probably a better heuristic here
+  const size_t a_size = a->fd_count + a->pollset_count;
+  const size_t b_size = b->fd_count + b->pollset_count;
+  if (b_size > a_size) {
+    GPR_SWAP(grpc_pollset_set*, a, b);
+  }
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_INFO, "PSS: parent %p to %p", b, a);
+  }
+  gpr_ref(&a->refs);
+  b->parent = a;
+  if (a->fd_capacity < a->fd_count + b->fd_count) {
+    a->fd_capacity = GPR_MAX(2 * a->fd_capacity, a->fd_count + b->fd_count);
+    a->fds = static_cast<grpc_fd**>(
+        gpr_realloc(a->fds, a->fd_capacity * sizeof(*a->fds)));
+  }
+  size_t initial_a_fd_count = a->fd_count;
+  a->fd_count = 0;
+  append_error(
+      &error,
+      add_fds_to_pollsets(a->fds, initial_a_fd_count, b->pollsets,
+                          b->pollset_count, "merge_a2b", a->fds, &a->fd_count),
+      err_desc);
+  append_error(
+      &error,
+      add_fds_to_pollsets(b->fds, b->fd_count, a->pollsets, a->pollset_count,
+                          "merge_b2a", a->fds, &a->fd_count),
+      err_desc);
+  if (a->pollset_capacity < a->pollset_count + b->pollset_count) {
+    a->pollset_capacity =
+        GPR_MAX(2 * a->pollset_capacity, a->pollset_count + b->pollset_count);
+    a->pollsets = static_cast<grpc_pollset**>(
+        gpr_realloc(a->pollsets, a->pollset_capacity * sizeof(*a->pollsets)));
+  }
+  if (b->pollset_count > 0) {
+    memcpy(a->pollsets + a->pollset_count, b->pollsets,
+           b->pollset_count * sizeof(*b->pollsets));
+  }
+  a->pollset_count += b->pollset_count;
+  gpr_free(b->fds);
+  gpr_free(b->pollsets);
+  b->fds = nullptr;
+  b->pollsets = nullptr;
+  b->fd_count = b->fd_capacity = b->pollset_count = b->pollset_capacity = 0;
+  gpr_mu_unlock(&a->mu);
+  gpr_mu_unlock(&b->mu);
+}
+
+static void pollset_set_del_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {}
+
+/*******************************************************************************
+ * Event engine binding
+ */
+
+static void shutdown_background_closure(void) {}
+
+static void shutdown_engine(void) {
+  fd_global_shutdown();
+  pollset_global_shutdown();
+}
+
+static const grpc_event_engine_vtable vtable = {
+    sizeof(grpc_pollset),
+    true,
+    false,
+
+    fd_create,
+    fd_wrapped_fd,
+    fd_orphan,
+    fd_shutdown,
+    fd_notify_on_read,
+    fd_notify_on_write,
+    fd_notify_on_error,
+    fd_become_readable,
+    fd_become_writable,
+    fd_has_errors,
+    fd_is_shutdown,
+
+    pollset_init,
+    pollset_shutdown,
+    pollset_destroy,
+    pollset_work,
+    pollset_kick,
+    pollset_add_fd,
+
+    pollset_set_create,
+    pollset_set_unref,  // destroy ==> unref 1 public ref
+    pollset_set_add_pollset,
+    pollset_set_del_pollset,
+    pollset_set_add_pollset_set,
+    pollset_set_del_pollset_set,
+    pollset_set_add_fd,
+    pollset_set_del_fd,
+
+    shutdown_background_closure,
+    shutdown_engine,
+};
+
+const grpc_event_engine_vtable* grpc_init_epollex_linux(
+    bool explicitly_requested) {
+  if (!grpc_has_wakeup_fd()) {
+    gpr_log(GPR_ERROR, "Skipping epollex because of no wakeup fd.");
+    return nullptr;
+  }
+
+  if (!grpc_is_epollexclusive_available()) {
+    gpr_log(GPR_INFO, "Skipping epollex because it is not supported.");
+    return nullptr;
+  }
+
+  fd_global_init();
+
+  if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
+    pollset_global_shutdown();
+    fd_global_shutdown();
+    return nullptr;
+  }
+
+  return &vtable;
+}
+
+#else /* defined(GRPC_LINUX_EPOLL_CREATE1) */
+#if defined(GRPC_POSIX_SOCKET_EV_EPOLLEX)
+#include "src/core/lib/iomgr/ev_epollex_linux.h"
+/* If GRPC_LINUX_EPOLL_CREATE1 is not defined, it means
+   epoll_create1 is not available. Return NULL */
+const grpc_event_engine_vtable* grpc_init_epollex_linux(
+    bool explicitly_requested) {
+  return nullptr;
+}
+#endif /* defined(GRPC_POSIX_SOCKET_EV_EPOLLEX) */
+
+#endif /* !defined(GRPC_LINUX_EPOLL_CREATE1) */
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.h b/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.h
new file mode 100644
index 0000000..e70ba72
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H
+#define GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/port.h"
+
+const grpc_event_engine_vtable* grpc_init_epollex_linux(
+    bool explicitly_requested);
+
+#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.cc b/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.cc
new file mode 100644
index 0000000..67cbfbb
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.cc
@@ -0,0 +1,1883 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_EV_POLL
+
+#include "src/core/lib/iomgr/ev_poll_posix.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/murmur_hash.h"
+#include "src/core/lib/gpr/tls.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/block_annotate.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/wakeup_fd_cv.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/profiling/timers.h"
+
+#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1)
+
+/*******************************************************************************
+ * FD declarations
+ */
+typedef struct grpc_fd_watcher {
+  struct grpc_fd_watcher* next;
+  struct grpc_fd_watcher* prev;
+  grpc_pollset* pollset;
+  grpc_pollset_worker* worker;
+  grpc_fd* fd;
+} grpc_fd_watcher;
+
+typedef struct grpc_cached_wakeup_fd grpc_cached_wakeup_fd;
+
+/* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */
+struct grpc_fork_fd_list {
+  /* Only one of fd or cached_wakeup_fd will be set. The unused field will be
+  set to nullptr. */
+  grpc_fd* fd;
+  grpc_cached_wakeup_fd* cached_wakeup_fd;
+
+  grpc_fork_fd_list* next;
+  grpc_fork_fd_list* prev;
+};
+
+struct grpc_fd {
+  int fd;
+  /* refst format:
+     bit0:   1=active/0=orphaned
+     bit1-n: refcount
+     meaning that mostly we ref by two to avoid altering the orphaned bit,
+     and just unref by 1 when we're ready to flag the object as orphaned */
+  gpr_atm refst;
+
+  gpr_mu mu;
+  int shutdown;
+  int closed;
+  int released;
+  gpr_atm pollhup;
+  grpc_error* shutdown_error;
+
+  /* The watcher list.
+
+     The following watcher related fields are protected by watcher_mu.
+
+     An fd_watcher is an ephemeral object created when an fd wants to
+     begin polling, and destroyed after the poll.
+
+     It denotes the fd's interest in whether to read poll or write poll
+     or both or neither on this fd.
+
+     If a watcher is asked to poll for reads or writes, the read_watcher
+     or write_watcher fields are set respectively. A watcher may be asked
+     to poll for both, in which case both fields will be set.
+
+     read_watcher and write_watcher may be NULL if no watcher has been
+     asked to poll for reads or writes.
+
+     If an fd_watcher is not asked to poll for reads or writes, it's added
+     to a linked list of inactive watchers, rooted at inactive_watcher_root.
+     If at a later time there becomes need of a poller to poll, one of
+     the inactive pollers may be kicked out of their poll loops to take
+     that responsibility. */
+  grpc_fd_watcher inactive_watcher_root;
+  grpc_fd_watcher* read_watcher;
+  grpc_fd_watcher* write_watcher;
+
+  grpc_closure* read_closure;
+  grpc_closure* write_closure;
+
+  grpc_closure* on_done_closure;
+
+  grpc_iomgr_object iomgr_object;
+
+  /* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */
+  grpc_fork_fd_list* fork_fd_list;
+};
+
+/* True when GRPC_ENABLE_FORK_SUPPORT=1. We do not support fork with poll-cv */
+static bool track_fds_for_fork = false;
+
+/* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */
+static grpc_fork_fd_list* fork_fd_list_head = nullptr;
+static gpr_mu fork_fd_list_mu;
+
+/* Begin polling on an fd.
+   Registers that the given pollset is interested in this fd - so that if read
+   or writability interest changes, the pollset can be kicked to pick up that
+   new interest.
+   Return value is:
+     (fd_needs_read? read_mask : 0) | (fd_needs_write? write_mask : 0)
+   i.e. a combination of read_mask and write_mask determined by the fd's current
+   interest in said events.
+   Polling strategies that do not need to alter their behavior depending on the
+   fd's current interest (such as epoll) do not need to call this function.
+   MUST NOT be called with a pollset lock taken */
+static uint32_t fd_begin_poll(grpc_fd* fd, grpc_pollset* pollset,
+                              grpc_pollset_worker* worker, uint32_t read_mask,
+                              uint32_t write_mask, grpc_fd_watcher* rec);
+/* Complete polling previously started with fd_begin_poll
+   MUST NOT be called with a pollset lock taken
+   if got_read or got_write are 1, also does the become_{readable,writable} as
+   appropriate. */
+static void fd_end_poll(grpc_fd_watcher* rec, int got_read, int got_write);
+
+/* Return 1 if this fd is orphaned, 0 otherwise */
+static bool fd_is_orphaned(grpc_fd* fd);
+
+#ifndef NDEBUG
+static void fd_ref(grpc_fd* fd, const char* reason, const char* file, int line);
+static void fd_unref(grpc_fd* fd, const char* reason, const char* file,
+                     int line);
+#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__)
+#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__)
+#else
+static void fd_ref(grpc_fd* fd);
+static void fd_unref(grpc_fd* fd);
+#define GRPC_FD_REF(fd, reason) fd_ref(fd)
+#define GRPC_FD_UNREF(fd, reason) fd_unref(fd)
+#endif
+
+#define CLOSURE_NOT_READY ((grpc_closure*)0)
+#define CLOSURE_READY ((grpc_closure*)1)
+
+/*******************************************************************************
+ * pollset declarations
+ */
+
+typedef struct grpc_cached_wakeup_fd {
+  grpc_wakeup_fd fd;
+  struct grpc_cached_wakeup_fd* next;
+
+  /* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */
+  grpc_fork_fd_list* fork_fd_list;
+} grpc_cached_wakeup_fd;
+
+struct grpc_pollset_worker {
+  grpc_cached_wakeup_fd* wakeup_fd;
+  int reevaluate_polling_on_wakeup;
+  int kicked_specifically;
+  struct grpc_pollset_worker* next;
+  struct grpc_pollset_worker* prev;
+};
+
+struct grpc_pollset {
+  gpr_mu mu;
+  grpc_pollset_worker root_worker;
+  int shutting_down;
+  int called_shutdown;
+  int kicked_without_pollers;
+  grpc_closure* shutdown_done;
+  int pollset_set_count;
+  /* all polled fds */
+  size_t fd_count;
+  size_t fd_capacity;
+  grpc_fd** fds;
+  /* Local cache of eventfds for workers */
+  grpc_cached_wakeup_fd* local_wakeup_cache;
+};
+
+/* Add an fd to a pollset */
+static void pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd);
+
+static void pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd);
+
+/* Convert a timespec to milliseconds:
+   - very small or negative poll times are clamped to zero to do a
+     non-blocking poll (which becomes spin polling)
+   - other small values are rounded up to one millisecond
+   - longer than a millisecond polls are rounded up to the next nearest
+     millisecond to avoid spinning
+   - infinite timeouts are converted to -1 */
+static int poll_deadline_to_millis_timeout(grpc_millis deadline);
+
+/* Allow kick to wakeup the currently polling worker */
+#define GRPC_POLLSET_CAN_KICK_SELF 1
+/* Force the wakee to repoll when awoken */
+#define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2
+/* As per pollset_kick, with an extended set of flags (defined above)
+   -- mostly for fd_posix's use. */
+static grpc_error* pollset_kick_ext(grpc_pollset* p,
+                                    grpc_pollset_worker* specific_worker,
+                                    uint32_t flags) GRPC_MUST_USE_RESULT;
+
+/* Return 1 if the pollset has active threads in pollset_work (pollset must
+ * be locked) */
+static bool pollset_has_workers(grpc_pollset* pollset);
+
+/*******************************************************************************
+ * pollset_set definitions
+ */
+
+struct grpc_pollset_set {
+  gpr_mu mu;
+
+  size_t pollset_count;
+  size_t pollset_capacity;
+  grpc_pollset** pollsets;
+
+  size_t pollset_set_count;
+  size_t pollset_set_capacity;
+  struct grpc_pollset_set** pollset_sets;
+
+  size_t fd_count;
+  size_t fd_capacity;
+  grpc_fd** fds;
+};
+
+/*******************************************************************************
+ * condition variable polling definitions
+ */
+
+#define POLLCV_THREAD_GRACE_MS 1000
+#define CV_POLL_PERIOD_MS 1000
+#define CV_DEFAULT_TABLE_SIZE 16
+
+typedef struct poll_result {
+  gpr_refcount refcount;
+  grpc_cv_node* watchers;
+  int watchcount;
+  struct pollfd* fds;
+  nfds_t nfds;
+  int retval;
+  int err;
+  int completed;
+} poll_result;
+
+typedef struct poll_args {
+  grpc_core::Thread poller_thd;
+  gpr_cv trigger;
+  int trigger_set;
+  bool harvestable;
+  gpr_cv harvest;
+  bool joinable;
+  gpr_cv join;
+  struct pollfd* fds;
+  nfds_t nfds;
+  poll_result* result;
+  struct poll_args* next;
+  struct poll_args* prev;
+} poll_args;
+
+// This is a 2-tiered cache, we mantain a hash table
+// of active poll calls, so we can wait on the result
+// of that call.  We also maintain freelists of inactive
+// poll args and of dead poller threads.
+typedef struct poll_hash_table {
+  poll_args* free_pollers;
+  poll_args** active_pollers;
+  poll_args* dead_pollers;
+  unsigned int size;
+  unsigned int count;
+} poll_hash_table;
+
+// TODO(kpayson64): Eliminate use of global non-POD variables
+poll_hash_table poll_cache;
+grpc_cv_fd_table g_cvfds;
+
+/*******************************************************************************
+ * functions to track opened fds. No-ops unless track_fds_for_fork is true.
+ */
+
+static void fork_fd_list_remove_node(grpc_fork_fd_list* node) {
+  if (track_fds_for_fork) {
+    gpr_mu_lock(&fork_fd_list_mu);
+    if (fork_fd_list_head == node) {
+      fork_fd_list_head = node->next;
+    }
+    if (node->prev != nullptr) {
+      node->prev->next = node->next;
+    }
+    if (node->next != nullptr) {
+      node->next->prev = node->prev;
+    }
+    gpr_free(node);
+    gpr_mu_unlock(&fork_fd_list_mu);
+  }
+}
+
+static void fork_fd_list_add_node(grpc_fork_fd_list* node) {
+  gpr_mu_lock(&fork_fd_list_mu);
+  node->next = fork_fd_list_head;
+  node->prev = nullptr;
+  if (fork_fd_list_head != nullptr) {
+    fork_fd_list_head->prev = node;
+  }
+  fork_fd_list_head = node;
+  gpr_mu_unlock(&fork_fd_list_mu);
+}
+
+static void fork_fd_list_add_grpc_fd(grpc_fd* fd) {
+  if (track_fds_for_fork) {
+    fd->fork_fd_list =
+        static_cast<grpc_fork_fd_list*>(gpr_malloc(sizeof(grpc_fork_fd_list)));
+    fd->fork_fd_list->fd = fd;
+    fd->fork_fd_list->cached_wakeup_fd = nullptr;
+    fork_fd_list_add_node(fd->fork_fd_list);
+  }
+}
+
+static void fork_fd_list_add_wakeup_fd(grpc_cached_wakeup_fd* fd) {
+  if (track_fds_for_fork) {
+    fd->fork_fd_list =
+        static_cast<grpc_fork_fd_list*>(gpr_malloc(sizeof(grpc_fork_fd_list)));
+    fd->fork_fd_list->cached_wakeup_fd = fd;
+    fd->fork_fd_list->fd = nullptr;
+    fork_fd_list_add_node(fd->fork_fd_list);
+  }
+}
+
+  /*******************************************************************************
+   * fd_posix.c
+   */
+
+#ifndef NDEBUG
+#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__)
+#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
+static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
+                   int line) {
+  if (grpc_trace_fd_refcount.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
+            fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
+            gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line);
+  }
+#else
+#define REF_BY(fd, n, reason) ref_by(fd, n)
+#define UNREF_BY(fd, n, reason) unref_by(fd, n)
+static void ref_by(grpc_fd* fd, int n) {
+#endif
+  GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0);
+}
+
+#ifndef NDEBUG
+static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file,
+                     int line) {
+  if (grpc_trace_fd_refcount.enabled()) {
+    gpr_log(GPR_DEBUG,
+            "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
+            fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
+            gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line);
+  }
+#else
+static void unref_by(grpc_fd* fd, int n) {
+#endif
+  gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n);
+  if (old == n) {
+    gpr_mu_destroy(&fd->mu);
+    grpc_iomgr_unregister_object(&fd->iomgr_object);
+    fork_fd_list_remove_node(fd->fork_fd_list);
+    if (fd->shutdown) GRPC_ERROR_UNREF(fd->shutdown_error);
+    gpr_free(fd);
+  } else {
+    GPR_ASSERT(old > n);
+  }
+}
+
+static grpc_fd* fd_create(int fd, const char* name, bool track_err) {
+  GPR_DEBUG_ASSERT(track_err == false);
+  grpc_fd* r = static_cast<grpc_fd*>(gpr_malloc(sizeof(*r)));
+  gpr_mu_init(&r->mu);
+  gpr_atm_rel_store(&r->refst, 1);
+  r->shutdown = 0;
+  r->read_closure = CLOSURE_NOT_READY;
+  r->write_closure = CLOSURE_NOT_READY;
+  r->fd = fd;
+  r->inactive_watcher_root.next = r->inactive_watcher_root.prev =
+      &r->inactive_watcher_root;
+  r->read_watcher = r->write_watcher = nullptr;
+  r->on_done_closure = nullptr;
+  r->closed = 0;
+  r->released = 0;
+  gpr_atm_no_barrier_store(&r->pollhup, 0);
+
+  char* name2;
+  gpr_asprintf(&name2, "%s fd=%d", name, fd);
+  grpc_iomgr_register_object(&r->iomgr_object, name2);
+  gpr_free(name2);
+  fork_fd_list_add_grpc_fd(r);
+  return r;
+}
+
+static bool fd_is_orphaned(grpc_fd* fd) {
+  return (gpr_atm_acq_load(&fd->refst) & 1) == 0;
+}
+
+static grpc_error* pollset_kick_locked(grpc_fd_watcher* watcher) {
+  gpr_mu_lock(&watcher->pollset->mu);
+  GPR_ASSERT(watcher->worker);
+  grpc_error* err = pollset_kick_ext(watcher->pollset, watcher->worker,
+                                     GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
+  gpr_mu_unlock(&watcher->pollset->mu);
+  return err;
+}
+
+static void maybe_wake_one_watcher_locked(grpc_fd* fd) {
+  if (fd->inactive_watcher_root.next != &fd->inactive_watcher_root) {
+    pollset_kick_locked(fd->inactive_watcher_root.next);
+  } else if (fd->read_watcher) {
+    pollset_kick_locked(fd->read_watcher);
+  } else if (fd->write_watcher) {
+    pollset_kick_locked(fd->write_watcher);
+  }
+}
+
+static void wake_all_watchers_locked(grpc_fd* fd) {
+  grpc_fd_watcher* watcher;
+  for (watcher = fd->inactive_watcher_root.next;
+       watcher != &fd->inactive_watcher_root; watcher = watcher->next) {
+    pollset_kick_locked(watcher);
+  }
+  if (fd->read_watcher) {
+    pollset_kick_locked(fd->read_watcher);
+  }
+  if (fd->write_watcher && fd->write_watcher != fd->read_watcher) {
+    pollset_kick_locked(fd->write_watcher);
+  }
+}
+
+static int has_watchers(grpc_fd* fd) {
+  return fd->read_watcher != nullptr || fd->write_watcher != nullptr ||
+         fd->inactive_watcher_root.next != &fd->inactive_watcher_root;
+}
+
+static void close_fd_locked(grpc_fd* fd) {
+  fd->closed = 1;
+  if (!fd->released) {
+    close(fd->fd);
+  }
+  GRPC_CLOSURE_SCHED(fd->on_done_closure, GRPC_ERROR_NONE);
+}
+
+static int fd_wrapped_fd(grpc_fd* fd) {
+  if (fd->released || fd->closed) {
+    return -1;
+  } else {
+    return fd->fd;
+  }
+}
+
+static void fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd,
+                      const char* reason) {
+  fd->on_done_closure = on_done;
+  fd->released = release_fd != nullptr;
+  if (release_fd != nullptr) {
+    *release_fd = fd->fd;
+    fd->released = true;
+  }
+  gpr_mu_lock(&fd->mu);
+  REF_BY(fd, 1, reason); /* remove active status, but keep referenced */
+  if (!has_watchers(fd)) {
+    close_fd_locked(fd);
+  } else {
+    wake_all_watchers_locked(fd);
+  }
+  gpr_mu_unlock(&fd->mu);
+  UNREF_BY(fd, 2, reason); /* drop the reference */
+}
+
+/* increment refcount by two to avoid changing the orphan bit */
+#ifndef NDEBUG
+static void fd_ref(grpc_fd* fd, const char* reason, const char* file,
+                   int line) {
+  ref_by(fd, 2, reason, file, line);
+}
+
+static void fd_unref(grpc_fd* fd, const char* reason, const char* file,
+                     int line) {
+  unref_by(fd, 2, reason, file, line);
+}
+#else
+static void fd_ref(grpc_fd* fd) { ref_by(fd, 2); }
+
+static void fd_unref(grpc_fd* fd) { unref_by(fd, 2); }
+#endif
+
+static grpc_error* fd_shutdown_error(grpc_fd* fd) {
+  if (!fd->shutdown) {
+    return GRPC_ERROR_NONE;
+  } else {
+    return grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                                  "FD shutdown", &fd->shutdown_error, 1),
+                              GRPC_ERROR_INT_GRPC_STATUS,
+                              GRPC_STATUS_UNAVAILABLE);
+  }
+}
+
+static void notify_on_locked(grpc_fd* fd, grpc_closure** st,
+                             grpc_closure* closure) {
+  if (fd->shutdown || gpr_atm_no_barrier_load(&fd->pollhup)) {
+    GRPC_CLOSURE_SCHED(
+        closure, grpc_error_set_int(
+                     GRPC_ERROR_CREATE_FROM_STATIC_STRING("FD shutdown"),
+                     GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE));
+  } else if (*st == CLOSURE_NOT_READY) {
+    /* not ready ==> switch to a waiting state by setting the closure */
+    *st = closure;
+  } else if (*st == CLOSURE_READY) {
+    /* already ready ==> queue the closure to run immediately */
+    *st = CLOSURE_NOT_READY;
+    GRPC_CLOSURE_SCHED(closure, fd_shutdown_error(fd));
+    maybe_wake_one_watcher_locked(fd);
+  } else {
+    /* upcallptr was set to a different closure.  This is an error! */
+    gpr_log(GPR_ERROR,
+            "User called a notify_on function with a previous callback still "
+            "pending");
+    abort();
+  }
+}
+
+/* returns 1 if state becomes not ready */
+static int set_ready_locked(grpc_fd* fd, grpc_closure** st) {
+  if (*st == CLOSURE_READY) {
+    /* duplicate ready ==> ignore */
+    return 0;
+  } else if (*st == CLOSURE_NOT_READY) {
+    /* not ready, and not waiting ==> flag ready */
+    *st = CLOSURE_READY;
+    return 0;
+  } else {
+    /* waiting ==> queue closure */
+    GRPC_CLOSURE_SCHED(*st, fd_shutdown_error(fd));
+    *st = CLOSURE_NOT_READY;
+    return 1;
+  }
+}
+
+static void fd_shutdown(grpc_fd* fd, grpc_error* why) {
+  gpr_mu_lock(&fd->mu);
+  /* only shutdown once */
+  if (!fd->shutdown) {
+    fd->shutdown = 1;
+    fd->shutdown_error = why;
+    /* signal read/write closed to OS so that future operations fail */
+    shutdown(fd->fd, SHUT_RDWR);
+    set_ready_locked(fd, &fd->read_closure);
+    set_ready_locked(fd, &fd->write_closure);
+  } else {
+    GRPC_ERROR_UNREF(why);
+  }
+  gpr_mu_unlock(&fd->mu);
+}
+
+static bool fd_is_shutdown(grpc_fd* fd) {
+  gpr_mu_lock(&fd->mu);
+  bool r = fd->shutdown;
+  gpr_mu_unlock(&fd->mu);
+  return r;
+}
+
+static void fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) {
+  gpr_mu_lock(&fd->mu);
+  notify_on_locked(fd, &fd->read_closure, closure);
+  gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) {
+  gpr_mu_lock(&fd->mu);
+  notify_on_locked(fd, &fd->write_closure, closure);
+  gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) {
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_ERROR, "Polling engine does not support tracking errors.");
+  }
+  GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_CANCELLED);
+}
+
+static void fd_set_readable(grpc_fd* fd) {
+  gpr_mu_lock(&fd->mu);
+  set_ready_locked(fd, &fd->read_closure);
+  gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_set_writable(grpc_fd* fd) {
+  gpr_mu_lock(&fd->mu);
+  set_ready_locked(fd, &fd->write_closure);
+  gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_set_error(grpc_fd* fd) {
+  if (grpc_polling_trace.enabled()) {
+    gpr_log(GPR_ERROR, "Polling engine does not support tracking errors.");
+  }
+}
+
+static uint32_t fd_begin_poll(grpc_fd* fd, grpc_pollset* pollset,
+                              grpc_pollset_worker* worker, uint32_t read_mask,
+                              uint32_t write_mask, grpc_fd_watcher* watcher) {
+  uint32_t mask = 0;
+  grpc_closure* cur;
+  int requested;
+  /* keep track of pollers that have requested our events, in case they change
+   */
+  GRPC_FD_REF(fd, "poll");
+
+  gpr_mu_lock(&fd->mu);
+
+  /* if we are shutdown, then don't add to the watcher set */
+  if (fd->shutdown) {
+    watcher->fd = nullptr;
+    watcher->pollset = nullptr;
+    watcher->worker = nullptr;
+    gpr_mu_unlock(&fd->mu);
+    GRPC_FD_UNREF(fd, "poll");
+    return 0;
+  }
+
+  /* if there is nobody polling for read, but we need to, then start doing so */
+  cur = fd->read_closure;
+  requested = cur != CLOSURE_READY;
+  if (read_mask && fd->read_watcher == nullptr && requested) {
+    fd->read_watcher = watcher;
+    mask |= read_mask;
+  }
+  /* if there is nobody polling for write, but we need to, then start doing so
+   */
+  cur = fd->write_closure;
+  requested = cur != CLOSURE_READY;
+  if (write_mask && fd->write_watcher == nullptr && requested) {
+    fd->write_watcher = watcher;
+    mask |= write_mask;
+  }
+  /* if not polling, remember this watcher in case we need someone to later */
+  if (mask == 0 && worker != nullptr) {
+    watcher->next = &fd->inactive_watcher_root;
+    watcher->prev = watcher->next->prev;
+    watcher->next->prev = watcher->prev->next = watcher;
+  }
+  watcher->pollset = pollset;
+  watcher->worker = worker;
+  watcher->fd = fd;
+  gpr_mu_unlock(&fd->mu);
+
+  return mask;
+}
+
+static void fd_end_poll(grpc_fd_watcher* watcher, int got_read, int got_write) {
+  int was_polling = 0;
+  int kick = 0;
+  grpc_fd* fd = watcher->fd;
+
+  if (fd == nullptr) {
+    return;
+  }
+
+  gpr_mu_lock(&fd->mu);
+
+  if (watcher == fd->read_watcher) {
+    /* remove read watcher, kick if we still need a read */
+    was_polling = 1;
+    if (!got_read) {
+      kick = 1;
+    }
+    fd->read_watcher = nullptr;
+  }
+  if (watcher == fd->write_watcher) {
+    /* remove write watcher, kick if we still need a write */
+    was_polling = 1;
+    if (!got_write) {
+      kick = 1;
+    }
+    fd->write_watcher = nullptr;
+  }
+  if (!was_polling && watcher->worker != nullptr) {
+    /* remove from inactive list */
+    watcher->next->prev = watcher->prev;
+    watcher->prev->next = watcher->next;
+  }
+  if (got_read) {
+    if (set_ready_locked(fd, &fd->read_closure)) {
+      kick = 1;
+    }
+  }
+  if (got_write) {
+    if (set_ready_locked(fd, &fd->write_closure)) {
+      kick = 1;
+    }
+  }
+  if (kick) {
+    maybe_wake_one_watcher_locked(fd);
+  }
+  if (fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) {
+    close_fd_locked(fd);
+  }
+  gpr_mu_unlock(&fd->mu);
+
+  GRPC_FD_UNREF(fd, "poll");
+}
+
+/*******************************************************************************
+ * pollset_posix.c
+ */
+
+GPR_TLS_DECL(g_current_thread_poller);
+GPR_TLS_DECL(g_current_thread_worker);
+
+static void remove_worker(grpc_pollset* p, grpc_pollset_worker* worker) {
+  worker->prev->next = worker->next;
+  worker->next->prev = worker->prev;
+}
+
+static bool pollset_has_workers(grpc_pollset* p) {
+  return p->root_worker.next != &p->root_worker;
+}
+
+static bool pollset_in_pollset_sets(grpc_pollset* p) {
+  return p->pollset_set_count;
+}
+
+static bool pollset_has_observers(grpc_pollset* p) {
+  return pollset_has_workers(p) || pollset_in_pollset_sets(p);
+}
+
+static grpc_pollset_worker* pop_front_worker(grpc_pollset* p) {
+  if (pollset_has_workers(p)) {
+    grpc_pollset_worker* w = p->root_worker.next;
+    remove_worker(p, w);
+    return w;
+  } else {
+    return nullptr;
+  }
+}
+
+static void push_back_worker(grpc_pollset* p, grpc_pollset_worker* worker) {
+  worker->next = &p->root_worker;
+  worker->prev = worker->next->prev;
+  worker->prev->next = worker->next->prev = worker;
+}
+
+static void push_front_worker(grpc_pollset* p, grpc_pollset_worker* worker) {
+  worker->prev = &p->root_worker;
+  worker->next = worker->prev->next;
+  worker->prev->next = worker->next->prev = worker;
+}
+
+static void kick_append_error(grpc_error** composite, grpc_error* error) {
+  if (error == GRPC_ERROR_NONE) return;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Kick Failure");
+  }
+  *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error* pollset_kick_ext(grpc_pollset* p,
+                                    grpc_pollset_worker* specific_worker,
+                                    uint32_t flags) {
+  GPR_TIMER_SCOPE("pollset_kick_ext", 0);
+  grpc_error* error = GRPC_ERROR_NONE;
+  GRPC_STATS_INC_POLLSET_KICK();
+
+  /* pollset->mu already held */
+  if (specific_worker != nullptr) {
+    if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) {
+      GPR_TIMER_SCOPE("pollset_kick_ext.broadcast", 0);
+      GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0);
+      for (specific_worker = p->root_worker.next;
+           specific_worker != &p->root_worker;
+           specific_worker = specific_worker->next) {
+        kick_append_error(
+            &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
+      }
+      p->kicked_without_pollers = true;
+    } else if (gpr_tls_get(&g_current_thread_worker) !=
+               (intptr_t)specific_worker) {
+      GPR_TIMER_MARK("different_thread_worker", 0);
+      if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
+        specific_worker->reevaluate_polling_on_wakeup = true;
+      }
+      specific_worker->kicked_specifically = true;
+      kick_append_error(&error,
+                        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
+    } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) {
+      GPR_TIMER_MARK("kick_yoself", 0);
+      if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
+        specific_worker->reevaluate_polling_on_wakeup = true;
+      }
+      specific_worker->kicked_specifically = true;
+      kick_append_error(&error,
+                        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
+    }
+  } else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) {
+    GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0);
+    GPR_TIMER_MARK("kick_anonymous", 0);
+    specific_worker = pop_front_worker(p);
+    if (specific_worker != nullptr) {
+      if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) {
+        GPR_TIMER_MARK("kick_anonymous_not_self", 0);
+        push_back_worker(p, specific_worker);
+        specific_worker = pop_front_worker(p);
+        if ((flags & GRPC_POLLSET_CAN_KICK_SELF) == 0 &&
+            gpr_tls_get(&g_current_thread_worker) ==
+                (intptr_t)specific_worker) {
+          push_back_worker(p, specific_worker);
+          specific_worker = nullptr;
+        }
+      }
+      if (specific_worker != nullptr) {
+        GPR_TIMER_MARK("finally_kick", 0);
+        push_back_worker(p, specific_worker);
+        kick_append_error(
+            &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
+      }
+    } else {
+      GPR_TIMER_MARK("kicked_no_pollers", 0);
+      p->kicked_without_pollers = true;
+    }
+  }
+
+  GRPC_LOG_IF_ERROR("pollset_kick_ext", GRPC_ERROR_REF(error));
+  return error;
+}
+
+static grpc_error* pollset_kick(grpc_pollset* p,
+                                grpc_pollset_worker* specific_worker) {
+  return pollset_kick_ext(p, specific_worker, 0);
+}
+
+/* global state management */
+
+static grpc_error* pollset_global_init(void) {
+  gpr_tls_init(&g_current_thread_poller);
+  gpr_tls_init(&g_current_thread_worker);
+  return GRPC_ERROR_NONE;
+}
+
+static void pollset_global_shutdown(void) {
+  gpr_tls_destroy(&g_current_thread_poller);
+  gpr_tls_destroy(&g_current_thread_worker);
+}
+
+/* main interface */
+
+static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
+  gpr_mu_init(&pollset->mu);
+  *mu = &pollset->mu;
+  pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker;
+  pollset->shutting_down = 0;
+  pollset->called_shutdown = 0;
+  pollset->kicked_without_pollers = 0;
+  pollset->local_wakeup_cache = nullptr;
+  pollset->kicked_without_pollers = 0;
+  pollset->fd_count = 0;
+  pollset->fd_capacity = 0;
+  pollset->fds = nullptr;
+  pollset->pollset_set_count = 0;
+}
+
+static void pollset_destroy(grpc_pollset* pollset) {
+  GPR_ASSERT(!pollset_has_workers(pollset));
+  while (pollset->local_wakeup_cache) {
+    grpc_cached_wakeup_fd* next = pollset->local_wakeup_cache->next;
+    fork_fd_list_remove_node(pollset->local_wakeup_cache->fork_fd_list);
+    grpc_wakeup_fd_destroy(&pollset->local_wakeup_cache->fd);
+    gpr_free(pollset->local_wakeup_cache);
+    pollset->local_wakeup_cache = next;
+  }
+  gpr_free(pollset->fds);
+  gpr_mu_destroy(&pollset->mu);
+}
+
+static void pollset_add_fd(grpc_pollset* pollset, grpc_fd* fd) {
+  gpr_mu_lock(&pollset->mu);
+  size_t i;
+  /* TODO(ctiller): this is O(num_fds^2); maybe switch to a hash set here */
+  for (i = 0; i < pollset->fd_count; i++) {
+    if (pollset->fds[i] == fd) goto exit;
+  }
+  if (pollset->fd_count == pollset->fd_capacity) {
+    pollset->fd_capacity =
+        GPR_MAX(pollset->fd_capacity + 8, pollset->fd_count * 3 / 2);
+    pollset->fds = static_cast<grpc_fd**>(
+        gpr_realloc(pollset->fds, sizeof(grpc_fd*) * pollset->fd_capacity));
+  }
+  pollset->fds[pollset->fd_count++] = fd;
+  GRPC_FD_REF(fd, "multipoller");
+  pollset_kick(pollset, nullptr);
+exit:
+  gpr_mu_unlock(&pollset->mu);
+}
+
+static void finish_shutdown(grpc_pollset* pollset) {
+  size_t i;
+  for (i = 0; i < pollset->fd_count; i++) {
+    GRPC_FD_UNREF(pollset->fds[i], "multipoller");
+  }
+  pollset->fd_count = 0;
+  GRPC_CLOSURE_SCHED(pollset->shutdown_done, GRPC_ERROR_NONE);
+}
+
+static void work_combine_error(grpc_error** composite, grpc_error* error) {
+  if (error == GRPC_ERROR_NONE) return;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("pollset_work");
+  }
+  *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error* pollset_work(grpc_pollset* pollset,
+                                grpc_pollset_worker** worker_hdl,
+                                grpc_millis deadline) {
+  GPR_TIMER_SCOPE("pollset_work", 0);
+  grpc_pollset_worker worker;
+  if (worker_hdl) *worker_hdl = &worker;
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  /* Avoid malloc for small number of elements. */
+  enum { inline_elements = 96 };
+  struct pollfd pollfd_space[inline_elements];
+  struct grpc_fd_watcher watcher_space[inline_elements];
+
+  /* pollset->mu already held */
+  int added_worker = 0;
+  int locked = 1;
+  int queued_work = 0;
+  int keep_polling = 0;
+  /* this must happen before we (potentially) drop pollset->mu */
+  worker.next = worker.prev = nullptr;
+  worker.reevaluate_polling_on_wakeup = 0;
+  if (pollset->local_wakeup_cache != nullptr) {
+    worker.wakeup_fd = pollset->local_wakeup_cache;
+    pollset->local_wakeup_cache = worker.wakeup_fd->next;
+  } else {
+    worker.wakeup_fd = static_cast<grpc_cached_wakeup_fd*>(
+        gpr_malloc(sizeof(*worker.wakeup_fd)));
+    error = grpc_wakeup_fd_init(&worker.wakeup_fd->fd);
+    fork_fd_list_add_wakeup_fd(worker.wakeup_fd);
+    if (error != GRPC_ERROR_NONE) {
+      GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error));
+      return error;
+    }
+  }
+  worker.kicked_specifically = 0;
+  /* If we're shutting down then we don't execute any extended work */
+  if (pollset->shutting_down) {
+    GPR_TIMER_MARK("pollset_work.shutting_down", 0);
+    goto done;
+  }
+  /* Start polling, and keep doing so while we're being asked to
+     re-evaluate our pollers (this allows poll() based pollers to
+     ensure they don't miss wakeups) */
+  keep_polling = 1;
+  gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset);
+  while (keep_polling) {
+    keep_polling = 0;
+    if (!pollset->kicked_without_pollers ||
+        deadline <= grpc_core::ExecCtx::Get()->Now()) {
+      if (!added_worker) {
+        push_front_worker(pollset, &worker);
+        added_worker = 1;
+        gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker);
+      }
+      GPR_TIMER_SCOPE("maybe_work_and_unlock", 0);
+#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR)
+#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR)
+
+      int timeout;
+      int r;
+      size_t i, fd_count;
+      nfds_t pfd_count;
+      grpc_fd_watcher* watchers;
+      struct pollfd* pfds;
+
+      timeout = poll_deadline_to_millis_timeout(deadline);
+
+      if (pollset->fd_count + 2 <= inline_elements) {
+        pfds = pollfd_space;
+        watchers = watcher_space;
+      } else {
+        /* Allocate one buffer to hold both pfds and watchers arrays */
+        const size_t pfd_size = sizeof(*pfds) * (pollset->fd_count + 2);
+        const size_t watch_size = sizeof(*watchers) * (pollset->fd_count + 2);
+        void* buf = gpr_malloc(pfd_size + watch_size);
+        pfds = static_cast<struct pollfd*>(buf);
+        watchers = static_cast<grpc_fd_watcher*>(
+            (void*)(static_cast<char*>(buf) + pfd_size));
+      }
+
+      fd_count = 0;
+      pfd_count = 1;
+      pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker.wakeup_fd->fd);
+      pfds[0].events = POLLIN;
+      pfds[0].revents = 0;
+      for (i = 0; i < pollset->fd_count; i++) {
+        if (fd_is_orphaned(pollset->fds[i]) ||
+            gpr_atm_no_barrier_load(&pollset->fds[i]->pollhup) == 1) {
+          GRPC_FD_UNREF(pollset->fds[i], "multipoller");
+        } else {
+          pollset->fds[fd_count++] = pollset->fds[i];
+          watchers[pfd_count].fd = pollset->fds[i];
+          GRPC_FD_REF(watchers[pfd_count].fd, "multipoller_start");
+          pfds[pfd_count].fd = pollset->fds[i]->fd;
+          pfds[pfd_count].revents = 0;
+          pfd_count++;
+        }
+      }
+      pollset->fd_count = fd_count;
+      gpr_mu_unlock(&pollset->mu);
+
+      for (i = 1; i < pfd_count; i++) {
+        grpc_fd* fd = watchers[i].fd;
+        pfds[i].events = static_cast<short>(
+            fd_begin_poll(fd, pollset, &worker, POLLIN, POLLOUT, &watchers[i]));
+        GRPC_FD_UNREF(fd, "multipoller_start");
+      }
+
+      /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid
+         even going into the blocking annotation if possible */
+      GRPC_SCHEDULING_START_BLOCKING_REGION;
+      GRPC_STATS_INC_SYSCALL_POLL();
+      r = grpc_poll_function(pfds, pfd_count, timeout);
+      GRPC_SCHEDULING_END_BLOCKING_REGION;
+
+      if (grpc_polling_trace.enabled()) {
+        gpr_log(GPR_INFO, "%p poll=%d", pollset, r);
+      }
+
+      if (r < 0) {
+        if (errno != EINTR) {
+          work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
+        }
+
+        for (i = 1; i < pfd_count; i++) {
+          if (watchers[i].fd == nullptr) {
+            fd_end_poll(&watchers[i], 0, 0);
+          } else {
+            // Wake up all the file descriptors, if we have an invalid one
+            // we can identify it on the next pollset_work()
+            fd_end_poll(&watchers[i], 1, 1);
+          }
+        }
+      } else if (r == 0) {
+        for (i = 1; i < pfd_count; i++) {
+          fd_end_poll(&watchers[i], 0, 0);
+        }
+      } else {
+        if (pfds[0].revents & POLLIN_CHECK) {
+          if (grpc_polling_trace.enabled()) {
+            gpr_log(GPR_INFO, "%p: got_wakeup", pollset);
+          }
+          work_combine_error(
+              &error, grpc_wakeup_fd_consume_wakeup(&worker.wakeup_fd->fd));
+        }
+        for (i = 1; i < pfd_count; i++) {
+          if (watchers[i].fd == nullptr) {
+            fd_end_poll(&watchers[i], 0, 0);
+          } else {
+            if (grpc_polling_trace.enabled()) {
+              gpr_log(GPR_INFO, "%p got_event: %d r:%d w:%d [%d]", pollset,
+                      pfds[i].fd, (pfds[i].revents & POLLIN_CHECK) != 0,
+                      (pfds[i].revents & POLLOUT_CHECK) != 0, pfds[i].revents);
+            }
+            /* This is a mitigation to prevent poll() from spinning on a
+             ** POLLHUP https://github.com/grpc/grpc/pull/13665
+             */
+            if (pfds[i].revents & POLLHUP) {
+              gpr_atm_no_barrier_store(&watchers[i].fd->pollhup, 1);
+            }
+            fd_end_poll(&watchers[i], pfds[i].revents & POLLIN_CHECK,
+                        pfds[i].revents & POLLOUT_CHECK);
+          }
+        }
+      }
+
+      if (pfds != pollfd_space) {
+        /* pfds and watchers are in the same memory block pointed to by pfds */
+        gpr_free(pfds);
+      }
+
+      locked = 0;
+    } else {
+      GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0);
+      pollset->kicked_without_pollers = 0;
+    }
+  /* Finished execution - start cleaning up.
+     Note that we may arrive here from outside the enclosing while() loop.
+     In that case we won't loop though as we haven't added worker to the
+     worker list, which means nobody could ask us to re-evaluate polling). */
+  done:
+    if (!locked) {
+      queued_work |= grpc_core::ExecCtx::Get()->Flush();
+      gpr_mu_lock(&pollset->mu);
+      locked = 1;
+    }
+    /* If we're forced to re-evaluate polling (via pollset_kick with
+       GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force
+       a loop */
+    if (worker.reevaluate_polling_on_wakeup && error == GRPC_ERROR_NONE) {
+      worker.reevaluate_polling_on_wakeup = 0;
+      pollset->kicked_without_pollers = 0;
+      if (queued_work || worker.kicked_specifically) {
+        /* If there's queued work on the list, then set the deadline to be
+           immediate so we get back out of the polling loop quickly */
+        deadline = 0;
+      }
+      keep_polling = 1;
+    }
+  }
+  gpr_tls_set(&g_current_thread_poller, 0);
+  if (added_worker) {
+    remove_worker(pollset, &worker);
+    gpr_tls_set(&g_current_thread_worker, 0);
+  }
+  /* release wakeup fd to the local pool */
+  worker.wakeup_fd->next = pollset->local_wakeup_cache;
+  pollset->local_wakeup_cache = worker.wakeup_fd;
+  /* check shutdown conditions */
+  if (pollset->shutting_down) {
+    if (pollset_has_workers(pollset)) {
+      pollset_kick(pollset, nullptr);
+    } else if (!pollset->called_shutdown && !pollset_has_observers(pollset)) {
+      pollset->called_shutdown = 1;
+      gpr_mu_unlock(&pollset->mu);
+      finish_shutdown(pollset);
+      grpc_core::ExecCtx::Get()->Flush();
+      /* Continuing to access pollset here is safe -- it is the caller's
+       * responsibility to not destroy when it has outstanding calls to
+       * pollset_work.
+       * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */
+      gpr_mu_lock(&pollset->mu);
+    }
+  }
+  if (worker_hdl) *worker_hdl = nullptr;
+  GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error));
+  return error;
+}
+
+static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
+  GPR_ASSERT(!pollset->shutting_down);
+  pollset->shutting_down = 1;
+  pollset->shutdown_done = closure;
+  pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
+  if (!pollset->called_shutdown && !pollset_has_observers(pollset)) {
+    pollset->called_shutdown = 1;
+    finish_shutdown(pollset);
+  }
+}
+
+static int poll_deadline_to_millis_timeout(grpc_millis deadline) {
+  if (deadline == GRPC_MILLIS_INF_FUTURE) return -1;
+  if (deadline == 0) return 0;
+  grpc_millis n = deadline - grpc_core::ExecCtx::Get()->Now();
+  if (n < 0) return 0;
+  if (n > INT_MAX) return -1;
+  return static_cast<int>(n);
+}
+
+/*******************************************************************************
+ * pollset_set_posix.c
+ */
+
+static grpc_pollset_set* pollset_set_create(void) {
+  grpc_pollset_set* pollset_set =
+      static_cast<grpc_pollset_set*>(gpr_zalloc(sizeof(*pollset_set)));
+  gpr_mu_init(&pollset_set->mu);
+  return pollset_set;
+}
+
+static void pollset_set_destroy(grpc_pollset_set* pollset_set) {
+  size_t i;
+  gpr_mu_destroy(&pollset_set->mu);
+  for (i = 0; i < pollset_set->fd_count; i++) {
+    GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+  }
+  for (i = 0; i < pollset_set->pollset_count; i++) {
+    grpc_pollset* pollset = pollset_set->pollsets[i];
+    gpr_mu_lock(&pollset->mu);
+    pollset->pollset_set_count--;
+    /* check shutdown */
+    if (pollset->shutting_down && !pollset->called_shutdown &&
+        !pollset_has_observers(pollset)) {
+      pollset->called_shutdown = 1;
+      gpr_mu_unlock(&pollset->mu);
+      finish_shutdown(pollset);
+    } else {
+      gpr_mu_unlock(&pollset->mu);
+    }
+  }
+  gpr_free(pollset_set->pollsets);
+  gpr_free(pollset_set->pollset_sets);
+  gpr_free(pollset_set->fds);
+  gpr_free(pollset_set);
+}
+
+static void pollset_set_add_pollset(grpc_pollset_set* pollset_set,
+                                    grpc_pollset* pollset) {
+  size_t i, j;
+  gpr_mu_lock(&pollset->mu);
+  pollset->pollset_set_count++;
+  gpr_mu_unlock(&pollset->mu);
+  gpr_mu_lock(&pollset_set->mu);
+  if (pollset_set->pollset_count == pollset_set->pollset_capacity) {
+    pollset_set->pollset_capacity =
+        GPR_MAX(8, 2 * pollset_set->pollset_capacity);
+    pollset_set->pollsets = static_cast<grpc_pollset**>(gpr_realloc(
+        pollset_set->pollsets,
+        pollset_set->pollset_capacity * sizeof(*pollset_set->pollsets)));
+  }
+  pollset_set->pollsets[pollset_set->pollset_count++] = pollset;
+  for (i = 0, j = 0; i < pollset_set->fd_count; i++) {
+    if (fd_is_orphaned(pollset_set->fds[i])) {
+      GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+    } else {
+      pollset_add_fd(pollset, pollset_set->fds[i]);
+      pollset_set->fds[j++] = pollset_set->fds[i];
+    }
+  }
+  pollset_set->fd_count = j;
+  gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_del_pollset(grpc_pollset_set* pollset_set,
+                                    grpc_pollset* pollset) {
+  size_t i;
+  gpr_mu_lock(&pollset_set->mu);
+  for (i = 0; i < pollset_set->pollset_count; i++) {
+    if (pollset_set->pollsets[i] == pollset) {
+      pollset_set->pollset_count--;
+      GPR_SWAP(grpc_pollset*, pollset_set->pollsets[i],
+               pollset_set->pollsets[pollset_set->pollset_count]);
+      break;
+    }
+  }
+  gpr_mu_unlock(&pollset_set->mu);
+  gpr_mu_lock(&pollset->mu);
+  pollset->pollset_set_count--;
+  /* check shutdown */
+  if (pollset->shutting_down && !pollset->called_shutdown &&
+      !pollset_has_observers(pollset)) {
+    pollset->called_shutdown = 1;
+    gpr_mu_unlock(&pollset->mu);
+    finish_shutdown(pollset);
+  } else {
+    gpr_mu_unlock(&pollset->mu);
+  }
+}
+
+static void pollset_set_add_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {
+  size_t i, j;
+  gpr_mu_lock(&bag->mu);
+  if (bag->pollset_set_count == bag->pollset_set_capacity) {
+    bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity);
+    bag->pollset_sets = static_cast<grpc_pollset_set**>(
+        gpr_realloc(bag->pollset_sets,
+                    bag->pollset_set_capacity * sizeof(*bag->pollset_sets)));
+  }
+  bag->pollset_sets[bag->pollset_set_count++] = item;
+  for (i = 0, j = 0; i < bag->fd_count; i++) {
+    if (fd_is_orphaned(bag->fds[i])) {
+      GRPC_FD_UNREF(bag->fds[i], "pollset_set");
+    } else {
+      pollset_set_add_fd(item, bag->fds[i]);
+      bag->fds[j++] = bag->fds[i];
+    }
+  }
+  bag->fd_count = j;
+  gpr_mu_unlock(&bag->mu);
+}
+
+static void pollset_set_del_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {
+  size_t i;
+  gpr_mu_lock(&bag->mu);
+  for (i = 0; i < bag->pollset_set_count; i++) {
+    if (bag->pollset_sets[i] == item) {
+      bag->pollset_set_count--;
+      GPR_SWAP(grpc_pollset_set*, bag->pollset_sets[i],
+               bag->pollset_sets[bag->pollset_set_count]);
+      break;
+    }
+  }
+  gpr_mu_unlock(&bag->mu);
+}
+
+static void pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) {
+  size_t i;
+  gpr_mu_lock(&pollset_set->mu);
+  if (pollset_set->fd_count == pollset_set->fd_capacity) {
+    pollset_set->fd_capacity = GPR_MAX(8, 2 * pollset_set->fd_capacity);
+    pollset_set->fds = static_cast<grpc_fd**>(
+        gpr_realloc(pollset_set->fds,
+                    pollset_set->fd_capacity * sizeof(*pollset_set->fds)));
+  }
+  GRPC_FD_REF(fd, "pollset_set");
+  pollset_set->fds[pollset_set->fd_count++] = fd;
+  for (i = 0; i < pollset_set->pollset_count; i++) {
+    pollset_add_fd(pollset_set->pollsets[i], fd);
+  }
+  for (i = 0; i < pollset_set->pollset_set_count; i++) {
+    pollset_set_add_fd(pollset_set->pollset_sets[i], fd);
+  }
+  gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) {
+  size_t i;
+  gpr_mu_lock(&pollset_set->mu);
+  for (i = 0; i < pollset_set->fd_count; i++) {
+    if (pollset_set->fds[i] == fd) {
+      pollset_set->fd_count--;
+      GPR_SWAP(grpc_fd*, pollset_set->fds[i],
+               pollset_set->fds[pollset_set->fd_count]);
+      GRPC_FD_UNREF(fd, "pollset_set");
+      break;
+    }
+  }
+  for (i = 0; i < pollset_set->pollset_set_count; i++) {
+    pollset_set_del_fd(pollset_set->pollset_sets[i], fd);
+  }
+  gpr_mu_unlock(&pollset_set->mu);
+}
+
+/*******************************************************************************
+ * Condition Variable polling extensions
+ */
+
+static void run_poll(void* args);
+static void cache_poller_locked(poll_args* args);
+static void cache_harvest_locked();
+
+static void cache_insert_locked(poll_args* args) {
+  uint32_t key = gpr_murmur_hash3(args->fds, args->nfds * sizeof(struct pollfd),
+                                  0xDEADBEEF);
+  key = key % poll_cache.size;
+  if (poll_cache.active_pollers[key]) {
+    poll_cache.active_pollers[key]->prev = args;
+  }
+  args->next = poll_cache.active_pollers[key];
+  args->prev = nullptr;
+  poll_cache.active_pollers[key] = args;
+  poll_cache.count++;
+}
+
+static void init_result(poll_args* pargs) {
+  pargs->result = static_cast<poll_result*>(gpr_malloc(sizeof(poll_result)));
+  gpr_ref_init(&pargs->result->refcount, 1);
+  pargs->result->watchers = nullptr;
+  pargs->result->watchcount = 0;
+  pargs->result->fds = static_cast<struct pollfd*>(
+      gpr_malloc(sizeof(struct pollfd) * pargs->nfds));
+  memcpy(pargs->result->fds, pargs->fds, sizeof(struct pollfd) * pargs->nfds);
+  pargs->result->nfds = pargs->nfds;
+  pargs->result->retval = 0;
+  pargs->result->err = 0;
+  pargs->result->completed = 0;
+}
+
+// Creates a poll_args object for a given arguments to poll().
+// This object may return a poll_args in the cache.
+static poll_args* get_poller_locked(struct pollfd* fds, nfds_t count) {
+  uint32_t key =
+      gpr_murmur_hash3(fds, count * sizeof(struct pollfd), 0xDEADBEEF);
+  key = key % poll_cache.size;
+  poll_args* curr = poll_cache.active_pollers[key];
+  while (curr) {
+    if (curr->nfds == count &&
+        memcmp(curr->fds, fds, count * sizeof(struct pollfd)) == 0) {
+      gpr_free(fds);
+      return curr;
+    }
+    curr = curr->next;
+  }
+
+  if (poll_cache.free_pollers) {
+    poll_args* pargs = poll_cache.free_pollers;
+    poll_cache.free_pollers = pargs->next;
+    if (poll_cache.free_pollers) {
+      poll_cache.free_pollers->prev = nullptr;
+    }
+    pargs->fds = fds;
+    pargs->nfds = count;
+    pargs->next = nullptr;
+    pargs->prev = nullptr;
+    init_result(pargs);
+    cache_poller_locked(pargs);
+    return pargs;
+  }
+
+  poll_args* pargs =
+      static_cast<poll_args*>(gpr_malloc(sizeof(struct poll_args)));
+  gpr_cv_init(&pargs->trigger);
+  gpr_cv_init(&pargs->harvest);
+  gpr_cv_init(&pargs->join);
+  pargs->harvestable = false;
+  pargs->joinable = false;
+  pargs->fds = fds;
+  pargs->nfds = count;
+  pargs->next = nullptr;
+  pargs->prev = nullptr;
+  pargs->trigger_set = 0;
+  init_result(pargs);
+  cache_poller_locked(pargs);
+  gpr_ref(&g_cvfds.pollcount);
+  pargs->poller_thd = grpc_core::Thread("grpc_poller", &run_poll, pargs);
+  pargs->poller_thd.Start();
+  return pargs;
+}
+
+static void cache_delete_locked(poll_args* args) {
+  if (!args->prev) {
+    uint32_t key = gpr_murmur_hash3(
+        args->fds, args->nfds * sizeof(struct pollfd), 0xDEADBEEF);
+    key = key % poll_cache.size;
+    GPR_ASSERT(poll_cache.active_pollers[key] == args);
+    poll_cache.active_pollers[key] = args->next;
+  } else {
+    args->prev->next = args->next;
+  }
+
+  if (args->next) {
+    args->next->prev = args->prev;
+  }
+
+  poll_cache.count--;
+  if (poll_cache.free_pollers) {
+    poll_cache.free_pollers->prev = args;
+  }
+  args->prev = nullptr;
+  args->next = poll_cache.free_pollers;
+  gpr_free(args->fds);
+  poll_cache.free_pollers = args;
+}
+
+static void cache_poller_locked(poll_args* args) {
+  if (poll_cache.count + 1 > poll_cache.size / 2) {
+    poll_args** old_active_pollers = poll_cache.active_pollers;
+    poll_cache.size = poll_cache.size * 2;
+    poll_cache.count = 0;
+    poll_cache.active_pollers =
+        static_cast<poll_args**>(gpr_malloc(sizeof(void*) * poll_cache.size));
+    for (unsigned int i = 0; i < poll_cache.size; i++) {
+      poll_cache.active_pollers[i] = nullptr;
+    }
+    for (unsigned int i = 0; i < poll_cache.size / 2; i++) {
+      poll_args* curr = old_active_pollers[i];
+      poll_args* next = nullptr;
+      while (curr) {
+        next = curr->next;
+        cache_insert_locked(curr);
+        curr = next;
+      }
+    }
+    gpr_free(old_active_pollers);
+  }
+
+  cache_insert_locked(args);
+}
+
+static void cache_destroy_locked(poll_args* args) {
+  if (args->next) {
+    args->next->prev = args->prev;
+  }
+
+  if (args->prev) {
+    args->prev->next = args->next;
+  } else {
+    poll_cache.free_pollers = args->next;
+  }
+
+  // Now move this args to the dead poller list for later join
+  if (poll_cache.dead_pollers != nullptr) {
+    poll_cache.dead_pollers->prev = args;
+  }
+  args->prev = nullptr;
+  args->next = poll_cache.dead_pollers;
+  poll_cache.dead_pollers = args;
+}
+
+static void cache_harvest_locked() {
+  while (poll_cache.dead_pollers) {
+    poll_args* args = poll_cache.dead_pollers;
+    poll_cache.dead_pollers = poll_cache.dead_pollers->next;
+    // Keep the list consistent in case new dead pollers get added when we
+    // release the lock below to wait on joining
+    if (poll_cache.dead_pollers) {
+      poll_cache.dead_pollers->prev = nullptr;
+    }
+    args->harvestable = true;
+    gpr_cv_signal(&args->harvest);
+    while (!args->joinable) {
+      gpr_cv_wait(&args->join, &g_cvfds.mu,
+                  gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    }
+    args->poller_thd.Join();
+    gpr_free(args);
+  }
+}
+
+static void decref_poll_result(poll_result* res) {
+  if (gpr_unref(&res->refcount)) {
+    GPR_ASSERT(!res->watchers);
+    gpr_free(res->fds);
+    gpr_free(res);
+  }
+}
+
+void remove_cvn(grpc_cv_node** head, grpc_cv_node* target) {
+  if (target->next) {
+    target->next->prev = target->prev;
+  }
+
+  if (target->prev) {
+    target->prev->next = target->next;
+  } else {
+    *head = target->next;
+  }
+}
+
+gpr_timespec thread_grace;
+
+// Poll in a background thread
+static void run_poll(void* args) {
+  poll_args* pargs = static_cast<poll_args*>(args);
+  while (1) {
+    poll_result* result = pargs->result;
+    int retval = g_cvfds.poll(result->fds, result->nfds, CV_POLL_PERIOD_MS);
+    gpr_mu_lock(&g_cvfds.mu);
+    cache_harvest_locked();
+    if (retval != 0) {
+      result->completed = 1;
+      result->retval = retval;
+      result->err = errno;
+      grpc_cv_node* watcher = result->watchers;
+      while (watcher) {
+        gpr_cv_signal(watcher->cv);
+        watcher = watcher->next;
+      }
+    }
+    if (result->watchcount == 0 || result->completed) {
+      cache_delete_locked(pargs);
+      decref_poll_result(result);
+      // Leave this polling thread alive for a grace period to do another poll()
+      // op
+      gpr_timespec deadline = gpr_now(GPR_CLOCK_MONOTONIC);
+      deadline = gpr_time_add(deadline, thread_grace);
+      pargs->trigger_set = 0;
+      gpr_cv_wait(&pargs->trigger, &g_cvfds.mu, deadline);
+      cache_harvest_locked();
+      if (!pargs->trigger_set) {
+        cache_destroy_locked(pargs);
+        break;
+      }
+    }
+    gpr_mu_unlock(&g_cvfds.mu);
+  }
+
+  if (gpr_unref(&g_cvfds.pollcount)) {
+    gpr_cv_signal(&g_cvfds.shutdown_cv);
+  }
+  while (!pargs->harvestable) {
+    gpr_cv_wait(&pargs->harvest, &g_cvfds.mu,
+                gpr_inf_future(GPR_CLOCK_MONOTONIC));
+  }
+  pargs->joinable = true;
+  gpr_cv_signal(&pargs->join);
+  gpr_mu_unlock(&g_cvfds.mu);
+}
+
+// This function overrides poll() to handle condition variable wakeup fds
+static int cvfd_poll(struct pollfd* fds, nfds_t nfds, int timeout) {
+  if (timeout == 0) {
+    // Don't bother using background threads for polling if timeout is 0,
+    // poll-cv might not wait for a poll to return otherwise.
+    // https://github.com/grpc/grpc/issues/13298
+    return poll(fds, nfds, 0);
+  }
+  unsigned int i;
+  int res, idx;
+  grpc_cv_node* pollcv;
+  int skip_poll = 0;
+  nfds_t nsockfds = 0;
+  poll_result* result = nullptr;
+  gpr_mu_lock(&g_cvfds.mu);
+  cache_harvest_locked();
+  pollcv = static_cast<grpc_cv_node*>(gpr_malloc(sizeof(grpc_cv_node)));
+  pollcv->next = nullptr;
+  gpr_cv pollcv_cv;
+  gpr_cv_init(&pollcv_cv);
+  pollcv->cv = &pollcv_cv;
+  grpc_cv_node* fd_cvs =
+      static_cast<grpc_cv_node*>(gpr_malloc(nfds * sizeof(grpc_cv_node)));
+
+  for (i = 0; i < nfds; i++) {
+    fds[i].revents = 0;
+    if (fds[i].fd < 0 && (fds[i].events & POLLIN)) {
+      idx = GRPC_FD_TO_IDX(fds[i].fd);
+      fd_cvs[i].cv = &pollcv_cv;
+      fd_cvs[i].prev = nullptr;
+      fd_cvs[i].next = g_cvfds.cvfds[idx].cvs;
+      if (g_cvfds.cvfds[idx].cvs) {
+        g_cvfds.cvfds[idx].cvs->prev = &(fd_cvs[i]);
+      }
+      g_cvfds.cvfds[idx].cvs = &(fd_cvs[i]);
+      // Don't bother polling if a wakeup fd is ready
+      if (g_cvfds.cvfds[idx].is_set) {
+        skip_poll = 1;
+      }
+    } else if (fds[i].fd >= 0) {
+      nsockfds++;
+    }
+  }
+
+  gpr_timespec deadline = gpr_now(GPR_CLOCK_MONOTONIC);
+  if (timeout < 0) {
+    deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  } else {
+    deadline =
+        gpr_time_add(deadline, gpr_time_from_millis(timeout, GPR_TIMESPAN));
+  }
+
+  res = 0;
+  if (!skip_poll && nsockfds > 0) {
+    struct pollfd* pollfds = static_cast<struct pollfd*>(
+        gpr_malloc(sizeof(struct pollfd) * nsockfds));
+    idx = 0;
+    for (i = 0; i < nfds; i++) {
+      if (fds[i].fd >= 0) {
+        pollfds[idx].fd = fds[i].fd;
+        pollfds[idx].events = fds[i].events;
+        pollfds[idx].revents = 0;
+        idx++;
+      }
+    }
+    poll_args* pargs = get_poller_locked(pollfds, nsockfds);
+    result = pargs->result;
+    pollcv->next = result->watchers;
+    pollcv->prev = nullptr;
+    if (result->watchers) {
+      result->watchers->prev = pollcv;
+    }
+    result->watchers = pollcv;
+    result->watchcount++;
+    gpr_ref(&result->refcount);
+
+    pargs->trigger_set = 1;
+    gpr_cv_signal(&pargs->trigger);
+    gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline);
+    cache_harvest_locked();
+    res = result->retval;
+    errno = result->err;
+    result->watchcount--;
+    remove_cvn(&result->watchers, pollcv);
+  } else if (!skip_poll) {
+    gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline);
+    cache_harvest_locked();
+  }
+
+  idx = 0;
+  for (i = 0; i < nfds; i++) {
+    if (fds[i].fd < 0 && (fds[i].events & POLLIN)) {
+      remove_cvn(&g_cvfds.cvfds[GRPC_FD_TO_IDX(fds[i].fd)].cvs, &(fd_cvs[i]));
+      if (g_cvfds.cvfds[GRPC_FD_TO_IDX(fds[i].fd)].is_set) {
+        fds[i].revents = POLLIN;
+        if (res >= 0) res++;
+      }
+    } else if (!skip_poll && fds[i].fd >= 0 && result->completed) {
+      fds[i].revents = result->fds[idx].revents;
+      idx++;
+    }
+  }
+
+  gpr_free(fd_cvs);
+  gpr_free(pollcv);
+  if (result) {
+    decref_poll_result(result);
+  }
+
+  gpr_mu_unlock(&g_cvfds.mu);
+
+  return res;
+}
+
+static void global_cv_fd_table_init() {
+  gpr_mu_init(&g_cvfds.mu);
+  gpr_mu_lock(&g_cvfds.mu);
+  gpr_cv_init(&g_cvfds.shutdown_cv);
+  gpr_ref_init(&g_cvfds.pollcount, 1);
+  g_cvfds.size = CV_DEFAULT_TABLE_SIZE;
+  g_cvfds.cvfds = static_cast<grpc_fd_node*>(
+      gpr_malloc(sizeof(grpc_fd_node) * CV_DEFAULT_TABLE_SIZE));
+  g_cvfds.free_fds = nullptr;
+  thread_grace = gpr_time_from_millis(POLLCV_THREAD_GRACE_MS, GPR_TIMESPAN);
+  for (int i = 0; i < CV_DEFAULT_TABLE_SIZE; i++) {
+    g_cvfds.cvfds[i].is_set = 0;
+    g_cvfds.cvfds[i].cvs = nullptr;
+    g_cvfds.cvfds[i].next_free = g_cvfds.free_fds;
+    g_cvfds.free_fds = &g_cvfds.cvfds[i];
+  }
+  // Override the poll function with one that supports cvfds
+  g_cvfds.poll = grpc_poll_function;
+  grpc_poll_function = &cvfd_poll;
+
+  // Initialize the cache
+  poll_cache.size = 32;
+  poll_cache.count = 0;
+  poll_cache.free_pollers = nullptr;
+  poll_cache.active_pollers =
+      static_cast<poll_args**>(gpr_malloc(sizeof(void*) * 32));
+  for (unsigned int i = 0; i < poll_cache.size; i++) {
+    poll_cache.active_pollers[i] = nullptr;
+  }
+  poll_cache.dead_pollers = nullptr;
+
+  gpr_mu_unlock(&g_cvfds.mu);
+}
+
+static void global_cv_fd_table_shutdown() {
+  gpr_mu_lock(&g_cvfds.mu);
+  // Attempt to wait for all abandoned poll() threads to terminate
+  // Not doing so will result in reported memory leaks
+  if (!gpr_unref(&g_cvfds.pollcount)) {
+    int res = gpr_cv_wait(&g_cvfds.shutdown_cv, &g_cvfds.mu,
+                          gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                                       gpr_time_from_seconds(3, GPR_TIMESPAN)));
+    GPR_ASSERT(res == 0);
+  }
+  gpr_cv_destroy(&g_cvfds.shutdown_cv);
+  grpc_poll_function = g_cvfds.poll;
+  gpr_free(g_cvfds.cvfds);
+
+  cache_harvest_locked();
+  gpr_free(poll_cache.active_pollers);
+
+  gpr_mu_unlock(&g_cvfds.mu);
+  gpr_mu_destroy(&g_cvfds.mu);
+}
+
+/*******************************************************************************
+ * event engine binding
+ */
+
+static void shutdown_background_closure(void) {}
+
+static void shutdown_engine(void) {
+  pollset_global_shutdown();
+  if (grpc_cv_wakeup_fds_enabled()) {
+    global_cv_fd_table_shutdown();
+  }
+  if (track_fds_for_fork) {
+    gpr_mu_destroy(&fork_fd_list_mu);
+    grpc_core::Fork::SetResetChildPollingEngineFunc(nullptr);
+  }
+}
+
+static const grpc_event_engine_vtable vtable = {
+    sizeof(grpc_pollset),
+    false,
+    false,
+
+    fd_create,
+    fd_wrapped_fd,
+    fd_orphan,
+    fd_shutdown,
+    fd_notify_on_read,
+    fd_notify_on_write,
+    fd_notify_on_error,
+    fd_set_readable,
+    fd_set_writable,
+    fd_set_error,
+    fd_is_shutdown,
+
+    pollset_init,
+    pollset_shutdown,
+    pollset_destroy,
+    pollset_work,
+    pollset_kick,
+    pollset_add_fd,
+
+    pollset_set_create,
+    pollset_set_destroy,
+    pollset_set_add_pollset,
+    pollset_set_del_pollset,
+    pollset_set_add_pollset_set,
+    pollset_set_del_pollset_set,
+    pollset_set_add_fd,
+    pollset_set_del_fd,
+
+    shutdown_background_closure,
+    shutdown_engine,
+};
+
+/* Called by the child process's post-fork handler to close open fds, including
+ * worker wakeup fds. This allows gRPC to shutdown in the child process without
+ * interfering with connections or RPCs ongoing in the parent. */
+static void reset_event_manager_on_fork() {
+  gpr_mu_lock(&fork_fd_list_mu);
+  while (fork_fd_list_head != nullptr) {
+    if (fork_fd_list_head->fd != nullptr) {
+      close(fork_fd_list_head->fd->fd);
+      fork_fd_list_head->fd->fd = -1;
+    } else {
+      close(fork_fd_list_head->cached_wakeup_fd->fd.read_fd);
+      fork_fd_list_head->cached_wakeup_fd->fd.read_fd = -1;
+      close(fork_fd_list_head->cached_wakeup_fd->fd.write_fd);
+      fork_fd_list_head->cached_wakeup_fd->fd.write_fd = -1;
+    }
+    fork_fd_list_head = fork_fd_list_head->next;
+  }
+  gpr_mu_unlock(&fork_fd_list_mu);
+}
+
+const grpc_event_engine_vtable* grpc_init_poll_posix(bool explicit_request) {
+  if (!grpc_has_wakeup_fd()) {
+    gpr_log(GPR_ERROR, "Skipping poll because of no wakeup fd.");
+    return nullptr;
+  }
+  if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
+    return nullptr;
+  }
+  if (grpc_core::Fork::Enabled()) {
+    track_fds_for_fork = true;
+    gpr_mu_init(&fork_fd_list_mu);
+    grpc_core::Fork::SetResetChildPollingEngineFunc(
+        reset_event_manager_on_fork);
+  }
+  return &vtable;
+}
+
+const grpc_event_engine_vtable* grpc_init_poll_cv_posix(bool explicit_request) {
+  global_cv_fd_table_init();
+  grpc_enable_cv_wakeup_fds(1);
+  if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
+    global_cv_fd_table_shutdown();
+    grpc_enable_cv_wakeup_fds(0);
+    return nullptr;
+  }
+  return &vtable;
+}
+
+#endif /* GRPC_POSIX_SOCKET_EV_POLL */
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.h b/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.h
new file mode 100644
index 0000000..ab3cd90
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/ev_posix.h"
+
+const grpc_event_engine_vtable* grpc_init_poll_posix(bool explicit_request);
+const grpc_event_engine_vtable* grpc_init_poll_cv_posix(bool explicit_request);
+
+#endif /* GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_posix.cc b/third_party/grpc/src/core/lib/iomgr/ev_posix.cc
new file mode 100644
index 0000000..32d1b6c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_posix.cc
@@ -0,0 +1,406 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_EV
+
+#include "src/core/lib/iomgr/ev_posix.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/ev_epoll1_linux.h"
+#include "src/core/lib/iomgr/ev_epollex_linux.h"
+#include "src/core/lib/iomgr/ev_poll_posix.h"
+#include "src/core/lib/iomgr/internal_errqueue.h"
+
+grpc_core::TraceFlag grpc_polling_trace(false,
+                                        "polling"); /* Disabled by default */
+
+/* Traces fd create/close operations */
+grpc_core::TraceFlag grpc_fd_trace(false, "fd_trace");
+grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
+grpc_core::DebugOnlyTraceFlag grpc_polling_api_trace(false, "polling_api");
+
+#ifndef NDEBUG
+
+// Polling API trace only enabled in debug builds
+#define GRPC_POLLING_API_TRACE(format, ...)                  \
+  if (grpc_polling_api_trace.enabled()) {                    \
+    gpr_log(GPR_INFO, "(polling-api) " format, __VA_ARGS__); \
+  }
+#else
+#define GRPC_POLLING_API_TRACE(...)
+#endif
+
+/** Default poll() function - a pointer so that it can be overridden by some
+ *  tests */
+#ifndef GPR_AIX
+grpc_poll_function_type grpc_poll_function = poll;
+#else
+int aix_poll(struct pollfd fds[], nfds_t nfds, int timeout) {
+  return poll(fds, nfds, timeout);
+}
+grpc_poll_function_type grpc_poll_function = aix_poll;
+#endif
+
+grpc_wakeup_fd grpc_global_wakeup_fd;
+
+static const grpc_event_engine_vtable* g_event_engine = nullptr;
+static const char* g_poll_strategy_name = nullptr;
+
+typedef const grpc_event_engine_vtable* (*event_engine_factory_fn)(
+    bool explicit_request);
+
+typedef struct {
+  const char* name;
+  event_engine_factory_fn factory;
+} event_engine_factory;
+
+namespace {
+
+grpc_poll_function_type real_poll_function;
+
+int dummy_poll(struct pollfd fds[], nfds_t nfds, int timeout) {
+  if (timeout == 0) {
+    return real_poll_function(fds, nfds, 0);
+  } else {
+    gpr_log(GPR_ERROR, "Attempted a blocking poll when declared non-polling.");
+    GPR_ASSERT(false);
+    return -1;
+  }
+}
+
+const grpc_event_engine_vtable* init_non_polling(bool explicit_request) {
+  if (!explicit_request) {
+    return nullptr;
+  }
+  // return the simplest engine as a dummy but also override the poller
+  auto ret = grpc_init_poll_posix(explicit_request);
+  real_poll_function = grpc_poll_function;
+  grpc_poll_function = dummy_poll;
+
+  return ret;
+}
+}  // namespace
+
+#define ENGINE_HEAD_CUSTOM "head_custom"
+#define ENGINE_TAIL_CUSTOM "tail_custom"
+
+// The global array of event-engine factories. Each entry is a pair with a name
+// and an event-engine generator function (nullptr if there is no generator
+// registered for this name). The middle entries are the engines predefined by
+// open-source gRPC. The head entries represent an opportunity for specific
+// high-priority custom pollers to be added by the initializer plugins of
+// custom-built gRPC libraries. The tail entries represent the same, but for
+// low-priority custom pollers. The actual poller selected is either the first
+// available one in the list if no specific poller is requested, or the first
+// specific poller that is requested by name in the GRPC_POLL_STRATEGY
+// environment variable if that variable is set (which should be a
+// comma-separated list of one or more event engine names)
+static event_engine_factory g_factories[] = {
+    {ENGINE_HEAD_CUSTOM, nullptr},        {ENGINE_HEAD_CUSTOM, nullptr},
+    {ENGINE_HEAD_CUSTOM, nullptr},        {ENGINE_HEAD_CUSTOM, nullptr},
+    {"epollex", grpc_init_epollex_linux}, {"epoll1", grpc_init_epoll1_linux},
+    {"poll", grpc_init_poll_posix},       {"poll-cv", grpc_init_poll_cv_posix},
+    {"none", init_non_polling},           {ENGINE_TAIL_CUSTOM, nullptr},
+    {ENGINE_TAIL_CUSTOM, nullptr},        {ENGINE_TAIL_CUSTOM, nullptr},
+    {ENGINE_TAIL_CUSTOM, nullptr},
+};
+
+static void add(const char* beg, const char* end, char*** ss, size_t* ns) {
+  size_t n = *ns;
+  size_t np = n + 1;
+  char* s;
+  size_t len;
+  GPR_ASSERT(end >= beg);
+  len = static_cast<size_t>(end - beg);
+  s = static_cast<char*>(gpr_malloc(len + 1));
+  memcpy(s, beg, len);
+  s[len] = 0;
+  *ss = static_cast<char**>(gpr_realloc(*ss, sizeof(char**) * np));
+  (*ss)[n] = s;
+  *ns = np;
+}
+
+static void split(const char* s, char*** ss, size_t* ns) {
+  const char* c = strchr(s, ',');
+  if (c == nullptr) {
+    add(s, s + strlen(s), ss, ns);
+  } else {
+    add(s, c, ss, ns);
+    split(c + 1, ss, ns);
+  }
+}
+
+static bool is(const char* want, const char* have) {
+  return 0 == strcmp(want, "all") || 0 == strcmp(want, have);
+}
+
+static void try_engine(const char* engine) {
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) {
+    if (g_factories[i].factory != nullptr && is(engine, g_factories[i].name)) {
+      if ((g_event_engine = g_factories[i].factory(
+               0 == strcmp(engine, g_factories[i].name)))) {
+        g_poll_strategy_name = g_factories[i].name;
+        gpr_log(GPR_DEBUG, "Using polling engine: %s", g_factories[i].name);
+        return;
+      }
+    }
+  }
+}
+
+/* Call this before calling grpc_event_engine_init() */
+void grpc_register_event_engine_factory(const char* name,
+                                        event_engine_factory_fn factory,
+                                        bool add_at_head) {
+  const char* custom_match =
+      add_at_head ? ENGINE_HEAD_CUSTOM : ENGINE_TAIL_CUSTOM;
+
+  // Overwrite an existing registration if already registered
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) {
+    if (0 == strcmp(name, g_factories[i].name)) {
+      g_factories[i].factory = factory;
+      return;
+    }
+  }
+
+  // Otherwise fill in an available custom slot
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) {
+    if (0 == strcmp(g_factories[i].name, custom_match)) {
+      g_factories[i].name = name;
+      g_factories[i].factory = factory;
+      return;
+    }
+  }
+
+  // Otherwise fail
+  GPR_ASSERT(false);
+}
+
+/* Call this only after calling grpc_event_engine_init() */
+const char* grpc_get_poll_strategy_name() { return g_poll_strategy_name; }
+
+void grpc_event_engine_init(void) {
+  char* s = gpr_getenv("GRPC_POLL_STRATEGY");
+  if (s == nullptr) {
+    s = gpr_strdup("all");
+  }
+
+  char** strings = nullptr;
+  size_t nstrings = 0;
+  split(s, &strings, &nstrings);
+
+  for (size_t i = 0; g_event_engine == nullptr && i < nstrings; i++) {
+    try_engine(strings[i]);
+  }
+
+  for (size_t i = 0; i < nstrings; i++) {
+    gpr_free(strings[i]);
+  }
+  gpr_free(strings);
+
+  if (g_event_engine == nullptr) {
+    gpr_log(GPR_ERROR, "No event engine could be initialized from %s", s);
+    abort();
+  }
+  gpr_free(s);
+}
+
+void grpc_event_engine_shutdown(void) {
+  g_event_engine->shutdown_engine();
+  g_event_engine = nullptr;
+}
+
+bool grpc_event_engine_can_track_errors(void) {
+  /* Only track errors if platform supports errqueue. */
+  if (grpc_core::kernel_supports_errqueue()) {
+    return g_event_engine->can_track_err;
+  }
+  return false;
+}
+
+bool grpc_event_engine_run_in_background(void) {
+  return g_event_engine->run_in_background;
+}
+
+grpc_fd* grpc_fd_create(int fd, const char* name, bool track_err) {
+  GRPC_POLLING_API_TRACE("fd_create(%d, %s, %d)", fd, name, track_err);
+  GRPC_FD_TRACE("fd_create(%d, %s, %d)", fd, name, track_err);
+  return g_event_engine->fd_create(
+      fd, name, track_err && grpc_event_engine_can_track_errors());
+}
+
+int grpc_fd_wrapped_fd(grpc_fd* fd) {
+  return g_event_engine->fd_wrapped_fd(fd);
+}
+
+void grpc_fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd,
+                    const char* reason) {
+  GRPC_POLLING_API_TRACE("fd_orphan(%d, %p, %p, %s)", grpc_fd_wrapped_fd(fd),
+                         on_done, release_fd, reason);
+  GRPC_FD_TRACE("grpc_fd_orphan, fd:%d closed", grpc_fd_wrapped_fd(fd));
+
+  g_event_engine->fd_orphan(fd, on_done, release_fd, reason);
+}
+
+void grpc_fd_shutdown(grpc_fd* fd, grpc_error* why) {
+  GRPC_POLLING_API_TRACE("fd_shutdown(%d)", grpc_fd_wrapped_fd(fd));
+  GRPC_FD_TRACE("fd_shutdown(%d)", grpc_fd_wrapped_fd(fd));
+  g_event_engine->fd_shutdown(fd, why);
+}
+
+bool grpc_fd_is_shutdown(grpc_fd* fd) {
+  return g_event_engine->fd_is_shutdown(fd);
+}
+
+void grpc_fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) {
+  g_event_engine->fd_notify_on_read(fd, closure);
+}
+
+void grpc_fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) {
+  g_event_engine->fd_notify_on_write(fd, closure);
+}
+
+void grpc_fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) {
+  g_event_engine->fd_notify_on_error(fd, closure);
+}
+
+void grpc_fd_set_readable(grpc_fd* fd) { g_event_engine->fd_set_readable(fd); }
+
+void grpc_fd_set_writable(grpc_fd* fd) { g_event_engine->fd_set_writable(fd); }
+
+void grpc_fd_set_error(grpc_fd* fd) { g_event_engine->fd_set_error(fd); }
+
+static size_t pollset_size(void) { return g_event_engine->pollset_size; }
+
+static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
+  GRPC_POLLING_API_TRACE("pollset_init(%p)", pollset);
+  g_event_engine->pollset_init(pollset, mu);
+}
+
+static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
+  GRPC_POLLING_API_TRACE("pollset_shutdown(%p)", pollset);
+  g_event_engine->pollset_shutdown(pollset, closure);
+}
+
+static void pollset_destroy(grpc_pollset* pollset) {
+  GRPC_POLLING_API_TRACE("pollset_destroy(%p)", pollset);
+  g_event_engine->pollset_destroy(pollset);
+}
+
+static grpc_error* pollset_work(grpc_pollset* pollset,
+                                grpc_pollset_worker** worker,
+                                grpc_millis deadline) {
+  GRPC_POLLING_API_TRACE("pollset_work(%p, %" PRId64 ") begin", pollset,
+                         deadline);
+  grpc_error* err = g_event_engine->pollset_work(pollset, worker, deadline);
+  GRPC_POLLING_API_TRACE("pollset_work(%p, %" PRId64 ") end", pollset,
+                         deadline);
+  return err;
+}
+
+static grpc_error* pollset_kick(grpc_pollset* pollset,
+                                grpc_pollset_worker* specific_worker) {
+  GRPC_POLLING_API_TRACE("pollset_kick(%p, %p)", pollset, specific_worker);
+  return g_event_engine->pollset_kick(pollset, specific_worker);
+}
+
+void grpc_pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd) {
+  GRPC_POLLING_API_TRACE("pollset_add_fd(%p, %d)", pollset,
+                         grpc_fd_wrapped_fd(fd));
+  g_event_engine->pollset_add_fd(pollset, fd);
+}
+
+void pollset_global_init() {}
+void pollset_global_shutdown() {}
+
+grpc_pollset_vtable grpc_posix_pollset_vtable = {
+    pollset_global_init, pollset_global_shutdown,
+    pollset_init,        pollset_shutdown,
+    pollset_destroy,     pollset_work,
+    pollset_kick,        pollset_size};
+
+static grpc_pollset_set* pollset_set_create(void) {
+  grpc_pollset_set* pss = g_event_engine->pollset_set_create();
+  GRPC_POLLING_API_TRACE("pollset_set_create(%p)", pss);
+  return pss;
+}
+
+static void pollset_set_destroy(grpc_pollset_set* pollset_set) {
+  GRPC_POLLING_API_TRACE("pollset_set_destroy(%p)", pollset_set);
+  g_event_engine->pollset_set_destroy(pollset_set);
+}
+
+static void pollset_set_add_pollset(grpc_pollset_set* pollset_set,
+                                    grpc_pollset* pollset) {
+  GRPC_POLLING_API_TRACE("pollset_set_add_pollset(%p, %p)", pollset_set,
+                         pollset);
+  g_event_engine->pollset_set_add_pollset(pollset_set, pollset);
+}
+
+static void pollset_set_del_pollset(grpc_pollset_set* pollset_set,
+                                    grpc_pollset* pollset) {
+  GRPC_POLLING_API_TRACE("pollset_set_del_pollset(%p, %p)", pollset_set,
+                         pollset);
+  g_event_engine->pollset_set_del_pollset(pollset_set, pollset);
+}
+
+static void pollset_set_add_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {
+  GRPC_POLLING_API_TRACE("pollset_set_add_pollset_set(%p, %p)", bag, item);
+  g_event_engine->pollset_set_add_pollset_set(bag, item);
+}
+
+static void pollset_set_del_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {
+  GRPC_POLLING_API_TRACE("pollset_set_del_pollset_set(%p, %p)", bag, item);
+  g_event_engine->pollset_set_del_pollset_set(bag, item);
+}
+
+grpc_pollset_set_vtable grpc_posix_pollset_set_vtable = {
+    pollset_set_create,          pollset_set_destroy,
+    pollset_set_add_pollset,     pollset_set_del_pollset,
+    pollset_set_add_pollset_set, pollset_set_del_pollset_set};
+
+void grpc_pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) {
+  GRPC_POLLING_API_TRACE("pollset_set_add_fd(%p, %d)", pollset_set,
+                         grpc_fd_wrapped_fd(fd));
+  g_event_engine->pollset_set_add_fd(pollset_set, fd);
+}
+
+void grpc_pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) {
+  GRPC_POLLING_API_TRACE("pollset_set_del_fd(%p, %d)", pollset_set,
+                         grpc_fd_wrapped_fd(fd));
+  g_event_engine->pollset_set_del_fd(pollset_set, fd);
+}
+
+void grpc_shutdown_background_closure(void) {
+  g_event_engine->shutdown_background_closure();
+}
+
+#endif  // GRPC_POSIX_SOCKET_EV
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_posix.h b/third_party/grpc/src/core/lib/iomgr/ev_posix.h
new file mode 100644
index 0000000..812c7a0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_posix.h
@@ -0,0 +1,191 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_EV_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_EV_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include <poll.h>
+
+#include "src/core/lib/debug/trace.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/wakeup_fd_posix.h"
+
+extern grpc_core::TraceFlag grpc_fd_trace;      /* Disabled by default */
+extern grpc_core::TraceFlag grpc_polling_trace; /* Disabled by default */
+
+#define GRPC_FD_TRACE(format, ...)                        \
+  if (grpc_fd_trace.enabled()) {                          \
+    gpr_log(GPR_INFO, "(fd-trace) " format, __VA_ARGS__); \
+  }
+
+typedef struct grpc_fd grpc_fd;
+
+typedef struct grpc_event_engine_vtable {
+  size_t pollset_size;
+  bool can_track_err;
+  bool run_in_background;
+
+  grpc_fd* (*fd_create)(int fd, const char* name, bool track_err);
+  int (*fd_wrapped_fd)(grpc_fd* fd);
+  void (*fd_orphan)(grpc_fd* fd, grpc_closure* on_done, int* release_fd,
+                    const char* reason);
+  void (*fd_shutdown)(grpc_fd* fd, grpc_error* why);
+  void (*fd_notify_on_read)(grpc_fd* fd, grpc_closure* closure);
+  void (*fd_notify_on_write)(grpc_fd* fd, grpc_closure* closure);
+  void (*fd_notify_on_error)(grpc_fd* fd, grpc_closure* closure);
+  void (*fd_set_readable)(grpc_fd* fd);
+  void (*fd_set_writable)(grpc_fd* fd);
+  void (*fd_set_error)(grpc_fd* fd);
+  bool (*fd_is_shutdown)(grpc_fd* fd);
+
+  void (*pollset_init)(grpc_pollset* pollset, gpr_mu** mu);
+  void (*pollset_shutdown)(grpc_pollset* pollset, grpc_closure* closure);
+  void (*pollset_destroy)(grpc_pollset* pollset);
+  grpc_error* (*pollset_work)(grpc_pollset* pollset,
+                              grpc_pollset_worker** worker,
+                              grpc_millis deadline);
+  grpc_error* (*pollset_kick)(grpc_pollset* pollset,
+                              grpc_pollset_worker* specific_worker);
+  void (*pollset_add_fd)(grpc_pollset* pollset, struct grpc_fd* fd);
+
+  grpc_pollset_set* (*pollset_set_create)(void);
+  void (*pollset_set_destroy)(grpc_pollset_set* pollset_set);
+  void (*pollset_set_add_pollset)(grpc_pollset_set* pollset_set,
+                                  grpc_pollset* pollset);
+  void (*pollset_set_del_pollset)(grpc_pollset_set* pollset_set,
+                                  grpc_pollset* pollset);
+  void (*pollset_set_add_pollset_set)(grpc_pollset_set* bag,
+                                      grpc_pollset_set* item);
+  void (*pollset_set_del_pollset_set)(grpc_pollset_set* bag,
+                                      grpc_pollset_set* item);
+  void (*pollset_set_add_fd)(grpc_pollset_set* pollset_set, grpc_fd* fd);
+  void (*pollset_set_del_fd)(grpc_pollset_set* pollset_set, grpc_fd* fd);
+
+  void (*shutdown_background_closure)(void);
+  void (*shutdown_engine)(void);
+} grpc_event_engine_vtable;
+
+/* register a new event engine factory */
+void grpc_register_event_engine_factory(
+    const char* name, const grpc_event_engine_vtable* (*factory)(bool),
+    bool add_at_head);
+
+void grpc_event_engine_init(void);
+void grpc_event_engine_shutdown(void);
+
+/* Return the name of the poll strategy */
+const char* grpc_get_poll_strategy_name();
+
+/* Returns true if polling engine can track errors separately, false otherwise.
+ * If this is true, fd can be created with track_err set. After this, error
+ * events will be reported using fd_notify_on_error. If it is not set, errors
+ * will continue to be reported through fd_notify_on_read and
+ * fd_notify_on_write.
+ */
+bool grpc_event_engine_can_track_errors();
+
+/* Returns true if polling engine runs in the background, false otherwise.
+ * Currently only 'epollbg' runs in the background.
+ */
+bool grpc_event_engine_run_in_background();
+
+/* Create a wrapped file descriptor.
+   Requires fd is a non-blocking file descriptor.
+   \a track_err if true means that error events would be tracked separately
+   using grpc_fd_notify_on_error. Currently, valid only for linux systems.
+   This takes ownership of closing fd. */
+grpc_fd* grpc_fd_create(int fd, const char* name, bool track_err);
+
+/* Return the wrapped fd, or -1 if it has been released or closed. */
+int grpc_fd_wrapped_fd(grpc_fd* fd);
+
+/* Releases fd to be asynchronously destroyed.
+   on_done is called when the underlying file descriptor is definitely close()d.
+   If on_done is NULL, no callback will be made.
+   If release_fd is not NULL, it's set to fd and fd will not be closed.
+   Requires: *fd initialized; no outstanding notify_on_read or
+   notify_on_write.
+   MUST NOT be called with a pollset lock taken */
+void grpc_fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd,
+                    const char* reason);
+
+/* Has grpc_fd_shutdown been called on an fd? */
+bool grpc_fd_is_shutdown(grpc_fd* fd);
+
+/* Cause any current and future callbacks to fail. */
+void grpc_fd_shutdown(grpc_fd* fd, grpc_error* why);
+
+/* Register read interest, causing read_cb to be called once when fd becomes
+   readable, on deadline specified by deadline, or on shutdown triggered by
+   grpc_fd_shutdown.
+   read_cb will be called with read_cb_arg when *fd becomes readable.
+   read_cb is Called with status of GRPC_CALLBACK_SUCCESS if readable,
+   GRPC_CALLBACK_TIMED_OUT if the call timed out,
+   and CANCELLED if the call was cancelled.
+
+   Requires:This method must not be called before the read_cb for any previous
+   call runs. Edge triggered events are used whenever they are supported by the
+   underlying platform. This means that users must drain fd in read_cb before
+   calling notify_on_read again. Users are also expected to handle spurious
+   events, i.e read_cb is called while nothing can be readable from fd  */
+void grpc_fd_notify_on_read(grpc_fd* fd, grpc_closure* closure);
+
+/* Exactly the same semantics as above, except based on writable events.  */
+void grpc_fd_notify_on_write(grpc_fd* fd, grpc_closure* closure);
+
+/* Exactly the same semantics as above, except based on error events. track_err
+ * needs to have been set on grpc_fd_create */
+void grpc_fd_notify_on_error(grpc_fd* fd, grpc_closure* closure);
+
+/* Forcibly set the fd to be readable, resulting in the closure registered with
+ * grpc_fd_notify_on_read being invoked.
+ */
+void grpc_fd_set_readable(grpc_fd* fd);
+
+/* Forcibly set the fd to be writable, resulting in the closure registered with
+ * grpc_fd_notify_on_write being invoked.
+ */
+void grpc_fd_set_writable(grpc_fd* fd);
+
+/* Forcibly set the fd to have errored, resulting in the closure registered with
+ * grpc_fd_notify_on_error being invoked.
+ */
+void grpc_fd_set_error(grpc_fd* fd);
+
+/* pollset_posix functions */
+
+/* Add an fd to a pollset */
+void grpc_pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd);
+
+/* pollset_set_posix functions */
+
+void grpc_pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd);
+void grpc_pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd);
+
+/* Shut down all the closures registered in the background poller. */
+void grpc_shutdown_background_closure();
+
+/* override to allow tests to hook poll() usage */
+typedef int (*grpc_poll_function_type)(struct pollfd*, nfds_t, int);
+extern grpc_poll_function_type grpc_poll_function;
+
+#endif /* GRPC_CORE_LIB_IOMGR_EV_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/ev_windows.cc b/third_party/grpc/src/core/lib/iomgr/ev_windows.cc
new file mode 100644
index 0000000..32c62b7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/ev_windows.cc
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include "src/core/lib/debug/trace.h"
+
+grpc_core::TraceFlag grpc_polling_trace(false,
+                                        "polling"); /* Disabled by default */
+
+#endif  // GRPC_WINSOCK_SOCKET
diff --git a/third_party/grpc/src/core/lib/iomgr/exec_ctx.cc b/third_party/grpc/src/core/lib/iomgr/exec_ctx.cc
new file mode 100644
index 0000000..683dd2f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/exec_ctx.cc
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/profiling/timers.h"
+
+static void exec_ctx_run(grpc_closure* closure, grpc_error* error) {
+#ifndef NDEBUG
+  closure->scheduled = false;
+  if (grpc_trace_closure.enabled()) {
+    gpr_log(GPR_DEBUG, "running closure %p: created [%s:%d]: %s [%s:%d]",
+            closure, closure->file_created, closure->line_created,
+            closure->run ? "run" : "scheduled", closure->file_initiated,
+            closure->line_initiated);
+  }
+#endif
+  closure->cb(closure->cb_arg, error);
+#ifndef NDEBUG
+  if (grpc_trace_closure.enabled()) {
+    gpr_log(GPR_DEBUG, "closure %p finished", closure);
+  }
+#endif
+  GRPC_ERROR_UNREF(error);
+}
+
+static void exec_ctx_sched(grpc_closure* closure, grpc_error* error) {
+  grpc_closure_list_append(grpc_core::ExecCtx::Get()->closure_list(), closure,
+                           error);
+}
+
+static gpr_timespec g_start_time;
+
+// For debug of the timer manager crash only.
+// TODO (mxyan): remove after bug is fixed.
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+extern int64_t g_start_time_sec;
+extern int64_t g_start_time_nsec;
+#endif  // GRPC_DEBUG_TIMER_MANAGER
+
+static grpc_millis timespec_to_millis_round_down(gpr_timespec ts) {
+  ts = gpr_time_sub(ts, g_start_time);
+  double x = GPR_MS_PER_SEC * static_cast<double>(ts.tv_sec) +
+             static_cast<double>(ts.tv_nsec) / GPR_NS_PER_MS;
+  if (x < 0) return 0;
+  if (x > GRPC_MILLIS_INF_FUTURE) return GRPC_MILLIS_INF_FUTURE;
+  return static_cast<grpc_millis>(x);
+}
+
+static grpc_millis timespec_to_millis_round_up(gpr_timespec ts) {
+  ts = gpr_time_sub(ts, g_start_time);
+  double x = GPR_MS_PER_SEC * static_cast<double>(ts.tv_sec) +
+             static_cast<double>(ts.tv_nsec) / GPR_NS_PER_MS +
+             static_cast<double>(GPR_NS_PER_SEC - 1) /
+                 static_cast<double>(GPR_NS_PER_SEC);
+  if (x < 0) return 0;
+  if (x > GRPC_MILLIS_INF_FUTURE) return GRPC_MILLIS_INF_FUTURE;
+  return static_cast<grpc_millis>(x);
+}
+
+gpr_timespec grpc_millis_to_timespec(grpc_millis millis,
+                                     gpr_clock_type clock_type) {
+  // special-case infinities as grpc_millis can be 32bit on some platforms
+  // while gpr_time_from_millis always takes an int64_t.
+  if (millis == GRPC_MILLIS_INF_FUTURE) {
+    return gpr_inf_future(clock_type);
+  }
+  if (millis == GRPC_MILLIS_INF_PAST) {
+    return gpr_inf_past(clock_type);
+  }
+
+  if (clock_type == GPR_TIMESPAN) {
+    return gpr_time_from_millis(millis, GPR_TIMESPAN);
+  }
+  return gpr_time_add(gpr_convert_clock_type(g_start_time, clock_type),
+                      gpr_time_from_millis(millis, GPR_TIMESPAN));
+}
+
+grpc_millis grpc_timespec_to_millis_round_down(gpr_timespec ts) {
+  return timespec_to_millis_round_down(
+      gpr_convert_clock_type(ts, g_start_time.clock_type));
+}
+
+grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec ts) {
+  return timespec_to_millis_round_up(
+      gpr_convert_clock_type(ts, g_start_time.clock_type));
+}
+
+static const grpc_closure_scheduler_vtable exec_ctx_scheduler_vtable = {
+    exec_ctx_run, exec_ctx_sched, "exec_ctx"};
+static grpc_closure_scheduler exec_ctx_scheduler = {&exec_ctx_scheduler_vtable};
+grpc_closure_scheduler* grpc_schedule_on_exec_ctx = &exec_ctx_scheduler;
+
+namespace grpc_core {
+GPR_TLS_CLASS_DEF(ExecCtx::exec_ctx_);
+
+// WARNING: for testing purposes only!
+void ExecCtx::TestOnlyGlobalInit(gpr_timespec new_val) {
+  g_start_time = new_val;
+  gpr_tls_init(&exec_ctx_);
+}
+
+void ExecCtx::GlobalInit(void) {
+  g_start_time = gpr_now(GPR_CLOCK_MONOTONIC);
+  // For debug of the timer manager crash only.
+  // TODO (mxyan): remove after bug is fixed.
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+  g_start_time_sec = g_start_time.tv_sec;
+  g_start_time_nsec = g_start_time.tv_nsec;
+#endif
+  gpr_tls_init(&exec_ctx_);
+}
+
+bool ExecCtx::Flush() {
+  bool did_something = 0;
+  GPR_TIMER_SCOPE("grpc_exec_ctx_flush", 0);
+  for (;;) {
+    if (!grpc_closure_list_empty(closure_list_)) {
+      grpc_closure* c = closure_list_.head;
+      closure_list_.head = closure_list_.tail = nullptr;
+      while (c != nullptr) {
+        grpc_closure* next = c->next_data.next;
+        grpc_error* error = c->error_data.error;
+        did_something = true;
+        exec_ctx_run(c, error);
+        c = next;
+      }
+    } else if (!grpc_combiner_continue_exec_ctx()) {
+      break;
+    }
+  }
+  GPR_ASSERT(combiner_data_.active_combiner == nullptr);
+  return did_something;
+}
+
+grpc_millis ExecCtx::Now() {
+  if (!now_is_valid_) {
+    now_ = timespec_to_millis_round_down(gpr_now(GPR_CLOCK_MONOTONIC));
+    now_is_valid_ = true;
+  }
+  return now_;
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/iomgr/exec_ctx.h b/third_party/grpc/src/core/lib/iomgr/exec_ctx.h
new file mode 100644
index 0000000..e90eb54
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/exec_ctx.h
@@ -0,0 +1,231 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_EXEC_CTX_H
+#define GRPC_CORE_LIB_IOMGR_EXEC_CTX_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/tls.h"
+#include "src/core/lib/gprpp/fork.h"
+#include "src/core/lib/iomgr/closure.h"
+
+typedef int64_t grpc_millis;
+
+#define GRPC_MILLIS_INF_FUTURE INT64_MAX
+#define GRPC_MILLIS_INF_PAST INT64_MIN
+
+/** A workqueue represents a list of work to be executed asynchronously.
+    Forward declared here to avoid a circular dependency with workqueue.h. */
+typedef struct grpc_workqueue grpc_workqueue;
+typedef struct grpc_combiner grpc_combiner;
+
+/* This exec_ctx is ready to return: either pre-populated, or cached as soon as
+   the finish_check returns true */
+#define GRPC_EXEC_CTX_FLAG_IS_FINISHED 1
+/* The exec_ctx's thread is (potentially) owned by a call or channel: care
+   should be given to not delete said call/channel from this exec_ctx */
+#define GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP 2
+/* This exec ctx was initialized by an internal thread, and should not
+   be counted by fork handlers */
+#define GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD 4
+
+extern grpc_closure_scheduler* grpc_schedule_on_exec_ctx;
+
+gpr_timespec grpc_millis_to_timespec(grpc_millis millis, gpr_clock_type clock);
+grpc_millis grpc_timespec_to_millis_round_down(gpr_timespec timespec);
+grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec timespec);
+
+namespace grpc_core {
+/** Execution context.
+ *  A bag of data that collects information along a callstack.
+ *  It is created on the stack at public API entry points, and stored internally
+ *  as a thread-local variable.
+ *
+ *  Generally, to create an exec_ctx instance, add the following line at the top
+ *  of the public API entry point or at the start of a thread's work function :
+ *
+ *  grpc_core::ExecCtx exec_ctx;
+ *
+ *  Access the created ExecCtx instance using :
+ *  grpc_core::ExecCtx::Get()
+ *
+ *  Specific responsibilities (this may grow in the future):
+ *  - track a list of work that needs to be delayed until the top of the
+ *    call stack (this provides a convenient mechanism to run callbacks
+ *    without worrying about locking issues)
+ *  - provide a decision maker (via IsReadyToFinish) that provides a
+ *    signal as to whether a borrowed thread should continue to do work or
+ *    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.
+ *  - Exactly one instance of ExecCtx must be created per thread. Instances must
+ *    always be called exec_ctx.
+ *  - Do not pass exec_ctx as a parameter to a function. Always access it using
+ *    grpc_core::ExecCtx::Get().
+ */
+class ExecCtx {
+ public:
+  /** Default Constructor */
+
+  ExecCtx() : flags_(GRPC_EXEC_CTX_FLAG_IS_FINISHED) {
+    grpc_core::Fork::IncExecCtxCount();
+    Set(this);
+  }
+
+  /** Parameterised Constructor */
+  ExecCtx(uintptr_t fl) : flags_(fl) {
+    if (!(GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD & flags_)) {
+      grpc_core::Fork::IncExecCtxCount();
+    }
+    Set(this);
+  }
+
+  /** Destructor */
+  virtual ~ExecCtx() {
+    flags_ |= GRPC_EXEC_CTX_FLAG_IS_FINISHED;
+    Flush();
+    Set(last_exec_ctx_);
+    if (!(GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD & flags_)) {
+      grpc_core::Fork::DecExecCtxCount();
+    }
+  }
+
+  /** Disallow copy and assignment operators */
+  ExecCtx(const ExecCtx&) = delete;
+  ExecCtx& operator=(const ExecCtx&) = delete;
+
+  unsigned starting_cpu() const { return starting_cpu_; }
+
+  struct CombinerData {
+    /* currently active combiner: updated only via combiner.c */
+    grpc_combiner* active_combiner;
+    /* last active combiner in the active combiner list */
+    grpc_combiner* last_combiner;
+  };
+
+  /** Only to be used by grpc-combiner code */
+  CombinerData* combiner_data() { return &combiner_data_; }
+
+  /** Return pointer to grpc_closure_list */
+  grpc_closure_list* closure_list() { return &closure_list_; }
+
+  /** Return flags */
+  uintptr_t flags() { return flags_; }
+
+  /** Checks if there is work to be done */
+  bool HasWork() {
+    return combiner_data_.active_combiner != nullptr ||
+           !grpc_closure_list_empty(closure_list_);
+  }
+
+  /** Flush any work that has been enqueued onto this grpc_exec_ctx.
+   *  Caller must guarantee that no interfering locks are held.
+   *  Returns true if work was performed, false otherwise.
+   */
+  bool Flush();
+
+  /** Returns true if we'd like to leave this execution context as soon as
+   *  possible: useful for deciding whether to do something more or not
+   *  depending on outside context.
+   */
+  bool IsReadyToFinish() {
+    if ((flags_ & GRPC_EXEC_CTX_FLAG_IS_FINISHED) == 0) {
+      if (CheckReadyToFinish()) {
+        flags_ |= GRPC_EXEC_CTX_FLAG_IS_FINISHED;
+        return true;
+      }
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  /** Returns the stored current time relative to start if valid,
+   *  otherwise refreshes the stored time, sets it valid and returns the new
+   *  value.
+   */
+  grpc_millis Now();
+
+  /** Invalidates the stored time value. A new time value will be set on calling
+   *  Now().
+   */
+  void InvalidateNow() { now_is_valid_ = false; }
+
+  /** To be used only by shutdown code in iomgr */
+  void SetNowIomgrShutdown() {
+    now_ = GRPC_MILLIS_INF_FUTURE;
+    now_is_valid_ = true;
+  }
+
+  /** To be used only for testing.
+   *  Sets the now value.
+   */
+  void TestOnlySetNow(grpc_millis new_val) {
+    now_ = new_val;
+    now_is_valid_ = true;
+  }
+
+  static void TestOnlyGlobalInit(gpr_timespec new_val);
+
+  /** Global initialization for ExecCtx. Called by iomgr. */
+  static void GlobalInit(void);
+
+  /** Global shutdown for ExecCtx. Called by iomgr. */
+  static void GlobalShutdown(void) { gpr_tls_destroy(&exec_ctx_); }
+
+  /** Gets pointer to current exec_ctx. */
+  static ExecCtx* Get() {
+    return reinterpret_cast<ExecCtx*>(gpr_tls_get(&exec_ctx_));
+  }
+
+  static void Set(ExecCtx* exec_ctx) {
+    gpr_tls_set(&exec_ctx_, reinterpret_cast<intptr_t>(exec_ctx));
+  }
+
+ protected:
+  /** Check if ready to finish. */
+  virtual bool CheckReadyToFinish() { return false; }
+
+  /** Disallow delete on ExecCtx. */
+  static void operator delete(void* p) { abort(); }
+
+ private:
+  /** Set exec_ctx_ to exec_ctx. */
+
+  grpc_closure_list closure_list_ = GRPC_CLOSURE_LIST_INIT;
+  CombinerData combiner_data_ = {nullptr, nullptr};
+  uintptr_t flags_;
+
+  unsigned starting_cpu_ = gpr_cpu_current_cpu();
+
+  bool now_is_valid_ = false;
+  grpc_millis now_ = 0;
+
+  GPR_TLS_CLASS_DECL(exec_ctx_);
+  ExecCtx* last_exec_ctx_ = Get();
+};
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_IOMGR_EXEC_CTX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/executor.cc b/third_party/grpc/src/core/lib/iomgr/executor.cc
new file mode 100644
index 0000000..45d96b8
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/executor.cc
@@ -0,0 +1,433 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/executor.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/tls.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+#define MAX_DEPTH 2
+
+#define EXECUTOR_TRACE(format, ...)                     \
+  if (executor_trace.enabled()) {                       \
+    gpr_log(GPR_INFO, "EXECUTOR " format, __VA_ARGS__); \
+  }
+
+#define EXECUTOR_TRACE0(str)            \
+  if (executor_trace.enabled()) {       \
+    gpr_log(GPR_INFO, "EXECUTOR " str); \
+  }
+
+grpc_core::TraceFlag executor_trace(false, "executor");
+
+GPR_TLS_DECL(g_this_thread_state);
+
+GrpcExecutor::GrpcExecutor(const char* name) : name_(name) {
+  adding_thread_lock_ = GPR_SPINLOCK_STATIC_INITIALIZER;
+  gpr_atm_rel_store(&num_threads_, 0);
+  max_threads_ = GPR_MAX(1, 2 * gpr_cpu_num_cores());
+}
+
+void GrpcExecutor::Init() { SetThreading(true); }
+
+size_t GrpcExecutor::RunClosures(const char* executor_name,
+                                 grpc_closure_list list) {
+  size_t n = 0;
+
+  grpc_closure* c = list.head;
+  while (c != nullptr) {
+    grpc_closure* next = c->next_data.next;
+    grpc_error* error = c->error_data.error;
+#ifndef NDEBUG
+    EXECUTOR_TRACE("(%s) run %p [created by %s:%d]", executor_name, c,
+                   c->file_created, c->line_created);
+    c->scheduled = false;
+#else
+    EXECUTOR_TRACE("(%s) run %p", executor_name, c);
+#endif
+    c->cb(c->cb_arg, error);
+    GRPC_ERROR_UNREF(error);
+    c = next;
+    n++;
+    grpc_core::ExecCtx::Get()->Flush();
+  }
+
+  return n;
+}
+
+bool GrpcExecutor::IsThreaded() const {
+  return gpr_atm_acq_load(&num_threads_) > 0;
+}
+
+void GrpcExecutor::SetThreading(bool threading) {
+  gpr_atm curr_num_threads = gpr_atm_acq_load(&num_threads_);
+  EXECUTOR_TRACE("(%s) SetThreading(%d) begin", name_, threading);
+
+  if (threading) {
+    if (curr_num_threads > 0) {
+      EXECUTOR_TRACE("(%s) SetThreading(true). curr_num_threads == 0", name_);
+      return;
+    }
+
+    GPR_ASSERT(num_threads_ == 0);
+    gpr_atm_rel_store(&num_threads_, 1);
+    gpr_tls_init(&g_this_thread_state);
+    thd_state_ = static_cast<ThreadState*>(
+        gpr_zalloc(sizeof(ThreadState) * max_threads_));
+
+    for (size_t i = 0; i < max_threads_; i++) {
+      gpr_mu_init(&thd_state_[i].mu);
+      gpr_cv_init(&thd_state_[i].cv);
+      thd_state_[i].id = i;
+      thd_state_[i].name = name_;
+      thd_state_[i].thd = grpc_core::Thread();
+      thd_state_[i].elems = GRPC_CLOSURE_LIST_INIT;
+    }
+
+    thd_state_[0].thd =
+        grpc_core::Thread(name_, &GrpcExecutor::ThreadMain, &thd_state_[0]);
+    thd_state_[0].thd.Start();
+  } else {  // !threading
+    if (curr_num_threads == 0) {
+      EXECUTOR_TRACE("(%s) SetThreading(false). curr_num_threads == 0", name_);
+      return;
+    }
+
+    for (size_t i = 0; i < max_threads_; i++) {
+      gpr_mu_lock(&thd_state_[i].mu);
+      thd_state_[i].shutdown = true;
+      gpr_cv_signal(&thd_state_[i].cv);
+      gpr_mu_unlock(&thd_state_[i].mu);
+    }
+
+    /* Ensure no thread is adding a new thread. Once this is past, then no
+     * thread will try to add a new one either (since shutdown is true) */
+    gpr_spinlock_lock(&adding_thread_lock_);
+    gpr_spinlock_unlock(&adding_thread_lock_);
+
+    curr_num_threads = gpr_atm_no_barrier_load(&num_threads_);
+    for (gpr_atm i = 0; i < curr_num_threads; i++) {
+      thd_state_[i].thd.Join();
+      EXECUTOR_TRACE("(%s) Thread %" PRIdPTR " of %" PRIdPTR " joined", name_,
+                     i + 1, curr_num_threads);
+    }
+
+    gpr_atm_rel_store(&num_threads_, 0);
+    for (size_t i = 0; i < max_threads_; i++) {
+      gpr_mu_destroy(&thd_state_[i].mu);
+      gpr_cv_destroy(&thd_state_[i].cv);
+      RunClosures(thd_state_[i].name, thd_state_[i].elems);
+    }
+
+    gpr_free(thd_state_);
+    gpr_tls_destroy(&g_this_thread_state);
+  }
+
+  EXECUTOR_TRACE("(%s) SetThreading(%d) done", name_, threading);
+}
+
+void GrpcExecutor::Shutdown() { SetThreading(false); }
+
+void GrpcExecutor::ThreadMain(void* arg) {
+  ThreadState* ts = static_cast<ThreadState*>(arg);
+  gpr_tls_set(&g_this_thread_state, reinterpret_cast<intptr_t>(ts));
+
+  grpc_core::ExecCtx exec_ctx(GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD);
+
+  size_t subtract_depth = 0;
+  for (;;) {
+    EXECUTOR_TRACE("(%s) [%" PRIdPTR "]: step (sub_depth=%" PRIdPTR ")",
+                   ts->name, ts->id, subtract_depth);
+
+    gpr_mu_lock(&ts->mu);
+    ts->depth -= subtract_depth;
+    // Wait for closures to be enqueued or for the executor to be shutdown
+    while (grpc_closure_list_empty(ts->elems) && !ts->shutdown) {
+      ts->queued_long_job = false;
+      gpr_cv_wait(&ts->cv, &ts->mu, gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    }
+
+    if (ts->shutdown) {
+      EXECUTOR_TRACE("(%s) [%" PRIdPTR "]: shutdown", ts->name, ts->id);
+      gpr_mu_unlock(&ts->mu);
+      break;
+    }
+
+    GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED();
+    grpc_closure_list closures = ts->elems;
+    ts->elems = GRPC_CLOSURE_LIST_INIT;
+    gpr_mu_unlock(&ts->mu);
+
+    EXECUTOR_TRACE("(%s) [%" PRIdPTR "]: execute", ts->name, ts->id);
+
+    grpc_core::ExecCtx::Get()->InvalidateNow();
+    subtract_depth = RunClosures(ts->name, closures);
+  }
+}
+
+void GrpcExecutor::Enqueue(grpc_closure* closure, grpc_error* error,
+                           bool is_short) {
+  bool retry_push;
+  if (is_short) {
+    GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS();
+  } else {
+    GRPC_STATS_INC_EXECUTOR_SCHEDULED_LONG_ITEMS();
+  }
+
+  do {
+    retry_push = false;
+    size_t cur_thread_count =
+        static_cast<size_t>(gpr_atm_acq_load(&num_threads_));
+
+    // If the number of threads is zero(i.e either the executor is not threaded
+    // or already shutdown), then queue the closure on the exec context itself
+    if (cur_thread_count == 0) {
+#ifndef NDEBUG
+      EXECUTOR_TRACE("(%s) schedule %p (created %s:%d) inline", name_, closure,
+                     closure->file_created, closure->line_created);
+#else
+      EXECUTOR_TRACE("(%s) schedule %p inline", name_, closure);
+#endif
+      grpc_closure_list_append(grpc_core::ExecCtx::Get()->closure_list(),
+                               closure, error);
+      return;
+    }
+
+    ThreadState* ts = (ThreadState*)gpr_tls_get(&g_this_thread_state);
+    if (ts == nullptr) {
+      ts = &thd_state_[GPR_HASH_POINTER(grpc_core::ExecCtx::Get(),
+                                        cur_thread_count)];
+    } else {
+      GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF();
+    }
+
+    ThreadState* orig_ts = ts;
+    bool try_new_thread = false;
+
+    for (;;) {
+#ifndef NDEBUG
+      EXECUTOR_TRACE(
+          "(%s) try to schedule %p (%s) (created %s:%d) to thread "
+          "%" PRIdPTR,
+          name_, closure, is_short ? "short" : "long", closure->file_created,
+          closure->line_created, ts->id);
+#else
+      EXECUTOR_TRACE("(%s) try to schedule %p (%s) to thread %" PRIdPTR, name_,
+                     closure, is_short ? "short" : "long", ts->id);
+#endif
+
+      gpr_mu_lock(&ts->mu);
+      if (ts->queued_long_job) {
+        // if there's a long job queued, we never queue anything else to this
+        // queue (since long jobs can take 'infinite' time and we need to
+        // guarantee no starvation). Spin through queues and try again
+        gpr_mu_unlock(&ts->mu);
+        size_t idx = ts->id;
+        ts = &thd_state_[(idx + 1) % cur_thread_count];
+        if (ts == orig_ts) {
+          // We cycled through all the threads. Retry enqueue again by creating
+          // a new thread
+          //
+          // TODO (sreek): There is a potential issue here. We are
+          // unconditionally setting try_new_thread to true here. What if the
+          // executor is shutdown OR if cur_thread_count is already equal to
+          // max_threads ?
+          // (Fortunately, this is not an issue yet (as of july 2018) because
+          // there is only one instance of long job in gRPC and hence we will
+          // not hit this code path)
+          retry_push = true;
+          try_new_thread = true;
+          break;
+        }
+
+        continue;  // Try the next thread-state
+      }
+
+      // == Found the thread state (i.e thread) to enqueue this closure! ==
+
+      // Also, if this thread has been waiting for closures, wake it up.
+      // - If grpc_closure_list_empty() is true and the Executor is not
+      //   shutdown, it means that the thread must be waiting in ThreadMain()
+      // - Note that gpr_cv_signal() won't immediately wakeup the thread. That
+      //   happens after we release the mutex &ts->mu a few lines below
+      if (grpc_closure_list_empty(ts->elems) && !ts->shutdown) {
+        GRPC_STATS_INC_EXECUTOR_WAKEUP_INITIATED();
+        gpr_cv_signal(&ts->cv);
+      }
+
+      grpc_closure_list_append(&ts->elems, closure, error);
+
+      // If we already queued more than MAX_DEPTH number of closures on this
+      // thread, use this as a hint to create more threads
+      ts->depth++;
+      try_new_thread = ts->depth > MAX_DEPTH &&
+                       cur_thread_count < max_threads_ && !ts->shutdown;
+
+      ts->queued_long_job = !is_short;
+
+      gpr_mu_unlock(&ts->mu);
+      break;
+    }
+
+    if (try_new_thread && gpr_spinlock_trylock(&adding_thread_lock_)) {
+      cur_thread_count = static_cast<size_t>(gpr_atm_acq_load(&num_threads_));
+      if (cur_thread_count < max_threads_) {
+        // Increment num_threads (safe to do a store instead of a cas because we
+        // always increment num_threads under the 'adding_thread_lock')
+        gpr_atm_rel_store(&num_threads_, cur_thread_count + 1);
+
+        thd_state_[cur_thread_count].thd = grpc_core::Thread(
+            name_, &GrpcExecutor::ThreadMain, &thd_state_[cur_thread_count]);
+        thd_state_[cur_thread_count].thd.Start();
+      }
+      gpr_spinlock_unlock(&adding_thread_lock_);
+    }
+
+    if (retry_push) {
+      GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES();
+    }
+  } while (retry_push);
+}
+
+static GrpcExecutor* executors[GRPC_NUM_EXECUTORS];
+
+void default_enqueue_short(grpc_closure* closure, grpc_error* error) {
+  executors[GRPC_DEFAULT_EXECUTOR]->Enqueue(closure, error,
+                                            true /* is_short */);
+}
+
+void default_enqueue_long(grpc_closure* closure, grpc_error* error) {
+  executors[GRPC_DEFAULT_EXECUTOR]->Enqueue(closure, error,
+                                            false /* is_short */);
+}
+
+void resolver_enqueue_short(grpc_closure* closure, grpc_error* error) {
+  executors[GRPC_RESOLVER_EXECUTOR]->Enqueue(closure, error,
+                                             true /* is_short */);
+}
+
+void resolver_enqueue_long(grpc_closure* closure, grpc_error* error) {
+  executors[GRPC_RESOLVER_EXECUTOR]->Enqueue(closure, error,
+                                             false /* is_short */);
+}
+
+static const grpc_closure_scheduler_vtable
+    vtables_[GRPC_NUM_EXECUTORS][GRPC_NUM_EXECUTOR_JOB_TYPES] = {
+        {{&default_enqueue_short, &default_enqueue_short, "def-ex-short"},
+         {&default_enqueue_long, &default_enqueue_long, "def-ex-long"}},
+        {{&resolver_enqueue_short, &resolver_enqueue_short, "res-ex-short"},
+         {&resolver_enqueue_long, &resolver_enqueue_long, "res-ex-long"}}};
+
+static grpc_closure_scheduler
+    schedulers_[GRPC_NUM_EXECUTORS][GRPC_NUM_EXECUTOR_JOB_TYPES] = {
+        {{&vtables_[GRPC_DEFAULT_EXECUTOR][GRPC_EXECUTOR_SHORT]},
+         {&vtables_[GRPC_DEFAULT_EXECUTOR][GRPC_EXECUTOR_LONG]}},
+        {{&vtables_[GRPC_RESOLVER_EXECUTOR][GRPC_EXECUTOR_SHORT]},
+         {&vtables_[GRPC_RESOLVER_EXECUTOR][GRPC_EXECUTOR_LONG]}}};
+
+// grpc_executor_init() and grpc_executor_shutdown() functions are called in the
+// the grpc_init() and grpc_shutdown() code paths which are protected by a
+// global mutex. So it is okay to assume that these functions are thread-safe
+void grpc_executor_init() {
+  EXECUTOR_TRACE0("grpc_executor_init() enter");
+
+  // Return if grpc_executor_init() is already called earlier
+  if (executors[GRPC_DEFAULT_EXECUTOR] != nullptr) {
+    GPR_ASSERT(executors[GRPC_RESOLVER_EXECUTOR] != nullptr);
+    return;
+  }
+
+  executors[GRPC_DEFAULT_EXECUTOR] =
+      grpc_core::New<GrpcExecutor>("default-executor");
+  executors[GRPC_RESOLVER_EXECUTOR] =
+      grpc_core::New<GrpcExecutor>("resolver-executor");
+
+  executors[GRPC_DEFAULT_EXECUTOR]->Init();
+  executors[GRPC_RESOLVER_EXECUTOR]->Init();
+
+  EXECUTOR_TRACE0("grpc_executor_init() done");
+}
+
+grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorType executor_type,
+                                                GrpcExecutorJobType job_type) {
+  return &schedulers_[executor_type][job_type];
+}
+
+grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorJobType job_type) {
+  return grpc_executor_scheduler(GRPC_DEFAULT_EXECUTOR, job_type);
+}
+
+void grpc_executor_shutdown() {
+  EXECUTOR_TRACE0("grpc_executor_shutdown() enter");
+
+  // Return if grpc_executor_shutdown() is already called earlier
+  if (executors[GRPC_DEFAULT_EXECUTOR] == nullptr) {
+    GPR_ASSERT(executors[GRPC_RESOLVER_EXECUTOR] == nullptr);
+    return;
+  }
+
+  executors[GRPC_DEFAULT_EXECUTOR]->Shutdown();
+  executors[GRPC_RESOLVER_EXECUTOR]->Shutdown();
+
+  // Delete the executor objects.
+  //
+  // NOTE: It is important to call Shutdown() on all executors first before
+  // calling Delete() because it is possible for one executor (that is not
+  // shutdown yet) to call Enqueue() on a different executor which is already
+  // shutdown. This is legal and in such cases, the Enqueue() operation
+  // effectively "fails" and enqueues that closure on the calling thread's
+  // exec_ctx.
+  //
+  // By ensuring that all executors are shutdown first, we are also ensuring
+  // that no thread is active across all executors.
+
+  grpc_core::Delete<GrpcExecutor>(executors[GRPC_DEFAULT_EXECUTOR]);
+  grpc_core::Delete<GrpcExecutor>(executors[GRPC_RESOLVER_EXECUTOR]);
+  executors[GRPC_DEFAULT_EXECUTOR] = nullptr;
+  executors[GRPC_RESOLVER_EXECUTOR] = nullptr;
+
+  EXECUTOR_TRACE0("grpc_executor_shutdown() done");
+}
+
+bool grpc_executor_is_threaded(GrpcExecutorType executor_type) {
+  GPR_ASSERT(executor_type < GRPC_NUM_EXECUTORS);
+  return executors[executor_type]->IsThreaded();
+}
+
+bool grpc_executor_is_threaded() {
+  return grpc_executor_is_threaded(GRPC_DEFAULT_EXECUTOR);
+}
+
+void grpc_executor_set_threading(bool enable) {
+  EXECUTOR_TRACE("grpc_executor_set_threading(%d) called", enable);
+  for (int i = 0; i < GRPC_NUM_EXECUTORS; i++) {
+    executors[i]->SetThreading(enable);
+  }
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/executor.h b/third_party/grpc/src/core/lib/iomgr/executor.h
new file mode 100644
index 0000000..8829138
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/executor.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_EXECUTOR_H
+#define GRPC_CORE_LIB_IOMGR_EXECUTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gpr/spinlock.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/closure.h"
+
+typedef struct {
+  gpr_mu mu;
+  size_t id;         // For debugging purposes
+  const char* name;  // Thread state name
+  gpr_cv cv;
+  grpc_closure_list elems;
+  size_t depth;  // Number of closures in the closure list
+  bool shutdown;
+  bool queued_long_job;
+  grpc_core::Thread thd;
+} ThreadState;
+
+typedef enum {
+  GRPC_EXECUTOR_SHORT = 0,
+  GRPC_EXECUTOR_LONG,
+  GRPC_NUM_EXECUTOR_JOB_TYPES  // Add new values above this
+} GrpcExecutorJobType;
+
+class GrpcExecutor {
+ public:
+  GrpcExecutor(const char* executor_name);
+
+  void Init();
+
+  /** Is the executor multi-threaded? */
+  bool IsThreaded() const;
+
+  /* Enable/disable threading - must be called after Init and Shutdown() */
+  void SetThreading(bool threading);
+
+  /** Shutdown the executor, running all pending work as part of the call */
+  void Shutdown();
+
+  /** Enqueue the closure onto the executor. is_short is true if the closure is
+   * a short job (i.e expected to not block and complete quickly) */
+  void Enqueue(grpc_closure* closure, grpc_error* error, bool is_short);
+
+ private:
+  static size_t RunClosures(const char* executor_name, grpc_closure_list list);
+  static void ThreadMain(void* arg);
+
+  const char* name_;
+  ThreadState* thd_state_;
+  size_t max_threads_;
+  gpr_atm num_threads_;
+  gpr_spinlock adding_thread_lock_;
+};
+
+// == Global executor functions ==
+
+typedef enum {
+  GRPC_DEFAULT_EXECUTOR = 0,
+  GRPC_RESOLVER_EXECUTOR,
+
+  GRPC_NUM_EXECUTORS  // Add new values above this
+} GrpcExecutorType;
+
+// TODO(sreek): Currently we have two executors (available globally): The
+// default executor and the resolver executor.
+//
+// Some of the functions below operate on the DEFAULT executor only while some
+// operate of ALL the executors. This is a bit confusing and should be cleaned
+// up in future (where we make all the following functions take executor_type
+// and/or job_type)
+
+// Initialize ALL the executors
+void grpc_executor_init();
+
+// Shutdown ALL the executors
+void grpc_executor_shutdown();
+
+// Set the threading mode for ALL the executors
+void grpc_executor_set_threading(bool enable);
+
+// Get the DEFAULT executor scheduler for the given job_type
+grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorJobType job_type);
+
+// Get the executor scheduler for a given executor_type and a job_type
+grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorType executor_type,
+                                                GrpcExecutorJobType job_type);
+
+// Return if a given executor is running in threaded mode (i.e if
+// grpc_executor_set_threading(true) was called previously on that executor)
+bool grpc_executor_is_threaded(GrpcExecutorType executor_type);
+
+// Return if the DEFAULT executor is threaded
+bool grpc_executor_is_threaded();
+
+#endif /* GRPC_CORE_LIB_IOMGR_EXECUTOR_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/fork_posix.cc b/third_party/grpc/src/core/lib/iomgr/fork_posix.cc
new file mode 100644
index 0000000..05ecd2a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/fork_posix.cc
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_FORK
+
+#include <string.h>
+
+#include <grpc/fork.h>
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gprpp/fork.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/timer_manager.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+
+/*
+ * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK
+ *       AROUND VERY SPECIFIC USE CASES.
+ */
+
+namespace {
+bool skipped_handler = true;
+bool registered_handlers = false;
+}  // namespace
+
+void grpc_prefork() {
+  grpc_core::ExecCtx exec_ctx;
+  skipped_handler = true;
+  if (!grpc_is_initialized()) {
+    return;
+  }
+  if (!grpc_core::Fork::Enabled()) {
+    gpr_log(GPR_ERROR,
+            "Fork support not enabled; try running with the "
+            "environment variable GRPC_ENABLE_FORK_SUPPORT=1");
+    return;
+  }
+  if (strcmp(grpc_get_poll_strategy_name(), "epoll1") != 0 &&
+      strcmp(grpc_get_poll_strategy_name(), "poll") != 0) {
+    gpr_log(GPR_INFO,
+            "Fork support is only compatible with the epoll1 and poll polling "
+            "strategies");
+  }
+  if (!grpc_core::Fork::BlockExecCtx()) {
+    gpr_log(GPR_INFO,
+            "Other threads are currently calling into gRPC, skipping fork() "
+            "handlers");
+    return;
+  }
+  grpc_timer_manager_set_threading(false);
+  grpc_executor_set_threading(false);
+  grpc_core::ExecCtx::Get()->Flush();
+  grpc_core::Fork::AwaitThreads();
+  skipped_handler = false;
+}
+
+void grpc_postfork_parent() {
+  if (!skipped_handler) {
+    grpc_core::Fork::AllowExecCtx();
+    grpc_core::ExecCtx exec_ctx;
+    grpc_timer_manager_set_threading(true);
+    grpc_executor_set_threading(true);
+  }
+}
+
+void grpc_postfork_child() {
+  if (!skipped_handler) {
+    grpc_core::Fork::AllowExecCtx();
+    grpc_core::ExecCtx exec_ctx;
+    grpc_core::Fork::child_postfork_func reset_polling_engine =
+        grpc_core::Fork::GetResetChildPollingEngineFunc();
+    if (reset_polling_engine != nullptr) {
+      reset_polling_engine();
+    }
+    grpc_timer_manager_set_threading(true);
+    grpc_executor_set_threading(true);
+  }
+}
+
+void grpc_fork_handlers_auto_register() {
+  if (grpc_core::Fork::Enabled() & !registered_handlers) {
+#ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
+    pthread_atfork(grpc_prefork, grpc_postfork_parent, grpc_postfork_child);
+    registered_handlers = true;
+#endif  // GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
+  }
+}
+
+#endif  // GRPC_POSIX_FORK
diff --git a/third_party/grpc/src/core/lib/iomgr/fork_windows.cc b/third_party/grpc/src/core/lib/iomgr/fork_windows.cc
new file mode 100644
index 0000000..798f671
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/fork_windows.cc
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifndef GRPC_POSIX_FORK
+
+#include <grpc/fork.h>
+#include <grpc/support/log.h>
+
+/*
+ * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK
+ *       AROUND VERY SPECIFIC USE CASES.
+ */
+
+void grpc_prefork() { gpr_log(GPR_ERROR, "Forking not supported on Windows"); }
+
+void grpc_postfork_parent() {}
+
+void grpc_postfork_child() {}
+
+void grpc_fork_handlers_auto_register() {}
+
+#endif  // GRPC_POSIX_FORK
diff --git a/third_party/grpc/src/core/lib/iomgr/gethostname.h b/third_party/grpc/src/core/lib/iomgr/gethostname.h
new file mode 100644
index 0000000..9f10b4a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/gethostname.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H
+#define GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H
+
+// Returns the hostname of the local machine.
+// Caller takes ownership of result.
+char* grpc_gethostname();
+
+#endif /* GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/gethostname_fallback.cc b/third_party/grpc/src/core/lib/iomgr/gethostname_fallback.cc
new file mode 100644
index 0000000..65ae818
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/gethostname_fallback.cc
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/gethostname.h"
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_GETHOSTNAME_FALLBACK
+
+#include <stddef.h>
+
+char* grpc_gethostname() { return NULL; }
+
+#endif  // GRPC_GETHOSTNAME_FALLBACK
diff --git a/third_party/grpc/src/core/lib/iomgr/gethostname_host_name_max.cc b/third_party/grpc/src/core/lib/iomgr/gethostname_host_name_max.cc
new file mode 100644
index 0000000..79f5daa
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/gethostname_host_name_max.cc
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/gethostname.h"
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_HOST_NAME_MAX
+
+#include <limits.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+
+char* grpc_gethostname() {
+  char* hostname = static_cast<char*>(gpr_malloc(HOST_NAME_MAX));
+  if (gethostname(hostname, HOST_NAME_MAX) != 0) {
+    gpr_free(hostname);
+    return nullptr;
+  }
+  return hostname;
+}
+
+#endif  // GRPC_POSIX_HOST_NAME_MAX
diff --git a/third_party/grpc/src/core/lib/iomgr/gethostname_sysconf.cc b/third_party/grpc/src/core/lib/iomgr/gethostname_sysconf.cc
new file mode 100644
index 0000000..92c5de3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/gethostname_sysconf.cc
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/gethostname.h"
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SYSCONF
+
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+
+char* grpc_gethostname() {
+  size_t host_name_max = (size_t)sysconf(_SC_HOST_NAME_MAX);
+  char* hostname = (char*)gpr_malloc(host_name_max);
+  if (gethostname(hostname, host_name_max) != 0) {
+    gpr_free(hostname);
+    return nullptr;
+  }
+  return hostname;
+}
+
+#endif  // GRPC_POSIX_SYSCONF
diff --git a/third_party/grpc/src/core/lib/iomgr/gevent_util.h b/third_party/grpc/src/core/lib/iomgr/gevent_util.h
new file mode 100644
index 0000000..de30543
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/gevent_util.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_GEVENT_UTIL_H
+#define GRPC_CORE_LIB_IOMGR_GEVENT_UTIL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/slice.h>
+#include <grpc/status.h>
+#include "src/core/lib/iomgr/error.h"
+
+// These are only used by the gRPC Python extension for gevent
+// support.  They are easier to define here (rather than in Cython)
+// because Cython doesn't handle #defines well.
+
+grpc_error* grpc_socket_error(char* error) {
+  return grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error),
+                            GRPC_ERROR_INT_GRPC_STATUS,
+                            GRPC_STATUS_UNAVAILABLE);
+}
+
+char* grpc_slice_buffer_start(grpc_slice_buffer* buffer, int i) {
+  return (char*)GRPC_SLICE_START_PTR(buffer->slices[i]);
+}
+
+int grpc_slice_buffer_length(grpc_slice_buffer* buffer, int i) {
+  return GRPC_SLICE_LENGTH(buffer->slices[i]);
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/internal_errqueue.cc b/third_party/grpc/src/core/lib/iomgr/internal_errqueue.cc
new file mode 100644
index 0000000..982d709
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/internal_errqueue.cc
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/impl/codegen/log.h>
+#include "src/core/lib/iomgr/internal_errqueue.h"
+
+#ifdef GRPC_POSIX_SOCKET_TCP
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+namespace grpc_core {
+static bool errqueue_supported = false;
+
+bool kernel_supports_errqueue() { return errqueue_supported; }
+
+void grpc_errqueue_init() {
+/* Both-compile time and run-time linux kernel versions should be atleast 4.0.0
+ */
+#ifdef LINUX_VERSION_CODE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+  struct utsname buffer;
+  if (uname(&buffer) != 0) {
+    gpr_log(GPR_ERROR, "uname: %s", strerror(errno));
+    return;
+  }
+  char* release = buffer.release;
+  if (release == nullptr) {
+    return;
+  }
+
+  if (strtol(release, nullptr, 10) >= 4) {
+    errqueue_supported = true;
+  } else {
+    gpr_log(GPR_DEBUG, "ERRQUEUE support not enabled");
+  }
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(4, 0, 0) */
+#endif /* LINUX_VERSION_CODE */
+}
+} /* namespace grpc_core */
+
+#else
+
+namespace grpc_core {
+void grpc_errqueue_init() {}
+} /* namespace grpc_core */
+
+#endif /* GRPC_POSIX_SOCKET_TCP */
diff --git a/third_party/grpc/src/core/lib/iomgr/internal_errqueue.h b/third_party/grpc/src/core/lib/iomgr/internal_errqueue.h
new file mode 100644
index 0000000..f8644c2
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/internal_errqueue.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* This file contains constants defined in <linux/errqueue.h> and
+ * <linux/net_tstamp.h> so as to allow collecting network timestamps in the
+ * kernel. This file allows tcp_posix.cc to compile on platforms that do not
+ * have <linux/errqueue.h> and <linux/net_tstamp.h>.
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_INTERNAL_ERRQUEUE_H
+#define GRPC_CORE_LIB_IOMGR_INTERNAL_ERRQUEUE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_TCP
+
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef GRPC_LINUX_ERRQUEUE
+#include <linux/errqueue.h>
+#include <linux/net_tstamp.h>
+#include <sys/socket.h>
+#endif /* GRPC_LINUX_ERRQUEUE */
+
+namespace grpc_core {
+
+#ifdef GRPC_LINUX_ERRQUEUE
+
+/* Redefining scm_timestamping in the same way that <linux/errqueue.h> defines
+ * it, so that code compiles on systems that don't have it. */
+struct scm_timestamping {
+  struct timespec ts[3];
+};
+/* Also redefine timestamp types */
+/* The timestamp type for when the driver passed skb to NIC, or HW. */
+constexpr int SCM_TSTAMP_SND = 0;
+/* The timestamp type for when data entered the packet scheduler. */
+constexpr int SCM_TSTAMP_SCHED = 1;
+/* The timestamp type for when data acknowledged by peer. */
+constexpr int SCM_TSTAMP_ACK = 2;
+/* Redefine required constants from <linux/net_tstamp.h> */
+constexpr uint32_t SOF_TIMESTAMPING_TX_SOFTWARE = 1u << 1;
+constexpr uint32_t SOF_TIMESTAMPING_SOFTWARE = 1u << 4;
+constexpr uint32_t SOF_TIMESTAMPING_OPT_ID = 1u << 7;
+constexpr uint32_t SOF_TIMESTAMPING_TX_SCHED = 1u << 8;
+constexpr uint32_t SOF_TIMESTAMPING_TX_ACK = 1u << 9;
+constexpr uint32_t SOF_TIMESTAMPING_OPT_TSONLY = 1u << 11;
+
+constexpr uint32_t kTimestampingSocketOptions = SOF_TIMESTAMPING_SOFTWARE |
+                                                SOF_TIMESTAMPING_OPT_ID |
+                                                SOF_TIMESTAMPING_OPT_TSONLY;
+constexpr uint32_t kTimestampingRecordingOptions =
+    SOF_TIMESTAMPING_TX_SCHED | SOF_TIMESTAMPING_TX_SOFTWARE |
+    SOF_TIMESTAMPING_TX_ACK;
+#endif /* GRPC_LINUX_ERRQUEUE */
+
+/* Returns true if kernel is capable of supporting errqueue and timestamping.
+ * Currently allowing only linux kernels above 4.0.0
+ */
+bool kernel_supports_errqueue();
+
+} /* namespace grpc_core */
+
+#endif /* GRPC_POSIX_SOCKET_TCP */
+
+namespace grpc_core {
+/* Initializes errqueue support */
+void grpc_errqueue_init();
+} /* namespace grpc_core */
+
+#endif /* GRPC_CORE_LIB_IOMGR_INTERNAL_ERRQUEUE_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/iocp_windows.cc b/third_party/grpc/src/core/lib/iomgr/iocp_windows.cc
new file mode 100644
index 0000000..ad325fe
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iocp_windows.cc
@@ -0,0 +1,157 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include <winsock2.h>
+#include <limits>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_windows.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+#include "src/core/lib/iomgr/timer.h"
+
+static ULONG g_iocp_kick_token;
+static OVERLAPPED g_iocp_custom_overlap;
+
+static gpr_atm g_custom_events = 0;
+
+static HANDLE g_iocp;
+
+static DWORD deadline_to_millis_timeout(grpc_millis deadline) {
+  if (deadline == GRPC_MILLIS_INF_FUTURE) {
+    return INFINITE;
+  }
+  grpc_millis now = grpc_core::ExecCtx::Get()->Now();
+  if (deadline < now) return 0;
+  grpc_millis timeout = deadline - now;
+  if (timeout > std::numeric_limits<DWORD>::max()) return INFINITE;
+  return static_cast<DWORD>(deadline - now);
+}
+
+grpc_iocp_work_status grpc_iocp_work(grpc_millis deadline) {
+  BOOL success;
+  DWORD bytes = 0;
+  DWORD flags = 0;
+  ULONG_PTR completion_key;
+  LPOVERLAPPED overlapped;
+  grpc_winsocket* socket;
+  grpc_winsocket_callback_info* info;
+  GRPC_STATS_INC_SYSCALL_POLL();
+  success =
+      GetQueuedCompletionStatus(g_iocp, &bytes, &completion_key, &overlapped,
+                                deadline_to_millis_timeout(deadline));
+  grpc_core::ExecCtx::Get()->InvalidateNow();
+  if (success == 0 && overlapped == NULL) {
+    return GRPC_IOCP_WORK_TIMEOUT;
+  }
+  GPR_ASSERT(completion_key && overlapped);
+  if (overlapped == &g_iocp_custom_overlap) {
+    gpr_atm_full_fetch_add(&g_custom_events, -1);
+    if (completion_key == (ULONG_PTR)&g_iocp_kick_token) {
+      /* We were awoken from a kick. */
+      return GRPC_IOCP_WORK_KICK;
+    }
+    gpr_log(GPR_ERROR, "Unknown custom completion key.");
+    abort();
+  }
+
+  socket = (grpc_winsocket*)completion_key;
+  if (overlapped == &socket->write_info.overlapped) {
+    info = &socket->write_info;
+  } else if (overlapped == &socket->read_info.overlapped) {
+    info = &socket->read_info;
+  } else {
+    abort();
+  }
+  if (socket->shutdown_called) {
+    info->bytes_transfered = 0;
+    info->wsa_error = WSA_OPERATION_ABORTED;
+  } else {
+    success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes,
+                                     FALSE, &flags);
+    info->bytes_transfered = bytes;
+    info->wsa_error = success ? 0 : WSAGetLastError();
+  }
+  GPR_ASSERT(overlapped == &info->overlapped);
+  grpc_socket_become_ready(socket, info);
+  return GRPC_IOCP_WORK_WORK;
+}
+
+void grpc_iocp_init(void) {
+  g_iocp =
+      CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0);
+  GPR_ASSERT(g_iocp);
+}
+
+void grpc_iocp_kick(void) {
+  BOOL success;
+
+  gpr_atm_full_fetch_add(&g_custom_events, 1);
+  success = PostQueuedCompletionStatus(g_iocp, 0, (ULONG_PTR)&g_iocp_kick_token,
+                                       &g_iocp_custom_overlap);
+  GPR_ASSERT(success);
+}
+
+void grpc_iocp_flush(void) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_iocp_work_status work_status;
+
+  do {
+    work_status = grpc_iocp_work(GRPC_MILLIS_INF_PAST);
+  } while (work_status == GRPC_IOCP_WORK_KICK ||
+           grpc_core::ExecCtx::Get()->Flush());
+}
+
+void grpc_iocp_shutdown(void) {
+  grpc_core::ExecCtx exec_ctx;
+  while (gpr_atm_acq_load(&g_custom_events)) {
+    grpc_iocp_work(GRPC_MILLIS_INF_FUTURE);
+    grpc_core::ExecCtx::Get()->Flush();
+  }
+
+  GPR_ASSERT(CloseHandle(g_iocp));
+}
+
+void grpc_iocp_add_socket(grpc_winsocket* socket) {
+  HANDLE ret;
+  if (socket->added_to_iocp) return;
+  ret = CreateIoCompletionPort((HANDLE)socket->socket, g_iocp,
+                               (uintptr_t)socket, 0);
+  if (!ret) {
+    char* utf8_message = gpr_format_message(WSAGetLastError());
+    gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message);
+    gpr_free(utf8_message);
+    __debugbreak();
+    abort();
+  }
+  socket->added_to_iocp = 1;
+  GPR_ASSERT(ret == g_iocp);
+}
+
+#endif /* GRPC_WINSOCK_SOCKET */
diff --git a/third_party/grpc/src/core/lib/iomgr/iocp_windows.h b/third_party/grpc/src/core/lib/iomgr/iocp_windows.h
new file mode 100644
index 0000000..68d9de6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iocp_windows.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+
+typedef enum {
+  GRPC_IOCP_WORK_WORK,
+  GRPC_IOCP_WORK_TIMEOUT,
+  GRPC_IOCP_WORK_KICK
+} grpc_iocp_work_status;
+
+grpc_iocp_work_status grpc_iocp_work(grpc_millis deadline);
+void grpc_iocp_init(void);
+void grpc_iocp_kick(void);
+void grpc_iocp_flush(void);
+void grpc_iocp_shutdown(void);
+void grpc_iocp_add_socket(grpc_winsocket*);
+
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr.cc b/third_party/grpc/src/core/lib/iomgr/iomgr.cc
new file mode 100644
index 0000000..eb29973
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr.cc
@@ -0,0 +1,187 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/iomgr.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/buffer_list.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/internal_errqueue.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/network_status_tracker.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/timer_manager.h"
+
+static gpr_mu g_mu;
+static gpr_cv g_rcv;
+static int g_shutdown;
+static grpc_iomgr_object g_root_object;
+
+void grpc_iomgr_init() {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_determine_iomgr_platform();
+  g_shutdown = 0;
+  gpr_mu_init(&g_mu);
+  gpr_cv_init(&g_rcv);
+  grpc_executor_init();
+  grpc_timer_list_init();
+  g_root_object.next = g_root_object.prev = &g_root_object;
+  g_root_object.name = (char*)"root";
+  grpc_network_status_init();
+  grpc_iomgr_platform_init();
+  grpc_core::grpc_errqueue_init();
+}
+
+void grpc_iomgr_start() { grpc_timer_manager_init(); }
+
+static size_t count_objects(void) {
+  grpc_iomgr_object* obj;
+  size_t n = 0;
+  for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) {
+    n++;
+  }
+  return n;
+}
+
+size_t grpc_iomgr_count_objects_for_testing(void) { return count_objects(); }
+
+static void dump_objects(const char* kind) {
+  grpc_iomgr_object* obj;
+  for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) {
+    gpr_log(GPR_DEBUG, "%s OBJECT: %s %p", kind, obj->name, obj);
+  }
+}
+
+void grpc_iomgr_shutdown() {
+  gpr_timespec shutdown_deadline = gpr_time_add(
+      gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN));
+  gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
+
+  {
+    grpc_timer_manager_shutdown();
+    grpc_iomgr_platform_flush();
+    grpc_executor_shutdown();
+
+    gpr_mu_lock(&g_mu);
+    g_shutdown = 1;
+    while (g_root_object.next != &g_root_object) {
+      if (gpr_time_cmp(
+              gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time),
+              gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) {
+        if (g_root_object.next != &g_root_object) {
+          gpr_log(GPR_DEBUG,
+                  "Waiting for %" PRIuPTR " iomgr objects to be destroyed",
+                  count_objects());
+        }
+        last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
+      }
+      grpc_core::ExecCtx::Get()->SetNowIomgrShutdown();
+      if (grpc_timer_check(nullptr) == GRPC_TIMERS_FIRED) {
+        gpr_mu_unlock(&g_mu);
+        grpc_core::ExecCtx::Get()->Flush();
+        grpc_iomgr_platform_flush();
+        gpr_mu_lock(&g_mu);
+        continue;
+      }
+      if (g_root_object.next != &g_root_object) {
+        if (grpc_iomgr_abort_on_leaks()) {
+          gpr_log(GPR_DEBUG,
+                  "Failed to free %" PRIuPTR
+                  " iomgr objects before shutdown deadline: "
+                  "memory leaks are likely",
+                  count_objects());
+          dump_objects("LEAKED");
+          abort();
+        }
+        gpr_timespec short_deadline =
+            gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                         gpr_time_from_millis(100, GPR_TIMESPAN));
+        if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline)) {
+          if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) >
+              0) {
+            if (g_root_object.next != &g_root_object) {
+              gpr_log(GPR_DEBUG,
+                      "Failed to free %" PRIuPTR
+                      " iomgr objects before shutdown deadline: "
+                      "memory leaks are likely",
+                      count_objects());
+              dump_objects("LEAKED");
+            }
+            break;
+          }
+        }
+      }
+    }
+    gpr_mu_unlock(&g_mu);
+    grpc_timer_list_shutdown();
+    grpc_core::ExecCtx::Get()->Flush();
+  }
+
+  /* ensure all threads have left g_mu */
+  gpr_mu_lock(&g_mu);
+  gpr_mu_unlock(&g_mu);
+
+  grpc_iomgr_platform_shutdown();
+  grpc_network_status_shutdown();
+  gpr_mu_destroy(&g_mu);
+  gpr_cv_destroy(&g_rcv);
+}
+
+void grpc_iomgr_shutdown_background_closure() {
+  grpc_iomgr_platform_shutdown_background_closure();
+}
+
+void grpc_iomgr_register_object(grpc_iomgr_object* obj, const char* name) {
+  obj->name = gpr_strdup(name);
+  gpr_mu_lock(&g_mu);
+  obj->next = &g_root_object;
+  obj->prev = g_root_object.prev;
+  obj->next->prev = obj->prev->next = obj;
+  gpr_mu_unlock(&g_mu);
+}
+
+void grpc_iomgr_unregister_object(grpc_iomgr_object* obj) {
+  gpr_mu_lock(&g_mu);
+  obj->next->prev = obj->prev;
+  obj->prev->next = obj->next;
+  gpr_cv_signal(&g_rcv);
+  gpr_mu_unlock(&g_mu);
+  gpr_free(obj->name);
+}
+
+bool grpc_iomgr_abort_on_leaks(void) {
+  char* env = gpr_getenv("GRPC_ABORT_ON_LEAKS");
+  bool should_we = gpr_is_true(env);
+  gpr_free(env);
+  return should_we;
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr.h b/third_party/grpc/src/core/lib/iomgr/iomgr.h
new file mode 100644
index 0000000..8ea9289
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_H
+#define GRPC_CORE_LIB_IOMGR_IOMGR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <stdlib.h>
+
+/** Initializes the iomgr. */
+void grpc_iomgr_init();
+
+/** Starts any background threads for iomgr. */
+void grpc_iomgr_start();
+
+/** Signals the intention to shutdown the iomgr. Expects to be able to flush
+ * exec_ctx. */
+void grpc_iomgr_shutdown();
+
+/** Signals the intention to shutdown all the closures registered in the
+ * background poller. */
+void grpc_iomgr_shutdown_background_closure();
+
+/* Exposed only for testing */
+size_t grpc_iomgr_count_objects_for_testing();
+
+#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_custom.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_custom.cc
new file mode 100644
index 0000000..4b112c9
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_custom.cc
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/thd_id.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/pollset_custom.h"
+#include "src/core/lib/iomgr/pollset_set_custom.h"
+#include "src/core/lib/iomgr/resolve_address_custom.h"
+
+gpr_thd_id g_init_thread;
+
+static void iomgr_platform_init(void) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_executor_set_threading(false);
+  g_init_thread = gpr_thd_currentid();
+  grpc_pollset_global_init();
+}
+static void iomgr_platform_flush(void) {}
+static void iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); }
+static void iomgr_platform_shutdown_background_closure(void) {}
+
+static grpc_iomgr_platform_vtable vtable = {
+    iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown,
+    iomgr_platform_shutdown_background_closure};
+
+void grpc_custom_iomgr_init(grpc_socket_vtable* socket,
+                            grpc_custom_resolver_vtable* resolver,
+                            grpc_custom_timer_vtable* timer,
+                            grpc_custom_poller_vtable* poller) {
+  grpc_custom_endpoint_init(socket);
+  grpc_custom_timer_init(timer);
+  grpc_custom_pollset_init(poller);
+  grpc_custom_pollset_set_init();
+  grpc_custom_resolver_init(resolver);
+  grpc_set_iomgr_platform_vtable(&vtable);
+}
+
+#ifdef GRPC_CUSTOM_SOCKET
+grpc_iomgr_platform_vtable* grpc_default_iomgr_platform_vtable() {
+  return &vtable;
+}
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_custom.h b/third_party/grpc/src/core/lib/iomgr/iomgr_custom.h
new file mode 100644
index 0000000..57cc2f9
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_custom.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H
+#define GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/pollset_custom.h"
+#include "src/core/lib/iomgr/resolve_address_custom.h"
+#include "src/core/lib/iomgr/tcp_custom.h"
+#include "src/core/lib/iomgr/timer_custom.h"
+
+#include <grpc/support/thd_id.h>
+
+/* The thread ID of the thread on which grpc was initialized. Used to verify
+ * that all calls into the custom iomgr are made on that same thread */
+extern gpr_thd_id g_init_thread;
+
+#ifdef GRPC_CUSTOM_IOMGR_THREAD_CHECK
+#define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD() \
+  GPR_ASSERT(gpr_thd_currentid() == g_init_thread)
+#else
+#define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD()
+#endif /* GRPC_CUSTOM_IOMGR_THREAD_CHECK */
+
+void grpc_custom_iomgr_init(grpc_socket_vtable* socket,
+                            grpc_custom_resolver_vtable* resolver,
+                            grpc_custom_timer_vtable* timer,
+                            grpc_custom_poller_vtable* poller);
+
+#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_internal.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_internal.cc
new file mode 100644
index 0000000..b6c9211
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_internal.cc
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/timer_manager.h"
+
+static grpc_iomgr_platform_vtable* iomgr_platform_vtable = nullptr;
+
+void grpc_set_iomgr_platform_vtable(grpc_iomgr_platform_vtable* vtable) {
+  iomgr_platform_vtable = vtable;
+}
+
+void grpc_determine_iomgr_platform() {
+  if (iomgr_platform_vtable == nullptr) {
+    grpc_set_default_iomgr_platform();
+  }
+}
+
+void grpc_iomgr_platform_init() { iomgr_platform_vtable->init(); }
+
+void grpc_iomgr_platform_flush() { iomgr_platform_vtable->flush(); }
+
+void grpc_iomgr_platform_shutdown() { iomgr_platform_vtable->shutdown(); }
+
+void grpc_iomgr_platform_shutdown_background_closure() {
+  iomgr_platform_vtable->shutdown_background_closure();
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_internal.h b/third_party/grpc/src/core/lib/iomgr/iomgr_internal.h
new file mode 100644
index 0000000..bca7409
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_internal.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H
+#define GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/lib/iomgr/iomgr.h"
+
+typedef struct grpc_iomgr_object {
+  char* name;
+  struct grpc_iomgr_object* next;
+  struct grpc_iomgr_object* prev;
+} grpc_iomgr_object;
+
+typedef struct grpc_iomgr_platform_vtable {
+  void (*init)(void);
+  void (*flush)(void);
+  void (*shutdown)(void);
+  void (*shutdown_background_closure)(void);
+} grpc_iomgr_platform_vtable;
+
+void grpc_iomgr_register_object(grpc_iomgr_object* obj, const char* name);
+void grpc_iomgr_unregister_object(grpc_iomgr_object* obj);
+
+void grpc_determine_iomgr_platform();
+
+void grpc_set_iomgr_platform_vtable(grpc_iomgr_platform_vtable* vtable);
+
+void grpc_set_default_iomgr_platform();
+
+void grpc_iomgr_platform_init(void);
+/** flush any globally queued work from iomgr */
+void grpc_iomgr_platform_flush(void);
+/** tear down all platform specific global iomgr structures */
+void grpc_iomgr_platform_shutdown(void);
+
+/** shut down all the closures registered in the background poller */
+void grpc_iomgr_platform_shutdown_background_closure(void);
+
+bool grpc_iomgr_abort_on_leaks(void);
+
+#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_posix.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_posix.cc
new file mode 100644
index 0000000..9386adf
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_posix.cc
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_IOMGR
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/iomgr_posix.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/iomgr/timer.h"
+
+extern grpc_tcp_server_vtable grpc_posix_tcp_server_vtable;
+extern grpc_tcp_client_vtable grpc_posix_tcp_client_vtable;
+extern grpc_timer_vtable grpc_generic_timer_vtable;
+extern grpc_pollset_vtable grpc_posix_pollset_vtable;
+extern grpc_pollset_set_vtable grpc_posix_pollset_set_vtable;
+extern grpc_address_resolver_vtable grpc_posix_resolver_vtable;
+
+static void iomgr_platform_init(void) {
+  grpc_wakeup_fd_global_init();
+  grpc_event_engine_init();
+}
+
+static void iomgr_platform_flush(void) {}
+
+static void iomgr_platform_shutdown(void) {
+  grpc_event_engine_shutdown();
+  grpc_wakeup_fd_global_destroy();
+}
+
+static void iomgr_platform_shutdown_background_closure(void) {
+  grpc_shutdown_background_closure();
+}
+
+static grpc_iomgr_platform_vtable vtable = {
+    iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown,
+    iomgr_platform_shutdown_background_closure};
+
+void grpc_set_default_iomgr_platform() {
+  grpc_set_tcp_client_impl(&grpc_posix_tcp_client_vtable);
+  grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable);
+  grpc_set_timer_impl(&grpc_generic_timer_vtable);
+  grpc_set_pollset_vtable(&grpc_posix_pollset_vtable);
+  grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable);
+  grpc_set_resolver_impl(&grpc_posix_resolver_vtable);
+  grpc_set_iomgr_platform_vtable(&vtable);
+}
+
+#endif /* GRPC_POSIX_SOCKET_IOMGR */
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_posix.h b/third_party/grpc/src/core/lib/iomgr/iomgr_posix.h
new file mode 100644
index 0000000..54ec46e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_posix.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/iomgr_internal.h"
+
+#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_posix_cfstream.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_posix_cfstream.cc
new file mode 100644
index 0000000..552ef43
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_posix_cfstream.cc
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_CFSTREAM_IOMGR
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/iomgr_posix.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/iomgr/timer.h"
+
+static const char* grpc_cfstream_env_var = "grpc_cfstream";
+
+extern grpc_tcp_server_vtable grpc_posix_tcp_server_vtable;
+extern grpc_tcp_client_vtable grpc_posix_tcp_client_vtable;
+extern grpc_tcp_client_vtable grpc_cfstream_client_vtable;
+extern grpc_timer_vtable grpc_generic_timer_vtable;
+extern grpc_pollset_vtable grpc_posix_pollset_vtable;
+extern grpc_pollset_set_vtable grpc_posix_pollset_set_vtable;
+extern grpc_address_resolver_vtable grpc_posix_resolver_vtable;
+
+static void iomgr_platform_init(void) {
+  grpc_wakeup_fd_global_init();
+  grpc_event_engine_init();
+}
+
+static void iomgr_platform_flush(void) {}
+
+static void iomgr_platform_shutdown(void) {
+  grpc_event_engine_shutdown();
+  grpc_wakeup_fd_global_destroy();
+}
+
+static void iomgr_platform_shutdown_background_closure(void) {
+  grpc_shutdown_background_closure();
+}
+
+static grpc_iomgr_platform_vtable vtable = {
+    iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown,
+    iomgr_platform_shutdown_background_closure};
+
+void grpc_set_default_iomgr_platform() {
+  char* enable_cfstream = getenv(grpc_cfstream_env_var);
+  grpc_tcp_client_vtable* client_vtable = &grpc_posix_tcp_client_vtable;
+  if (enable_cfstream != nullptr && enable_cfstream[0] == '1') {
+    client_vtable = &grpc_cfstream_client_vtable;
+  }
+  grpc_set_tcp_client_impl(client_vtable);
+  grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable);
+  grpc_set_timer_impl(&grpc_generic_timer_vtable);
+  grpc_set_pollset_vtable(&grpc_posix_pollset_vtable);
+  grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable);
+  grpc_set_resolver_impl(&grpc_posix_resolver_vtable);
+  grpc_set_iomgr_platform_vtable(&vtable);
+}
+
+#endif /* GRPC_CFSTREAM_IOMGR */
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_uv.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_uv.cc
new file mode 100644
index 0000000..4a98444
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_uv.cc
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#if defined(GRPC_CUSTOM_SOCKET) && defined(GRPC_UV)
+
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/pollset_custom.h"
+#include "src/core/lib/iomgr/tcp_custom.h"
+#include "src/core/lib/iomgr/timer_custom.h"
+
+extern grpc_socket_vtable grpc_uv_socket_vtable;
+extern grpc_custom_resolver_vtable uv_resolver_vtable;
+extern grpc_custom_timer_vtable uv_timer_vtable;
+extern grpc_custom_poller_vtable uv_pollset_vtable;
+
+void grpc_set_default_iomgr_platform() {
+  grpc_custom_iomgr_init(&grpc_uv_socket_vtable, &uv_resolver_vtable,
+                         &uv_timer_vtable, &uv_pollset_vtable);
+}
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_windows.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_windows.cc
new file mode 100644
index 0000000..24ef0db
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/iomgr_windows.cc
@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/sockaddr_windows.h"
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/pollset_windows.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/iomgr/timer.h"
+
+extern grpc_tcp_server_vtable grpc_windows_tcp_server_vtable;
+extern grpc_tcp_client_vtable grpc_windows_tcp_client_vtable;
+extern grpc_timer_vtable grpc_generic_timer_vtable;
+extern grpc_pollset_vtable grpc_windows_pollset_vtable;
+extern grpc_pollset_set_vtable grpc_windows_pollset_set_vtable;
+extern grpc_address_resolver_vtable grpc_windows_resolver_vtable;
+
+/* Windows' io manager is going to be fully designed using IO completion
+   ports. All of what we're doing here is basically make sure that
+   Windows sockets are initialized in and out. */
+
+static void winsock_init(void) {
+  WSADATA wsaData;
+  int status = WSAStartup(MAKEWORD(2, 0), &wsaData);
+  GPR_ASSERT(status == 0);
+}
+
+static void winsock_shutdown(void) {
+  int status = WSACleanup();
+  GPR_ASSERT(status == 0);
+}
+
+static void iomgr_platform_init(void) {
+  winsock_init();
+  grpc_iocp_init();
+  grpc_pollset_global_init();
+}
+
+static void iomgr_platform_flush(void) { grpc_iocp_flush(); }
+
+static void iomgr_platform_shutdown(void) {
+  grpc_pollset_global_shutdown();
+  grpc_iocp_shutdown();
+  winsock_shutdown();
+}
+
+static void iomgr_platform_shutdown_background_closure(void) {}
+
+static grpc_iomgr_platform_vtable vtable = {
+    iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown,
+    iomgr_platform_shutdown_background_closure};
+
+void grpc_set_default_iomgr_platform() {
+  grpc_set_tcp_client_impl(&grpc_windows_tcp_client_vtable);
+  grpc_set_tcp_server_impl(&grpc_windows_tcp_server_vtable);
+  grpc_set_timer_impl(&grpc_generic_timer_vtable);
+  grpc_set_pollset_vtable(&grpc_windows_pollset_vtable);
+  grpc_set_pollset_set_vtable(&grpc_windows_pollset_set_vtable);
+  grpc_set_resolver_impl(&grpc_windows_resolver_vtable);
+  grpc_set_iomgr_platform_vtable(&vtable);
+}
+
+#endif /* GRPC_WINSOCK_SOCKET */
diff --git a/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.cc b/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.cc
new file mode 100644
index 0000000..8df6a66
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.cc
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include "src/core/lib/iomgr/is_epollexclusive_available.h"
+
+#ifdef GRPC_LINUX_EPOLL_CREATE1
+
+#include <grpc/support/log.h>
+
+#include <errno.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+#include "src/core/lib/iomgr/sys_epoll_wrapper.h"
+
+/* This polling engine is only relevant on linux kernels supporting epoll() */
+bool grpc_is_epollexclusive_available(void) {
+  static bool logged_why_not = false;
+
+  int fd = epoll_create1(EPOLL_CLOEXEC);
+  if (fd < 0) {
+    if (!logged_why_not) {
+      gpr_log(GPR_DEBUG,
+              "epoll_create1 failed with error: %d. Not using epollex polling "
+              "engine.",
+              fd);
+      logged_why_not = true;
+    }
+    return false;
+  }
+  int evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+  if (evfd < 0) {
+    if (!logged_why_not) {
+      gpr_log(GPR_DEBUG,
+              "eventfd failed with error: %d. Not using epollex polling "
+              "engine.",
+              fd);
+      logged_why_not = true;
+    }
+    close(fd);
+    return false;
+  }
+  struct epoll_event ev;
+  /* choose events that should cause an error on
+     EPOLLEXCLUSIVE enabled kernels - specifically the combination of
+     EPOLLONESHOT and EPOLLEXCLUSIVE */
+  ev.events =
+      static_cast<uint32_t>(EPOLLET | EPOLLIN | EPOLLEXCLUSIVE | EPOLLONESHOT);
+  ev.data.ptr = nullptr;
+  if (epoll_ctl(fd, EPOLL_CTL_ADD, evfd, &ev) != 0) {
+    if (errno != EINVAL) {
+      if (!logged_why_not) {
+        gpr_log(
+            GPR_ERROR,
+            "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT failed with error: "
+            "%d. Not using epollex polling engine.",
+            errno);
+        logged_why_not = true;
+      }
+      close(fd);
+      close(evfd);
+      return false;
+    }
+  } else {
+    if (!logged_why_not) {
+      gpr_log(GPR_DEBUG,
+              "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT succeeded. This is "
+              "evidence of no EPOLLEXCLUSIVE support. Not using "
+              "epollex polling engine.");
+      logged_why_not = true;
+    }
+    close(fd);
+    close(evfd);
+    return false;
+  }
+  close(evfd);
+  close(fd);
+  return true;
+}
+
+#else
+
+bool grpc_is_epollexclusive_available(void) { return false; }
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.h b/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.h
new file mode 100644
index 0000000..8a44113
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H
+#define GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool grpc_is_epollexclusive_available(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/load_file.cc b/third_party/grpc/src/core/lib/iomgr/load_file.cc
new file mode 100644
index 0000000..f6431d0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/load_file.cc
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/load_file.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/block_annotate.h"
+
+grpc_error* grpc_load_file(const char* filename, int add_null_terminator,
+                           grpc_slice* output) {
+  unsigned char* contents = nullptr;
+  size_t contents_size = 0;
+  grpc_slice result = grpc_empty_slice();
+  FILE* file;
+  size_t bytes_read = 0;
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  GRPC_SCHEDULING_START_BLOCKING_REGION;
+  file = fopen(filename, "rb");
+  if (file == nullptr) {
+    error = GRPC_OS_ERROR(errno, "fopen");
+    goto end;
+  }
+  fseek(file, 0, SEEK_END);
+  /* Converting to size_t on the assumption that it will not fail */
+  contents_size = static_cast<size_t>(ftell(file));
+  fseek(file, 0, SEEK_SET);
+  contents = static_cast<unsigned char*>(
+      gpr_malloc(contents_size + (add_null_terminator ? 1 : 0)));
+  bytes_read = fread(contents, 1, contents_size, file);
+  if (bytes_read < contents_size) {
+    error = GRPC_OS_ERROR(errno, "fread");
+    GPR_ASSERT(ferror(file));
+    goto end;
+  }
+  if (add_null_terminator) {
+    contents[contents_size++] = 0;
+  }
+  result = grpc_slice_new(contents, contents_size, gpr_free);
+
+end:
+  *output = result;
+  if (file != nullptr) fclose(file);
+  if (error != GRPC_ERROR_NONE) {
+    grpc_error* error_out =
+        grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                               "Failed to load file", &error, 1),
+                           GRPC_ERROR_STR_FILENAME,
+                           grpc_slice_from_copied_string(
+                               filename));  // TODO(ncteisen), always static?
+    GRPC_ERROR_UNREF(error);
+    error = error_out;
+  }
+  GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX;
+  return error;
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/load_file.h b/third_party/grpc/src/core/lib/iomgr/load_file.h
new file mode 100644
index 0000000..1cb2b5d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/load_file.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_LOAD_FILE_H
+#define GRPC_CORE_LIB_IOMGR_LOAD_FILE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdio.h>
+
+#include <grpc/slice.h>
+
+#include "src/core/lib/iomgr/error.h"
+
+/* Loads the content of a file into a slice. add_null_terminator will add
+   a NULL terminator if non-zero. */
+grpc_error* grpc_load_file(const char* filename, int add_null_terminator,
+                           grpc_slice* slice);
+
+#endif /* GRPC_CORE_LIB_IOMGR_LOAD_FILE_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/lockfree_event.cc b/third_party/grpc/src/core/lib/iomgr/lockfree_event.cc
new file mode 100644
index 0000000..085fea4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/lockfree_event.cc
@@ -0,0 +1,254 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/lockfree_event.h"
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/debug/trace.h"
+
+extern grpc_core::TraceFlag grpc_polling_trace;
+
+/* 'state' holds the to call when the fd is readable or writable respectively.
+   It can contain one of the following values:
+     kClosureReady     : The fd has an I/O event of interest but there is no
+                         closure yet to execute
+
+     kClosureNotReady : The fd has no I/O event of interest
+
+     closure ptr       : The closure to be executed when the fd has an I/O
+                         event of interest
+
+     shutdown_error | kShutdownBit :
+                        'shutdown_error' field ORed with kShutdownBit.
+                         This indicates that the fd is shutdown. Since all
+                         memory allocations are word-aligned, the lower two
+                         bits of the shutdown_error pointer are always 0. So
+                         it is safe to OR these with kShutdownBit
+
+   Valid state transitions:
+
+     <closure ptr> <-----3------ kClosureNotReady -----1------->  kClosureReady
+       |  |                         ^   |    ^                         |  |
+       |  |                         |   |    |                         |  |
+       |  +--------------4----------+   6    +---------2---------------+  |
+       |                                |                                 |
+       |                                v                                 |
+       +-----5------->  [shutdown_error | kShutdownBit] <-------7---------+
+
+    For 1, 4 : See SetReady() function
+    For 2, 3 : See NotifyOn() function
+    For 5,6,7: See SetShutdown() function */
+
+namespace grpc_core {
+
+LockfreeEvent::LockfreeEvent() { InitEvent(); }
+
+void LockfreeEvent::InitEvent() {
+  /* Perform an atomic store to start the state machine.
+
+     Note carefully that LockfreeEvent *MAY* be used whilst in a destroyed
+     state, while a file descriptor is on a freelist. In such a state it may
+     be SetReady'd, and so we need to perform an atomic operation here to
+     ensure no races */
+  gpr_atm_no_barrier_store(&state_, kClosureNotReady);
+}
+
+void LockfreeEvent::DestroyEvent() {
+  gpr_atm curr;
+  do {
+    curr = gpr_atm_no_barrier_load(&state_);
+    if (curr & kShutdownBit) {
+      GRPC_ERROR_UNREF((grpc_error*)(curr & ~kShutdownBit));
+    } else {
+      GPR_ASSERT(curr == kClosureNotReady || curr == kClosureReady);
+    }
+    /* we CAS in a shutdown, no error value here. If this event is interacted
+       with post-deletion (see the note in the constructor) we want the bit
+       pattern to prevent error retention in a deleted object */
+  } while (!gpr_atm_no_barrier_cas(&state_, curr,
+                                   kShutdownBit /* shutdown, no error */));
+}
+
+void LockfreeEvent::NotifyOn(grpc_closure* closure) {
+  while (true) {
+    /* This load needs to be an acquire load because this can be a shutdown
+     * error that we might need to reference. Adding acquire semantics makes
+     * sure that the shutdown error has been initialized properly before us
+     * referencing it. */
+    gpr_atm curr = gpr_atm_acq_load(&state_);
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_ERROR, "LockfreeEvent::NotifyOn: %p curr=%p closure=%p", this,
+              (void*)curr, closure);
+    }
+    switch (curr) {
+      case kClosureNotReady: {
+        /* kClosureNotReady -> <closure>.
+
+           We're guaranteed by API that there's an acquire barrier before here,
+           so there's no need to double-dip and this can be a release-only.
+
+           The release itself pairs with the acquire half of a set_ready full
+           barrier. */
+        if (gpr_atm_rel_cas(&state_, kClosureNotReady, (gpr_atm)closure)) {
+          return; /* Successful. Return */
+        }
+
+        break; /* retry */
+      }
+
+      case kClosureReady: {
+        /* Change the state to kClosureNotReady. Schedule the closure if
+           successful. If not, the state most likely transitioned to shutdown.
+           We should retry.
+
+           This can be a no-barrier cas since the state is being transitioned to
+           kClosureNotReady; set_ready and set_shutdown do not schedule any
+           closure when transitioning out of CLOSURE_NO_READY state (i.e there
+           is no other code that needs to 'happen-after' this) */
+        if (gpr_atm_no_barrier_cas(&state_, kClosureReady, kClosureNotReady)) {
+          GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
+          return; /* Successful. Return */
+        }
+
+        break; /* retry */
+      }
+
+      default: {
+        /* 'curr' is either a closure or the fd is shutdown(in which case 'curr'
+           contains a pointer to the shutdown-error). If the fd is shutdown,
+           schedule the closure with the shutdown error */
+        if ((curr & kShutdownBit) > 0) {
+          grpc_error* shutdown_err = (grpc_error*)(curr & ~kShutdownBit);
+          GRPC_CLOSURE_SCHED(closure,
+                             GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                                 "FD Shutdown", &shutdown_err, 1));
+          return;
+        }
+
+        /* There is already a closure!. This indicates a bug in the code */
+        gpr_log(GPR_ERROR,
+                "LockfreeEvent::NotifyOn: notify_on called with a previous "
+                "callback still pending");
+        abort();
+      }
+    }
+  }
+
+  GPR_UNREACHABLE_CODE(return );
+}
+
+bool LockfreeEvent::SetShutdown(grpc_error* shutdown_err) {
+  gpr_atm new_state = (gpr_atm)shutdown_err | kShutdownBit;
+
+  while (true) {
+    gpr_atm curr = gpr_atm_no_barrier_load(&state_);
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_ERROR, "LockfreeEvent::SetShutdown: %p curr=%p err=%s",
+              &state_, (void*)curr, grpc_error_string(shutdown_err));
+    }
+    switch (curr) {
+      case kClosureReady:
+      case kClosureNotReady:
+        /* Need a full barrier here so that the initial load in notify_on
+           doesn't need a barrier */
+        if (gpr_atm_full_cas(&state_, curr, new_state)) {
+          return true; /* early out */
+        }
+        break; /* retry */
+
+      default: {
+        /* 'curr' is either a closure or the fd is already shutdown */
+
+        /* If fd is already shutdown, we are done */
+        if ((curr & kShutdownBit) > 0) {
+          GRPC_ERROR_UNREF(shutdown_err);
+          return false;
+        }
+
+        /* Fd is not shutdown. Schedule the closure and move the state to
+           shutdown state.
+           Needs an acquire to pair with setting the closure (and get a
+           happens-after on that edge), and a release to pair with anything
+           loading the shutdown state. */
+        if (gpr_atm_full_cas(&state_, curr, new_state)) {
+          GRPC_CLOSURE_SCHED((grpc_closure*)curr,
+                             GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                                 "FD Shutdown", &shutdown_err, 1));
+          return true;
+        }
+
+        /* 'curr' was a closure but now changed to a different state. We will
+          have to retry */
+        break;
+      }
+    }
+  }
+
+  GPR_UNREACHABLE_CODE(return false);
+}
+
+void LockfreeEvent::SetReady() {
+  while (true) {
+    gpr_atm curr = gpr_atm_no_barrier_load(&state_);
+
+    if (grpc_polling_trace.enabled()) {
+      gpr_log(GPR_ERROR, "LockfreeEvent::SetReady: %p curr=%p", &state_,
+              (void*)curr);
+    }
+
+    switch (curr) {
+      case kClosureReady: {
+        /* Already ready. We are done here */
+        return;
+      }
+
+      case kClosureNotReady: {
+        /* No barrier required as we're transitioning to a state that does not
+           involve a closure */
+        if (gpr_atm_no_barrier_cas(&state_, kClosureNotReady, kClosureReady)) {
+          return; /* early out */
+        }
+        break; /* retry */
+      }
+
+      default: {
+        /* 'curr' is either a closure or the fd is shutdown */
+        if ((curr & kShutdownBit) > 0) {
+          /* The fd is shutdown. Do nothing */
+          return;
+        }
+        /* Full cas: acquire pairs with this cas' release in the event of a
+           spurious set_ready; release pairs with this or the acquire in
+           notify_on (or set_shutdown) */
+        else if (gpr_atm_full_cas(&state_, curr, kClosureNotReady)) {
+          GRPC_CLOSURE_SCHED((grpc_closure*)curr, GRPC_ERROR_NONE);
+          return;
+        }
+        /* else the state changed again (only possible by either a racing
+           set_ready or set_shutdown functions. In both these cases, the closure
+           would have been scheduled for execution. So we are done here */
+        return;
+      }
+    }
+  }
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/iomgr/lockfree_event.h b/third_party/grpc/src/core/lib/iomgr/lockfree_event.h
new file mode 100644
index 0000000..d6a6c22
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/lockfree_event.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_LOCKFREE_EVENT_H
+#define GRPC_CORE_LIB_IOMGR_LOCKFREE_EVENT_H
+
+/* Lock free event notification for file descriptors */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/atm.h>
+
+#include "src/core/lib/iomgr/closure.h"
+
+namespace grpc_core {
+
+class LockfreeEvent {
+ public:
+  LockfreeEvent();
+
+  LockfreeEvent(const LockfreeEvent&) = delete;
+  LockfreeEvent& operator=(const LockfreeEvent&) = delete;
+
+  // These methods are used to initialize and destroy the internal state. These
+  // cannot be done in constructor and destructor because SetReady may be called
+  // when the event is destroyed and put in a freelist.
+  void InitEvent();
+  void DestroyEvent();
+
+  // Returns true if fd has been shutdown, false otherwise.
+  bool IsShutdown() const {
+    return (gpr_atm_no_barrier_load(&state_) & kShutdownBit) != 0;
+  }
+
+  // Schedules \a closure when the event is received (see SetReady()) or the
+  // shutdown state has been set. Note that the event may have already been
+  // received, in which case the closure would be scheduled immediately.
+  // If the shutdown state has already been set, then \a closure is scheduled
+  // with the shutdown error.
+  void NotifyOn(grpc_closure* closure);
+
+  // Sets the shutdown state. If a closure had been provided by NotifyOn and has
+  // not yet been scheduled, it will be scheduled with \a error.
+  bool SetShutdown(grpc_error* error);
+
+  // Signals that the event has been received.
+  void SetReady();
+
+ private:
+  enum State { kClosureNotReady = 0, kClosureReady = 2, kShutdownBit = 1 };
+
+  gpr_atm state_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_IOMGR_LOCKFREE_EVENT_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/nameser.h b/third_party/grpc/src/core/lib/iomgr/nameser.h
new file mode 100644
index 0000000..22a00cd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/nameser.h
@@ -0,0 +1,106 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_NAMESER_H
+#define GRPC_CORE_LIB_IOMGR_NAMESER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_HAVE_ARPA_NAMESER
+
+#include <arpa/nameser.h>
+
+#else /* GRPC_HAVE_ARPA_NAMESER */
+
+typedef enum __ns_class {
+  ns_c_invalid = 0, /* Cookie. */
+  ns_c_in = 1,      /* Internet. */
+  ns_c_2 = 2,       /* unallocated/unsupported. */
+  ns_c_chaos = 3,   /* MIT Chaos-net. */
+  ns_c_hs = 4,      /* MIT Hesiod. */
+  /* Query class values which do not appear in resource records */
+  ns_c_none = 254, /* for prereq. sections in update requests */
+  ns_c_any = 255,  /* Wildcard match. */
+  ns_c_max = 65536
+} ns_class;
+
+typedef enum __ns_type {
+  ns_t_invalid = 0,   /* Cookie. */
+  ns_t_a = 1,         /* Host address. */
+  ns_t_ns = 2,        /* Authoritative server. */
+  ns_t_md = 3,        /* Mail destination. */
+  ns_t_mf = 4,        /* Mail forwarder. */
+  ns_t_cname = 5,     /* Canonical name. */
+  ns_t_soa = 6,       /* Start of authority zone. */
+  ns_t_mb = 7,        /* Mailbox domain name. */
+  ns_t_mg = 8,        /* Mail group member. */
+  ns_t_mr = 9,        /* Mail rename name. */
+  ns_t_null = 10,     /* Null resource record. */
+  ns_t_wks = 11,      /* Well known service. */
+  ns_t_ptr = 12,      /* Domain name pointer. */
+  ns_t_hinfo = 13,    /* Host information. */
+  ns_t_minfo = 14,    /* Mailbox information. */
+  ns_t_mx = 15,       /* Mail routing information. */
+  ns_t_txt = 16,      /* Text strings. */
+  ns_t_rp = 17,       /* Responsible person. */
+  ns_t_afsdb = 18,    /* AFS cell database. */
+  ns_t_x25 = 19,      /* X_25 calling address. */
+  ns_t_isdn = 20,     /* ISDN calling address. */
+  ns_t_rt = 21,       /* Router. */
+  ns_t_nsap = 22,     /* NSAP address. */
+  ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */
+  ns_t_sig = 24,      /* Security signature. */
+  ns_t_key = 25,      /* Security key. */
+  ns_t_px = 26,       /* X.400 mail mapping. */
+  ns_t_gpos = 27,     /* Geographical position (withdrawn). */
+  ns_t_aaaa = 28,     /* Ip6 Address. */
+  ns_t_loc = 29,      /* Location Information. */
+  ns_t_nxt = 30,      /* Next domain (security). */
+  ns_t_eid = 31,      /* Endpoint identifier. */
+  ns_t_nimloc = 32,   /* Nimrod Locator. */
+  ns_t_srv = 33,      /* Server Selection. */
+  ns_t_atma = 34,     /* ATM Address */
+  ns_t_naptr = 35,    /* Naming Authority PoinTeR */
+  ns_t_kx = 36,       /* Key Exchange */
+  ns_t_cert = 37,     /* Certification record */
+  ns_t_a6 = 38,       /* IPv6 address (deprecates AAAA) */
+  ns_t_dname = 39,    /* Non-terminal DNAME (for IPv6) */
+  ns_t_sink = 40,     /* Kitchen sink (experimentatl) */
+  ns_t_opt = 41,      /* EDNS0 option (meta-RR) */
+  ns_t_apl = 42,      /* Address prefix list (RFC3123) */
+  ns_t_ds = 43,       /* Delegation Signer (RFC4034) */
+  ns_t_sshfp = 44,    /* SSH Key Fingerprint (RFC4255) */
+  ns_t_rrsig = 46,    /* Resource Record Signature (RFC4034) */
+  ns_t_nsec = 47,     /* Next Secure (RFC4034) */
+  ns_t_dnskey = 48,   /* DNS Public Key (RFC4034) */
+  ns_t_tkey = 249,    /* Transaction key */
+  ns_t_tsig = 250,    /* Transaction signature. */
+  ns_t_ixfr = 251,    /* Incremental zone transfer. */
+  ns_t_axfr = 252,    /* Transfer zone of authority. */
+  ns_t_mailb = 253,   /* Transfer mailbox records. */
+  ns_t_maila = 254,   /* Transfer mail agent records. */
+  ns_t_any = 255,     /* Wildcard match. */
+  ns_t_zxfr = 256,    /* BIND-specific, nonstandard. */
+  ns_t_max = 65536
+} ns_type;
+
+#endif /* GRPC_HAVE_ARPA_NAMESER */
+
+#endif /* GRPC_CORE_LIB_IOMGR_NAMESER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/network_status_tracker.cc b/third_party/grpc/src/core/lib/iomgr/network_status_tracker.cc
new file mode 100644
index 0000000..d4b7f4a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/network_status_tracker.cc
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/network_status_tracker.h"
+
+void grpc_network_status_shutdown(void) {}
+
+void grpc_network_status_init(void) {
+  // TODO(makarandd): Install callback with OS to monitor network status.
+}
+
+void grpc_destroy_network_status_monitor() {}
+
+void grpc_network_status_register_endpoint(grpc_endpoint* ep) { (void)ep; }
+
+void grpc_network_status_unregister_endpoint(grpc_endpoint* ep) { (void)ep; }
+
+void grpc_network_status_shutdown_all_endpoints() {}
diff --git a/third_party/grpc/src/core/lib/iomgr/network_status_tracker.h b/third_party/grpc/src/core/lib/iomgr/network_status_tracker.h
new file mode 100644
index 0000000..198877f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/network_status_tracker.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H
+#define GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+
+void grpc_network_status_init(void);
+void grpc_network_status_shutdown(void);
+
+void grpc_network_status_register_endpoint(grpc_endpoint* ep);
+void grpc_network_status_unregister_endpoint(grpc_endpoint* ep);
+void grpc_network_status_shutdown_all_endpoints();
+
+#endif /* GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/polling_entity.cc b/third_party/grpc/src/core/lib/iomgr/polling_entity.cc
new file mode 100644
index 0000000..dea07ca
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/polling_entity.cc
@@ -0,0 +1,96 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/polling_entity.h"
+
+grpc_polling_entity grpc_polling_entity_create_from_pollset_set(
+    grpc_pollset_set* pollset_set) {
+  grpc_polling_entity pollent;
+  pollent.pollent.pollset_set = pollset_set;
+  pollent.tag = GRPC_POLLS_POLLSET_SET;
+  return pollent;
+}
+
+grpc_polling_entity grpc_polling_entity_create_from_pollset(
+    grpc_pollset* pollset) {
+  grpc_polling_entity pollent;
+  pollent.pollent.pollset = pollset;
+  pollent.tag = GRPC_POLLS_POLLSET;
+  return pollent;
+}
+
+grpc_pollset* grpc_polling_entity_pollset(grpc_polling_entity* pollent) {
+  if (pollent->tag == GRPC_POLLS_POLLSET) {
+    return pollent->pollent.pollset;
+  }
+  return nullptr;
+}
+
+grpc_pollset_set* grpc_polling_entity_pollset_set(
+    grpc_polling_entity* pollent) {
+  if (pollent->tag == GRPC_POLLS_POLLSET_SET) {
+    return pollent->pollent.pollset_set;
+  }
+  return nullptr;
+}
+
+bool grpc_polling_entity_is_empty(const grpc_polling_entity* pollent) {
+  return pollent->tag == GRPC_POLLS_NONE;
+}
+
+void grpc_polling_entity_add_to_pollset_set(grpc_polling_entity* pollent,
+                                            grpc_pollset_set* pss_dst) {
+  if (pollent->tag == GRPC_POLLS_POLLSET) {
+    // CFStream does not use file destriptors. When CFStream is used, the fd
+    // pollset is possible to be null.
+    if (pollent->pollent.pollset != nullptr) {
+      grpc_pollset_set_add_pollset(pss_dst, pollent->pollent.pollset);
+    }
+  } else if (pollent->tag == GRPC_POLLS_POLLSET_SET) {
+    GPR_ASSERT(pollent->pollent.pollset_set != nullptr);
+    grpc_pollset_set_add_pollset_set(pss_dst, pollent->pollent.pollset_set);
+  } else {
+    gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag);
+    abort();
+  }
+}
+
+void grpc_polling_entity_del_from_pollset_set(grpc_polling_entity* pollent,
+                                              grpc_pollset_set* pss_dst) {
+  if (pollent->tag == GRPC_POLLS_POLLSET) {
+#ifdef GRPC_CFSTREAM
+    if (pollent->pollent.pollset != nullptr) {
+      grpc_pollset_set_del_pollset(pss_dst, pollent->pollent.pollset);
+    }
+#else
+    GPR_ASSERT(pollent->pollent.pollset != nullptr);
+    grpc_pollset_set_del_pollset(pss_dst, pollent->pollent.pollset);
+#endif
+  } else if (pollent->tag == GRPC_POLLS_POLLSET_SET) {
+    GPR_ASSERT(pollent->pollent.pollset_set != nullptr);
+    grpc_pollset_set_del_pollset_set(pss_dst, pollent->pollent.pollset_set);
+  } else {
+    gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag);
+    abort();
+  }
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/polling_entity.h b/third_party/grpc/src/core/lib/iomgr/polling_entity.h
new file mode 100644
index 0000000..6f4c5bd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/polling_entity.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H
+#define GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+typedef enum grpc_pollset_tag {
+  GRPC_POLLS_NONE,
+  GRPC_POLLS_POLLSET,
+  GRPC_POLLS_POLLSET_SET
+} grpc_pollset_tag;
+
+/* A grpc_polling_entity is a pollset-or-pollset_set container. It allows
+ * functions that accept a pollset XOR a pollset_set to do so through an
+ * abstract interface. No ownership is taken. */
+
+struct grpc_polling_entity {
+  union {
+    grpc_pollset* pollset = nullptr;
+    grpc_pollset_set* pollset_set;
+  } pollent;
+  grpc_pollset_tag tag = GRPC_POLLS_NONE;
+};
+
+grpc_polling_entity grpc_polling_entity_create_from_pollset_set(
+    grpc_pollset_set* pollset_set);
+grpc_polling_entity grpc_polling_entity_create_from_pollset(
+    grpc_pollset* pollset);
+
+/** If \a pollent contains a pollset, return it. Otherwise, return NULL */
+grpc_pollset* grpc_polling_entity_pollset(grpc_polling_entity* pollent);
+
+/** If \a pollent contains a pollset_set, return it. Otherwise, return NULL */
+grpc_pollset_set* grpc_polling_entity_pollset_set(grpc_polling_entity* pollent);
+
+bool grpc_polling_entity_is_empty(const grpc_polling_entity* pollent);
+
+/** Add the pollset or pollset_set in \a pollent to the destination pollset_set
+ * \a * pss_dst */
+void grpc_polling_entity_add_to_pollset_set(grpc_polling_entity* pollent,
+                                            grpc_pollset_set* pss_dst);
+
+/** Delete the pollset or pollset_set in \a pollent from the destination
+ * pollset_set \a * pss_dst */
+void grpc_polling_entity_del_from_pollset_set(grpc_polling_entity* pollent,
+                                              grpc_pollset_set* pss_dst);
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset.cc b/third_party/grpc/src/core/lib/iomgr/pollset.cc
new file mode 100644
index 0000000..ebfef1d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset.cc
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/pollset.h"
+
+grpc_pollset_vtable* grpc_pollset_impl;
+
+void grpc_set_pollset_vtable(grpc_pollset_vtable* vtable) {
+  grpc_pollset_impl = vtable;
+}
+
+void grpc_pollset_global_init() { grpc_pollset_impl->global_init(); }
+
+void grpc_pollset_global_shutdown() { grpc_pollset_impl->global_shutdown(); }
+
+void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
+  grpc_pollset_impl->init(pollset, mu);
+}
+
+void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
+  grpc_pollset_impl->shutdown(pollset, closure);
+}
+
+void grpc_pollset_destroy(grpc_pollset* pollset) {
+  grpc_pollset_impl->destroy(pollset);
+}
+
+grpc_error* grpc_pollset_work(grpc_pollset* pollset,
+                              grpc_pollset_worker** worker,
+                              grpc_millis deadline) {
+  return grpc_pollset_impl->work(pollset, worker, deadline);
+}
+
+grpc_error* grpc_pollset_kick(grpc_pollset* pollset,
+                              grpc_pollset_worker* specific_worker) {
+  return grpc_pollset_impl->kick(pollset, specific_worker);
+}
+
+size_t grpc_pollset_size(void) { return grpc_pollset_impl->pollset_size(); }
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset.h b/third_party/grpc/src/core/lib/iomgr/pollset.h
new file mode 100644
index 0000000..28472b3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount;
+
+/* A grpc_pollset is a set of file descriptors that a higher level item is
+   interested in. For example:
+    - a server will typically keep a pollset containing all connected channels,
+      so that it can find new calls to service
+    - a completion queue might keep a pollset with an entry for each transport
+      that is servicing a call that it's tracking */
+
+typedef struct grpc_pollset grpc_pollset;
+typedef struct grpc_pollset_worker grpc_pollset_worker;
+
+typedef struct grpc_pollset_vtable {
+  void (*global_init)(void);
+  void (*global_shutdown)(void);
+  void (*init)(grpc_pollset* pollset, gpr_mu** mu);
+  void (*shutdown)(grpc_pollset* pollset, grpc_closure* closure);
+  void (*destroy)(grpc_pollset* pollset);
+  grpc_error* (*work)(grpc_pollset* pollset, grpc_pollset_worker** worker,
+                      grpc_millis deadline);
+  grpc_error* (*kick)(grpc_pollset* pollset,
+                      grpc_pollset_worker* specific_worker);
+  size_t (*pollset_size)(void);
+} grpc_pollset_vtable;
+
+void grpc_set_pollset_vtable(grpc_pollset_vtable* vtable);
+
+void grpc_pollset_global_init(void);
+void grpc_pollset_global_shutdown(void);
+
+size_t grpc_pollset_size(void);
+/* Initialize a pollset: assumes *pollset contains all zeros */
+void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu);
+/* Begin shutting down the pollset, and call closure when done.
+ * pollset's mutex must be held */
+void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure);
+void grpc_pollset_destroy(grpc_pollset* pollset);
+
+/* Do some work on a pollset.
+   May involve invoking asynchronous callbacks, or actually polling file
+   descriptors.
+   Requires pollset's mutex locked.
+   May unlock its mutex during its execution.
+
+   worker is a (platform-specific) handle that can be used to wake up
+   from grpc_pollset_work before any events are received and before the timeout
+   has expired. It is both initialized and destroyed by grpc_pollset_work.
+   Initialization of worker is guaranteed to occur BEFORE the
+   pollset's mutex is released for the first time by grpc_pollset_work
+   and it is guaranteed that it will not be released by grpc_pollset_work
+   AFTER worker has been destroyed.
+
+   It's legal for worker to be NULL: in that case, this specific thread can not
+   be directly woken with a kick, but maybe be indirectly (with a kick against
+   the pollset as a whole).
+
+   Tries not to block past deadline.
+   May call grpc_closure_list_run on grpc_closure_list, without holding the
+   pollset
+   lock */
+grpc_error* grpc_pollset_work(grpc_pollset* pollset,
+                              grpc_pollset_worker** worker,
+                              grpc_millis deadline) GRPC_MUST_USE_RESULT;
+
+/* Break one polling thread out of polling work for this pollset.
+   If specific_worker is non-NULL, then kick that worker. */
+grpc_error* grpc_pollset_kick(grpc_pollset* pollset,
+                              grpc_pollset_worker* specific_worker)
+    GRPC_MUST_USE_RESULT;
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_custom.cc b/third_party/grpc/src/core/lib/iomgr/pollset_custom.cc
new file mode 100644
index 0000000..70e8a45
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_custom.cc
@@ -0,0 +1,106 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_custom.h"
+#include "src/core/lib/iomgr/timer.h"
+
+#include "src/core/lib/debug/trace.h"
+
+static grpc_custom_poller_vtable* poller_vtable;
+
+struct grpc_pollset {
+  gpr_mu mu;
+};
+
+static size_t pollset_size() { return sizeof(grpc_pollset); }
+
+static void pollset_global_init() { poller_vtable->init(); }
+
+static void pollset_global_shutdown() { poller_vtable->shutdown(); }
+
+static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  gpr_mu_init(&pollset->mu);
+  *mu = &pollset->mu;
+}
+
+static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
+}
+
+static void pollset_destroy(grpc_pollset* pollset) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  gpr_mu_destroy(&pollset->mu);
+}
+
+static grpc_error* pollset_work(grpc_pollset* pollset,
+                                grpc_pollset_worker** worker_hdl,
+                                grpc_millis deadline) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  gpr_mu_unlock(&pollset->mu);
+  grpc_millis now = grpc_core::ExecCtx::Get()->Now();
+  grpc_millis timeout = 0;
+  if (deadline > now) {
+    timeout = deadline - now;
+  }
+  // We yield here because the poll() call might yield
+  // control back to the application
+  grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get();
+  grpc_core::ExecCtx::Set(nullptr);
+  poller_vtable->poll(static_cast<size_t>(timeout));
+  grpc_core::ExecCtx::Set(curr);
+  grpc_core::ExecCtx::Get()->InvalidateNow();
+  if (grpc_core::ExecCtx::Get()->HasWork()) {
+    grpc_core::ExecCtx::Get()->Flush();
+  }
+  gpr_mu_lock(&pollset->mu);
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* pollset_kick(grpc_pollset* pollset,
+                                grpc_pollset_worker* specific_worker) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  poller_vtable->kick();
+  return GRPC_ERROR_NONE;
+}
+
+grpc_pollset_vtable custom_pollset_vtable = {
+    pollset_global_init, pollset_global_shutdown,
+    pollset_init,        pollset_shutdown,
+    pollset_destroy,     pollset_work,
+    pollset_kick,        pollset_size};
+
+void grpc_custom_pollset_init(grpc_custom_poller_vtable* vtable) {
+  poller_vtable = vtable;
+  grpc_set_pollset_vtable(&custom_pollset_vtable);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_custom.h b/third_party/grpc/src/core/lib/iomgr/pollset_custom.h
new file mode 100644
index 0000000..9e2027f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_custom.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+typedef struct grpc_custom_poller_vtable {
+  void (*init)();
+  void (*poll)(size_t timeout_ms);
+  void (*kick)();
+  void (*shutdown)();
+} grpc_custom_poller_vtable;
+
+void grpc_custom_pollset_init(grpc_custom_poller_vtable* vtable);
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set.cc b/third_party/grpc/src/core/lib/iomgr/pollset_set.cc
new file mode 100644
index 0000000..42a647a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_set.cc
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/pollset_set.h"
+
+grpc_pollset_set_vtable* grpc_pollset_set_impl;
+
+void grpc_set_pollset_set_vtable(grpc_pollset_set_vtable* vtable) {
+  grpc_pollset_set_impl = vtable;
+}
+
+grpc_pollset_set* grpc_pollset_set_create() {
+  return grpc_pollset_set_impl->create();
+}
+
+void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) {
+  grpc_pollset_set_impl->destroy(pollset_set);
+}
+
+void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set,
+                                  grpc_pollset* pollset) {
+  grpc_pollset_set_impl->add_pollset(pollset_set, pollset);
+}
+
+void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set,
+                                  grpc_pollset* pollset) {
+  grpc_pollset_set_impl->del_pollset(pollset_set, pollset);
+}
+
+void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag,
+                                      grpc_pollset_set* item) {
+  grpc_pollset_set_impl->add_pollset_set(bag, item);
+}
+
+void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag,
+                                      grpc_pollset_set* item) {
+  grpc_pollset_set_impl->del_pollset_set(bag, item);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set.h b/third_party/grpc/src/core/lib/iomgr/pollset_set.h
new file mode 100644
index 0000000..d3355b8
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_set.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/pollset.h"
+
+/* A grpc_pollset_set is a set of pollsets that are interested in an
+   action. Adding a pollset to a pollset_set automatically adds any
+   fd's (etc) that have been registered with the set_set to that pollset.
+   Registering fd's automatically adds them to all current pollsets. */
+
+typedef struct grpc_pollset_set grpc_pollset_set;
+
+typedef struct grpc_pollset_set_vtable {
+  grpc_pollset_set* (*create)(void);
+  void (*destroy)(grpc_pollset_set* pollset_set);
+  void (*add_pollset)(grpc_pollset_set* pollset_set, grpc_pollset* pollset);
+  void (*del_pollset)(grpc_pollset_set* pollset_set, grpc_pollset* pollset);
+  void (*add_pollset_set)(grpc_pollset_set* bag, grpc_pollset_set* item);
+  void (*del_pollset_set)(grpc_pollset_set* bag, grpc_pollset_set* item);
+} grpc_pollset_set_vtable;
+
+void grpc_set_pollset_set_vtable(grpc_pollset_set_vtable* vtable);
+
+grpc_pollset_set* grpc_pollset_set_create(void);
+void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set);
+void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set,
+                                  grpc_pollset* pollset);
+void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set,
+                                  grpc_pollset* pollset);
+void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag,
+                                      grpc_pollset_set* item);
+void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag,
+                                      grpc_pollset_set* item);
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.cc b/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.cc
new file mode 100644
index 0000000..b1ee660
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.cc
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include "src/core/lib/iomgr/pollset_set.h"
+
+grpc_pollset_set* pollset_set_create(void) {
+  return (grpc_pollset_set*)((intptr_t)0xdeafbeef);
+}
+
+void pollset_set_destroy(grpc_pollset_set* pollset_set) {}
+
+void pollset_set_add_pollset(grpc_pollset_set* pollset_set,
+                             grpc_pollset* pollset) {}
+
+void pollset_set_del_pollset(grpc_pollset_set* pollset_set,
+                             grpc_pollset* pollset) {}
+
+void pollset_set_add_pollset_set(grpc_pollset_set* bag,
+                                 grpc_pollset_set* item) {}
+
+void pollset_set_del_pollset_set(grpc_pollset_set* bag,
+                                 grpc_pollset_set* item) {}
+
+static grpc_pollset_set_vtable vtable = {
+    pollset_set_create,          pollset_set_destroy,
+    pollset_set_add_pollset,     pollset_set_del_pollset,
+    pollset_set_add_pollset_set, pollset_set_del_pollset_set};
+
+void grpc_custom_pollset_set_init() { grpc_set_pollset_set_vtable(&vtable); }
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.h b/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.h
new file mode 100644
index 0000000..80e19a1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H
+
+#include <grpc/support/port_platform.h>
+
+void grpc_custom_pollset_set_init();
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.cc b/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.cc
new file mode 100644
index 0000000..bb9e7f5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.cc
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdint.h>
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/pollset_set_windows.h"
+
+static grpc_pollset_set* pollset_set_create(void) {
+  return (grpc_pollset_set*)((intptr_t)0xdeafbeef);
+}
+
+static void pollset_set_destroy(grpc_pollset_set* pollset_set) {}
+
+static void pollset_set_add_pollset(grpc_pollset_set* pollset_set,
+                                    grpc_pollset* pollset) {}
+
+static void pollset_set_del_pollset(grpc_pollset_set* pollset_set,
+                                    grpc_pollset* pollset) {}
+
+static void pollset_set_add_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {}
+
+static void pollset_set_del_pollset_set(grpc_pollset_set* bag,
+                                        grpc_pollset_set* item) {}
+
+grpc_pollset_set_vtable grpc_windows_pollset_set_vtable = {
+    pollset_set_create,          pollset_set_destroy,
+    pollset_set_add_pollset,     pollset_set_del_pollset,
+    pollset_set_add_pollset_set, pollset_set_del_pollset_set};
+
+#endif /* GRPC_WINSOCK_SOCKET */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.h b/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.h
new file mode 100644
index 0000000..5ac9d18
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/pollset_set.h"
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_uv.cc b/third_party/grpc/src/core/lib/iomgr/pollset_uv.cc
new file mode 100644
index 0000000..bade6ea
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_uv.cc
@@ -0,0 +1,93 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_UV
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/iomgr/pollset_custom.h"
+
+#include <uv.h>
+
+/* Indicates that grpc_pollset_work should run an iteration of the UV loop
+   before running callbacks. This defaults to 1, and should be disabled if
+   grpc_pollset_work will be called within the callstack of uv_run */
+int grpc_pollset_work_run_loop = 1;
+
+static bool g_kicked = false;
+
+typedef struct uv_poller_handle {
+  uv_timer_t poll_timer;
+  uv_timer_t kick_timer;
+  int refs;
+} uv_poller_handle;
+
+static uv_poller_handle* g_handle;
+
+static void init() {
+  g_handle = (uv_poller_handle*)gpr_malloc(sizeof(uv_poller_handle));
+  g_handle->refs = 2;
+  uv_timer_init(uv_default_loop(), &g_handle->poll_timer);
+  uv_timer_init(uv_default_loop(), &g_handle->kick_timer);
+}
+
+static void empty_timer_cb(uv_timer_t* handle) {}
+
+static void kick_timer_cb(uv_timer_t* handle) { g_kicked = false; }
+
+static void run_loop(size_t timeout) {
+  if (grpc_pollset_work_run_loop) {
+    if (timeout == 0) {
+      uv_run(uv_default_loop(), UV_RUN_NOWAIT);
+    } else {
+      uv_timer_start(&g_handle->poll_timer, empty_timer_cb, timeout, 0);
+      uv_run(uv_default_loop(), UV_RUN_ONCE);
+      uv_timer_stop(&g_handle->poll_timer);
+    }
+  }
+}
+
+static void kick() {
+  if (!g_kicked) {
+    g_kicked = true;
+    uv_timer_start(&g_handle->kick_timer, kick_timer_cb, 0, 0);
+  }
+}
+
+static void close_timer_cb(uv_handle_t* handle) {
+  g_handle->refs--;
+  if (g_handle->refs == 0) {
+    gpr_free(g_handle);
+  }
+}
+
+static void shutdown() {
+  uv_close((uv_handle_t*)&g_handle->poll_timer, close_timer_cb);
+  uv_close((uv_handle_t*)&g_handle->kick_timer, close_timer_cb);
+  if (grpc_pollset_work_run_loop) {
+    GPR_ASSERT(uv_run(uv_default_loop(), UV_RUN_DEFAULT) == 0);
+  }
+}
+
+grpc_custom_poller_vtable uv_pollset_vtable = {init, run_loop, kick, shutdown};
+
+#endif /* GRPC_UV */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_uv.h b/third_party/grpc/src/core/lib/iomgr/pollset_uv.h
new file mode 100644
index 0000000..de82bcc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_uv.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_UV_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_UV_H
+
+extern int grpc_pollset_work_run_loop;
+
+typedef struct grpc_custom_poller_vtable {
+  void (*init)(void);
+  void (*run_loop)(int blocking);
+} grpc_custom_poller_vtable;
+
+void grpc_custom_pollset_global_init(grpc_custom_poller_vtable* vtable);
+void grpc_custom_pollset_global_shutdown(void);
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_UV_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_windows.cc b/third_party/grpc/src/core/lib/iomgr/pollset_windows.cc
new file mode 100644
index 0000000..e9a808d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_windows.cc
@@ -0,0 +1,229 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_windows.h"
+
+#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1)
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
+
+gpr_mu grpc_polling_mu;
+static grpc_pollset_worker* g_active_poller;
+static grpc_pollset_worker g_global_root_worker;
+
+static void pollset_global_init(void) {
+  gpr_mu_init(&grpc_polling_mu);
+  g_active_poller = NULL;
+  g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next =
+      g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev =
+          &g_global_root_worker;
+}
+
+static void pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); }
+
+static void remove_worker(grpc_pollset_worker* worker,
+                          grpc_pollset_worker_link_type type) {
+  worker->links[type].prev->links[type].next = worker->links[type].next;
+  worker->links[type].next->links[type].prev = worker->links[type].prev;
+  worker->links[type].next = worker->links[type].prev = worker;
+}
+
+static int has_workers(grpc_pollset_worker* root,
+                       grpc_pollset_worker_link_type type) {
+  return root->links[type].next != root;
+}
+
+static grpc_pollset_worker* pop_front_worker(
+    grpc_pollset_worker* root, grpc_pollset_worker_link_type type) {
+  if (has_workers(root, type)) {
+    grpc_pollset_worker* w = root->links[type].next;
+    remove_worker(w, type);
+    return w;
+  } else {
+    return NULL;
+  }
+}
+
+static void push_front_worker(grpc_pollset_worker* root,
+                              grpc_pollset_worker_link_type type,
+                              grpc_pollset_worker* worker) {
+  worker->links[type].prev = root;
+  worker->links[type].next = worker->links[type].prev->links[type].next;
+  worker->links[type].prev->links[type].next =
+      worker->links[type].next->links[type].prev = worker;
+}
+
+static size_t pollset_size(void) { return sizeof(grpc_pollset); }
+
+/* There isn't really any such thing as a pollset under Windows, due to the
+   nature of the IO completion ports. We're still going to provide a minimal
+   set of features for the sake of the rest of grpc. But grpc_pollset_work
+   won't actually do any polling, and return as quickly as possible. */
+
+static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) {
+  *mu = &grpc_polling_mu;
+  pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next =
+      pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev =
+          &pollset->root_worker;
+}
+
+static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) {
+  pollset->shutting_down = 1;
+  grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
+  if (!pollset->is_iocp_worker) {
+    GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
+  } else {
+    pollset->on_shutdown = closure;
+  }
+}
+
+static void pollset_destroy(grpc_pollset* pollset) {}
+
+static grpc_error* pollset_work(grpc_pollset* pollset,
+                                grpc_pollset_worker** worker_hdl,
+                                grpc_millis deadline) {
+  grpc_pollset_worker worker;
+  if (worker_hdl) *worker_hdl = &worker;
+
+  int added_worker = 0;
+  worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next =
+      worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev =
+          worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next =
+              worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = NULL;
+  worker.kicked = 0;
+  worker.pollset = pollset;
+  gpr_cv_init(&worker.cv);
+  if (!pollset->kicked_without_pollers && !pollset->shutting_down) {
+    if (g_active_poller == NULL) {
+      grpc_pollset_worker* next_worker;
+      /* become poller */
+      pollset->is_iocp_worker = 1;
+      g_active_poller = &worker;
+      gpr_mu_unlock(&grpc_polling_mu);
+      grpc_iocp_work(deadline);
+      grpc_core::ExecCtx::Get()->Flush();
+      gpr_mu_lock(&grpc_polling_mu);
+      pollset->is_iocp_worker = 0;
+      g_active_poller = NULL;
+      /* try to get a worker from this pollsets worker list */
+      next_worker = pop_front_worker(&pollset->root_worker,
+                                     GRPC_POLLSET_WORKER_LINK_POLLSET);
+      if (next_worker == NULL) {
+        /* try to get a worker from the global list */
+        next_worker = pop_front_worker(&g_global_root_worker,
+                                       GRPC_POLLSET_WORKER_LINK_GLOBAL);
+      }
+      if (next_worker != NULL) {
+        next_worker->kicked = 1;
+        gpr_cv_signal(&next_worker->cv);
+      }
+
+      if (pollset->shutting_down && pollset->on_shutdown != NULL) {
+        GRPC_CLOSURE_SCHED(pollset->on_shutdown, GRPC_ERROR_NONE);
+        pollset->on_shutdown = NULL;
+      }
+      goto done;
+    }
+    push_front_worker(&g_global_root_worker, GRPC_POLLSET_WORKER_LINK_GLOBAL,
+                      &worker);
+    push_front_worker(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET,
+                      &worker);
+    added_worker = 1;
+    while (!worker.kicked) {
+      if (gpr_cv_wait(&worker.cv, &grpc_polling_mu,
+                      grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) {
+        grpc_core::ExecCtx::Get()->InvalidateNow();
+        break;
+      }
+      grpc_core::ExecCtx::Get()->InvalidateNow();
+    }
+  } else {
+    pollset->kicked_without_pollers = 0;
+  }
+done:
+  if (!grpc_closure_list_empty(*grpc_core::ExecCtx::Get()->closure_list())) {
+    gpr_mu_unlock(&grpc_polling_mu);
+    grpc_core::ExecCtx::Get()->Flush();
+    gpr_mu_lock(&grpc_polling_mu);
+  }
+  if (added_worker) {
+    remove_worker(&worker, GRPC_POLLSET_WORKER_LINK_GLOBAL);
+    remove_worker(&worker, GRPC_POLLSET_WORKER_LINK_POLLSET);
+  }
+  gpr_cv_destroy(&worker.cv);
+  if (worker_hdl) *worker_hdl = NULL;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* pollset_kick(grpc_pollset* p,
+                                grpc_pollset_worker* specific_worker) {
+  if (specific_worker != NULL) {
+    if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) {
+      for (specific_worker =
+               p->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next;
+           specific_worker != &p->root_worker;
+           specific_worker =
+               specific_worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next) {
+        specific_worker->kicked = 1;
+        gpr_cv_signal(&specific_worker->cv);
+      }
+      p->kicked_without_pollers = 1;
+      if (p->is_iocp_worker) {
+        grpc_iocp_kick();
+      }
+    } else {
+      if (p->is_iocp_worker && g_active_poller == specific_worker) {
+        grpc_iocp_kick();
+      } else {
+        specific_worker->kicked = 1;
+        gpr_cv_signal(&specific_worker->cv);
+      }
+    }
+  } else {
+    specific_worker =
+        pop_front_worker(&p->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET);
+    if (specific_worker != NULL) {
+      grpc_pollset_kick(p, specific_worker);
+    } else if (p->is_iocp_worker) {
+      grpc_iocp_kick();
+    } else {
+      p->kicked_without_pollers = 1;
+    }
+  }
+  return GRPC_ERROR_NONE;
+}
+
+grpc_pollset_vtable grpc_windows_pollset_vtable = {
+    pollset_global_init, pollset_global_shutdown,
+    pollset_init,        pollset_shutdown,
+    pollset_destroy,     pollset_work,
+    pollset_kick,        pollset_size};
+
+#endif /* GRPC_WINSOCK_SOCKET */
diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_windows.h b/third_party/grpc/src/core/lib/iomgr/pollset_windows.h
new file mode 100644
index 0000000..e89758c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/pollset_windows.h
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+#include "src/core/lib/iomgr/socket_windows.h"
+
+/* There isn't really any such thing as a pollset under Windows, due to the
+   nature of the IO completion ports. A Windows "pollset" is merely a mutex
+   used to synchronize with the IOCP, and workers are condition variables
+   used to block threads until work is ready. */
+
+typedef enum {
+  GRPC_POLLSET_WORKER_LINK_POLLSET = 0,
+  GRPC_POLLSET_WORKER_LINK_GLOBAL,
+  GRPC_POLLSET_WORKER_LINK_TYPES
+} grpc_pollset_worker_link_type;
+
+typedef struct grpc_pollset_worker_link {
+  struct grpc_pollset_worker* next;
+  struct grpc_pollset_worker* prev;
+} grpc_pollset_worker_link;
+
+struct grpc_pollset;
+typedef struct grpc_pollset grpc_pollset;
+
+typedef struct grpc_pollset_worker {
+  gpr_cv cv;
+  int kicked;
+  struct grpc_pollset* pollset;
+  grpc_pollset_worker_link links[GRPC_POLLSET_WORKER_LINK_TYPES];
+} grpc_pollset_worker;
+
+struct grpc_pollset {
+  int shutting_down;
+  int kicked_without_pollers;
+  int is_iocp_worker;
+  grpc_pollset_worker root_worker;
+  grpc_closure* on_shutdown;
+};
+
+void grpc_pollset_global_init(void);
+void grpc_pollset_global_shutdown(void);
+
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/port.h b/third_party/grpc/src/core/lib/iomgr/port.h
new file mode 100644
index 0000000..1aea6c1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/port.h
@@ -0,0 +1,214 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifndef GRPC_CORE_LIB_IOMGR_PORT_H
+#define GRPC_CORE_LIB_IOMGR_PORT_H
+
+#ifdef GRPC_UV
+#ifndef GRPC_CUSTOM_SOCKET
+#define GRPC_CUSTOM_SOCKET
+#endif
+#endif
+#if defined(GRPC_CUSTOM_SOCKET)
+// Do Nothing
+#elif defined(GPR_MANYLINUX1)
+#define GRPC_HAVE_ARPA_NAMESER 1
+#define GRPC_HAVE_IFADDRS 1
+#define GRPC_HAVE_IPV6_RECVPKTINFO 1
+#define GRPC_HAVE_IP_PKTINFO 1
+#define GRPC_HAVE_MSG_NOSIGNAL 1
+#define GRPC_HAVE_UNIX_SOCKET 1
+#define GRPC_POSIX_FORK 1
+#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
+#define GRPC_POSIX_SOCKET 1
+#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#define GRPC_LINUX_EPOLL 1
+#elif defined(GPR_WINDOWS)
+#define GRPC_WINSOCK_SOCKET 1
+#define GRPC_WINDOWS_SOCKETUTILS 1
+#elif defined(GPR_ANDROID)
+#define GRPC_HAVE_IPV6_RECVPKTINFO 1
+#define GRPC_HAVE_IP_PKTINFO 1
+#define GRPC_HAVE_MSG_NOSIGNAL 1
+#define GRPC_HAVE_UNIX_SOCKET 1
+#define GRPC_LINUX_EVENTFD 1
+#define GRPC_POSIX_SOCKET 1
+#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#elif defined(GPR_LINUX)
+#define GRPC_HAVE_ARPA_NAMESER 1
+#define GRPC_HAVE_IFADDRS 1
+#define GRPC_HAVE_IPV6_RECVPKTINFO 1
+#define GRPC_HAVE_IP_PKTINFO 1
+#define GRPC_HAVE_MSG_NOSIGNAL 1
+#define GRPC_HAVE_UNIX_SOCKET 1
+#ifdef LINUX_VERSION_CODE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+#define GRPC_LINUX_ERRQUEUE 1
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */
+#endif /* LINUX_VERSION_CODE */
+#define GRPC_LINUX_MULTIPOLL_WITH_EPOLL 1
+#define GRPC_POSIX_FORK 1
+#define GRPC_POSIX_HOST_NAME_MAX 1
+#define GRPC_POSIX_SOCKET 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#ifdef __GLIBC_PREREQ
+#if __GLIBC_PREREQ(2, 4)
+#define GRPC_LINUX_EPOLL 1
+#endif
+#if __GLIBC_PREREQ(2, 9)
+#define GRPC_LINUX_EPOLL_CREATE1 1
+#define GRPC_LINUX_EVENTFD 1
+#endif
+#if __GLIBC_PREREQ(2, 10)
+#define GRPC_LINUX_SOCKETUTILS 1
+#endif
+#endif
+#ifdef LINUX_VERSION_CODE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+#define GRPC_HAVE_TCP_USER_TIMEOUT
+#ifdef __GLIBC_PREREQ
+#if !(__GLIBC_PREREQ(2, 17))
+/*
+ * TCP_USER_TIMEOUT wasn't imported to glibc until 2.17. Use Linux system
+ * header instead.
+ */
+#define GRPC_LINUX_TCP_H 1
+#endif /* __GLIBC_PREREQ(2, 17) */
+#endif /* ifdef __GLIBC_PREREQ */
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) */
+#endif /* LINUX_VERSION_CODE */
+#ifndef __GLIBC__
+#define GRPC_LINUX_EPOLL 1
+#define GRPC_LINUX_EPOLL_CREATE1 1
+#define GRPC_LINUX_EVENTFD 1
+#define GRPC_MSG_IOVLEN_TYPE int
+#endif
+#ifndef GRPC_LINUX_EVENTFD
+#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
+#endif
+#ifndef GRPC_LINUX_SOCKETUTILS
+#define GRPC_POSIX_SOCKETUTILS
+#endif
+#elif defined(GPR_APPLE)
+#define GRPC_HAVE_ARPA_NAMESER 1
+#define GRPC_HAVE_IFADDRS 1
+#define GRPC_HAVE_SO_NOSIGPIPE 1
+#define GRPC_HAVE_UNIX_SOCKET 1
+#define GRPC_MSG_IOVLEN_TYPE int
+#define GRPC_POSIX_FORK 1
+#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
+#ifdef GRPC_CFSTREAM
+#define GRPC_CFSTREAM_IOMGR 1
+#define GRPC_CFSTREAM_CLIENT 1
+#define GRPC_CFSTREAM_ENDPOINT 1
+#define GRPC_POSIX_SOCKET_ARES_EV_DRIVER 1
+#define GRPC_POSIX_SOCKET_EV 1
+#define GRPC_POSIX_SOCKET_EV_EPOLL1 1
+#define GRPC_POSIX_SOCKET_EV_EPOLLEX 1
+#define GRPC_POSIX_SOCKET_EV_POLL 1
+#define GRPC_POSIX_SOCKET_RESOLVE_ADDRESS 1
+#define GRPC_POSIX_SOCKET_SOCKADDR 1
+#define GRPC_POSIX_SOCKET_SOCKET_FACTORY 1
+#define GRPC_POSIX_SOCKET_TCP 1
+#define GRPC_POSIX_SOCKET_TCP_CLIENT 1
+#define GRPC_POSIX_SOCKET_TCP_SERVER 1
+#define GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON 1
+#define GRPC_POSIX_SOCKET_UTILS_COMMON 1
+#else
+#define GRPC_POSIX_SOCKET 1
+#endif
+#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_SYSCONF 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#elif defined(GPR_FREEBSD)
+#define GRPC_HAVE_ARPA_NAMESER 1
+#define GRPC_HAVE_IFADDRS 1
+#define GRPC_HAVE_IPV6_RECVPKTINFO 1
+#define GRPC_HAVE_SO_NOSIGPIPE 1
+#define GRPC_HAVE_UNIX_SOCKET 1
+#define GRPC_POSIX_FORK 1
+#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
+#define GRPC_POSIX_SOCKET 1
+#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#elif defined(GPR_OPENBSD)
+#define GRPC_HAVE_IFADDRS 1
+#define GRPC_HAVE_IPV6_RECVPKTINFO 1
+#define GRPC_HAVE_UNIX_SOCKET 1
+#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
+#define GRPC_POSIX_SOCKET 1
+#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#elif defined(GPR_SOLARIS)
+#define GRPC_HAVE_UNIX_SOCKET 1
+#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
+#define GRPC_POSIX_SOCKET 1
+#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#elif defined(GPR_AIX)
+#define GRPC_HAVE_UNIX_SOCKET 1
+#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
+#define GRPC_POSIX_SOCKET 1
+#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#elif defined(GPR_NACL)
+#define GRPC_HAVE_ARPA_NAMESER 1
+#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1
+#define GRPC_POSIX_SOCKET 1
+#define GRPC_POSIX_SOCKETUTILS 1
+#define GRPC_POSIX_WAKEUP_FD 1
+#elif !defined(GPR_NO_AUTODETECT_PLATFORM)
+#error "Platform not recognized"
+#endif
+
+#if defined(GRPC_POSIX_SOCKET) + defined(GRPC_WINSOCK_SOCKET) + \
+        defined(GRPC_CUSTOM_SOCKET) + defined(GRPC_CFSTREAM) != \
+    1
+#error \
+    "Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GRPC_CUSTOM_SOCKET"
+#endif
+
+#ifdef GRPC_POSIX_SOCKET
+#define GRPC_POSIX_SOCKET_ARES_EV_DRIVER 1
+#define GRPC_POSIX_SOCKET_EV 1
+#define GRPC_POSIX_SOCKET_EV_EPOLLEX 1
+#define GRPC_POSIX_SOCKET_EV_POLL 1
+#define GRPC_POSIX_SOCKET_EV_EPOLL1 1
+#define GRPC_POSIX_SOCKET_IOMGR 1
+#define GRPC_POSIX_SOCKET_RESOLVE_ADDRESS 1
+#define GRPC_POSIX_SOCKET_SOCKADDR 1
+#define GRPC_POSIX_SOCKET_SOCKET_FACTORY 1
+#define GRPC_POSIX_SOCKET_TCP 1
+#define GRPC_POSIX_SOCKET_TCP_CLIENT 1
+#define GRPC_POSIX_SOCKET_TCP_SERVER 1
+#define GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON 1
+#define GRPC_POSIX_SOCKET_UTILS_COMMON 1
+#endif
+
+#if defined(GRPC_POSIX_HOST_NAME_MAX) && defined(GRPC_POSIX_SYSCONF)
+#error "Cannot define both GRPC_POSIX_HOST_NAME_MAX and GRPC_POSIX_SYSCONF"
+#endif
+#if !defined(GRPC_POSIX_HOST_NAME_MAX) && !defined(GRPC_POSIX_SYSCONF)
+#define GRPC_GETHOSTNAME_FALLBACK 1
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_PORT_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address.cc b/third_party/grpc/src/core/lib/iomgr/resolve_address.cc
new file mode 100644
index 0000000..f2a4676
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/resolve_address.cc
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+#include "src/core/lib/iomgr/resolve_address.h"
+
+grpc_address_resolver_vtable* grpc_resolve_address_impl;
+
+void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable) {
+  grpc_resolve_address_impl = vtable;
+}
+
+void grpc_resolve_address(const char* addr, const char* default_port,
+                          grpc_pollset_set* interested_parties,
+                          grpc_closure* on_done,
+                          grpc_resolved_addresses** addresses) {
+  grpc_resolve_address_impl->resolve_address(
+      addr, default_port, interested_parties, on_done, addresses);
+}
+
+void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) {
+  if (addrs != nullptr) {
+    gpr_free(addrs->addrs);
+  }
+  gpr_free(addrs);
+}
+
+grpc_error* grpc_blocking_resolve_address(const char* name,
+                                          const char* default_port,
+                                          grpc_resolved_addresses** addresses) {
+  return grpc_resolve_address_impl->blocking_resolve_address(name, default_port,
+                                                             addresses);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address.h b/third_party/grpc/src/core/lib/iomgr/resolve_address.h
new file mode 100644
index 0000000..7016ffc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/resolve_address.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H
+#define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_UV
+#include <uv.h>
+#endif
+
+#ifdef GRPC_WINSOCK_SOCKET
+#include <ws2tcpip.h>
+#endif
+
+#if defined(GRPC_POSIX_SOCKET) || defined(GRPC_CFSTREAM)
+#include <sys/socket.h>
+#endif
+
+#include "src/core/lib/iomgr/pollset_set.h"
+
+#define GRPC_MAX_SOCKADDR_SIZE 128
+
+typedef struct {
+  char addr[GRPC_MAX_SOCKADDR_SIZE];
+  socklen_t len;
+} grpc_resolved_address;
+
+typedef struct {
+  size_t naddrs;
+  grpc_resolved_address* addrs;
+} grpc_resolved_addresses;
+
+typedef struct grpc_address_resolver_vtable {
+  void (*resolve_address)(const char* addr, const char* default_port,
+                          grpc_pollset_set* interested_parties,
+                          grpc_closure* on_done,
+                          grpc_resolved_addresses** addresses);
+  grpc_error* (*blocking_resolve_address)(const char* name,
+                                          const char* default_port,
+                                          grpc_resolved_addresses** addresses);
+} grpc_address_resolver_vtable;
+
+void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable);
+
+/* Asynchronously resolve addr. Use default_port if a port isn't designated
+   in addr, otherwise use the port in addr. */
+/* TODO(apolcyn): add a timeout here */
+void grpc_resolve_address(const char* addr, const char* default_port,
+                          grpc_pollset_set* interested_parties,
+                          grpc_closure* on_done,
+                          grpc_resolved_addresses** addresses);
+
+/* Destroy resolved addresses */
+void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addresses);
+
+/* Resolve addr in a blocking fashion. On success,
+   result must be freed with grpc_resolved_addresses_destroy. */
+grpc_error* grpc_blocking_resolve_address(const char* name,
+                                          const char* default_port,
+                                          grpc_resolved_addresses** addresses);
+
+#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.cc b/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.cc
new file mode 100644
index 0000000..9cf7817
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.cc
@@ -0,0 +1,187 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include <grpc/support/log.h>
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/resolve_address_custom.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+#include <string.h>
+
+typedef struct grpc_custom_resolver {
+  grpc_closure* on_done;
+  grpc_resolved_addresses** addresses;
+  char* host;
+  char* port;
+} grpc_custom_resolver;
+
+static grpc_custom_resolver_vtable* resolve_address_vtable = nullptr;
+
+static int retry_named_port_failure(grpc_custom_resolver* r,
+                                    grpc_resolved_addresses** res) {
+  // This loop is copied from resolve_address_posix.c
+  const char* svc[][2] = {{"http", "80"}, {"https", "443"}};
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) {
+    if (strcmp(r->port, svc[i][0]) == 0) {
+      gpr_free(r->port);
+      r->port = gpr_strdup(svc[i][1]);
+      if (res) {
+        grpc_error* error =
+            resolve_address_vtable->resolve(r->host, r->port, res);
+        if (error != GRPC_ERROR_NONE) {
+          GRPC_ERROR_UNREF(error);
+          return 0;
+        }
+      } else {
+        resolve_address_vtable->resolve_async(r, r->host, r->port);
+      }
+      return 1;
+    }
+  }
+  return 0;
+}
+
+void grpc_custom_resolve_callback(grpc_custom_resolver* r,
+                                  grpc_resolved_addresses* result,
+                                  grpc_error* error) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  grpc_core::ExecCtx exec_ctx;
+  if (error == GRPC_ERROR_NONE) {
+    *r->addresses = result;
+  } else if (retry_named_port_failure(r, nullptr)) {
+    return;
+  }
+  if (r->on_done) {
+    GRPC_CLOSURE_SCHED(r->on_done, error);
+  }
+  gpr_free(r->host);
+  gpr_free(r->port);
+  gpr_free(r);
+}
+
+static grpc_error* try_split_host_port(const char* name,
+                                       const char* default_port, char** host,
+                                       char** port) {
+  /* parse name, splitting it into host and port parts */
+  grpc_error* error;
+  gpr_split_host_port(name, host, port);
+  if (*host == nullptr) {
+    char* msg;
+    gpr_asprintf(&msg, "unparseable host:port: '%s'", name);
+    error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return error;
+  }
+  if (*port == nullptr) {
+    // TODO(murgatroid99): add tests for this case
+    if (default_port == nullptr) {
+      char* msg;
+      gpr_asprintf(&msg, "no port in name '%s'", name);
+      error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      gpr_free(msg);
+      return error;
+    }
+    *port = gpr_strdup(default_port);
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* blocking_resolve_address_impl(
+    const char* name, const char* default_port,
+    grpc_resolved_addresses** addresses) {
+  char* host;
+  char* port;
+  grpc_error* err;
+
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+
+  err = try_split_host_port(name, default_port, &host, &port);
+  if (err != GRPC_ERROR_NONE) {
+    gpr_free(host);
+    gpr_free(port);
+    return err;
+  }
+
+  /* Call getaddrinfo */
+  grpc_custom_resolver resolver;
+  resolver.host = host;
+  resolver.port = port;
+
+  grpc_resolved_addresses* addrs;
+  grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get();
+  grpc_core::ExecCtx::Set(nullptr);
+  err = resolve_address_vtable->resolve(host, port, &addrs);
+  if (err != GRPC_ERROR_NONE) {
+    if (retry_named_port_failure(&resolver, &addrs)) {
+      GRPC_ERROR_UNREF(err);
+      err = GRPC_ERROR_NONE;
+    }
+  }
+  grpc_core::ExecCtx::Set(curr);
+  if (err == GRPC_ERROR_NONE) {
+    *addresses = addrs;
+  }
+  gpr_free(resolver.host);
+  gpr_free(resolver.port);
+  return err;
+}
+
+static void resolve_address_impl(const char* name, const char* default_port,
+                                 grpc_pollset_set* interested_parties,
+                                 grpc_closure* on_done,
+                                 grpc_resolved_addresses** addrs) {
+  grpc_custom_resolver* r = nullptr;
+  char* host = nullptr;
+  char* port = nullptr;
+  grpc_error* err;
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  err = try_split_host_port(name, default_port, &host, &port);
+  if (err != GRPC_ERROR_NONE) {
+    GRPC_CLOSURE_SCHED(on_done, err);
+    gpr_free(host);
+    gpr_free(port);
+    return;
+  }
+  r = (grpc_custom_resolver*)gpr_malloc(sizeof(grpc_custom_resolver));
+  r->on_done = on_done;
+  r->addresses = addrs;
+  r->host = host;
+  r->port = port;
+
+  /* Call getaddrinfo */
+  resolve_address_vtable->resolve_async(r, r->host, r->port);
+}
+
+static grpc_address_resolver_vtable custom_resolver_vtable = {
+    resolve_address_impl, blocking_resolve_address_impl};
+
+void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl) {
+  resolve_address_vtable = impl;
+  grpc_set_resolver_impl(&custom_resolver_vtable);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.h b/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.h
new file mode 100644
index 0000000..e0c6714
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H
+#define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+typedef struct grpc_custom_resolver grpc_custom_resolver;
+
+typedef struct grpc_custom_resolver_vtable {
+  grpc_error* (*resolve)(char* host, char* port, grpc_resolved_addresses** res);
+  void (*resolve_async)(grpc_custom_resolver* resolver, char* host, char* port);
+} grpc_custom_resolver_vtable;
+
+void grpc_custom_resolve_callback(grpc_custom_resolver* resolver,
+                                  grpc_resolved_addresses* result,
+                                  grpc_error* error);
+
+/* Internal APIs */
+void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl);
+
+#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address_posix.cc b/third_party/grpc/src/core/lib/iomgr/resolve_address_posix.cc
new file mode 100644
index 0000000..c285d7e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/resolve_address_posix.cc
@@ -0,0 +1,181 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#ifdef GRPC_POSIX_SOCKET_RESOLVE_ADDRESS
+
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include "src/core/lib/iomgr/resolve_address.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#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/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/block_annotate.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+
+static grpc_error* posix_blocking_resolve_address(
+    const char* name, const char* default_port,
+    grpc_resolved_addresses** addresses) {
+  grpc_core::ExecCtx exec_ctx;
+  struct addrinfo hints;
+  struct addrinfo *result = nullptr, *resp;
+  char* host;
+  char* port;
+  int s;
+  size_t i;
+  grpc_error* err;
+
+  if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' &&
+      name[4] == ':' && name[5] != 0) {
+    return grpc_resolve_unix_domain_address(name + 5, addresses);
+  }
+
+  /* parse name, splitting it into host and port parts */
+  gpr_split_host_port(name, &host, &port);
+  if (host == nullptr) {
+    err = grpc_error_set_str(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"),
+        GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name));
+    goto done;
+  }
+  if (port == nullptr) {
+    if (default_port == nullptr) {
+      err = grpc_error_set_str(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"),
+          GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name));
+      goto done;
+    }
+    port = gpr_strdup(default_port);
+  }
+
+  /* Call getaddrinfo */
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;     /* ipv4 or ipv6 */
+  hints.ai_socktype = SOCK_STREAM; /* stream socket */
+  hints.ai_flags = AI_PASSIVE;     /* for wildcard IP address */
+
+  GRPC_SCHEDULING_START_BLOCKING_REGION;
+  s = getaddrinfo(host, port, &hints, &result);
+  GRPC_SCHEDULING_END_BLOCKING_REGION;
+
+  if (s != 0) {
+    /* Retry if well-known service name is recognized */
+    const char* svc[][2] = {{"http", "80"}, {"https", "443"}};
+    for (i = 0; i < GPR_ARRAY_SIZE(svc); i++) {
+      if (strcmp(port, svc[i][0]) == 0) {
+        GRPC_SCHEDULING_START_BLOCKING_REGION;
+        s = getaddrinfo(host, svc[i][1], &hints, &result);
+        GRPC_SCHEDULING_END_BLOCKING_REGION;
+        break;
+      }
+    }
+  }
+
+  if (s != 0) {
+    err = grpc_error_set_str(
+        grpc_error_set_str(
+            grpc_error_set_str(
+                grpc_error_set_int(
+                    GRPC_ERROR_CREATE_FROM_STATIC_STRING("OS Error"),
+                    GRPC_ERROR_INT_ERRNO, s),
+                GRPC_ERROR_STR_OS_ERROR,
+                grpc_slice_from_static_string(gai_strerror(s))),
+            GRPC_ERROR_STR_SYSCALL,
+            grpc_slice_from_static_string("getaddrinfo")),
+        GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name));
+    goto done;
+  }
+
+  /* Success path: set addrs non-NULL, fill it in */
+  *addresses = static_cast<grpc_resolved_addresses*>(
+      gpr_malloc(sizeof(grpc_resolved_addresses)));
+  (*addresses)->naddrs = 0;
+  for (resp = result; resp != nullptr; resp = resp->ai_next) {
+    (*addresses)->naddrs++;
+  }
+  (*addresses)->addrs = static_cast<grpc_resolved_address*>(
+      gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs));
+  i = 0;
+  for (resp = result; resp != nullptr; resp = resp->ai_next) {
+    memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
+    (*addresses)->addrs[i].len = resp->ai_addrlen;
+    i++;
+  }
+  err = GRPC_ERROR_NONE;
+
+done:
+  gpr_free(host);
+  gpr_free(port);
+  if (result) {
+    freeaddrinfo(result);
+  }
+  return err;
+}
+
+typedef struct {
+  char* name;
+  char* default_port;
+  grpc_closure* on_done;
+  grpc_resolved_addresses** addrs_out;
+  grpc_closure request_closure;
+  void* arg;
+} request;
+
+/* Callback to be passed to grpc_executor to asynch-ify
+ * grpc_blocking_resolve_address */
+static void do_request_thread(void* rp, grpc_error* error) {
+  request* r = static_cast<request*>(rp);
+  GRPC_CLOSURE_SCHED(r->on_done, grpc_blocking_resolve_address(
+                                     r->name, r->default_port, r->addrs_out));
+  gpr_free(r->name);
+  gpr_free(r->default_port);
+  gpr_free(r);
+}
+
+static void posix_resolve_address(const char* name, const char* default_port,
+                                  grpc_pollset_set* interested_parties,
+                                  grpc_closure* on_done,
+                                  grpc_resolved_addresses** addrs) {
+  request* r = static_cast<request*>(gpr_malloc(sizeof(request)));
+  GRPC_CLOSURE_INIT(
+      &r->request_closure, do_request_thread, r,
+      grpc_executor_scheduler(GRPC_RESOLVER_EXECUTOR, GRPC_EXECUTOR_SHORT));
+  r->name = gpr_strdup(name);
+  r->default_port = gpr_strdup(default_port);
+  r->on_done = on_done;
+  r->addrs_out = addrs;
+  GRPC_CLOSURE_SCHED(&r->request_closure, GRPC_ERROR_NONE);
+}
+
+grpc_address_resolver_vtable grpc_posix_resolver_vtable = {
+    posix_resolve_address, posix_blocking_resolve_address};
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address_windows.cc b/third_party/grpc/src/core/lib/iomgr/resolve_address_windows.cc
new file mode 100644
index 0000000..3e977dc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/resolve_address_windows.cc
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include "src/core/lib/iomgr/resolve_address.h"
+
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_windows.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/block_annotate.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+typedef struct {
+  char* name;
+  char* default_port;
+  grpc_closure request_closure;
+  grpc_closure* on_done;
+  grpc_resolved_addresses** addresses;
+} request;
+
+static grpc_error* windows_blocking_resolve_address(
+    const char* name, const char* default_port,
+    grpc_resolved_addresses** addresses) {
+  grpc_core::ExecCtx exec_ctx;
+  struct addrinfo hints;
+  struct addrinfo *result = NULL, *resp;
+  char* host;
+  char* port;
+  int s;
+  size_t i;
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  /* parse name, splitting it into host and port parts */
+  gpr_split_host_port(name, &host, &port);
+  if (host == NULL) {
+    char* msg;
+    gpr_asprintf(&msg, "unparseable host:port: '%s'", name);
+    error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    goto done;
+  }
+  if (port == NULL) {
+    if (default_port == NULL) {
+      char* msg;
+      gpr_asprintf(&msg, "no port in name '%s'", name);
+      error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+      gpr_free(msg);
+      goto done;
+    }
+    port = gpr_strdup(default_port);
+  }
+
+  /* Call getaddrinfo */
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;     /* ipv4 or ipv6 */
+  hints.ai_socktype = SOCK_STREAM; /* stream socket */
+  hints.ai_flags = AI_PASSIVE;     /* for wildcard IP address */
+
+  GRPC_SCHEDULING_START_BLOCKING_REGION;
+  s = getaddrinfo(host, port, &hints, &result);
+  GRPC_SCHEDULING_END_BLOCKING_REGION;
+  if (s != 0) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "getaddrinfo");
+    goto done;
+  }
+
+  /* Success path: set addrs non-NULL, fill it in */
+  (*addresses) =
+      (grpc_resolved_addresses*)gpr_malloc(sizeof(grpc_resolved_addresses));
+  (*addresses)->naddrs = 0;
+  for (resp = result; resp != NULL; resp = resp->ai_next) {
+    (*addresses)->naddrs++;
+  }
+  (*addresses)->addrs = (grpc_resolved_address*)gpr_malloc(
+      sizeof(grpc_resolved_address) * (*addresses)->naddrs);
+  i = 0;
+  for (resp = result; resp != NULL; resp = resp->ai_next) {
+    memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
+    (*addresses)->addrs[i].len = resp->ai_addrlen;
+    i++;
+  }
+
+  {
+    for (i = 0; i < (*addresses)->naddrs; i++) {
+      char* buf;
+      grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0);
+      gpr_free(buf);
+    }
+  }
+
+done:
+  gpr_free(host);
+  gpr_free(port);
+  if (result) {
+    freeaddrinfo(result);
+  }
+  return error;
+}
+
+/* Callback to be passed to grpc_executor to asynch-ify
+ * grpc_blocking_resolve_address */
+static void do_request_thread(void* rp, grpc_error* error) {
+  request* r = (request*)rp;
+  if (error == GRPC_ERROR_NONE) {
+    error =
+        grpc_blocking_resolve_address(r->name, r->default_port, r->addresses);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  GRPC_CLOSURE_SCHED(r->on_done, error);
+  gpr_free(r->name);
+  gpr_free(r->default_port);
+  gpr_free(r);
+}
+
+static void windows_resolve_address(const char* name, const char* default_port,
+                                    grpc_pollset_set* interested_parties,
+                                    grpc_closure* on_done,
+                                    grpc_resolved_addresses** addresses) {
+  request* r = (request*)gpr_malloc(sizeof(request));
+  GRPC_CLOSURE_INIT(
+      &r->request_closure, do_request_thread, r,
+      grpc_executor_scheduler(GRPC_RESOLVER_EXECUTOR, GRPC_EXECUTOR_SHORT));
+  r->name = gpr_strdup(name);
+  r->default_port = gpr_strdup(default_port);
+  r->on_done = on_done;
+  r->addresses = addresses;
+  GRPC_CLOSURE_SCHED(&r->request_closure, GRPC_ERROR_NONE);
+}
+
+grpc_address_resolver_vtable grpc_windows_resolver_vtable = {
+    windows_resolve_address, windows_blocking_resolve_address};
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/resource_quota.cc b/third_party/grpc/src/core/lib/iomgr/resource_quota.cc
new file mode 100644
index 0000000..61c3660
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/resource_quota.cc
@@ -0,0 +1,1003 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/resource_quota.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/combiner.h"
+
+grpc_core::TraceFlag grpc_resource_quota_trace(false, "resource_quota");
+
+#define MEMORY_USAGE_ESTIMATION_MAX 65536
+
+/* Internal linked list pointers for a resource user */
+typedef struct {
+  grpc_resource_user* next;
+  grpc_resource_user* prev;
+} grpc_resource_user_link;
+
+/* Resource users are kept in (potentially) several intrusive linked lists
+   at once. These are the list names. */
+typedef enum {
+  /* Resource users that are waiting for an allocation */
+  GRPC_RULIST_AWAITING_ALLOCATION,
+  /* Resource users that have free memory available for internal reclamation */
+  GRPC_RULIST_NON_EMPTY_FREE_POOL,
+  /* Resource users that have published a benign reclamation is available */
+  GRPC_RULIST_RECLAIMER_BENIGN,
+  /* Resource users that have published a destructive reclamation is
+     available */
+  GRPC_RULIST_RECLAIMER_DESTRUCTIVE,
+  /* Number of lists: must be last */
+  GRPC_RULIST_COUNT
+} grpc_rulist;
+
+struct grpc_resource_user {
+  /* The quota this resource user consumes from */
+  grpc_resource_quota* resource_quota;
+
+  /* Closure to schedule an allocation under the resource quota combiner lock */
+  grpc_closure allocate_closure;
+  /* Closure to publish a non empty free pool under the resource quota combiner
+     lock */
+  grpc_closure add_to_free_pool_closure;
+
+  /* one ref for each ref call (released by grpc_resource_user_unref), and one
+     ref for each byte allocated (released by grpc_resource_user_free) */
+  gpr_atm refs;
+  /* is this resource user unlocked? starts at 0, increases for each shutdown
+     call */
+  gpr_atm shutdown;
+
+  gpr_mu mu;
+  /* The amount of memory (in bytes) this user has cached for its own use: to
+     avoid quota contention, each resource user can keep some memory in
+     addition to what it is immediately using (e.g., for caching), and the quota
+     can pull it back under memory pressure.
+     This value can become negative if more memory has been requested than
+     existed in the free pool, at which point the quota is consulted to bring
+     this value non-negative (asynchronously). */
+  int64_t free_pool;
+  /* A list of closures to call once free_pool becomes non-negative - ie when
+     all outstanding allocations have been granted. */
+  grpc_closure_list on_allocated;
+  /* True if we are currently trying to allocate from the quota, false if not */
+  bool allocating;
+  /* The amount of memory (in bytes) that has been requested from this user
+   * asynchronously but hasn't been granted yet. */
+  int64_t outstanding_allocations;
+  /* True if we are currently trying to add ourselves to the non-free quota
+     list, false otherwise */
+  bool added_to_free_pool;
+
+  /* The number of threads currently allocated to this resource user */
+  gpr_atm num_threads_allocated;
+
+  /* Reclaimers: index 0 is the benign reclaimer, 1 is the destructive reclaimer
+   */
+  grpc_closure* reclaimers[2];
+  /* Reclaimers just posted: once we're in the combiner lock, we'll move them
+     to the array above */
+  grpc_closure* new_reclaimers[2];
+  /* Trampoline closures to finish reclamation and re-enter the quota combiner
+     lock */
+  grpc_closure post_reclaimer_closure[2];
+
+  /* Closure to execute under the quota combiner to de-register and shutdown the
+     resource user */
+  grpc_closure destroy_closure;
+
+  /* Links in the various grpc_rulist lists */
+  grpc_resource_user_link links[GRPC_RULIST_COUNT];
+
+  /* The name of this resource user, for debugging/tracing */
+  char* name;
+};
+
+struct grpc_resource_quota {
+  /* refcount */
+  gpr_refcount refs;
+
+  /* estimate of current memory usage
+     scaled to the range [0..RESOURCE_USAGE_ESTIMATION_MAX] */
+  gpr_atm memory_usage_estimation;
+
+  /* Master combiner lock: all activity on a quota executes under this combiner
+   * (so no mutex is needed for this data structure) */
+  grpc_combiner* combiner;
+  /* Size of the resource quota */
+  int64_t size;
+  /* Amount of free memory in the resource quota */
+  int64_t free_pool;
+  /* Used size of memory in the resource quota. Updated as soon as the resource
+   * users start to allocate or free the memory. */
+  gpr_atm used;
+
+  gpr_atm last_size;
+
+  /* Mutex to protect max_threads and num_threads_allocated */
+  /* Note: We could have used gpr_atm for max_threads and num_threads_allocated
+   * and avoid having this mutex; but in that case, each invocation of the
+   * function grpc_resource_user_allocate_threads() would have had to do at
+   * least two atomic loads (for max_threads and num_threads_allocated) followed
+   * by a CAS (on num_threads_allocated).
+   * Moreover, we expect grpc_resource_user_allocate_threads() to be often
+   * called concurrently thereby increasing the chances of failing the CAS
+   * operation. This additional complexity is not worth the tiny perf gain we
+   * may (or may not) have by using atomics */
+  gpr_mu thread_count_mu;
+
+  /* Max number of threads allowed */
+  int max_threads;
+
+  /* Number of threads currently allocated via this resource_quota object */
+  int num_threads_allocated;
+
+  /* Has rq_step been scheduled to occur? */
+  bool step_scheduled;
+
+  /* Are we currently reclaiming memory */
+  bool reclaiming;
+
+  /* Closure around rq_step */
+  grpc_closure rq_step_closure;
+
+  /* Closure around rq_reclamation_done */
+  grpc_closure rq_reclamation_done_closure;
+
+  /* This is only really usable for debugging: it's always a stale pointer, but
+     a stale pointer that might just be fresh enough to guide us to where the
+     reclamation system is stuck */
+  grpc_closure* debug_only_last_initiated_reclaimer;
+  grpc_resource_user* debug_only_last_reclaimer_resource_user;
+
+  /* Roots of all resource user lists */
+  grpc_resource_user* roots[GRPC_RULIST_COUNT];
+
+  char* name;
+};
+
+static void ru_unref_by(grpc_resource_user* resource_user, gpr_atm amount);
+
+/*******************************************************************************
+ * list management
+ */
+
+static void rulist_add_head(grpc_resource_user* resource_user,
+                            grpc_rulist list) {
+  grpc_resource_quota* resource_quota = resource_user->resource_quota;
+  grpc_resource_user** root = &resource_quota->roots[list];
+  if (*root == nullptr) {
+    *root = resource_user;
+    resource_user->links[list].next = resource_user->links[list].prev =
+        resource_user;
+  } else {
+    resource_user->links[list].next = *root;
+    resource_user->links[list].prev = (*root)->links[list].prev;
+    resource_user->links[list].next->links[list].prev =
+        resource_user->links[list].prev->links[list].next = resource_user;
+    *root = resource_user;
+  }
+}
+
+static void rulist_add_tail(grpc_resource_user* resource_user,
+                            grpc_rulist list) {
+  grpc_resource_quota* resource_quota = resource_user->resource_quota;
+  grpc_resource_user** root = &resource_quota->roots[list];
+  if (*root == nullptr) {
+    *root = resource_user;
+    resource_user->links[list].next = resource_user->links[list].prev =
+        resource_user;
+  } else {
+    resource_user->links[list].next = (*root)->links[list].next;
+    resource_user->links[list].prev = *root;
+    resource_user->links[list].next->links[list].prev =
+        resource_user->links[list].prev->links[list].next = resource_user;
+  }
+}
+
+static bool rulist_empty(grpc_resource_quota* resource_quota,
+                         grpc_rulist list) {
+  return resource_quota->roots[list] == nullptr;
+}
+
+static grpc_resource_user* rulist_pop_head(grpc_resource_quota* resource_quota,
+                                           grpc_rulist list) {
+  grpc_resource_user** root = &resource_quota->roots[list];
+  grpc_resource_user* resource_user = *root;
+  if (resource_user == nullptr) {
+    return nullptr;
+  }
+  if (resource_user->links[list].next == resource_user) {
+    *root = nullptr;
+  } else {
+    resource_user->links[list].next->links[list].prev =
+        resource_user->links[list].prev;
+    resource_user->links[list].prev->links[list].next =
+        resource_user->links[list].next;
+    *root = resource_user->links[list].next;
+  }
+  resource_user->links[list].next = resource_user->links[list].prev = nullptr;
+  return resource_user;
+}
+
+static void rulist_remove(grpc_resource_user* resource_user, grpc_rulist list) {
+  if (resource_user->links[list].next == nullptr) return;
+  grpc_resource_quota* resource_quota = resource_user->resource_quota;
+  if (resource_quota->roots[list] == resource_user) {
+    resource_quota->roots[list] = resource_user->links[list].next;
+    if (resource_quota->roots[list] == resource_user) {
+      resource_quota->roots[list] = nullptr;
+    }
+  }
+  resource_user->links[list].next->links[list].prev =
+      resource_user->links[list].prev;
+  resource_user->links[list].prev->links[list].next =
+      resource_user->links[list].next;
+  resource_user->links[list].next = resource_user->links[list].prev = nullptr;
+}
+
+/*******************************************************************************
+ * resource quota state machine
+ */
+
+static bool rq_alloc(grpc_resource_quota* resource_quota);
+static bool rq_reclaim_from_per_user_free_pool(
+    grpc_resource_quota* resource_quota);
+static bool rq_reclaim(grpc_resource_quota* resource_quota, bool destructive);
+
+static void rq_step(void* rq, grpc_error* error) {
+  grpc_resource_quota* resource_quota = static_cast<grpc_resource_quota*>(rq);
+  resource_quota->step_scheduled = false;
+  do {
+    if (rq_alloc(resource_quota)) goto done;
+  } while (rq_reclaim_from_per_user_free_pool(resource_quota));
+
+  if (!rq_reclaim(resource_quota, false)) {
+    rq_reclaim(resource_quota, true);
+  }
+
+done:
+  grpc_resource_quota_unref_internal(resource_quota);
+}
+
+static void rq_step_sched(grpc_resource_quota* resource_quota) {
+  if (resource_quota->step_scheduled) return;
+  resource_quota->step_scheduled = true;
+  grpc_resource_quota_ref_internal(resource_quota);
+  GRPC_CLOSURE_SCHED(&resource_quota->rq_step_closure, GRPC_ERROR_NONE);
+}
+
+/* update the atomically available resource estimate - use no barriers since
+   timeliness of delivery really doesn't matter much */
+static void rq_update_estimate(grpc_resource_quota* resource_quota) {
+  gpr_atm memory_usage_estimation = MEMORY_USAGE_ESTIMATION_MAX;
+  if (resource_quota->size != 0) {
+    memory_usage_estimation =
+        GPR_CLAMP((gpr_atm)((1.0 - ((double)resource_quota->free_pool) /
+                                       ((double)resource_quota->size)) *
+                            MEMORY_USAGE_ESTIMATION_MAX),
+                  0, MEMORY_USAGE_ESTIMATION_MAX);
+  }
+  gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation,
+                           memory_usage_estimation);
+}
+
+/* returns true if all allocations are completed */
+static bool rq_alloc(grpc_resource_quota* resource_quota) {
+  grpc_resource_user* resource_user;
+  while ((resource_user = rulist_pop_head(resource_quota,
+                                          GRPC_RULIST_AWAITING_ALLOCATION))) {
+    gpr_mu_lock(&resource_user->mu);
+    if (grpc_resource_quota_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "RQ: check allocation for user %p shutdown=%" PRIdPTR
+              " free_pool=%" PRId64,
+              resource_user, gpr_atm_no_barrier_load(&resource_user->shutdown),
+              resource_user->free_pool);
+    }
+    if (gpr_atm_no_barrier_load(&resource_user->shutdown)) {
+      resource_user->allocating = false;
+      grpc_closure_list_fail_all(
+          &resource_user->on_allocated,
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource user shutdown"));
+      int64_t aborted_allocations = resource_user->outstanding_allocations;
+      resource_user->outstanding_allocations = 0;
+      resource_user->free_pool += aborted_allocations;
+      GRPC_CLOSURE_LIST_SCHED(&resource_user->on_allocated);
+      gpr_mu_unlock(&resource_user->mu);
+      ru_unref_by(resource_user, static_cast<gpr_atm>(aborted_allocations));
+      continue;
+    }
+    if (resource_user->free_pool < 0 &&
+        -resource_user->free_pool <= resource_quota->free_pool) {
+      int64_t amt = -resource_user->free_pool;
+      resource_user->free_pool = 0;
+      resource_quota->free_pool -= amt;
+      rq_update_estimate(resource_quota);
+      if (grpc_resource_quota_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "RQ %s %s: grant alloc %" PRId64
+                " bytes; rq_free_pool -> %" PRId64,
+                resource_quota->name, resource_user->name, amt,
+                resource_quota->free_pool);
+      }
+    } else if (grpc_resource_quota_trace.enabled() &&
+               resource_user->free_pool >= 0) {
+      gpr_log(GPR_INFO, "RQ %s %s: discard already satisfied alloc request",
+              resource_quota->name, resource_user->name);
+    }
+    if (resource_user->free_pool >= 0) {
+      resource_user->allocating = false;
+      resource_user->outstanding_allocations = 0;
+      GRPC_CLOSURE_LIST_SCHED(&resource_user->on_allocated);
+      gpr_mu_unlock(&resource_user->mu);
+    } else {
+      rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
+      gpr_mu_unlock(&resource_user->mu);
+      return false;
+    }
+  }
+  return true;
+}
+
+/* returns true if any memory could be reclaimed from buffers */
+static bool rq_reclaim_from_per_user_free_pool(
+    grpc_resource_quota* resource_quota) {
+  grpc_resource_user* resource_user;
+  while ((resource_user = rulist_pop_head(resource_quota,
+                                          GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
+    gpr_mu_lock(&resource_user->mu);
+    resource_user->added_to_free_pool = false;
+    if (resource_user->free_pool > 0) {
+      int64_t amt = resource_user->free_pool;
+      resource_user->free_pool = 0;
+      resource_quota->free_pool += amt;
+      rq_update_estimate(resource_quota);
+      if (grpc_resource_quota_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
+                " bytes; rq_free_pool -> %" PRId64,
+                resource_quota->name, resource_user->name, amt,
+                resource_quota->free_pool);
+      }
+      gpr_mu_unlock(&resource_user->mu);
+      return true;
+    } else {
+      if (grpc_resource_quota_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "RQ %s %s: failed to reclaim_from_per_user_free_pool; "
+                "free_pool = %" PRId64 "; rq_free_pool = %" PRId64,
+                resource_quota->name, resource_user->name,
+                resource_user->free_pool, resource_quota->free_pool);
+      }
+      gpr_mu_unlock(&resource_user->mu);
+    }
+  }
+  return false;
+}
+
+/* returns true if reclamation is proceeding */
+static bool rq_reclaim(grpc_resource_quota* resource_quota, bool destructive) {
+  if (resource_quota->reclaiming) return true;
+  grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE
+                                 : GRPC_RULIST_RECLAIMER_BENIGN;
+  grpc_resource_user* resource_user = rulist_pop_head(resource_quota, list);
+  if (resource_user == nullptr) return false;
+  if (grpc_resource_quota_trace.enabled()) {
+    gpr_log(GPR_INFO, "RQ %s %s: initiate %s reclamation", resource_quota->name,
+            resource_user->name, destructive ? "destructive" : "benign");
+  }
+  resource_quota->reclaiming = true;
+  grpc_resource_quota_ref_internal(resource_quota);
+  grpc_closure* c = resource_user->reclaimers[destructive];
+  GPR_ASSERT(c);
+  resource_quota->debug_only_last_reclaimer_resource_user = resource_user;
+  resource_quota->debug_only_last_initiated_reclaimer = c;
+  resource_user->reclaimers[destructive] = nullptr;
+  GRPC_CLOSURE_SCHED(c, GRPC_ERROR_NONE);
+  return true;
+}
+
+/*******************************************************************************
+ * ru_slice: a slice implementation that is backed by a grpc_resource_user
+ */
+
+typedef struct {
+  grpc_slice_refcount base;
+  gpr_refcount refs;
+  grpc_resource_user* resource_user;
+  size_t size;
+} ru_slice_refcount;
+
+static void ru_slice_ref(void* p) {
+  ru_slice_refcount* rc = static_cast<ru_slice_refcount*>(p);
+  gpr_ref(&rc->refs);
+}
+
+static void ru_slice_unref(void* p) {
+  ru_slice_refcount* rc = static_cast<ru_slice_refcount*>(p);
+  if (gpr_unref(&rc->refs)) {
+    grpc_resource_user_free(rc->resource_user, rc->size);
+    gpr_free(rc);
+  }
+}
+
+static const grpc_slice_refcount_vtable ru_slice_vtable = {
+    ru_slice_ref, ru_slice_unref, grpc_slice_default_eq_impl,
+    grpc_slice_default_hash_impl};
+
+static grpc_slice ru_slice_create(grpc_resource_user* resource_user,
+                                  size_t size) {
+  ru_slice_refcount* rc = static_cast<ru_slice_refcount*>(
+      gpr_malloc(sizeof(ru_slice_refcount) + size));
+  rc->base.vtable = &ru_slice_vtable;
+  rc->base.sub_refcount = &rc->base;
+  gpr_ref_init(&rc->refs, 1);
+  rc->resource_user = resource_user;
+  rc->size = size;
+  grpc_slice slice;
+  slice.refcount = &rc->base;
+  slice.data.refcounted.bytes = reinterpret_cast<uint8_t*>(rc + 1);
+  slice.data.refcounted.length = size;
+  return slice;
+}
+
+/*******************************************************************************
+ * grpc_resource_quota internal implementation: resource user manipulation under
+ * the combiner
+ */
+
+static void ru_allocate(void* ru, grpc_error* error) {
+  grpc_resource_user* resource_user = static_cast<grpc_resource_user*>(ru);
+  if (rulist_empty(resource_user->resource_quota,
+                   GRPC_RULIST_AWAITING_ALLOCATION)) {
+    rq_step_sched(resource_user->resource_quota);
+  }
+  rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION);
+}
+
+static void ru_add_to_free_pool(void* ru, grpc_error* error) {
+  grpc_resource_user* resource_user = static_cast<grpc_resource_user*>(ru);
+  if (!rulist_empty(resource_user->resource_quota,
+                    GRPC_RULIST_AWAITING_ALLOCATION) &&
+      rulist_empty(resource_user->resource_quota,
+                   GRPC_RULIST_NON_EMPTY_FREE_POOL)) {
+    rq_step_sched(resource_user->resource_quota);
+  }
+  rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL);
+}
+
+static bool ru_post_reclaimer(grpc_resource_user* resource_user,
+                              bool destructive) {
+  grpc_closure* closure = resource_user->new_reclaimers[destructive];
+  GPR_ASSERT(closure != nullptr);
+  resource_user->new_reclaimers[destructive] = nullptr;
+  GPR_ASSERT(resource_user->reclaimers[destructive] == nullptr);
+  if (gpr_atm_acq_load(&resource_user->shutdown) > 0) {
+    GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_CANCELLED);
+    return false;
+  }
+  resource_user->reclaimers[destructive] = closure;
+  return true;
+}
+
+static void ru_post_benign_reclaimer(void* ru, grpc_error* error) {
+  grpc_resource_user* resource_user = static_cast<grpc_resource_user*>(ru);
+  if (!ru_post_reclaimer(resource_user, false)) return;
+  if (!rulist_empty(resource_user->resource_quota,
+                    GRPC_RULIST_AWAITING_ALLOCATION) &&
+      rulist_empty(resource_user->resource_quota,
+                   GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
+      rulist_empty(resource_user->resource_quota,
+                   GRPC_RULIST_RECLAIMER_BENIGN)) {
+    rq_step_sched(resource_user->resource_quota);
+  }
+  rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
+}
+
+static void ru_post_destructive_reclaimer(void* ru, grpc_error* error) {
+  grpc_resource_user* resource_user = static_cast<grpc_resource_user*>(ru);
+  if (!ru_post_reclaimer(resource_user, true)) return;
+  if (!rulist_empty(resource_user->resource_quota,
+                    GRPC_RULIST_AWAITING_ALLOCATION) &&
+      rulist_empty(resource_user->resource_quota,
+                   GRPC_RULIST_NON_EMPTY_FREE_POOL) &&
+      rulist_empty(resource_user->resource_quota,
+                   GRPC_RULIST_RECLAIMER_BENIGN) &&
+      rulist_empty(resource_user->resource_quota,
+                   GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) {
+    rq_step_sched(resource_user->resource_quota);
+  }
+  rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
+}
+
+static void ru_shutdown(void* ru, grpc_error* error) {
+  if (grpc_resource_quota_trace.enabled()) {
+    gpr_log(GPR_INFO, "RU shutdown %p", ru);
+  }
+  grpc_resource_user* resource_user = static_cast<grpc_resource_user*>(ru);
+  gpr_mu_lock(&resource_user->mu);
+  GRPC_CLOSURE_SCHED(resource_user->reclaimers[0], GRPC_ERROR_CANCELLED);
+  GRPC_CLOSURE_SCHED(resource_user->reclaimers[1], GRPC_ERROR_CANCELLED);
+  resource_user->reclaimers[0] = nullptr;
+  resource_user->reclaimers[1] = nullptr;
+  rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_BENIGN);
+  rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE);
+  if (resource_user->allocating) {
+    rq_step_sched(resource_user->resource_quota);
+  }
+  gpr_mu_unlock(&resource_user->mu);
+}
+
+static void ru_destroy(void* ru, grpc_error* error) {
+  grpc_resource_user* resource_user = static_cast<grpc_resource_user*>(ru);
+  GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->refs) == 0);
+  // Free all the remaining thread quota
+  grpc_resource_user_free_threads(resource_user,
+                                  static_cast<int>(gpr_atm_no_barrier_load(
+                                      &resource_user->num_threads_allocated)));
+
+  for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
+    rulist_remove(resource_user, static_cast<grpc_rulist>(i));
+  }
+  GRPC_CLOSURE_SCHED(resource_user->reclaimers[0], GRPC_ERROR_CANCELLED);
+  GRPC_CLOSURE_SCHED(resource_user->reclaimers[1], GRPC_ERROR_CANCELLED);
+  if (resource_user->free_pool != 0) {
+    resource_user->resource_quota->free_pool += resource_user->free_pool;
+    rq_step_sched(resource_user->resource_quota);
+  }
+  grpc_resource_quota_unref_internal(resource_user->resource_quota);
+  gpr_mu_destroy(&resource_user->mu);
+  gpr_free(resource_user->name);
+  gpr_free(resource_user);
+}
+
+static void ru_allocated_slices(void* arg, grpc_error* error) {
+  grpc_resource_user_slice_allocator* slice_allocator =
+      static_cast<grpc_resource_user_slice_allocator*>(arg);
+  if (error == GRPC_ERROR_NONE) {
+    for (size_t i = 0; i < slice_allocator->count; i++) {
+      grpc_slice_buffer_add_indexed(
+          slice_allocator->dest, ru_slice_create(slice_allocator->resource_user,
+                                                 slice_allocator->length));
+    }
+  }
+  GRPC_CLOSURE_RUN(&slice_allocator->on_done, GRPC_ERROR_REF(error));
+}
+
+/*******************************************************************************
+ * grpc_resource_quota internal implementation: quota manipulation under the
+ * combiner
+ */
+
+typedef struct {
+  int64_t size;
+  grpc_resource_quota* resource_quota;
+  grpc_closure closure;
+} rq_resize_args;
+
+static void rq_resize(void* args, grpc_error* error) {
+  rq_resize_args* a = static_cast<rq_resize_args*>(args);
+  int64_t delta = a->size - a->resource_quota->size;
+  a->resource_quota->size += delta;
+  a->resource_quota->free_pool += delta;
+  rq_update_estimate(a->resource_quota);
+  rq_step_sched(a->resource_quota);
+  grpc_resource_quota_unref_internal(a->resource_quota);
+  gpr_free(a);
+}
+
+static void rq_reclamation_done(void* rq, grpc_error* error) {
+  grpc_resource_quota* resource_quota = static_cast<grpc_resource_quota*>(rq);
+  resource_quota->reclaiming = false;
+  rq_step_sched(resource_quota);
+  grpc_resource_quota_unref_internal(resource_quota);
+}
+
+/*******************************************************************************
+ * grpc_resource_quota api
+ */
+
+/* Public API */
+grpc_resource_quota* grpc_resource_quota_create(const char* name) {
+  grpc_resource_quota* resource_quota =
+      static_cast<grpc_resource_quota*>(gpr_malloc(sizeof(*resource_quota)));
+  gpr_ref_init(&resource_quota->refs, 1);
+  resource_quota->combiner = grpc_combiner_create();
+  resource_quota->free_pool = INT64_MAX;
+  resource_quota->size = INT64_MAX;
+  resource_quota->used = 0;
+  gpr_atm_no_barrier_store(&resource_quota->last_size, GPR_ATM_MAX);
+  gpr_mu_init(&resource_quota->thread_count_mu);
+  resource_quota->max_threads = INT_MAX;
+  resource_quota->num_threads_allocated = 0;
+  resource_quota->step_scheduled = false;
+  resource_quota->reclaiming = false;
+  gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation, 0);
+  if (name != nullptr) {
+    resource_quota->name = gpr_strdup(name);
+  } else {
+    gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR,
+                 (intptr_t)resource_quota);
+  }
+  GRPC_CLOSURE_INIT(&resource_quota->rq_step_closure, rq_step, resource_quota,
+                    grpc_combiner_finally_scheduler(resource_quota->combiner));
+  GRPC_CLOSURE_INIT(&resource_quota->rq_reclamation_done_closure,
+                    rq_reclamation_done, resource_quota,
+                    grpc_combiner_scheduler(resource_quota->combiner));
+  for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
+    resource_quota->roots[i] = nullptr;
+  }
+  return resource_quota;
+}
+
+void grpc_resource_quota_unref_internal(grpc_resource_quota* resource_quota) {
+  if (gpr_unref(&resource_quota->refs)) {
+    // No outstanding thread quota
+    GPR_ASSERT(resource_quota->num_threads_allocated == 0);
+    GRPC_COMBINER_UNREF(resource_quota->combiner, "resource_quota");
+    gpr_free(resource_quota->name);
+    gpr_mu_destroy(&resource_quota->thread_count_mu);
+    gpr_free(resource_quota);
+  }
+}
+
+/* Public API */
+void grpc_resource_quota_unref(grpc_resource_quota* resource_quota) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_resource_quota_unref_internal(resource_quota);
+}
+
+grpc_resource_quota* grpc_resource_quota_ref_internal(
+    grpc_resource_quota* resource_quota) {
+  gpr_ref(&resource_quota->refs);
+  return resource_quota;
+}
+
+/* Public API */
+void grpc_resource_quota_ref(grpc_resource_quota* resource_quota) {
+  grpc_resource_quota_ref_internal(resource_quota);
+}
+
+double grpc_resource_quota_get_memory_pressure(
+    grpc_resource_quota* resource_quota) {
+  return (static_cast<double>(gpr_atm_no_barrier_load(
+             &resource_quota->memory_usage_estimation))) /
+         (static_cast<double>(MEMORY_USAGE_ESTIMATION_MAX));
+}
+
+/* Public API */
+void grpc_resource_quota_set_max_threads(grpc_resource_quota* resource_quota,
+                                         int new_max_threads) {
+  GPR_ASSERT(new_max_threads >= 0);
+  gpr_mu_lock(&resource_quota->thread_count_mu);
+  resource_quota->max_threads = new_max_threads;
+  gpr_mu_unlock(&resource_quota->thread_count_mu);
+}
+
+/* Public API */
+void grpc_resource_quota_resize(grpc_resource_quota* resource_quota,
+                                size_t size) {
+  grpc_core::ExecCtx exec_ctx;
+  rq_resize_args* a = static_cast<rq_resize_args*>(gpr_malloc(sizeof(*a)));
+  a->resource_quota = grpc_resource_quota_ref_internal(resource_quota);
+  a->size = static_cast<int64_t>(size);
+  gpr_atm_no_barrier_store(&resource_quota->last_size,
+                           (gpr_atm)GPR_MIN((size_t)GPR_ATM_MAX, size));
+  GRPC_CLOSURE_INIT(&a->closure, rq_resize, a, grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_SCHED(&a->closure, GRPC_ERROR_NONE);
+}
+
+size_t grpc_resource_quota_peek_size(grpc_resource_quota* resource_quota) {
+  return static_cast<size_t>(
+      gpr_atm_no_barrier_load(&resource_quota->last_size));
+}
+
+/*******************************************************************************
+ * grpc_resource_user channel args api
+ */
+
+grpc_resource_quota* grpc_resource_quota_from_channel_args(
+    const grpc_channel_args* channel_args, bool create) {
+  for (size_t i = 0; i < channel_args->num_args; i++) {
+    if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
+      if (channel_args->args[i].type == GRPC_ARG_POINTER) {
+        return grpc_resource_quota_ref_internal(
+            static_cast<grpc_resource_quota*>(
+                channel_args->args[i].value.pointer.p));
+      } else {
+        gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer");
+      }
+    }
+  }
+  return create ? grpc_resource_quota_create(nullptr) : nullptr;
+}
+
+static void* rq_copy(void* rq) {
+  grpc_resource_quota_ref(static_cast<grpc_resource_quota*>(rq));
+  return rq;
+}
+
+static void rq_destroy(void* rq) {
+  grpc_resource_quota_unref_internal(static_cast<grpc_resource_quota*>(rq));
+}
+
+static int rq_cmp(void* a, void* b) { return GPR_ICMP(a, b); }
+
+const grpc_arg_pointer_vtable* grpc_resource_quota_arg_vtable(void) {
+  static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp};
+  return &vtable;
+}
+
+/*******************************************************************************
+ * grpc_resource_user api
+ */
+
+grpc_resource_user* grpc_resource_user_create(
+    grpc_resource_quota* resource_quota, const char* name) {
+  grpc_resource_user* resource_user =
+      static_cast<grpc_resource_user*>(gpr_malloc(sizeof(*resource_user)));
+  resource_user->resource_quota =
+      grpc_resource_quota_ref_internal(resource_quota);
+  GRPC_CLOSURE_INIT(&resource_user->allocate_closure, &ru_allocate,
+                    resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner));
+  GRPC_CLOSURE_INIT(&resource_user->add_to_free_pool_closure,
+                    &ru_add_to_free_pool, resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner));
+  GRPC_CLOSURE_INIT(&resource_user->post_reclaimer_closure[0],
+                    &ru_post_benign_reclaimer, resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner));
+  GRPC_CLOSURE_INIT(&resource_user->post_reclaimer_closure[1],
+                    &ru_post_destructive_reclaimer, resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner));
+  GRPC_CLOSURE_INIT(&resource_user->destroy_closure, &ru_destroy, resource_user,
+                    grpc_combiner_scheduler(resource_quota->combiner));
+  gpr_mu_init(&resource_user->mu);
+  gpr_atm_rel_store(&resource_user->refs, 1);
+  gpr_atm_rel_store(&resource_user->shutdown, 0);
+  resource_user->free_pool = 0;
+  grpc_closure_list_init(&resource_user->on_allocated);
+  resource_user->allocating = false;
+  resource_user->added_to_free_pool = false;
+  gpr_atm_no_barrier_store(&resource_user->num_threads_allocated, 0);
+  resource_user->reclaimers[0] = nullptr;
+  resource_user->reclaimers[1] = nullptr;
+  resource_user->new_reclaimers[0] = nullptr;
+  resource_user->new_reclaimers[1] = nullptr;
+  resource_user->outstanding_allocations = 0;
+  for (int i = 0; i < GRPC_RULIST_COUNT; i++) {
+    resource_user->links[i].next = resource_user->links[i].prev = nullptr;
+  }
+  if (name != nullptr) {
+    resource_user->name = gpr_strdup(name);
+  } else {
+    gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR,
+                 (intptr_t)resource_user);
+  }
+  return resource_user;
+}
+
+grpc_resource_quota* grpc_resource_user_quota(
+    grpc_resource_user* resource_user) {
+  return resource_user->resource_quota;
+}
+
+static void ru_ref_by(grpc_resource_user* resource_user, gpr_atm amount) {
+  GPR_ASSERT(amount > 0);
+  GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&resource_user->refs, amount) != 0);
+}
+
+static void ru_unref_by(grpc_resource_user* resource_user, gpr_atm amount) {
+  GPR_ASSERT(amount > 0);
+  gpr_atm old = gpr_atm_full_fetch_add(&resource_user->refs, -amount);
+  GPR_ASSERT(old >= amount);
+  if (old == amount) {
+    GRPC_CLOSURE_SCHED(&resource_user->destroy_closure, GRPC_ERROR_NONE);
+  }
+}
+
+void grpc_resource_user_ref(grpc_resource_user* resource_user) {
+  ru_ref_by(resource_user, 1);
+}
+
+void grpc_resource_user_unref(grpc_resource_user* resource_user) {
+  ru_unref_by(resource_user, 1);
+}
+
+void grpc_resource_user_shutdown(grpc_resource_user* resource_user) {
+  if (gpr_atm_full_fetch_add(&resource_user->shutdown, 1) == 0) {
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_CREATE(
+            ru_shutdown, resource_user,
+            grpc_combiner_scheduler(resource_user->resource_quota->combiner)),
+        GRPC_ERROR_NONE);
+  }
+}
+
+bool grpc_resource_user_allocate_threads(grpc_resource_user* resource_user,
+                                         int thread_count) {
+  GPR_ASSERT(thread_count >= 0);
+  bool is_success = false;
+  gpr_mu_lock(&resource_user->resource_quota->thread_count_mu);
+  grpc_resource_quota* rq = resource_user->resource_quota;
+  if (rq->num_threads_allocated + thread_count <= rq->max_threads) {
+    rq->num_threads_allocated += thread_count;
+    gpr_atm_no_barrier_fetch_add(&resource_user->num_threads_allocated,
+                                 thread_count);
+    is_success = true;
+  }
+  gpr_mu_unlock(&resource_user->resource_quota->thread_count_mu);
+  return is_success;
+}
+
+void grpc_resource_user_free_threads(grpc_resource_user* resource_user,
+                                     int thread_count) {
+  GPR_ASSERT(thread_count >= 0);
+  gpr_mu_lock(&resource_user->resource_quota->thread_count_mu);
+  grpc_resource_quota* rq = resource_user->resource_quota;
+  rq->num_threads_allocated -= thread_count;
+  int old_count = static_cast<int>(gpr_atm_no_barrier_fetch_add(
+      &resource_user->num_threads_allocated, -thread_count));
+  if (old_count < thread_count || rq->num_threads_allocated < 0) {
+    gpr_log(GPR_ERROR,
+            "Releasing more threads (%d) than currently allocated (rq threads: "
+            "%d, ru threads: %d)",
+            thread_count, rq->num_threads_allocated + thread_count, old_count);
+    abort();
+  }
+  gpr_mu_unlock(&resource_user->resource_quota->thread_count_mu);
+}
+
+static void resource_user_alloc_locked(grpc_resource_user* resource_user,
+                                       size_t size,
+                                       grpc_closure* optional_on_done) {
+  ru_ref_by(resource_user, static_cast<gpr_atm>(size));
+  resource_user->free_pool -= static_cast<int64_t>(size);
+  if (grpc_resource_quota_trace.enabled()) {
+    gpr_log(GPR_INFO, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64,
+            resource_user->resource_quota->name, resource_user->name, size,
+            resource_user->free_pool);
+  }
+  if (resource_user->free_pool < 0) {
+    if (optional_on_done != nullptr) {
+      resource_user->outstanding_allocations += static_cast<int64_t>(size);
+      grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
+                               GRPC_ERROR_NONE);
+    }
+    if (!resource_user->allocating) {
+      resource_user->allocating = true;
+      GRPC_CLOSURE_SCHED(&resource_user->allocate_closure, GRPC_ERROR_NONE);
+    }
+  } else {
+    GRPC_CLOSURE_SCHED(optional_on_done, GRPC_ERROR_NONE);
+  }
+}
+
+bool grpc_resource_user_safe_alloc(grpc_resource_user* resource_user,
+                                   size_t size) {
+  if (gpr_atm_no_barrier_load(&resource_user->shutdown)) return false;
+  gpr_mu_lock(&resource_user->mu);
+  grpc_resource_quota* resource_quota = resource_user->resource_quota;
+  bool cas_success;
+  do {
+    gpr_atm used = gpr_atm_no_barrier_load(&resource_quota->used);
+    gpr_atm new_used = used + size;
+    if (static_cast<size_t>(new_used) >
+        grpc_resource_quota_peek_size(resource_quota)) {
+      gpr_mu_unlock(&resource_user->mu);
+      return false;
+    }
+    cas_success = gpr_atm_full_cas(&resource_quota->used, used, new_used);
+  } while (!cas_success);
+  resource_user_alloc_locked(resource_user, size, nullptr);
+  gpr_mu_unlock(&resource_user->mu);
+  return true;
+}
+
+void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size,
+                              grpc_closure* optional_on_done) {
+  // TODO(juanlishen): Maybe return immediately if shutting down. Deferring this
+  // because some tests become flaky after the change.
+  gpr_mu_lock(&resource_user->mu);
+  grpc_resource_quota* resource_quota = resource_user->resource_quota;
+  gpr_atm_no_barrier_fetch_add(&resource_quota->used, size);
+  resource_user_alloc_locked(resource_user, size, optional_on_done);
+  gpr_mu_unlock(&resource_user->mu);
+}
+
+void grpc_resource_user_free(grpc_resource_user* resource_user, size_t size) {
+  gpr_mu_lock(&resource_user->mu);
+  grpc_resource_quota* resource_quota = resource_user->resource_quota;
+  gpr_atm prior = gpr_atm_no_barrier_fetch_add(&resource_quota->used, -size);
+  GPR_ASSERT(prior >= static_cast<long>(size));
+  bool was_zero_or_negative = resource_user->free_pool <= 0;
+  resource_user->free_pool += static_cast<int64_t>(size);
+  if (grpc_resource_quota_trace.enabled()) {
+    gpr_log(GPR_INFO, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64,
+            resource_user->resource_quota->name, resource_user->name, size,
+            resource_user->free_pool);
+  }
+  bool is_bigger_than_zero = resource_user->free_pool > 0;
+  if (is_bigger_than_zero && was_zero_or_negative &&
+      !resource_user->added_to_free_pool) {
+    resource_user->added_to_free_pool = true;
+    GRPC_CLOSURE_SCHED(&resource_user->add_to_free_pool_closure,
+                       GRPC_ERROR_NONE);
+  }
+  gpr_mu_unlock(&resource_user->mu);
+  ru_unref_by(resource_user, static_cast<gpr_atm>(size));
+}
+
+void grpc_resource_user_post_reclaimer(grpc_resource_user* resource_user,
+                                       bool destructive,
+                                       grpc_closure* closure) {
+  GPR_ASSERT(resource_user->new_reclaimers[destructive] == nullptr);
+  resource_user->new_reclaimers[destructive] = closure;
+  GRPC_CLOSURE_SCHED(&resource_user->post_reclaimer_closure[destructive],
+                     GRPC_ERROR_NONE);
+}
+
+void grpc_resource_user_finish_reclamation(grpc_resource_user* resource_user) {
+  if (grpc_resource_quota_trace.enabled()) {
+    gpr_log(GPR_INFO, "RQ %s %s: reclamation complete",
+            resource_user->resource_quota->name, resource_user->name);
+  }
+  GRPC_CLOSURE_SCHED(
+      &resource_user->resource_quota->rq_reclamation_done_closure,
+      GRPC_ERROR_NONE);
+}
+
+void grpc_resource_user_slice_allocator_init(
+    grpc_resource_user_slice_allocator* slice_allocator,
+    grpc_resource_user* resource_user, grpc_iomgr_cb_func cb, void* p) {
+  GRPC_CLOSURE_INIT(&slice_allocator->on_allocated, ru_allocated_slices,
+                    slice_allocator, grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&slice_allocator->on_done, cb, p,
+                    grpc_schedule_on_exec_ctx);
+  slice_allocator->resource_user = resource_user;
+}
+
+void grpc_resource_user_alloc_slices(
+    grpc_resource_user_slice_allocator* slice_allocator, size_t length,
+    size_t count, grpc_slice_buffer* dest) {
+  if (gpr_atm_no_barrier_load(&slice_allocator->resource_user->shutdown)) {
+    GRPC_CLOSURE_SCHED(
+        &slice_allocator->on_allocated,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource user shutdown"));
+    return;
+  }
+  slice_allocator->length = length;
+  slice_allocator->count = count;
+  slice_allocator->dest = dest;
+  grpc_resource_user_alloc(slice_allocator->resource_user, count * length,
+                           &slice_allocator->on_allocated);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/resource_quota.h b/third_party/grpc/src/core/lib/iomgr/resource_quota.h
new file mode 100644
index 0000000..1c79b52
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/resource_quota.h
@@ -0,0 +1,173 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H
+#define GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/closure.h"
+
+/** \file Tracks resource usage against a pool.
+
+    The current implementation tracks only memory usage, but in the future
+    this may be extended to (for example) threads and file descriptors.
+
+    A grpc_resource_quota represents the pooled resources, and
+    grpc_resource_user instances attach to the quota and consume those
+    resources. They also offer a vector for reclamation: if we become
+    resource constrained, grpc_resource_user instances are asked (in turn) to
+    free up whatever they can so that the system as a whole can make progress.
+
+    There are three kinds of reclamation that take place, in order of increasing
+    invasiveness:
+    - an internal reclamation, where cached resource at the resource user level
+      is returned to the quota
+    - a benign reclamation phase, whereby resources that are in use but are not
+      helping anything make progress are reclaimed
+    - a destructive reclamation, whereby resources that are helping something
+      make progress may be enacted so that at least one part of the system can
+      complete.
+
+    Only one reclamation will be outstanding for a given quota at a given time.
+    On each reclamation attempt, the kinds of reclamation are tried in order of
+    increasing invasiveness, stopping at the first one that succeeds. Thus, on a
+    given reclamation attempt, if internal and benign reclamation both fail, it
+    will wind up doing a destructive reclamation. However, the next reclamation
+    attempt may then be able to get what it needs via internal or benign
+    reclamation, due to resources that may have been freed up by the destructive
+    reclamation in the previous attempt.
+
+    Future work will be to expose the current resource pressure so that back
+    pressure can be applied to avoid reclamation phases starting.
+
+    Resource users own references to resource quotas, and resource quotas
+    maintain lists of users (which users arrange to leave before they are
+    destroyed) */
+
+extern grpc_core::TraceFlag grpc_resource_quota_trace;
+
+// TODO(juanlishen): This is a hack. We need to do real accounting instead of
+// hard coding.
+constexpr size_t GRPC_RESOURCE_QUOTA_CALL_SIZE = 15 * 1024;
+constexpr size_t GRPC_RESOURCE_QUOTA_CHANNEL_SIZE = 50 * 1024;
+
+grpc_resource_quota* grpc_resource_quota_ref_internal(
+    grpc_resource_quota* resource_quota);
+void grpc_resource_quota_unref_internal(grpc_resource_quota* resource_quota);
+grpc_resource_quota* grpc_resource_quota_from_channel_args(
+    const grpc_channel_args* channel_args, bool create = true);
+
+/* Return a number indicating current memory pressure:
+   0.0 ==> no memory usage
+   1.0 ==> maximum memory usage */
+double grpc_resource_quota_get_memory_pressure(
+    grpc_resource_quota* resource_quota);
+
+size_t grpc_resource_quota_peek_size(grpc_resource_quota* resource_quota);
+
+typedef struct grpc_resource_user grpc_resource_user;
+
+grpc_resource_user* grpc_resource_user_create(
+    grpc_resource_quota* resource_quota, const char* name);
+
+/* Returns a borrowed reference to the underlying resource quota for this
+   resource user. */
+grpc_resource_quota* grpc_resource_user_quota(
+    grpc_resource_user* resource_user);
+
+void grpc_resource_user_ref(grpc_resource_user* resource_user);
+void grpc_resource_user_unref(grpc_resource_user* resource_user);
+void grpc_resource_user_shutdown(grpc_resource_user* resource_user);
+
+/* Attempts to get quota from the resource_user to create 'thread_count' number
+ * of threads. Returns true if successful (i.e the caller is now free to create
+ * 'thread_count' number of threads) or false if quota is not available */
+bool grpc_resource_user_allocate_threads(grpc_resource_user* resource_user,
+                                         int thread_count);
+/* Releases 'thread_count' worth of quota back to the resource user. The quota
+ * should have been previously obtained successfully by calling
+ * grpc_resource_user_allocate_threads().
+ *
+ * Note: There need not be an exact one-to-one correspondence between
+ * grpc_resource_user_allocate_threads() and grpc_resource_user_free_threads()
+ * calls. The only requirement is that the number of threads allocated should
+ * all be eventually released */
+void grpc_resource_user_free_threads(grpc_resource_user* resource_user,
+                                     int thread_count);
+
+/* Allocates from the resource user 'size' worth of memory if this won't exceed
+ * the resource quota's total size. Returns whether the allocation is done
+ * successfully. If allocated successfully, the memory should be freed by the
+ * caller eventually. */
+bool grpc_resource_user_safe_alloc(grpc_resource_user* resource_user,
+                                   size_t size);
+/* Allocates from the resource user 'size' worth of memory.
+ * If optional_on_done is NULL, then allocate immediately. This may push the
+ * quota over-limit, at which point reclamation will kick in. The caller is
+ * always responsible to free the memory eventually.
+ * If optional_on_done is non-NULL, it will be scheduled without error when the
+ * allocation has been granted by the quota, and the caller is responsible to
+ * free the memory eventually. Or it may be scheduled with an error, in which
+ * case the caller fails to allocate the memory and shouldn't free the memory.
+ */
+void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size,
+                              grpc_closure* optional_on_done);
+/* Release memory back to the quota */
+void grpc_resource_user_free(grpc_resource_user* resource_user, size_t size);
+/* Post a memory reclaimer to the resource user. Only one benign and one
+   destructive reclaimer can be posted at once. When executed, the reclaimer
+   MUST call grpc_resource_user_finish_reclamation before it completes, to
+   return control to the resource quota. */
+void grpc_resource_user_post_reclaimer(grpc_resource_user* resource_user,
+                                       bool destructive, grpc_closure* closure);
+/* Finish a reclamation step */
+void grpc_resource_user_finish_reclamation(grpc_resource_user* resource_user);
+
+/* Helper to allocate slices from a resource user */
+typedef struct grpc_resource_user_slice_allocator {
+  /* Closure for when a resource user allocation completes */
+  grpc_closure on_allocated;
+  /* Closure to call when slices have been allocated */
+  grpc_closure on_done;
+  /* Length of slices to allocate on the current request */
+  size_t length;
+  /* Number of slices to allocate on the current request */
+  size_t count;
+  /* Destination for slices to allocate on the current request */
+  grpc_slice_buffer* dest;
+  /* Parent resource user */
+  grpc_resource_user* resource_user;
+} grpc_resource_user_slice_allocator;
+
+/* Initialize a slice allocator.
+   When an allocation is completed, calls \a cb with arg \p. */
+void grpc_resource_user_slice_allocator_init(
+    grpc_resource_user_slice_allocator* slice_allocator,
+    grpc_resource_user* resource_user, grpc_iomgr_cb_func cb, void* p);
+
+/* Allocate \a count slices of length \a length into \a dest. Only one request
+   can be outstanding at a time. */
+void grpc_resource_user_alloc_slices(
+    grpc_resource_user_slice_allocator* slice_allocator, size_t length,
+    size_t count, grpc_slice_buffer* dest);
+
+#endif /* GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr.h b/third_party/grpc/src/core/lib/iomgr/sockaddr.h
new file mode 100644
index 0000000..5edf735
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/sockaddr.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* This header transitively includes other headers that care about include
+ * order, so it should be included first. As a consequence, it should not be
+ * included in any other header. */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/sockaddr_custom.h"
+#include "src/core/lib/iomgr/sockaddr_posix.h"
+#include "src/core/lib/iomgr/sockaddr_windows.h"
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_custom.h b/third_party/grpc/src/core/lib/iomgr/sockaddr_custom.h
new file mode 100644
index 0000000..d85cc50
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_custom.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_UV
+
+#include <uv.h>
+
+// TODO(kpayson)  It would be nice to abstract this so we don't
+// depend on anything uv specific
+typedef struct sockaddr grpc_sockaddr;
+typedef struct sockaddr_in grpc_sockaddr_in;
+typedef struct in_addr grpc_in_addr;
+typedef struct sockaddr_in6 grpc_sockaddr_in6;
+typedef struct in6_addr grpc_in6_addr;
+
+#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN
+#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN
+
+#define GRPC_SOCK_STREAM SOCK_STREAM
+#define GRPC_SOCK_DGRAM SOCK_DGRAM
+
+#define GRPC_AF_UNSPEC AF_UNSPEC
+#define GRPC_AF_UNIX AF_UNIX
+#define GRPC_AF_INET AF_INET
+#define GRPC_AF_INET6 AF_INET6
+
+#define GRPC_AI_PASSIVE AI_PASSIVE
+
+#endif  // GRPC_UV
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_posix.h b/third_party/grpc/src/core/lib/iomgr/sockaddr_posix.h
new file mode 100644
index 0000000..3cedd90
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_posix.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_SOCKADDR
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+typedef struct sockaddr grpc_sockaddr;
+typedef struct sockaddr_in grpc_sockaddr_in;
+typedef struct in_addr grpc_in_addr;
+typedef struct sockaddr_in6 grpc_sockaddr_in6;
+typedef struct in6_addr grpc_in6_addr;
+
+#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN
+#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN
+
+#define GRPC_SOCK_STREAM SOCK_STREAM
+#define GRPC_SOCK_DGRAM SOCK_DGRAM
+
+#define GRPC_AF_UNSPEC AF_UNSPEC
+#define GRPC_AF_UNIX AF_UNIX
+#define GRPC_AF_INET AF_INET
+#define GRPC_AF_INET6 AF_INET6
+
+#define GRPC_AI_PASSIVE AI_PASSIVE
+
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.cc b/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.cc
new file mode 100644
index 0000000..0839bdf
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.cc
@@ -0,0 +1,299 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/socket_utils.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+
+static const uint8_t kV4MappedPrefix[] = {0, 0, 0, 0, 0,    0,
+                                          0, 0, 0, 0, 0xff, 0xff};
+
+int grpc_sockaddr_is_v4mapped(const grpc_resolved_address* resolved_addr,
+                              grpc_resolved_address* resolved_addr4_out) {
+  GPR_ASSERT(resolved_addr != resolved_addr4_out);
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  grpc_sockaddr_in* addr4_out =
+      resolved_addr4_out == nullptr
+          ? nullptr
+          : reinterpret_cast<grpc_sockaddr_in*>(resolved_addr4_out->addr);
+  if (addr->sa_family == GRPC_AF_INET6) {
+    const grpc_sockaddr_in6* addr6 =
+        reinterpret_cast<const grpc_sockaddr_in6*>(addr);
+    if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix,
+               sizeof(kV4MappedPrefix)) == 0) {
+      if (resolved_addr4_out != nullptr) {
+        /* Normalize ::ffff:0.0.0.0/96 to IPv4. */
+        memset(resolved_addr4_out, 0, sizeof(*resolved_addr4_out));
+        addr4_out->sin_family = GRPC_AF_INET;
+        /* s6_addr32 would be nice, but it's non-standard. */
+        memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4);
+        addr4_out->sin_port = addr6->sin6_port;
+        resolved_addr4_out->len =
+            static_cast<socklen_t>(sizeof(grpc_sockaddr_in));
+      }
+      return 1;
+    }
+  }
+  return 0;
+}
+
+int grpc_sockaddr_to_v4mapped(const grpc_resolved_address* resolved_addr,
+                              grpc_resolved_address* resolved_addr6_out) {
+  GPR_ASSERT(resolved_addr != resolved_addr6_out);
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  grpc_sockaddr_in6* addr6_out =
+      reinterpret_cast<grpc_sockaddr_in6*>(resolved_addr6_out->addr);
+  if (addr->sa_family == GRPC_AF_INET) {
+    const grpc_sockaddr_in* addr4 =
+        reinterpret_cast<const grpc_sockaddr_in*>(addr);
+    memset(resolved_addr6_out, 0, sizeof(*resolved_addr6_out));
+    addr6_out->sin6_family = GRPC_AF_INET6;
+    memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12);
+    memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4);
+    addr6_out->sin6_port = addr4->sin_port;
+    resolved_addr6_out->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in6));
+    return 1;
+  }
+  return 0;
+}
+
+int grpc_sockaddr_is_wildcard(const grpc_resolved_address* resolved_addr,
+                              int* port_out) {
+  const grpc_sockaddr* addr;
+  grpc_resolved_address addr4_normalized;
+  if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr4_normalized)) {
+    resolved_addr = &addr4_normalized;
+  }
+  addr = reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  if (addr->sa_family == GRPC_AF_INET) {
+    /* Check for 0.0.0.0 */
+    const grpc_sockaddr_in* addr4 =
+        reinterpret_cast<const grpc_sockaddr_in*>(addr);
+    if (addr4->sin_addr.s_addr != 0) {
+      return 0;
+    }
+    *port_out = grpc_ntohs(addr4->sin_port);
+    return 1;
+  } else if (addr->sa_family == GRPC_AF_INET6) {
+    /* Check for :: */
+    const grpc_sockaddr_in6* addr6 =
+        reinterpret_cast<const grpc_sockaddr_in6*>(addr);
+    int i;
+    for (i = 0; i < 16; i++) {
+      if (addr6->sin6_addr.s6_addr[i] != 0) {
+        return 0;
+      }
+    }
+    *port_out = grpc_ntohs(addr6->sin6_port);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address* wild4_out,
+                                  grpc_resolved_address* wild6_out) {
+  grpc_sockaddr_make_wildcard4(port, wild4_out);
+  grpc_sockaddr_make_wildcard6(port, wild6_out);
+}
+
+void grpc_sockaddr_make_wildcard4(int port,
+                                  grpc_resolved_address* resolved_wild_out) {
+  grpc_sockaddr_in* wild_out =
+      reinterpret_cast<grpc_sockaddr_in*>(resolved_wild_out->addr);
+  GPR_ASSERT(port >= 0 && port < 65536);
+  memset(resolved_wild_out, 0, sizeof(*resolved_wild_out));
+  wild_out->sin_family = GRPC_AF_INET;
+  wild_out->sin_port = grpc_htons(static_cast<uint16_t>(port));
+  resolved_wild_out->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in));
+}
+
+void grpc_sockaddr_make_wildcard6(int port,
+                                  grpc_resolved_address* resolved_wild_out) {
+  grpc_sockaddr_in6* wild_out =
+      reinterpret_cast<grpc_sockaddr_in6*>(resolved_wild_out->addr);
+  GPR_ASSERT(port >= 0 && port < 65536);
+  memset(resolved_wild_out, 0, sizeof(*resolved_wild_out));
+  wild_out->sin6_family = GRPC_AF_INET6;
+  wild_out->sin6_port = grpc_htons(static_cast<uint16_t>(port));
+  resolved_wild_out->len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in6));
+}
+
+int grpc_sockaddr_to_string(char** out,
+                            const grpc_resolved_address* resolved_addr,
+                            int normalize) {
+  const grpc_sockaddr* addr;
+  const int save_errno = errno;
+  grpc_resolved_address addr_normalized;
+  char ntop_buf[GRPC_INET6_ADDRSTRLEN];
+  const void* ip = nullptr;
+  int port = 0;
+  uint32_t sin6_scope_id = 0;
+  int ret;
+
+  *out = nullptr;
+  if (normalize && grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) {
+    resolved_addr = &addr_normalized;
+  }
+  addr = reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  if (addr->sa_family == GRPC_AF_INET) {
+    const grpc_sockaddr_in* addr4 =
+        reinterpret_cast<const grpc_sockaddr_in*>(addr);
+    ip = &addr4->sin_addr;
+    port = grpc_ntohs(addr4->sin_port);
+  } else if (addr->sa_family == GRPC_AF_INET6) {
+    const grpc_sockaddr_in6* addr6 =
+        reinterpret_cast<const grpc_sockaddr_in6*>(addr);
+    ip = &addr6->sin6_addr;
+    port = grpc_ntohs(addr6->sin6_port);
+    sin6_scope_id = addr6->sin6_scope_id;
+  }
+  if (ip != nullptr && grpc_inet_ntop(addr->sa_family, ip, ntop_buf,
+                                      sizeof(ntop_buf)) != nullptr) {
+    if (sin6_scope_id != 0) {
+      char* host_with_scope;
+      /* Enclose sin6_scope_id with the format defined in RFC 6784 section 2. */
+      gpr_asprintf(&host_with_scope, "%s%%25%" PRIu32, ntop_buf, sin6_scope_id);
+      ret = gpr_join_host_port(out, host_with_scope, port);
+      gpr_free(host_with_scope);
+    } else {
+      ret = gpr_join_host_port(out, ntop_buf, port);
+    }
+  } else {
+    ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family);
+  }
+  /* This is probably redundant, but we wouldn't want to log the wrong error. */
+  errno = save_errno;
+  return ret;
+}
+
+void grpc_string_to_sockaddr(grpc_resolved_address* out, char* addr, int port) {
+  grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)out->addr;
+  grpc_sockaddr_in* addr4 = (grpc_sockaddr_in*)out->addr;
+
+  if (grpc_inet_pton(GRPC_AF_INET6, addr, &addr6->sin6_addr) == 1) {
+    addr6->sin6_family = GRPC_AF_INET6;
+    addr6->sin6_flowinfo = 0;
+    addr6->sin6_scope_id = 0;
+    out->len = sizeof(grpc_sockaddr_in6);
+  } else if (grpc_inet_pton(GRPC_AF_INET, addr, &addr4->sin_addr) == 1) {
+    addr4->sin_family = GRPC_AF_INET;
+    out->len = sizeof(grpc_sockaddr_in);
+  } else {
+    GPR_ASSERT(0);
+  }
+  grpc_sockaddr_set_port(out, port);
+}
+
+char* grpc_sockaddr_to_uri(const grpc_resolved_address* resolved_addr) {
+  if (resolved_addr->len == 0) return nullptr;
+  grpc_resolved_address addr_normalized;
+  if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) {
+    resolved_addr = &addr_normalized;
+  }
+  const char* scheme = grpc_sockaddr_get_uri_scheme(resolved_addr);
+  if (scheme == nullptr || strcmp("unix", scheme) == 0) {
+    return grpc_sockaddr_to_uri_unix_if_possible(resolved_addr);
+  }
+  char* path = nullptr;
+  char* uri_str = nullptr;
+  if (grpc_sockaddr_to_string(&path, resolved_addr,
+                              false /* suppress errors */) &&
+      scheme != nullptr) {
+    gpr_asprintf(&uri_str, "%s:%s", scheme, path);
+  }
+  gpr_free(path);
+  return uri_str != nullptr ? uri_str : nullptr;
+}
+
+const char* grpc_sockaddr_get_uri_scheme(
+    const grpc_resolved_address* resolved_addr) {
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  switch (addr->sa_family) {
+    case GRPC_AF_INET:
+      return "ipv4";
+    case GRPC_AF_INET6:
+      return "ipv6";
+    case GRPC_AF_UNIX:
+      return "unix";
+  }
+  return nullptr;
+}
+
+int grpc_sockaddr_get_family(const grpc_resolved_address* resolved_addr) {
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  return addr->sa_family;
+}
+
+int grpc_sockaddr_get_port(const grpc_resolved_address* resolved_addr) {
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  switch (addr->sa_family) {
+    case GRPC_AF_INET:
+      return grpc_ntohs(((grpc_sockaddr_in*)addr)->sin_port);
+    case GRPC_AF_INET6:
+      return grpc_ntohs(((grpc_sockaddr_in6*)addr)->sin6_port);
+    default:
+      if (grpc_is_unix_socket(resolved_addr)) {
+        return 1;
+      }
+      gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_get_port",
+              addr->sa_family);
+      return 0;
+  }
+}
+
+int grpc_sockaddr_set_port(const grpc_resolved_address* resolved_addr,
+                           int port) {
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  switch (addr->sa_family) {
+    case GRPC_AF_INET:
+      GPR_ASSERT(port >= 0 && port < 65536);
+      ((grpc_sockaddr_in*)addr)->sin_port =
+          grpc_htons(static_cast<uint16_t>(port));
+      return 1;
+    case GRPC_AF_INET6:
+      GPR_ASSERT(port >= 0 && port < 65536);
+      ((grpc_sockaddr_in6*)addr)->sin6_port =
+          grpc_htons(static_cast<uint16_t>(port));
+      return 1;
+    default:
+      gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_set_port",
+              addr->sa_family);
+      return 0;
+  }
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.h b/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.h
new file mode 100644
index 0000000..a4e90a7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/resolve_address.h"
+
+/* Returns true if addr is an IPv4-mapped IPv6 address within the
+   ::ffff:0.0.0.0/96 range, or false otherwise.
+
+   If addr4_out is non-NULL, the inner IPv4 address will be copied here when
+   returning true. */
+int grpc_sockaddr_is_v4mapped(const grpc_resolved_address* addr,
+                              grpc_resolved_address* addr4_out);
+
+/* If addr is an AF_INET address, writes the corresponding ::ffff:0.0.0.0/96
+   address to addr6_out and returns true.  Otherwise returns false. */
+int grpc_sockaddr_to_v4mapped(const grpc_resolved_address* addr,
+                              grpc_resolved_address* addr6_out);
+
+/* If addr is ::, 0.0.0.0, or ::ffff:0.0.0.0, writes the port number to
+ *port_out (if not NULL) and returns true, otherwise returns false. */
+int grpc_sockaddr_is_wildcard(const grpc_resolved_address* addr, int* port_out);
+
+/* Writes 0.0.0.0:port and [::]:port to separate sockaddrs. */
+void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address* wild4_out,
+                                  grpc_resolved_address* wild6_out);
+
+/* Writes 0.0.0.0:port. */
+void grpc_sockaddr_make_wildcard4(int port, grpc_resolved_address* wild_out);
+
+/* Writes [::]:port. */
+void grpc_sockaddr_make_wildcard6(int port, grpc_resolved_address* wild_out);
+
+/* Return the IP port number of a sockaddr */
+int grpc_sockaddr_get_port(const grpc_resolved_address* addr);
+
+/* Set IP port number of a sockaddr */
+int grpc_sockaddr_set_port(const grpc_resolved_address* addr, int port);
+
+/* Converts a sockaddr into a newly-allocated human-readable string.
+
+   Currently, only the AF_INET and AF_INET6 families are recognized.
+   If the normalize flag is enabled, ::ffff:0.0.0.0/96 IPv6 addresses are
+   displayed as plain IPv4.
+
+   Usage is similar to gpr_asprintf: returns the number of bytes written
+   (excluding the final '\0'), and *out points to a string which must later be
+   destroyed using gpr_free().
+
+   In the unlikely event of an error, returns -1 and sets *out to NULL.
+   The existing value of errno is always preserved. */
+int grpc_sockaddr_to_string(char** out, const grpc_resolved_address* addr,
+                            int normalize);
+
+void grpc_string_to_sockaddr(grpc_resolved_address* out, char* addr, int port);
+
+/* Returns the URI string corresponding to \a addr */
+char* grpc_sockaddr_to_uri(const grpc_resolved_address* addr);
+
+/* Returns the URI scheme corresponding to \a addr */
+const char* grpc_sockaddr_get_uri_scheme(const grpc_resolved_address* addr);
+
+int grpc_sockaddr_get_family(const grpc_resolved_address* resolved_addr);
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_windows.h b/third_party/grpc/src/core/lib/iomgr/sockaddr_windows.h
new file mode 100644
index 0000000..4d63725
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_windows.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+// must be included after the above
+#include <mswsock.h>
+
+typedef struct sockaddr grpc_sockaddr;
+typedef struct sockaddr_in grpc_sockaddr_in;
+typedef struct in_addr grpc_in_addr;
+typedef struct sockaddr_in6 grpc_sockaddr_in6;
+typedef struct in6_addr grpc_in6_addr;
+
+#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN
+#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN
+
+#define GRPC_SOCK_STREAM SOCK_STREAM
+#define GRPC_SOCK_DGRAM SOCK_DGRAM
+
+#define GRPC_AF_UNSPEC AF_UNSPEC
+#define GRPC_AF_UNIX AF_UNIX
+#define GRPC_AF_INET AF_INET
+#define GRPC_AF_INET6 AF_INET6
+
+#define GRPC_AI_PASSIVE AI_PASSIVE
+
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.cc b/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.cc
new file mode 100644
index 0000000..5713776
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.cc
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_SOCKET_FACTORY
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/socket_factory_posix.h"
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/sync.h>
+
+void grpc_socket_factory_init(grpc_socket_factory* factory,
+                              const grpc_socket_factory_vtable* vtable) {
+  factory->vtable = vtable;
+  gpr_ref_init(&factory->refcount, 1);
+}
+
+int grpc_socket_factory_socket(grpc_socket_factory* factory, int domain,
+                               int type, int protocol) {
+  return factory->vtable->socket(factory, domain, type, protocol);
+}
+
+int grpc_socket_factory_bind(grpc_socket_factory* factory, int sockfd,
+                             const grpc_resolved_address* addr) {
+  return factory->vtable->bind(factory, sockfd, addr);
+}
+
+int grpc_socket_factory_compare(grpc_socket_factory* a,
+                                grpc_socket_factory* b) {
+  int c = GPR_ICMP(a, b);
+  if (c != 0) {
+    grpc_socket_factory* sma = a;
+    grpc_socket_factory* smb = b;
+    c = GPR_ICMP(sma->vtable, smb->vtable);
+    if (c == 0) {
+      c = sma->vtable->compare(sma, smb);
+    }
+  }
+  return c;
+}
+
+grpc_socket_factory* grpc_socket_factory_ref(grpc_socket_factory* factory) {
+  gpr_ref(&factory->refcount);
+  return factory;
+}
+
+void grpc_socket_factory_unref(grpc_socket_factory* factory) {
+  if (gpr_unref(&factory->refcount)) {
+    factory->vtable->destroy(factory);
+  }
+}
+
+static void* socket_factory_arg_copy(void* p) {
+  return grpc_socket_factory_ref(static_cast<grpc_socket_factory*>(p));
+}
+
+static void socket_factory_arg_destroy(void* p) {
+  grpc_socket_factory_unref(static_cast<grpc_socket_factory*>(p));
+}
+
+static int socket_factory_cmp(void* a, void* b) {
+  return grpc_socket_factory_compare(static_cast<grpc_socket_factory*>(a),
+                                     static_cast<grpc_socket_factory*>(b));
+}
+
+static const grpc_arg_pointer_vtable socket_factory_arg_vtable = {
+    socket_factory_arg_copy, socket_factory_arg_destroy, socket_factory_cmp};
+
+grpc_arg grpc_socket_factory_to_arg(grpc_socket_factory* factory) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_ARG_SOCKET_FACTORY,
+                                         factory, &socket_factory_arg_vtable);
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.h b/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.h
new file mode 100644
index 0000000..9a52f4e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/sync.h>
+#include "src/core/lib/iomgr/resolve_address.h"
+
+/** The virtual table of grpc_socket_factory */
+typedef struct {
+  /** Replacement for socket(2) */
+  int (*socket)(grpc_socket_factory* factory, int domain, int type,
+                int protocol);
+  /** Replacement for bind(2) */
+  int (*bind)(grpc_socket_factory* factory, int sockfd,
+              const grpc_resolved_address* addr);
+  /** Compare socket factory \a a and \a b */
+  int (*compare)(grpc_socket_factory* a, grpc_socket_factory* b);
+  /** Destroys the socket factory instance */
+  void (*destroy)(grpc_socket_factory* factory);
+} grpc_socket_factory_vtable;
+
+/** The Socket Factory interface allows changes on socket options */
+struct grpc_socket_factory {
+  const grpc_socket_factory_vtable* vtable;
+  gpr_refcount refcount;
+};
+
+/** called by concrete implementations to initialize the base struct */
+void grpc_socket_factory_init(grpc_socket_factory* factory,
+                              const grpc_socket_factory_vtable* vtable);
+
+/** Wrap \a factory as a grpc_arg */
+grpc_arg grpc_socket_factory_to_arg(grpc_socket_factory* factory);
+
+/** Perform the equivalent of a socket(2) operation using \a factory */
+int grpc_socket_factory_socket(grpc_socket_factory* factory, int domain,
+                               int type, int protocol);
+
+/** Perform the equivalent of a bind(2) operation using \a factory */
+int grpc_socket_factory_bind(grpc_socket_factory* factory, int sockfd,
+                             const grpc_resolved_address* addr);
+
+/** Compare if \a a and \a b are the same factory or have same settings */
+int grpc_socket_factory_compare(grpc_socket_factory* a, grpc_socket_factory* b);
+
+grpc_socket_factory* grpc_socket_factory_ref(grpc_socket_factory* factory);
+void grpc_socket_factory_unref(grpc_socket_factory* factory);
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_mutator.cc b/third_party/grpc/src/core/lib/iomgr/socket_mutator.cc
new file mode 100644
index 0000000..a448c9f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_mutator.cc
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/socket_mutator.h"
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/useful.h"
+
+void grpc_socket_mutator_init(grpc_socket_mutator* mutator,
+                              const grpc_socket_mutator_vtable* vtable) {
+  mutator->vtable = vtable;
+  gpr_ref_init(&mutator->refcount, 1);
+}
+
+grpc_socket_mutator* grpc_socket_mutator_ref(grpc_socket_mutator* mutator) {
+  gpr_ref(&mutator->refcount);
+  return mutator;
+}
+
+bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator* mutator, int fd) {
+  return mutator->vtable->mutate_fd(fd, mutator);
+}
+
+int grpc_socket_mutator_compare(grpc_socket_mutator* a,
+                                grpc_socket_mutator* b) {
+  int c = GPR_ICMP(a, b);
+  if (c != 0) {
+    grpc_socket_mutator* sma = a;
+    grpc_socket_mutator* smb = b;
+    c = GPR_ICMP(sma->vtable, smb->vtable);
+    if (c == 0) {
+      c = sma->vtable->compare(sma, smb);
+    }
+  }
+  return c;
+}
+
+void grpc_socket_mutator_unref(grpc_socket_mutator* mutator) {
+  if (gpr_unref(&mutator->refcount)) {
+    mutator->vtable->destroy(mutator);
+  }
+}
+
+static void* socket_mutator_arg_copy(void* p) {
+  return grpc_socket_mutator_ref(static_cast<grpc_socket_mutator*>(p));
+}
+
+static void socket_mutator_arg_destroy(void* p) {
+  grpc_socket_mutator_unref(static_cast<grpc_socket_mutator*>(p));
+}
+
+static int socket_mutator_cmp(void* a, void* b) {
+  return grpc_socket_mutator_compare(static_cast<grpc_socket_mutator*>(a),
+                                     static_cast<grpc_socket_mutator*>(b));
+}
+
+static const grpc_arg_pointer_vtable socket_mutator_arg_vtable = {
+    socket_mutator_arg_copy, socket_mutator_arg_destroy, socket_mutator_cmp};
+
+grpc_arg grpc_socket_mutator_to_arg(grpc_socket_mutator* mutator) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_ARG_SOCKET_MUTATOR,
+                                         mutator, &socket_mutator_arg_vtable);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_mutator.h b/third_party/grpc/src/core/lib/iomgr/socket_mutator.h
new file mode 100644
index 0000000..8742a3b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_mutator.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H
+#define GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/sync.h>
+
+#include <stdbool.h>
+
+/** The virtual table of grpc_socket_mutator */
+typedef struct {
+  /** Mutates the socket options of \a fd */
+  bool (*mutate_fd)(int fd, grpc_socket_mutator* mutator);
+  /** Compare socket mutator \a a and \a b */
+  int (*compare)(grpc_socket_mutator* a, grpc_socket_mutator* b);
+  /** Destroys the socket mutator instance */
+  void (*destroy)(grpc_socket_mutator* mutator);
+} grpc_socket_mutator_vtable;
+
+/** The Socket Mutator interface allows changes on socket options */
+struct grpc_socket_mutator {
+  const grpc_socket_mutator_vtable* vtable;
+  gpr_refcount refcount;
+};
+
+/** called by concrete implementations to initialize the base struct */
+void grpc_socket_mutator_init(grpc_socket_mutator* mutator,
+                              const grpc_socket_mutator_vtable* vtable);
+
+/** Wrap \a mutator as a grpc_arg */
+grpc_arg grpc_socket_mutator_to_arg(grpc_socket_mutator* mutator);
+
+/** Perform the file descriptor mutation operation of \a mutator on \a fd */
+bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator* mutator, int fd);
+
+/** Compare if \a a and \a b are the same mutator or have same settings */
+int grpc_socket_mutator_compare(grpc_socket_mutator* a, grpc_socket_mutator* b);
+
+grpc_socket_mutator* grpc_socket_mutator_ref(grpc_socket_mutator* mutator);
+void grpc_socket_mutator_unref(grpc_socket_mutator* mutator);
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils.h b/third_party/grpc/src/core/lib/iomgr/socket_utils.h
new file mode 100644
index 0000000..14bb081
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_utils.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H
+#define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+// TODO(juanlishen): The following functions might be simple enough to implement
+// ourselves, so that they don't cause any portability hassle.
+
+/* A wrapper for htons on POSIX and Windows */
+uint16_t grpc_htons(uint16_t hostshort);
+
+/* A wrapper for ntohs on POSIX and WINDOWS */
+uint16_t grpc_ntohs(uint16_t netshort);
+
+/* A wrapper for htonl on POSIX and Windows */
+uint32_t grpc_htonl(uint32_t hostlong);
+
+/* A wrapper for ntohl on POSIX and WINDOWS */
+uint32_t grpc_ntohl(uint32_t netlong);
+
+/* A wrapper for inet_pton on POSIX and WINDOWS */
+int grpc_inet_pton(int af, const char* src, void* dst);
+
+/* A wrapper for inet_ntop on POSIX systems and InetNtop on Windows systems */
+const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size);
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc
new file mode 100644
index 0000000..ea0adb1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc
@@ -0,0 +1,455 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_UTILS_COMMON
+
+#include "src/core/lib/iomgr/socket_utils.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netinet/in.h>
+#ifdef GRPC_LINUX_TCP_H
+#include <linux/tcp.h>
+#else
+#include <netinet/tcp.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+/* set a socket to non blocking mode */
+grpc_error* grpc_set_socket_nonblocking(int fd, int non_blocking) {
+  int oldflags = fcntl(fd, F_GETFL, 0);
+  if (oldflags < 0) {
+    return GRPC_OS_ERROR(errno, "fcntl");
+  }
+
+  if (non_blocking) {
+    oldflags |= O_NONBLOCK;
+  } else {
+    oldflags &= ~O_NONBLOCK;
+  }
+
+  if (fcntl(fd, F_SETFL, oldflags) != 0) {
+    return GRPC_OS_ERROR(errno, "fcntl");
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_set_socket_no_sigpipe_if_possible(int fd) {
+#ifdef GRPC_HAVE_SO_NOSIGPIPE
+  int val = 1;
+  int newval;
+  socklen_t intlen = sizeof(newval);
+  if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(SO_NOSIGPIPE)");
+  }
+  if (0 != getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen)) {
+    return GRPC_OS_ERROR(errno, "getsockopt(SO_NOSIGPIPE)");
+  }
+  if ((newval != 0) != (val != 0)) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_NOSIGPIPE");
+  }
+#endif
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_set_socket_ip_pktinfo_if_possible(int fd) {
+#ifdef GRPC_HAVE_IP_PKTINFO
+  int get_local_ip = 1;
+  if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
+                      sizeof(get_local_ip))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(IP_PKTINFO)");
+  }
+#endif
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) {
+#ifdef GRPC_HAVE_IPV6_RECVPKTINFO
+  int get_local_ip = 1;
+  if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
+                      sizeof(get_local_ip))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(IPV6_RECVPKTINFO)");
+  }
+#endif
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_set_socket_sndbuf(int fd, int buffer_size_bytes) {
+  return 0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size_bytes,
+                         sizeof(buffer_size_bytes))
+             ? GRPC_ERROR_NONE
+             : GRPC_OS_ERROR(errno, "setsockopt(SO_SNDBUF)");
+}
+
+grpc_error* grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes) {
+  return 0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size_bytes,
+                         sizeof(buffer_size_bytes))
+             ? GRPC_ERROR_NONE
+             : GRPC_OS_ERROR(errno, "setsockopt(SO_RCVBUF)");
+}
+
+/* set a socket to close on exec */
+grpc_error* grpc_set_socket_cloexec(int fd, int close_on_exec) {
+  int oldflags = fcntl(fd, F_GETFD, 0);
+  if (oldflags < 0) {
+    return GRPC_OS_ERROR(errno, "fcntl");
+  }
+
+  if (close_on_exec) {
+    oldflags |= FD_CLOEXEC;
+  } else {
+    oldflags &= ~FD_CLOEXEC;
+  }
+
+  if (fcntl(fd, F_SETFD, oldflags) != 0) {
+    return GRPC_OS_ERROR(errno, "fcntl");
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+/* set a socket to reuse old addresses */
+grpc_error* grpc_set_socket_reuse_addr(int fd, int reuse) {
+  int val = (reuse != 0);
+  int newval;
+  socklen_t intlen = sizeof(newval);
+  if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEADDR)");
+  }
+  if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen)) {
+    return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEADDR)");
+  }
+  if ((newval != 0) != val) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_REUSEADDR");
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+/* set a socket to reuse old addresses */
+grpc_error* grpc_set_socket_reuse_port(int fd, int reuse) {
+#ifndef SO_REUSEPORT
+  return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+      "SO_REUSEPORT unavailable on compiling system");
+#else
+  int val = (reuse != 0);
+  int newval;
+  socklen_t intlen = sizeof(newval);
+  if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEPORT)");
+  }
+  if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &newval, &intlen)) {
+    return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEPORT)");
+  }
+  if ((newval != 0) != val) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_REUSEPORT");
+  }
+
+  return GRPC_ERROR_NONE;
+#endif
+}
+
+static gpr_once g_probe_so_reuesport_once = GPR_ONCE_INIT;
+static int g_support_so_reuseport = false;
+
+void probe_so_reuseport_once(void) {
+#ifndef GPR_MANYLINUX1
+  int s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s < 0) {
+    /* This might be an ipv6-only environment in which case 'socket(AF_INET,..)'
+       call would fail. Try creating IPv6 socket in that case */
+    s = socket(AF_INET6, SOCK_STREAM, 0);
+  }
+  if (s >= 0) {
+    g_support_so_reuseport = GRPC_LOG_IF_ERROR(
+        "check for SO_REUSEPORT", grpc_set_socket_reuse_port(s, 1));
+    close(s);
+  }
+#endif
+}
+
+bool grpc_is_socket_reuse_port_supported() {
+  gpr_once_init(&g_probe_so_reuesport_once, probe_so_reuseport_once);
+  return g_support_so_reuseport;
+}
+
+/* disable nagle */
+grpc_error* grpc_set_socket_low_latency(int fd, int low_latency) {
+  int val = (low_latency != 0);
+  int newval;
+  socklen_t intlen = sizeof(newval);
+  if (0 != setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(TCP_NODELAY)");
+  }
+  if (0 != getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen)) {
+    return GRPC_OS_ERROR(errno, "getsockopt(TCP_NODELAY)");
+  }
+  if ((newval != 0) != val) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set TCP_NODELAY");
+  }
+  return GRPC_ERROR_NONE;
+}
+
+/* The default values for TCP_USER_TIMEOUT are currently configured to be in
+ * line with the default values of KEEPALIVE_TIMEOUT as proposed in
+ * https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md */
+#define DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS 20000 /* 20 seconds */
+#define DEFAULT_SERVER_TCP_USER_TIMEOUT_MS 20000 /* 20 seconds */
+
+static int g_default_client_tcp_user_timeout_ms =
+    DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS;
+static int g_default_server_tcp_user_timeout_ms =
+    DEFAULT_SERVER_TCP_USER_TIMEOUT_MS;
+static bool g_default_client_tcp_user_timeout_enabled = false;
+static bool g_default_server_tcp_user_timeout_enabled = true;
+
+void config_default_tcp_user_timeout(bool enable, int timeout, bool is_client) {
+  if (is_client) {
+    g_default_client_tcp_user_timeout_enabled = enable;
+    if (timeout > 0) {
+      g_default_client_tcp_user_timeout_ms = timeout;
+    }
+  } else {
+    g_default_server_tcp_user_timeout_enabled = enable;
+    if (timeout > 0) {
+      g_default_server_tcp_user_timeout_ms = timeout;
+    }
+  }
+}
+
+/* Set TCP_USER_TIMEOUT */
+grpc_error* grpc_set_socket_tcp_user_timeout(
+    int fd, const grpc_channel_args* channel_args, bool is_client) {
+#ifdef GRPC_HAVE_TCP_USER_TIMEOUT
+  bool enable;
+  int timeout;
+  if (is_client) {
+    enable = g_default_client_tcp_user_timeout_enabled;
+    timeout = g_default_client_tcp_user_timeout_ms;
+  } else {
+    enable = g_default_server_tcp_user_timeout_enabled;
+    timeout = g_default_server_tcp_user_timeout_ms;
+  }
+  if (channel_args) {
+    for (unsigned int i = 0; i < channel_args->num_args; i++) {
+      if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) {
+        const int value = grpc_channel_arg_get_integer(
+            &channel_args->args[i], grpc_integer_options{0, 1, INT_MAX});
+        /* Continue using default if value is 0 */
+        if (value == 0) {
+          continue;
+        }
+        /* Disable if value is INT_MAX */
+        enable = value != INT_MAX;
+      } else if (0 == strcmp(channel_args->args[i].key,
+                             GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) {
+        const int value = grpc_channel_arg_get_integer(
+            &channel_args->args[i], grpc_integer_options{0, 1, INT_MAX});
+        /* Continue using default if value is 0 */
+        if (value == 0) {
+          continue;
+        }
+        timeout = value;
+      }
+    }
+  }
+  if (enable) {
+    extern grpc_core::TraceFlag grpc_tcp_trace;
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "Enabling TCP_USER_TIMEOUT with a timeout of %d ms",
+              timeout);
+    }
+    int newval;
+    socklen_t len = sizeof(newval);
+    if (0 != setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout,
+                        sizeof(timeout))) {
+      gpr_log(GPR_ERROR, "setsockopt(TCP_USER_TIMEOUT) %s", strerror(errno));
+      return GRPC_ERROR_NONE;
+    }
+    if (0 != getsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &newval, &len)) {
+      gpr_log(GPR_ERROR, "getsockopt(TCP_USER_TIMEOUT) %s", strerror(errno));
+      return GRPC_ERROR_NONE;
+    }
+    if (newval != timeout) {
+      /* Do not fail on failing to set TCP_USER_TIMEOUT for now. */
+      gpr_log(GPR_ERROR, "Failed to set TCP_USER_TIMEOUT");
+      return GRPC_ERROR_NONE;
+    }
+  }
+#else
+  extern grpc_core::TraceFlag grpc_tcp_trace;
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP_USER_TIMEOUT not supported for this platform");
+  }
+#endif /* GRPC_HAVE_TCP_USER_TIMEOUT */
+  return GRPC_ERROR_NONE;
+}
+
+/* set a socket using a grpc_socket_mutator */
+grpc_error* grpc_set_socket_with_mutator(int fd, grpc_socket_mutator* mutator) {
+  GPR_ASSERT(mutator);
+  if (!grpc_socket_mutator_mutate_fd(mutator, fd)) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("grpc_socket_mutator failed.");
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
+static int g_ipv6_loopback_available;
+
+static void probe_ipv6_once(void) {
+  int fd = socket(AF_INET6, SOCK_STREAM, 0);
+  g_ipv6_loopback_available = 0;
+  if (fd < 0) {
+    gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed.");
+  } else {
+    grpc_sockaddr_in6 addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin6_family = AF_INET6;
+    addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */
+    if (bind(fd, reinterpret_cast<grpc_sockaddr*>(&addr), sizeof(addr)) == 0) {
+      g_ipv6_loopback_available = 1;
+    } else {
+      gpr_log(GPR_INFO,
+              "Disabling AF_INET6 sockets because ::1 is not available.");
+    }
+    close(fd);
+  }
+}
+
+int grpc_ipv6_loopback_available(void) {
+  gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once);
+  return g_ipv6_loopback_available;
+}
+
+/* This should be 0 in production, but it may be enabled for testing or
+   debugging purposes, to simulate an environment where IPv6 sockets can't
+   also speak IPv4. */
+int grpc_forbid_dualstack_sockets_for_testing = 0;
+
+static int set_socket_dualstack(int fd) {
+  if (!grpc_forbid_dualstack_sockets_for_testing) {
+    const int off = 0;
+    return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
+  } else {
+    /* Force an IPv6-only socket, for testing purposes. */
+    const int on = 1;
+    setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+    return 0;
+  }
+}
+
+static grpc_error* error_for_fd(int fd, const grpc_resolved_address* addr) {
+  if (fd >= 0) return GRPC_ERROR_NONE;
+  char* addr_str;
+  grpc_sockaddr_to_string(&addr_str, addr, 0);
+  grpc_error* err = grpc_error_set_str(GRPC_OS_ERROR(errno, "socket"),
+                                       GRPC_ERROR_STR_TARGET_ADDRESS,
+                                       grpc_slice_from_copied_string(addr_str));
+  gpr_free(addr_str);
+  return err;
+}
+
+grpc_error* grpc_create_dualstack_socket(
+    const grpc_resolved_address* resolved_addr, int type, int protocol,
+    grpc_dualstack_mode* dsmode, int* newfd) {
+  return grpc_create_dualstack_socket_using_factory(
+      nullptr, resolved_addr, type, protocol, dsmode, newfd);
+}
+
+static int create_socket(grpc_socket_factory* factory, int domain, int type,
+                         int protocol) {
+  return (factory != nullptr)
+             ? grpc_socket_factory_socket(factory, domain, type, protocol)
+             : socket(domain, type, protocol);
+}
+
+grpc_error* grpc_create_dualstack_socket_using_factory(
+    grpc_socket_factory* factory, const grpc_resolved_address* resolved_addr,
+    int type, int protocol, grpc_dualstack_mode* dsmode, int* newfd) {
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  int family = addr->sa_family;
+  if (family == AF_INET6) {
+    if (grpc_ipv6_loopback_available()) {
+      *newfd = create_socket(factory, family, type, protocol);
+    } else {
+      *newfd = -1;
+      errno = EAFNOSUPPORT;
+    }
+    /* Check if we've got a valid dualstack socket. */
+    if (*newfd >= 0 && set_socket_dualstack(*newfd)) {
+      *dsmode = GRPC_DSMODE_DUALSTACK;
+      return GRPC_ERROR_NONE;
+    }
+    /* If this isn't an IPv4 address, then return whatever we've got. */
+    if (!grpc_sockaddr_is_v4mapped(resolved_addr, nullptr)) {
+      *dsmode = GRPC_DSMODE_IPV6;
+      return error_for_fd(*newfd, resolved_addr);
+    }
+    /* Fall back to AF_INET. */
+    if (*newfd >= 0) {
+      close(*newfd);
+    }
+    family = AF_INET;
+  }
+  *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
+  *newfd = create_socket(factory, family, type, protocol);
+  return error_for_fd(*newfd, resolved_addr);
+}
+
+uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); }
+
+uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); }
+
+uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); }
+
+uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); }
+
+int grpc_inet_pton(int af, const char* src, void* dst) {
+  return inet_pton(af, src, dst);
+}
+
+const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) {
+  GPR_ASSERT(size <= (socklen_t)-1);
+  return inet_ntop(af, src, dst, static_cast<socklen_t>(size));
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_linux.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_linux.cc
new file mode 100644
index 0000000..34f93cc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_linux.cc
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_LINUX_SOCKETUTILS
+
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+#include <grpc/support/log.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock,
+                 int cloexec) {
+  int flags = 0;
+  flags |= nonblock ? SOCK_NONBLOCK : 0;
+  flags |= cloexec ? SOCK_CLOEXEC : 0;
+  return accept4(sockfd, reinterpret_cast<grpc_sockaddr*>(resolved_addr->addr),
+                 &resolved_addr->len, flags);
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.cc
new file mode 100644
index 0000000..c48da52
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.cc
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKETUTILS
+
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+#include "src/core/lib/iomgr/sockaddr.h"
+
+int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock,
+                 int cloexec) {
+  int fd, flags;
+  fd = accept(sockfd, reinterpret_cast<grpc_sockaddr*>(resolved_addr->addr),
+              &resolved_addr->len);
+  if (fd >= 0) {
+    if (nonblock) {
+      flags = fcntl(fd, F_GETFL, 0);
+      if (flags < 0) goto close_and_error;
+      if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) goto close_and_error;
+    }
+    if (cloexec) {
+      flags = fcntl(fd, F_GETFD, 0);
+      if (flags < 0) goto close_and_error;
+      if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != 0) goto close_and_error;
+    }
+  }
+  return fd;
+
+close_and_error:
+  close(fd);
+  return -1;
+}
+
+#endif /* GRPC_POSIX_SOCKETUTILS */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.h b/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.h
new file mode 100644
index 0000000..71a304d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/resolve_address.h"
+
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/socket_factory_posix.h"
+#include "src/core/lib/iomgr/socket_mutator.h"
+
+/* a wrapper for accept or accept4 */
+int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock,
+                 int cloexec);
+
+/* set a socket to non blocking mode */
+grpc_error* grpc_set_socket_nonblocking(int fd, int non_blocking);
+
+/* set a socket to close on exec */
+grpc_error* grpc_set_socket_cloexec(int fd, int close_on_exec);
+
+/* set a socket to reuse old addresses */
+grpc_error* grpc_set_socket_reuse_addr(int fd, int reuse);
+
+/* return true if SO_REUSEPORT is supported */
+bool grpc_is_socket_reuse_port_supported();
+
+/* disable nagle */
+grpc_error* grpc_set_socket_low_latency(int fd, int low_latency);
+
+/* set SO_REUSEPORT */
+grpc_error* grpc_set_socket_reuse_port(int fd, int reuse);
+
+/* Configure the default values for TCP_USER_TIMEOUT */
+void config_default_tcp_user_timeout(bool enable, int timeout, bool is_client);
+
+/* Set TCP_USER_TIMEOUT */
+grpc_error* grpc_set_socket_tcp_user_timeout(
+    int fd, const grpc_channel_args* channel_args, bool is_client);
+
+/* Returns true if this system can create AF_INET6 sockets bound to ::1.
+   The value is probed once, and cached for the life of the process.
+
+   This is more restrictive than checking for socket(AF_INET6) to succeed,
+   because Linux with "net.ipv6.conf.all.disable_ipv6 = 1" is able to create
+   and bind IPv6 sockets, but cannot connect to a getsockname() of [::]:port
+   without a valid loopback interface.  Rather than expose this half-broken
+   state to library users, we turn off IPv6 sockets. */
+int grpc_ipv6_loopback_available(void);
+
+/* Tries to set SO_NOSIGPIPE if available on this platform.
+   If SO_NO_SIGPIPE is not available, returns 1. */
+grpc_error* grpc_set_socket_no_sigpipe_if_possible(int fd);
+
+/* Tries to set IP_PKTINFO if available on this platform.
+   If IP_PKTINFO is not available, returns 1. */
+grpc_error* grpc_set_socket_ip_pktinfo_if_possible(int fd);
+
+/* Tries to set IPV6_RECVPKTINFO if available on this platform.
+   If IPV6_RECVPKTINFO is not available, returns 1. */
+grpc_error* grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd);
+
+/* Tries to set the socket's send buffer to given size. */
+grpc_error* grpc_set_socket_sndbuf(int fd, int buffer_size_bytes);
+
+/* Tries to set the socket's receive buffer to given size. */
+grpc_error* grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes);
+
+/* Tries to set the socket using a grpc_socket_mutator */
+grpc_error* grpc_set_socket_with_mutator(int fd, grpc_socket_mutator* mutator);
+
+/* An enum to keep track of IPv4/IPv6 socket modes.
+
+   Currently, this information is only used when a socket is first created, but
+   in the future we may wish to store it alongside the fd.  This would let calls
+   like sendto() know which family to use without asking the kernel first. */
+typedef enum grpc_dualstack_mode {
+  /* Uninitialized, or a non-IP socket. */
+  GRPC_DSMODE_NONE,
+  /* AF_INET only. */
+  GRPC_DSMODE_IPV4,
+  /* AF_INET6 only, because IPV6_V6ONLY could not be cleared. */
+  GRPC_DSMODE_IPV6,
+  /* AF_INET6, which also supports ::ffff-mapped IPv4 addresses. */
+  GRPC_DSMODE_DUALSTACK
+} grpc_dualstack_mode;
+
+/* Only tests should use this flag. */
+extern int grpc_forbid_dualstack_sockets_for_testing;
+
+/* Creates a new socket for connecting to (or listening on) an address.
+
+   If addr is AF_INET6, this creates an IPv6 socket first.  If that fails,
+   and addr is within ::ffff:0.0.0.0/96, then it automatically falls back to
+   an IPv4 socket.
+
+   If addr is AF_INET, AF_UNIX, or anything else, then this is similar to
+   calling socket() directly.
+
+   Returns an fd on success, otherwise returns -1 with errno set to the result
+   of a failed socket() call.
+
+   The *dsmode output indicates which address family was actually created.
+   The recommended way to use this is:
+   - First convert to IPv6 using grpc_sockaddr_to_v4mapped().
+   - Create the socket.
+   - If *dsmode is IPV4, use grpc_sockaddr_is_v4mapped() to convert back to
+     IPv4, so that bind() or connect() see the correct family.
+   Also, it's important to distinguish between DUALSTACK and IPV6 when
+   listening on the [::] wildcard address. */
+grpc_error* grpc_create_dualstack_socket(const grpc_resolved_address* addr,
+                                         int type, int protocol,
+                                         grpc_dualstack_mode* dsmode,
+                                         int* newfd);
+
+/* Same as grpc_create_dualstack_socket(), but use the given socket factory (if
+   non-null) to create the socket, rather than calling socket() directly. */
+grpc_error* grpc_create_dualstack_socket_using_factory(
+    grpc_socket_factory* factory, const grpc_resolved_address* addr, int type,
+    int protocol, grpc_dualstack_mode* dsmode, int* newfd);
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_uv.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_uv.cc
new file mode 100644
index 0000000..b5f96b5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_uv.cc
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_UV
+
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/socket_utils.h"
+
+#include <grpc/support/log.h>
+
+#include <uv.h>
+
+uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); }
+
+uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); }
+
+uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); }
+
+uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); }
+
+int grpc_inet_pton(int af, const char* src, void* dst) {
+  return inet_pton(af, src, dst);
+}
+
+const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) {
+  uv_inet_ntop(af, src, dst, size);
+  return dst;
+}
+
+#endif /* GRPC_UV */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_windows.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_windows.cc
new file mode 100644
index 0000000..9137ab9
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_windows.cc
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINDOWS_SOCKETUTILS
+
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/socket_utils.h"
+
+#include <grpc/support/log.h>
+
+uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); }
+
+uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); }
+
+uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); }
+
+uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); }
+
+int grpc_inet_pton(int af, const char* src, void* dst) {
+  return inet_pton(af, src, dst);
+}
+
+const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) {
+  /* Windows InetNtopA wants a mutable ip pointer */
+  return InetNtopA(af, (void*)src, dst, size);
+}
+
+#endif /* GRPC_WINDOWS_SOCKETUTILS */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_windows.cc b/third_party/grpc/src/core/lib/iomgr/socket_windows.cc
new file mode 100644
index 0000000..999c664
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_windows.cc
@@ -0,0 +1,184 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include <winsock2.h>
+
+// must be included after winsock2.h
+#include <mswsock.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_windows.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_windows.h"
+#include "src/core/lib/iomgr/sockaddr_windows.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+
+grpc_winsocket* grpc_winsocket_create(SOCKET socket, const char* name) {
+  char* final_name;
+  grpc_winsocket* r = (grpc_winsocket*)gpr_malloc(sizeof(grpc_winsocket));
+  memset(r, 0, sizeof(grpc_winsocket));
+  r->socket = socket;
+  gpr_mu_init(&r->state_mu);
+  gpr_asprintf(&final_name, "%s:socket=0x%p", name, r);
+  grpc_iomgr_register_object(&r->iomgr_object, final_name);
+  gpr_free(final_name);
+  grpc_iocp_add_socket(r);
+  return r;
+}
+
+SOCKET grpc_winsocket_wrapped_socket(grpc_winsocket* socket) {
+  return socket->socket;
+}
+
+/* Schedule a shutdown of the socket operations. Will call the pending
+   operations to abort them. We need to do that this way because of the
+   various callsites of that function, which happens to be in various
+   mutex hold states, and that'd be unsafe to call them directly. */
+void grpc_winsocket_shutdown(grpc_winsocket* winsocket) {
+  /* Grab the function pointer for DisconnectEx for that specific socket.
+     It may change depending on the interface. */
+  int status;
+  GUID guid = WSAID_DISCONNECTEX;
+  LPFN_DISCONNECTEX DisconnectEx;
+  DWORD ioctl_num_bytes;
+
+  gpr_mu_lock(&winsocket->state_mu);
+  if (winsocket->shutdown_called) {
+    gpr_mu_unlock(&winsocket->state_mu);
+    return;
+  }
+  winsocket->shutdown_called = true;
+  gpr_mu_unlock(&winsocket->state_mu);
+
+  status = WSAIoctl(winsocket->socket, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                    &guid, sizeof(guid), &DisconnectEx, sizeof(DisconnectEx),
+                    &ioctl_num_bytes, NULL, NULL);
+
+  if (status == 0) {
+    DisconnectEx(winsocket->socket, NULL, 0, 0);
+  } else {
+    char* utf8_message = gpr_format_message(WSAGetLastError());
+    gpr_log(GPR_INFO, "Unable to retrieve DisconnectEx pointer : %s",
+            utf8_message);
+    gpr_free(utf8_message);
+  }
+  closesocket(winsocket->socket);
+}
+
+static void destroy(grpc_winsocket* winsocket) {
+  grpc_iomgr_unregister_object(&winsocket->iomgr_object);
+  gpr_mu_destroy(&winsocket->state_mu);
+  gpr_free(winsocket);
+}
+
+static bool check_destroyable(grpc_winsocket* winsocket) {
+  return winsocket->destroy_called == true &&
+         winsocket->write_info.closure == NULL &&
+         winsocket->read_info.closure == NULL;
+}
+
+void grpc_winsocket_destroy(grpc_winsocket* winsocket) {
+  gpr_mu_lock(&winsocket->state_mu);
+  GPR_ASSERT(!winsocket->destroy_called);
+  winsocket->destroy_called = true;
+  bool should_destroy = check_destroyable(winsocket);
+  gpr_mu_unlock(&winsocket->state_mu);
+  if (should_destroy) destroy(winsocket);
+}
+
+/* Calling notify_on_read or write means either of two things:
+-) The IOCP already completed in the background, and we need to call
+the callback now.
+-) The IOCP hasn't completed yet, and we're queuing it for later. */
+static void socket_notify_on_iocp(grpc_winsocket* socket, grpc_closure* closure,
+                                  grpc_winsocket_callback_info* info) {
+  GPR_ASSERT(info->closure == NULL);
+  gpr_mu_lock(&socket->state_mu);
+  if (info->has_pending_iocp) {
+    info->has_pending_iocp = 0;
+    GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
+  } else {
+    info->closure = closure;
+  }
+  gpr_mu_unlock(&socket->state_mu);
+}
+
+void grpc_socket_notify_on_write(grpc_winsocket* socket,
+                                 grpc_closure* closure) {
+  socket_notify_on_iocp(socket, closure, &socket->write_info);
+}
+
+void grpc_socket_notify_on_read(grpc_winsocket* socket, grpc_closure* closure) {
+  socket_notify_on_iocp(socket, closure, &socket->read_info);
+}
+
+void grpc_socket_become_ready(grpc_winsocket* socket,
+                              grpc_winsocket_callback_info* info) {
+  GPR_ASSERT(!info->has_pending_iocp);
+  gpr_mu_lock(&socket->state_mu);
+  if (info->closure) {
+    GRPC_CLOSURE_SCHED(info->closure, GRPC_ERROR_NONE);
+    info->closure = NULL;
+  } else {
+    info->has_pending_iocp = 1;
+  }
+  bool should_destroy = check_destroyable(socket);
+  gpr_mu_unlock(&socket->state_mu);
+  if (should_destroy) destroy(socket);
+}
+
+static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
+static bool g_ipv6_loopback_available = false;
+
+static void probe_ipv6_once(void) {
+  SOCKET s = socket(AF_INET6, SOCK_STREAM, 0);
+  g_ipv6_loopback_available = 0;
+  if (s == INVALID_SOCKET) {
+    gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed.");
+  } else {
+    grpc_sockaddr_in6 addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin6_family = AF_INET6;
+    addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */
+    if (bind(s, reinterpret_cast<grpc_sockaddr*>(&addr), sizeof(addr)) == 0) {
+      g_ipv6_loopback_available = 1;
+    } else {
+      gpr_log(GPR_INFO,
+              "Disabling AF_INET6 sockets because ::1 is not available.");
+    }
+    closesocket(s);
+  }
+}
+
+int grpc_ipv6_loopback_available(void) {
+  gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once);
+  return g_ipv6_loopback_available;
+}
+
+#endif /* GRPC_WINSOCK_SOCKET */
diff --git a/third_party/grpc/src/core/lib/iomgr/socket_windows.h b/third_party/grpc/src/core/lib/iomgr/socket_windows.h
new file mode 100644
index 0000000..46d7d58
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/socket_windows.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+#include <winsock2.h>
+
+#include <grpc/support/atm.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+
+/* This holds the data for an outstanding read or write on a socket.
+   The mutex to protect the concurrent access to that data is the one
+   inside the winsocket wrapper. */
+typedef struct grpc_winsocket_callback_info {
+  /* This is supposed to be a WSAOVERLAPPED, but in order to get that
+     definition, we need to include ws2tcpip.h, which needs to be included
+     from the top, otherwise it'll clash with a previous inclusion of
+     windows.h that in turns includes winsock.h. If anyone knows a way
+     to do it properly, feel free to send a patch. */
+  OVERLAPPED overlapped;
+  /* The callback information for the pending operation. May be empty if the
+     caller hasn't registered a callback yet. */
+  grpc_closure* closure;
+  /* A boolean to describe if the IO Completion Port got a notification for
+     that operation. This will happen if the operation completed before the
+     called had time to register a callback. We could avoid that behavior
+     altogether by forcing the caller to always register its callback before
+     proceeding queue an operation, but it is frequent for an IO Completion
+     Port to trigger quickly. This way we avoid a context switch for calling
+     the callback. We also simplify the read / write operations to avoid having
+     to hold a mutex for a long amount of time. */
+  int has_pending_iocp;
+  /* The results of the overlapped operation. */
+  DWORD bytes_transfered;
+  int wsa_error;
+} grpc_winsocket_callback_info;
+
+/* This is a wrapper to a Windows socket. A socket can have one outstanding
+   read, and one outstanding write. Doing an asynchronous accept means waiting
+   for a read operation. Doing an asynchronous connect means waiting for a
+   write operation. These are completely arbitrary ties between the operation
+   and the kind of event, because we can have one overlapped per pending
+   operation, whichever its nature is. So we could have more dedicated pending
+   operation callbacks for connect and listen. But given the scope of listen
+   and accept, we don't need to go to that extent and waste memory. Also, this
+   is closer to what happens in posix world. */
+typedef struct grpc_winsocket {
+  SOCKET socket;
+  bool destroy_called;
+
+  grpc_winsocket_callback_info write_info;
+  grpc_winsocket_callback_info read_info;
+
+  gpr_mu state_mu;
+  bool shutdown_called;
+
+  /* You can't add the same socket twice to the same IO Completion Port.
+     This prevents that. */
+  int added_to_iocp;
+
+  grpc_closure shutdown_closure;
+
+  /* A label for iomgr to track outstanding objects */
+  grpc_iomgr_object iomgr_object;
+} grpc_winsocket;
+
+/* Create a wrapped windows handle. This takes ownership of it, meaning that
+   it will be responsible for closing it. */
+grpc_winsocket* grpc_winsocket_create(SOCKET socket, const char* name);
+
+SOCKET grpc_winsocket_wrapped_socket(grpc_winsocket* socket);
+
+/* Initiate an asynchronous shutdown of the socket. Will call off any pending
+   operation to cancel them. */
+void grpc_winsocket_shutdown(grpc_winsocket* socket);
+
+/* Destroy a socket. Should only be called if there's no pending operation. */
+void grpc_winsocket_destroy(grpc_winsocket* socket);
+
+void grpc_socket_notify_on_write(grpc_winsocket* winsocket,
+                                 grpc_closure* closure);
+
+void grpc_socket_notify_on_read(grpc_winsocket* winsocket,
+                                grpc_closure* closure);
+
+void grpc_socket_become_ready(grpc_winsocket* winsocket,
+                              grpc_winsocket_callback_info* ci);
+
+/* Returns true if this system can create AF_INET6 sockets bound to ::1.
+   The value is probed once, and cached for the life of the process. */
+int grpc_ipv6_loopback_available(void);
+
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/sys_epoll_wrapper.h b/third_party/grpc/src/core/lib/iomgr/sys_epoll_wrapper.h
new file mode 100644
index 0000000..d21d853
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/sys_epoll_wrapper.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H
+#define GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <sys/epoll.h>
+
+#ifndef EPOLLEXCLUSIVE
+#define EPOLLEXCLUSIVE (1 << 28)
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client.cc
new file mode 100644
index 0000000..6c0ba40
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_client.cc
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/tcp_client.h"
+
+grpc_tcp_client_vtable* grpc_tcp_client_impl;
+
+void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep,
+                             grpc_pollset_set* interested_parties,
+                             const grpc_channel_args* channel_args,
+                             const grpc_resolved_address* addr,
+                             grpc_millis deadline) {
+  grpc_tcp_client_impl->connect(closure, ep, interested_parties, channel_args,
+                                addr, deadline);
+}
+
+void grpc_set_tcp_client_impl(grpc_tcp_client_vtable* impl) {
+  grpc_tcp_client_impl = impl;
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client.h b/third_party/grpc/src/core/lib/iomgr/tcp_client.h
new file mode 100644
index 0000000..d209eeb
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_client.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H
+#define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+
+typedef struct grpc_tcp_client_vtable {
+  void (*connect)(grpc_closure* on_connect, grpc_endpoint** endpoint,
+                  grpc_pollset_set* interested_parties,
+                  const grpc_channel_args* channel_args,
+                  const grpc_resolved_address* addr, grpc_millis deadline);
+} grpc_tcp_client_vtable;
+
+/* Asynchronously connect to an address (specified as (addr, len)), and call
+   cb with arg and the completed connection when done (or call cb with arg and
+   NULL on failure).
+   interested_parties points to a set of pollsets that would be interested
+   in this connection being established (in order to continue their work) */
+void grpc_tcp_client_connect(grpc_closure* on_connect, grpc_endpoint** endpoint,
+                             grpc_pollset_set* interested_parties,
+                             const grpc_channel_args* channel_args,
+                             const grpc_resolved_address* addr,
+                             grpc_millis deadline);
+
+void grpc_tcp_client_global_init();
+
+void grpc_set_tcp_client_impl(grpc_tcp_client_vtable* impl);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_cfstream.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client_cfstream.cc
new file mode 100644
index 0000000..4b21322
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_cfstream.cc
@@ -0,0 +1,216 @@
+
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_CFSTREAM_CLIENT
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include <netinet/in.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/iomgr/cfstream_handle.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/endpoint_cfstream.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/error_cfstream.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/timer.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+typedef struct CFStreamConnect {
+  gpr_mu mu;
+  gpr_refcount refcount;
+
+  CFReadStreamRef read_stream;
+  CFWriteStreamRef write_stream;
+  CFStreamHandle* stream_handle;
+
+  grpc_timer alarm;
+  grpc_closure on_alarm;
+  grpc_closure on_open;
+
+  bool read_stream_open;
+  bool write_stream_open;
+  bool failed;
+
+  grpc_closure* closure;
+  grpc_endpoint** endpoint;
+  int refs;
+  char* addr_name;
+  grpc_resource_quota* resource_quota;
+} CFStreamConnect;
+
+static void CFStreamConnectCleanup(CFStreamConnect* connect) {
+  grpc_resource_quota_unref_internal(connect->resource_quota);
+  CFSTREAM_HANDLE_UNREF(connect->stream_handle, "async connect clean up");
+  CFRelease(connect->read_stream);
+  CFRelease(connect->write_stream);
+  gpr_mu_destroy(&connect->mu);
+  gpr_free(connect->addr_name);
+  gpr_free(connect);
+}
+
+static void OnAlarm(void* arg, grpc_error* error) {
+  CFStreamConnect* connect = static_cast<CFStreamConnect*>(arg);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CLIENT_CONNECT :%p OnAlarm, error:%p", connect, error);
+  }
+  gpr_mu_lock(&connect->mu);
+  grpc_closure* closure = connect->closure;
+  connect->closure = nil;
+  const bool done = (--connect->refs == 0);
+  gpr_mu_unlock(&connect->mu);
+  // Only schedule a callback once, by either OnAlarm or OnOpen. The
+  // first one issues callback while the second one does cleanup.
+  if (done) {
+    CFStreamConnectCleanup(connect);
+  } else {
+    grpc_error* error =
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("connect() timed out");
+    GRPC_CLOSURE_SCHED(closure, error);
+  }
+}
+
+static void OnOpen(void* arg, grpc_error* error) {
+  CFStreamConnect* connect = static_cast<CFStreamConnect*>(arg);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CLIENT_CONNECT :%p OnOpen, error:%p", connect, error);
+  }
+  gpr_mu_lock(&connect->mu);
+  grpc_timer_cancel(&connect->alarm);
+  grpc_closure* closure = connect->closure;
+  connect->closure = nil;
+
+  bool done = (--connect->refs == 0);
+  grpc_endpoint** endpoint = connect->endpoint;
+
+  // Only schedule a callback once, by either OnAlarm or OnOpen. The
+  // first one issues callback while the second one does cleanup.
+  if (done) {
+    gpr_mu_unlock(&connect->mu);
+    CFStreamConnectCleanup(connect);
+  } else {
+    if (error == GRPC_ERROR_NONE) {
+      CFErrorRef stream_error = CFReadStreamCopyError(connect->read_stream);
+      if (stream_error == NULL) {
+        stream_error = CFWriteStreamCopyError(connect->write_stream);
+      }
+      if (stream_error) {
+        error = GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "connect() error");
+        CFRelease(stream_error);
+      }
+      if (error == GRPC_ERROR_NONE) {
+        *endpoint = grpc_cfstream_endpoint_create(
+            connect->read_stream, connect->write_stream, connect->addr_name,
+            connect->resource_quota, connect->stream_handle);
+      }
+    } else {
+      GRPC_ERROR_REF(error);
+    }
+    gpr_mu_unlock(&connect->mu);
+    GRPC_CLOSURE_SCHED(closure, error);
+  }
+}
+
+static void ParseResolvedAddress(const grpc_resolved_address* addr,
+                                 CFStringRef* host, int* port) {
+  char *host_port, *host_string, *port_string;
+  grpc_sockaddr_to_string(&host_port, addr, 1);
+  gpr_split_host_port(host_port, &host_string, &port_string);
+  *host = CFStringCreateWithCString(NULL, host_string, kCFStringEncodingUTF8);
+  gpr_free(host_string);
+  gpr_free(port_string);
+  gpr_free(host_port);
+  *port = grpc_sockaddr_get_port(addr);
+}
+
+static void CFStreamClientConnect(grpc_closure* closure, grpc_endpoint** ep,
+                                  grpc_pollset_set* interested_parties,
+                                  const grpc_channel_args* channel_args,
+                                  const grpc_resolved_address* resolved_addr,
+                                  grpc_millis deadline) {
+  CFStreamConnect* connect;
+
+  connect = (CFStreamConnect*)gpr_zalloc(sizeof(CFStreamConnect));
+  connect->closure = closure;
+  connect->endpoint = ep;
+  connect->addr_name = grpc_sockaddr_to_uri(resolved_addr);
+  // connect->resource_quota = resource_quota;
+  connect->refs = 2;  // One for the connect operation, one for the timer.
+  gpr_ref_init(&connect->refcount, 1);
+  gpr_mu_init(&connect->mu);
+
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %p, %s: asynchronously connecting",
+            connect, connect->addr_name);
+  }
+
+  grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL);
+  if (channel_args != NULL) {
+    for (size_t i = 0; i < channel_args->num_args; i++) {
+      if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
+        grpc_resource_quota_unref_internal(resource_quota);
+        resource_quota = grpc_resource_quota_ref_internal(
+            (grpc_resource_quota*)channel_args->args[i].value.pointer.p);
+      }
+    }
+  }
+  connect->resource_quota = resource_quota;
+
+  CFReadStreamRef read_stream;
+  CFWriteStreamRef write_stream;
+
+  CFStringRef host;
+  int port;
+  ParseResolvedAddress(resolved_addr, &host, &port);
+  CFStreamCreatePairWithSocketToHost(NULL, host, port, &read_stream,
+                                     &write_stream);
+  CFRelease(host);
+  connect->read_stream = read_stream;
+  connect->write_stream = write_stream;
+  connect->stream_handle =
+      CFStreamHandle::CreateStreamHandle(read_stream, write_stream);
+  GRPC_CLOSURE_INIT(&connect->on_open, OnOpen, static_cast<void*>(connect),
+                    grpc_schedule_on_exec_ctx);
+  connect->stream_handle->NotifyOnOpen(&connect->on_open);
+  GRPC_CLOSURE_INIT(&connect->on_alarm, OnAlarm, connect,
+                    grpc_schedule_on_exec_ctx);
+  gpr_mu_lock(&connect->mu);
+  CFReadStreamOpen(read_stream);
+  CFWriteStreamOpen(write_stream);
+  grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm);
+  gpr_mu_unlock(&connect->mu);
+}
+
+grpc_tcp_client_vtable grpc_cfstream_client_vtable = {CFStreamClientConnect};
+
+#endif /* GRPC_CFSTREAM_CLIENT */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_custom.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client_custom.cc
new file mode 100644
index 0000000..73344b1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_custom.cc
@@ -0,0 +1,162 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_custom.h"
+#include "src/core/lib/iomgr/timer.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+extern grpc_socket_vtable* grpc_custom_socket_vtable;
+
+struct grpc_custom_tcp_connect {
+  grpc_custom_socket* socket;
+  grpc_timer alarm;
+  grpc_closure on_alarm;
+  grpc_closure* closure;
+  grpc_endpoint** endpoint;
+  int refs;
+  char* addr_name;
+  grpc_resource_quota* resource_quota;
+};
+
+static void custom_tcp_connect_cleanup(grpc_custom_tcp_connect* connect) {
+  grpc_custom_socket* socket = connect->socket;
+  grpc_resource_quota_unref_internal(connect->resource_quota);
+  gpr_free(connect->addr_name);
+  gpr_free(connect);
+  socket->refs--;
+  if (socket->refs == 0) {
+    grpc_custom_socket_vtable->destroy(socket);
+    gpr_free(socket);
+  }
+}
+
+static void custom_close_callback(grpc_custom_socket* socket) {}
+
+static void on_alarm(void* acp, grpc_error* error) {
+  int done;
+  grpc_custom_socket* socket = (grpc_custom_socket*)acp;
+  grpc_custom_tcp_connect* connect = socket->connector;
+  if (grpc_tcp_trace.enabled()) {
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_alarm: error=%s",
+            connect->addr_name, str);
+  }
+  if (error == GRPC_ERROR_NONE) {
+    /* error == NONE implies that the timer ran out, and wasn't cancelled. If
+       it was cancelled, then the handler that cancelled it also should close
+       the handle, if applicable */
+    grpc_custom_socket_vtable->close(socket, custom_close_callback);
+  }
+  done = (--connect->refs == 0);
+  if (done) {
+    custom_tcp_connect_cleanup(connect);
+  }
+}
+
+static void custom_connect_callback_internal(grpc_custom_socket* socket,
+                                             grpc_error* error) {
+  grpc_custom_tcp_connect* connect = socket->connector;
+  int done;
+  grpc_closure* closure = connect->closure;
+  grpc_timer_cancel(&connect->alarm);
+  if (error == GRPC_ERROR_NONE) {
+    *connect->endpoint = custom_tcp_endpoint_create(
+        socket, connect->resource_quota, connect->addr_name);
+  }
+  done = (--connect->refs == 0);
+  if (done) {
+    grpc_core::ExecCtx::Get()->Flush();
+    custom_tcp_connect_cleanup(connect);
+  }
+  GRPC_CLOSURE_SCHED(closure, error);
+}
+
+static void custom_connect_callback(grpc_custom_socket* socket,
+                                    grpc_error* error) {
+  if (grpc_core::ExecCtx::Get() == nullptr) {
+    /* If we are being run on a thread which does not have an exec_ctx created
+     * yet, we should create one. */
+    grpc_core::ExecCtx exec_ctx;
+    custom_connect_callback_internal(socket, error);
+  } else {
+    custom_connect_callback_internal(socket, error);
+  }
+}
+
+static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep,
+                        grpc_pollset_set* interested_parties,
+                        const grpc_channel_args* channel_args,
+                        const grpc_resolved_address* resolved_addr,
+                        grpc_millis deadline) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  (void)channel_args;
+  (void)interested_parties;
+  grpc_custom_tcp_connect* connect;
+  grpc_resource_quota* resource_quota = grpc_resource_quota_create(nullptr);
+  if (channel_args != nullptr) {
+    for (size_t i = 0; i < channel_args->num_args; i++) {
+      if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
+        grpc_resource_quota_unref_internal(resource_quota);
+        resource_quota = grpc_resource_quota_ref_internal(
+            (grpc_resource_quota*)channel_args->args[i].value.pointer.p);
+      }
+    }
+  }
+  grpc_custom_socket* socket =
+      (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket));
+  socket->refs = 2;
+  grpc_custom_socket_vtable->init(socket, GRPC_AF_UNSPEC);
+  connect =
+      (grpc_custom_tcp_connect*)gpr_malloc(sizeof(grpc_custom_tcp_connect));
+  connect->closure = closure;
+  connect->endpoint = ep;
+  connect->addr_name = grpc_sockaddr_to_uri(resolved_addr);
+  connect->resource_quota = resource_quota;
+  connect->socket = socket;
+  socket->connector = connect;
+  socket->endpoint = nullptr;
+  socket->listener = nullptr;
+  connect->refs = 2;
+
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "CLIENT_CONNECT: %p %s: asynchronously connecting",
+            socket, connect->addr_name);
+  }
+
+  GRPC_CLOSURE_INIT(&connect->on_alarm, on_alarm, socket,
+                    grpc_schedule_on_exec_ctx);
+  grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm);
+  grpc_custom_socket_vtable->connect(
+      socket, (const grpc_sockaddr*)resolved_addr->addr, resolved_addr->len,
+      custom_connect_callback);
+}
+
+grpc_tcp_client_vtable custom_tcp_client_vtable = {tcp_connect};
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.cc
new file mode 100644
index 0000000..0bff74e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.cc
@@ -0,0 +1,363 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_TCP_CLIENT
+
+#include "src/core/lib/iomgr/tcp_client_posix.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <unistd.h>
+
+#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/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/iomgr_posix.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_mutator.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+typedef struct {
+  gpr_mu mu;
+  grpc_fd* fd;
+  grpc_timer alarm;
+  grpc_closure on_alarm;
+  int refs;
+  grpc_closure write_closure;
+  grpc_pollset_set* interested_parties;
+  char* addr_str;
+  grpc_endpoint** ep;
+  grpc_closure* closure;
+  grpc_channel_args* channel_args;
+} async_connect;
+
+static grpc_error* prepare_socket(const grpc_resolved_address* addr, int fd,
+                                  const grpc_channel_args* channel_args) {
+  grpc_error* err = GRPC_ERROR_NONE;
+
+  GPR_ASSERT(fd >= 0);
+
+  err = grpc_set_socket_nonblocking(fd, 1);
+  if (err != GRPC_ERROR_NONE) goto error;
+  err = grpc_set_socket_cloexec(fd, 1);
+  if (err != GRPC_ERROR_NONE) goto error;
+  if (!grpc_is_unix_socket(addr)) {
+    err = grpc_set_socket_low_latency(fd, 1);
+    if (err != GRPC_ERROR_NONE) goto error;
+    err = grpc_set_socket_reuse_addr(fd, 1);
+    if (err != GRPC_ERROR_NONE) goto error;
+    err = grpc_set_socket_tcp_user_timeout(fd, channel_args,
+                                           true /* is_client */);
+    if (err != GRPC_ERROR_NONE) goto error;
+  }
+  err = grpc_set_socket_no_sigpipe_if_possible(fd);
+  if (err != GRPC_ERROR_NONE) goto error;
+  if (channel_args) {
+    for (size_t i = 0; i < channel_args->num_args; i++) {
+      if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_SOCKET_MUTATOR)) {
+        GPR_ASSERT(channel_args->args[i].type == GRPC_ARG_POINTER);
+        grpc_socket_mutator* mutator = static_cast<grpc_socket_mutator*>(
+            channel_args->args[i].value.pointer.p);
+        err = grpc_set_socket_with_mutator(fd, mutator);
+        if (err != GRPC_ERROR_NONE) goto error;
+      }
+    }
+  }
+  goto done;
+
+error:
+  if (fd >= 0) {
+    close(fd);
+  }
+done:
+  return err;
+}
+
+static void tc_on_alarm(void* acp, grpc_error* error) {
+  int done;
+  async_connect* ac = static_cast<async_connect*>(acp);
+  if (grpc_tcp_trace.enabled()) {
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str,
+            str);
+  }
+  gpr_mu_lock(&ac->mu);
+  if (ac->fd != nullptr) {
+    grpc_fd_shutdown(
+        ac->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING("connect() timed out"));
+  }
+  done = (--ac->refs == 0);
+  gpr_mu_unlock(&ac->mu);
+  if (done) {
+    gpr_mu_destroy(&ac->mu);
+    gpr_free(ac->addr_str);
+    grpc_channel_args_destroy(ac->channel_args);
+    gpr_free(ac);
+  }
+}
+
+grpc_endpoint* grpc_tcp_client_create_from_fd(
+    grpc_fd* fd, const grpc_channel_args* channel_args, const char* addr_str) {
+  return grpc_tcp_create(fd, channel_args, addr_str);
+}
+
+static void on_writable(void* acp, grpc_error* error) {
+  async_connect* ac = static_cast<async_connect*>(acp);
+  int so_error = 0;
+  socklen_t so_error_size;
+  int err;
+  int done;
+  grpc_endpoint** ep = ac->ep;
+  grpc_closure* closure = ac->closure;
+  grpc_fd* fd;
+
+  GRPC_ERROR_REF(error);
+
+  if (grpc_tcp_trace.enabled()) {
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_writable: error=%s", ac->addr_str,
+            str);
+  }
+
+  gpr_mu_lock(&ac->mu);
+  GPR_ASSERT(ac->fd);
+  fd = ac->fd;
+  ac->fd = nullptr;
+  gpr_mu_unlock(&ac->mu);
+
+  grpc_timer_cancel(&ac->alarm);
+
+  gpr_mu_lock(&ac->mu);
+  if (error != GRPC_ERROR_NONE) {
+    error =
+        grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR,
+                           grpc_slice_from_static_string("Timeout occurred"));
+    goto finish;
+  }
+
+  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(ac->interested_parties, fd);
+      *ep = grpc_tcp_client_create_from_fd(fd, ac->channel_args, ac->addr_str);
+      fd = nullptr;
+      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(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 != nullptr) {
+    grpc_pollset_set_del_fd(ac->interested_parties, fd);
+    grpc_fd_orphan(fd, nullptr, nullptr, "tcp_client_orphan");
+    fd = nullptr;
+  }
+  done = (--ac->refs == 0);
+  // Create a copy of the data from "ac" to be accessed after the unlock, as
+  // "ac" and its contents may be deallocated by the time they are read.
+  const grpc_slice addr_str_slice = grpc_slice_from_copied_string(ac->addr_str);
+  gpr_mu_unlock(&ac->mu);
+  if (error != GRPC_ERROR_NONE) {
+    char* error_descr;
+    grpc_slice str;
+    bool ret = grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION, &str);
+    GPR_ASSERT(ret);
+    char* desc = grpc_slice_to_c_string(str);
+    gpr_asprintf(&error_descr, "Failed to connect to remote host: %s", desc);
+    error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION,
+                               grpc_slice_from_copied_string(error_descr));
+    gpr_free(error_descr);
+    gpr_free(desc);
+    error = grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS,
+                               addr_str_slice /* takes ownership */);
+  } else {
+    grpc_slice_unref_internal(addr_str_slice);
+  }
+  if (done) {
+    // This is safe even outside the lock, because "done", the sentinel, is
+    // populated *inside* the lock.
+    gpr_mu_destroy(&ac->mu);
+    gpr_free(ac->addr_str);
+    grpc_channel_args_destroy(ac->channel_args);
+    gpr_free(ac);
+  }
+  GRPC_CLOSURE_SCHED(closure, error);
+}
+
+grpc_error* grpc_tcp_client_prepare_fd(const grpc_channel_args* channel_args,
+                                       const grpc_resolved_address* addr,
+                                       grpc_resolved_address* mapped_addr,
+                                       grpc_fd** fdobj) {
+  grpc_dualstack_mode dsmode;
+  int fd;
+  grpc_error* error;
+  char* name;
+  char* addr_str;
+  *fdobj = nullptr;
+  /* Use dualstack sockets where available. Set mapped to v6 or v4 mapped to
+     v6. */
+  if (!grpc_sockaddr_to_v4mapped(addr, mapped_addr)) {
+    /* addr is v4 mapped to v6 or v6. */
+    memcpy(mapped_addr, addr, sizeof(*mapped_addr));
+  }
+  error =
+      grpc_create_dualstack_socket(mapped_addr, SOCK_STREAM, 0, &dsmode, &fd);
+  if (error != GRPC_ERROR_NONE) {
+    return error;
+  }
+  if (dsmode == GRPC_DSMODE_IPV4) {
+    /* Original addr is either v4 or v4 mapped to v6. Set mapped_addr to v4. */
+    if (!grpc_sockaddr_is_v4mapped(addr, mapped_addr)) {
+      memcpy(mapped_addr, addr, sizeof(*mapped_addr));
+    }
+  }
+  if ((error = prepare_socket(mapped_addr, fd, channel_args)) !=
+      GRPC_ERROR_NONE) {
+    return error;
+  }
+  addr_str = grpc_sockaddr_to_uri(mapped_addr);
+  gpr_asprintf(&name, "tcp-client:%s", addr_str);
+  *fdobj = grpc_fd_create(fd, name, true);
+  gpr_free(name);
+  gpr_free(addr_str);
+  return GRPC_ERROR_NONE;
+}
+
+void grpc_tcp_client_create_from_prepared_fd(
+    grpc_pollset_set* interested_parties, grpc_closure* closure, grpc_fd* fdobj,
+    const grpc_channel_args* channel_args, const grpc_resolved_address* addr,
+    grpc_millis deadline, grpc_endpoint** ep) {
+  const int fd = grpc_fd_wrapped_fd(fdobj);
+  int err;
+  async_connect* ac;
+  do {
+    err = connect(fd, reinterpret_cast<const grpc_sockaddr*>(addr->addr),
+                  addr->len);
+  } while (err < 0 && errno == EINTR);
+  if (err >= 0) {
+    char* addr_str = grpc_sockaddr_to_uri(addr);
+    *ep = grpc_tcp_client_create_from_fd(fdobj, channel_args, addr_str);
+    gpr_free(addr_str);
+    GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
+    return;
+  }
+  if (errno != EWOULDBLOCK && errno != EINPROGRESS) {
+    grpc_fd_orphan(fdobj, nullptr, nullptr, "tcp_client_connect_error");
+    GRPC_CLOSURE_SCHED(closure, GRPC_OS_ERROR(errno, "connect"));
+    return;
+  }
+
+  grpc_pollset_set_add_fd(interested_parties, fdobj);
+
+  ac = static_cast<async_connect*>(gpr_malloc(sizeof(async_connect)));
+  ac->closure = closure;
+  ac->ep = ep;
+  ac->fd = fdobj;
+  ac->interested_parties = interested_parties;
+  ac->addr_str = grpc_sockaddr_to_uri(addr);
+  gpr_mu_init(&ac->mu);
+  ac->refs = 2;
+  GRPC_CLOSURE_INIT(&ac->write_closure, on_writable, ac,
+                    grpc_schedule_on_exec_ctx);
+  ac->channel_args = grpc_channel_args_copy(channel_args);
+
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: asynchronously connecting fd %p",
+            ac->addr_str, fdobj);
+  }
+
+  gpr_mu_lock(&ac->mu);
+  GRPC_CLOSURE_INIT(&ac->on_alarm, tc_on_alarm, ac, grpc_schedule_on_exec_ctx);
+  grpc_timer_init(&ac->alarm, deadline, &ac->on_alarm);
+  grpc_fd_notify_on_write(ac->fd, &ac->write_closure);
+  gpr_mu_unlock(&ac->mu);
+}
+
+static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep,
+                        grpc_pollset_set* interested_parties,
+                        const grpc_channel_args* channel_args,
+                        const grpc_resolved_address* addr,
+                        grpc_millis deadline) {
+  grpc_resolved_address mapped_addr;
+  grpc_fd* fdobj = nullptr;
+  grpc_error* error;
+  *ep = nullptr;
+  if ((error = grpc_tcp_client_prepare_fd(channel_args, addr, &mapped_addr,
+                                          &fdobj)) != GRPC_ERROR_NONE) {
+    GRPC_CLOSURE_SCHED(closure, error);
+    return;
+  }
+  grpc_tcp_client_create_from_prepared_fd(interested_parties, closure, fdobj,
+                                          channel_args, &mapped_addr, deadline,
+                                          ep);
+}
+
+grpc_tcp_client_vtable grpc_posix_tcp_client_vtable = {tcp_connect};
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.h b/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.h
new file mode 100644
index 0000000..d0168ef
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+
+/* Create an endpoint from a connected grpc_fd.
+
+   fd: a connected FD. Ownership is taken.
+   channel_args: may contain custom settings for the endpoint
+   addr_str: destination address in printable format
+   Returns: a new endpoint
+*/
+grpc_endpoint* grpc_tcp_client_create_from_fd(
+    grpc_fd* fd, const grpc_channel_args* channel_args, const char* addr_str);
+
+/* Return a configured, unbound, unconnected TCP client grpc_fd.
+
+   channel_args: may contain custom settings for the fd
+   addr: the destination address
+   mapped_addr: out parameter. addr mapped to an address appropriate to the
+     type of socket FD created. For example, if addr is IPv4 and dual stack
+     sockets are available, mapped_addr will be an IPv4-mapped IPv6 address
+   fdobj: out parameter. The new FD
+   Returns: error, if any. Out parameters are not set on error
+*/
+grpc_error* grpc_tcp_client_prepare_fd(const grpc_channel_args* channel_args,
+                                       const grpc_resolved_address* addr,
+                                       grpc_resolved_address* mapped_addr,
+                                       grpc_fd** fdobj);
+
+/* Connect a configured TCP client grpc_fd.
+
+   interested_parties: a set of pollsets that would be interested in this
+     connection being established (in order to continue their work
+   closure: called when complete. On success, *ep will be set.
+   fdobj: an FD returned from grpc_tcp_client_prepare_fd(). Ownership is taken
+   channel_args: may contain custom settings for the endpoint
+   deadline: connection deadline
+   ep: out parameter. Set before closure is called if successful
+*/
+void grpc_tcp_client_create_from_prepared_fd(
+    grpc_pollset_set* interested_parties, grpc_closure* closure, grpc_fd* fdobj,
+    const grpc_channel_args* channel_args, const grpc_resolved_address* addr,
+    grpc_millis deadline, grpc_endpoint** ep);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_windows.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client_windows.cc
new file mode 100644
index 0000000..e5b5502
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_windows.cc
@@ -0,0 +1,231 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <inttypes.h>
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/sockaddr_windows.h"
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_windows.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_windows.h"
+#include "src/core/lib/iomgr/timer.h"
+
+typedef struct {
+  grpc_closure* on_done;
+  gpr_mu mu;
+  grpc_winsocket* socket;
+  grpc_timer alarm;
+  grpc_closure on_alarm;
+  char* addr_name;
+  int refs;
+  grpc_closure on_connect;
+  grpc_endpoint** endpoint;
+  grpc_channel_args* channel_args;
+} async_connect;
+
+static void async_connect_unlock_and_cleanup(async_connect* ac,
+                                             grpc_winsocket* socket) {
+  int done = (--ac->refs == 0);
+  gpr_mu_unlock(&ac->mu);
+  if (done) {
+    grpc_channel_args_destroy(ac->channel_args);
+    gpr_mu_destroy(&ac->mu);
+    gpr_free(ac->addr_name);
+    gpr_free(ac);
+  }
+  if (socket != NULL) grpc_winsocket_destroy(socket);
+}
+
+static void on_alarm(void* acp, grpc_error* error) {
+  async_connect* ac = (async_connect*)acp;
+  gpr_mu_lock(&ac->mu);
+  grpc_winsocket* socket = ac->socket;
+  ac->socket = NULL;
+  if (socket != NULL) {
+    grpc_winsocket_shutdown(socket);
+  }
+  async_connect_unlock_and_cleanup(ac, socket);
+}
+
+static void on_connect(void* acp, grpc_error* error) {
+  async_connect* ac = (async_connect*)acp;
+  grpc_endpoint** ep = ac->endpoint;
+  GPR_ASSERT(*ep == NULL);
+  grpc_closure* on_done = ac->on_done;
+
+  GRPC_ERROR_REF(error);
+
+  gpr_mu_lock(&ac->mu);
+  grpc_winsocket* socket = ac->socket;
+  ac->socket = NULL;
+  gpr_mu_unlock(&ac->mu);
+
+  grpc_timer_cancel(&ac->alarm);
+
+  gpr_mu_lock(&ac->mu);
+
+  if (error == GRPC_ERROR_NONE) {
+    if (socket != NULL) {
+      DWORD transfered_bytes = 0;
+      DWORD flags;
+      BOOL wsa_success =
+          WSAGetOverlappedResult(socket->socket, &socket->write_info.overlapped,
+                                 &transfered_bytes, FALSE, &flags);
+      GPR_ASSERT(transfered_bytes == 0);
+      if (!wsa_success) {
+        error = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx");
+        closesocket(socket->socket);
+      } else {
+        *ep = grpc_tcp_create(socket, ac->channel_args, ac->addr_name);
+        socket = NULL;
+      }
+    } else {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket is null");
+    }
+  }
+
+  async_connect_unlock_and_cleanup(ac, socket);
+  /* If the connection was aborted, the callback was already called when
+     the deadline was met. */
+  GRPC_CLOSURE_SCHED(on_done, error);
+}
+
+/* Tries to issue one async connection, then schedules both an IOCP
+   notification request for the connection, and one timeout alert. */
+static void tcp_connect(grpc_closure* on_done, grpc_endpoint** endpoint,
+                        grpc_pollset_set* interested_parties,
+                        const grpc_channel_args* channel_args,
+                        const grpc_resolved_address* addr,
+                        grpc_millis deadline) {
+  SOCKET sock = INVALID_SOCKET;
+  BOOL success;
+  int status;
+  grpc_resolved_address addr6_v4mapped;
+  grpc_resolved_address local_address;
+  async_connect* ac;
+  grpc_winsocket* socket = NULL;
+  LPFN_CONNECTEX ConnectEx;
+  GUID guid = WSAID_CONNECTEX;
+  DWORD ioctl_num_bytes;
+  grpc_winsocket_callback_info* info;
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  *endpoint = NULL;
+
+  /* Use dualstack sockets where available. */
+  if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+    addr = &addr6_v4mapped;
+  }
+
+  sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+                   WSA_FLAG_OVERLAPPED);
+  if (sock == INVALID_SOCKET) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
+    goto failure;
+  }
+
+  error = grpc_tcp_prepare_socket(sock);
+  if (error != GRPC_ERROR_NONE) {
+    goto failure;
+  }
+
+  /* Grab the function pointer for ConnectEx for that specific socket.
+     It may change depending on the interface. */
+  status =
+      WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
+               &ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL);
+
+  if (status != 0) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(),
+                           "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)");
+    goto failure;
+  }
+
+  grpc_sockaddr_make_wildcard6(0, &local_address);
+
+  status =
+      bind(sock, (grpc_sockaddr*)&local_address.addr, (int)local_address.len);
+  if (status != 0) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "bind");
+    goto failure;
+  }
+
+  socket = grpc_winsocket_create(sock, "client");
+  info = &socket->write_info;
+  success = ConnectEx(sock, (grpc_sockaddr*)&addr->addr, (int)addr->len, NULL,
+                      0, NULL, &info->overlapped);
+
+  /* It wouldn't be unusual to get a success immediately. But we'll still get
+     an IOCP notification, so let's ignore it. */
+  if (!success) {
+    int last_error = WSAGetLastError();
+    if (last_error != ERROR_IO_PENDING) {
+      error = GRPC_WSA_ERROR(last_error, "ConnectEx");
+      goto failure;
+    }
+  }
+
+  ac = (async_connect*)gpr_malloc(sizeof(async_connect));
+  ac->on_done = on_done;
+  ac->socket = socket;
+  gpr_mu_init(&ac->mu);
+  ac->refs = 2;
+  ac->addr_name = grpc_sockaddr_to_uri(addr);
+  ac->endpoint = endpoint;
+  ac->channel_args = grpc_channel_args_copy(channel_args);
+  GRPC_CLOSURE_INIT(&ac->on_connect, on_connect, ac, grpc_schedule_on_exec_ctx);
+
+  GRPC_CLOSURE_INIT(&ac->on_alarm, on_alarm, ac, grpc_schedule_on_exec_ctx);
+  grpc_timer_init(&ac->alarm, deadline, &ac->on_alarm);
+  grpc_socket_notify_on_write(socket, &ac->on_connect);
+  return;
+
+failure:
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  char* target_uri = grpc_sockaddr_to_uri(addr);
+  grpc_error* final_error = grpc_error_set_str(
+      GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Failed to connect",
+                                                       &error, 1),
+      GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(target_uri));
+  GRPC_ERROR_UNREF(error);
+  if (socket != NULL) {
+    grpc_winsocket_destroy(socket);
+  } else if (sock != INVALID_SOCKET) {
+    closesocket(sock);
+  }
+  GRPC_CLOSURE_SCHED(on_done, final_error);
+}
+
+grpc_tcp_client_vtable grpc_windows_tcp_client_vtable = {tcp_connect};
+
+#endif /* GRPC_WINSOCK_SOCKET */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_custom.cc b/third_party/grpc/src/core/lib/iomgr/tcp_custom.cc
new file mode 100644
index 0000000..f7a5f36
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_custom.cc
@@ -0,0 +1,368 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/network_status_tracker.h"
+#include "src/core/lib/iomgr/resource_quota.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_custom.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+grpc_socket_vtable* grpc_custom_socket_vtable = nullptr;
+extern grpc_tcp_server_vtable custom_tcp_server_vtable;
+extern grpc_tcp_client_vtable custom_tcp_client_vtable;
+
+void grpc_custom_endpoint_init(grpc_socket_vtable* impl) {
+  grpc_custom_socket_vtable = impl;
+  grpc_set_tcp_client_impl(&custom_tcp_client_vtable);
+  grpc_set_tcp_server_impl(&custom_tcp_server_vtable);
+}
+
+typedef struct {
+  grpc_endpoint base;
+  gpr_refcount refcount;
+  grpc_custom_socket* socket;
+
+  grpc_closure* read_cb;
+  grpc_closure* write_cb;
+
+  grpc_slice_buffer* read_slices;
+  grpc_slice_buffer* write_slices;
+
+  grpc_resource_user* resource_user;
+  grpc_resource_user_slice_allocator slice_allocator;
+
+  bool shutting_down;
+
+  char* peer_string;
+} custom_tcp_endpoint;
+
+static void tcp_free(grpc_custom_socket* s) {
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)s->endpoint;
+  grpc_resource_user_unref(tcp->resource_user);
+  gpr_free(tcp->peer_string);
+  gpr_free(tcp);
+  s->refs--;
+  if (s->refs == 0) {
+    grpc_custom_socket_vtable->destroy(s);
+    gpr_free(s);
+  }
+}
+
+#ifndef NDEBUG
+#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__)
+#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
+static void tcp_unref(custom_tcp_endpoint* tcp, const char* reason,
+                      const char* file, int line) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_ERROR,
+            "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason,
+            val, val - 1);
+  }
+  if (gpr_unref(&tcp->refcount)) {
+    tcp_free(tcp->socket);
+  }
+}
+
+static void tcp_ref(custom_tcp_endpoint* tcp, const char* reason,
+                    const char* file, int line) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_ERROR,
+            "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason,
+            val, val + 1);
+  }
+  gpr_ref(&tcp->refcount);
+}
+#else
+#define TCP_UNREF(tcp, reason) tcp_unref((tcp))
+#define TCP_REF(tcp, reason) tcp_ref((tcp))
+static void tcp_unref(custom_tcp_endpoint* tcp) {
+  if (gpr_unref(&tcp->refcount)) {
+    tcp_free(tcp->socket);
+  }
+}
+
+static void tcp_ref(custom_tcp_endpoint* tcp) { gpr_ref(&tcp->refcount); }
+#endif
+
+static void call_read_cb(custom_tcp_endpoint* tcp, grpc_error* error) {
+  grpc_closure* cb = tcp->read_cb;
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p call_cb %p %p:%p", tcp->socket, cb, cb->cb,
+            cb->cb_arg);
+    size_t i;
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_INFO, "read: error=%s", str);
+
+    for (i = 0; i < tcp->read_slices->count; i++) {
+      char* dump = grpc_dump_slice(tcp->read_slices->slices[i],
+                                   GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump);
+      gpr_free(dump);
+    }
+  }
+  TCP_UNREF(tcp, "read");
+  tcp->read_slices = nullptr;
+  tcp->read_cb = nullptr;
+  GRPC_CLOSURE_SCHED(cb, error);
+}
+
+static void custom_read_callback(grpc_custom_socket* socket, size_t nread,
+                                 grpc_error* error) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_slice_buffer garbage;
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint;
+  if (error == GRPC_ERROR_NONE && nread == 0) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF");
+  }
+  if (error == GRPC_ERROR_NONE) {
+    // Successful read
+    if ((size_t)nread < tcp->read_slices->length) {
+      /* TODO(murgatroid99): Instead of discarding the unused part of the read
+       * buffer, reuse it as the next read buffer. */
+      grpc_slice_buffer_init(&garbage);
+      grpc_slice_buffer_trim_end(
+          tcp->read_slices, tcp->read_slices->length - (size_t)nread, &garbage);
+      grpc_slice_buffer_reset_and_unref_internal(&garbage);
+    }
+  } else {
+    grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices);
+  }
+  call_read_cb(tcp, error);
+}
+
+static void tcp_read_allocation_done(void* tcpp, grpc_error* error) {
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)tcpp;
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p read_allocation_done: %s", tcp->socket,
+            grpc_error_string(error));
+  }
+  if (error == GRPC_ERROR_NONE) {
+    /* Before calling read, we allocate a buffer with exactly one slice
+     * to tcp->read_slices and wait for the callback indicating that the
+     * allocation was successful. So slices[0] should always exist here */
+    char* buffer = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[0]);
+    size_t len = GRPC_SLICE_LENGTH(tcp->read_slices->slices[0]);
+    grpc_custom_socket_vtable->read(tcp->socket, buffer, len,
+                                    custom_read_callback);
+  } else {
+    grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices);
+    call_read_cb(tcp, GRPC_ERROR_REF(error));
+  }
+  if (grpc_tcp_trace.enabled()) {
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_INFO, "Initiating read on %p: error=%s", tcp->socket, str);
+  }
+}
+
+static void endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices,
+                          grpc_closure* cb) {
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  GPR_ASSERT(tcp->read_cb == nullptr);
+  tcp->read_cb = cb;
+  tcp->read_slices = read_slices;
+  grpc_slice_buffer_reset_and_unref_internal(read_slices);
+  TCP_REF(tcp, "read");
+  grpc_resource_user_alloc_slices(&tcp->slice_allocator,
+                                  GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1,
+                                  tcp->read_slices);
+}
+
+static void custom_write_callback(grpc_custom_socket* socket,
+                                  grpc_error* error) {
+  grpc_core::ExecCtx exec_ctx;
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint;
+  grpc_closure* cb = tcp->write_cb;
+  tcp->write_cb = nullptr;
+  if (grpc_tcp_trace.enabled()) {
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_INFO, "write complete on %p: error=%s", tcp->socket, str);
+  }
+  TCP_UNREF(tcp, "write");
+  GRPC_CLOSURE_SCHED(cb, error);
+}
+
+static void endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* write_slices,
+                           grpc_closure* cb, void* arg) {
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+
+  if (grpc_tcp_trace.enabled()) {
+    size_t j;
+
+    for (j = 0; j < write_slices->count; j++) {
+      char* data = grpc_dump_slice(write_slices->slices[j],
+                                   GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp->socket, tcp->peer_string,
+              data);
+      gpr_free(data);
+    }
+  }
+
+  if (tcp->shutting_down) {
+    GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                               "TCP socket is shutting down"));
+    return;
+  }
+
+  GPR_ASSERT(tcp->write_cb == nullptr);
+  tcp->write_slices = write_slices;
+  GPR_ASSERT(tcp->write_slices->count <= UINT_MAX);
+  if (tcp->write_slices->count == 0) {
+    // No slices means we don't have to do anything,
+    // and libuv doesn't like empty writes
+    GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_NONE);
+    return;
+  }
+  tcp->write_cb = cb;
+  TCP_REF(tcp, "write");
+  grpc_custom_socket_vtable->write(tcp->socket, tcp->write_slices,
+                                   custom_write_callback);
+}
+
+static void endpoint_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) {
+  // No-op. We're ignoring pollsets currently
+  (void)ep;
+  (void)pollset;
+}
+
+static void endpoint_add_to_pollset_set(grpc_endpoint* ep,
+                                        grpc_pollset_set* pollset) {
+  // No-op. We're ignoring pollsets currently
+  (void)ep;
+  (void)pollset;
+}
+
+static void endpoint_delete_from_pollset_set(grpc_endpoint* ep,
+                                             grpc_pollset_set* pollset) {
+  // No-op. We're ignoring pollsets currently
+  (void)ep;
+  (void)pollset;
+}
+
+static void endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) {
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
+  if (!tcp->shutting_down) {
+    if (grpc_tcp_trace.enabled()) {
+      const char* str = grpc_error_string(why);
+      gpr_log(GPR_INFO, "TCP %p shutdown why=%s", tcp->socket, str);
+    }
+    tcp->shutting_down = true;
+    // GRPC_CLOSURE_SCHED(tcp->read_cb, GRPC_ERROR_REF(why));
+    // GRPC_CLOSURE_SCHED(tcp->write_cb, GRPC_ERROR_REF(why));
+    // tcp->read_cb = nullptr;
+    // tcp->write_cb = nullptr;
+    grpc_resource_user_shutdown(tcp->resource_user);
+    grpc_custom_socket_vtable->shutdown(tcp->socket);
+  }
+  GRPC_ERROR_UNREF(why);
+}
+
+static void custom_close_callback(grpc_custom_socket* socket) {
+  socket->refs--;
+  if (socket->refs == 0) {
+    grpc_custom_socket_vtable->destroy(socket);
+    gpr_free(socket);
+  } else if (socket->endpoint) {
+    grpc_core::ExecCtx exec_ctx;
+    custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint;
+    TCP_UNREF(tcp, "destroy");
+  }
+}
+
+static void endpoint_destroy(grpc_endpoint* ep) {
+  grpc_network_status_unregister_endpoint(ep);
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
+  grpc_custom_socket_vtable->close(tcp->socket, custom_close_callback);
+}
+
+static char* endpoint_get_peer(grpc_endpoint* ep) {
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
+  return gpr_strdup(tcp->peer_string);
+}
+
+static grpc_resource_user* endpoint_get_resource_user(grpc_endpoint* ep) {
+  custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep;
+  return tcp->resource_user;
+}
+
+static int endpoint_get_fd(grpc_endpoint* ep) { return -1; }
+
+static bool endpoint_can_track_err(grpc_endpoint* ep) { return false; }
+
+static grpc_endpoint_vtable vtable = {endpoint_read,
+                                      endpoint_write,
+                                      endpoint_add_to_pollset,
+                                      endpoint_add_to_pollset_set,
+                                      endpoint_delete_from_pollset_set,
+                                      endpoint_shutdown,
+                                      endpoint_destroy,
+                                      endpoint_get_resource_user,
+                                      endpoint_get_peer,
+                                      endpoint_get_fd,
+                                      endpoint_can_track_err};
+
+grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket,
+                                          grpc_resource_quota* resource_quota,
+                                          char* peer_string) {
+  custom_tcp_endpoint* tcp =
+      (custom_tcp_endpoint*)gpr_malloc(sizeof(custom_tcp_endpoint));
+  grpc_core::ExecCtx exec_ctx;
+
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "Creating TCP endpoint %p", socket);
+  }
+  memset(tcp, 0, sizeof(custom_tcp_endpoint));
+  socket->refs++;
+  socket->endpoint = (grpc_endpoint*)tcp;
+  tcp->socket = socket;
+  tcp->base.vtable = &vtable;
+  gpr_ref_init(&tcp->refcount, 1);
+  tcp->peer_string = gpr_strdup(peer_string);
+  tcp->shutting_down = false;
+  tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
+  grpc_resource_user_slice_allocator_init(
+      &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp);
+  /* Tell network status tracking code about the new endpoint */
+  grpc_network_status_register_endpoint(&tcp->base);
+
+  return &tcp->base;
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_custom.h b/third_party/grpc/src/core/lib/iomgr/tcp_custom.h
new file mode 100644
index 0000000..784ef84
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_custom.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H
+#define GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+typedef struct grpc_tcp_listener grpc_tcp_listener;
+typedef struct grpc_custom_tcp_connect grpc_custom_tcp_connect;
+
+typedef struct grpc_custom_socket {
+  // Implementation defined
+  void* impl;
+  grpc_endpoint* endpoint;
+  grpc_tcp_listener* listener;
+  grpc_custom_tcp_connect* connector;
+  int refs;
+} grpc_custom_socket;
+
+typedef void (*grpc_custom_connect_callback)(grpc_custom_socket* socket,
+                                             grpc_error* error);
+typedef void (*grpc_custom_write_callback)(grpc_custom_socket* socket,
+                                           grpc_error* error);
+typedef void (*grpc_custom_read_callback)(grpc_custom_socket* socket,
+                                          size_t nread, grpc_error* error);
+typedef void (*grpc_custom_accept_callback)(grpc_custom_socket* socket,
+                                            grpc_custom_socket* client,
+                                            grpc_error* error);
+typedef void (*grpc_custom_close_callback)(grpc_custom_socket* socket);
+
+typedef struct grpc_socket_vtable {
+  grpc_error* (*init)(grpc_custom_socket* socket, int domain);
+  void (*connect)(grpc_custom_socket* socket, const grpc_sockaddr* addr,
+                  size_t len, grpc_custom_connect_callback cb);
+  void (*destroy)(grpc_custom_socket* socket);
+  void (*shutdown)(grpc_custom_socket* socket);
+  void (*close)(grpc_custom_socket* socket, grpc_custom_close_callback cb);
+  void (*write)(grpc_custom_socket* socket, grpc_slice_buffer* slices,
+                grpc_custom_write_callback cb);
+  void (*read)(grpc_custom_socket* socket, char* buffer, size_t length,
+               grpc_custom_read_callback cb);
+  grpc_error* (*getpeername)(grpc_custom_socket* socket,
+                             const grpc_sockaddr* addr, int* len);
+  grpc_error* (*getsockname)(grpc_custom_socket* socket,
+                             const grpc_sockaddr* addr, int* len);
+  grpc_error* (*bind)(grpc_custom_socket* socket, const grpc_sockaddr* addr,
+                      size_t len, int flags);
+  grpc_error* (*listen)(grpc_custom_socket* socket);
+  void (*accept)(grpc_custom_socket* socket, grpc_custom_socket* client,
+                 grpc_custom_accept_callback cb);
+} grpc_socket_vtable;
+
+/* Internal APIs */
+void grpc_custom_endpoint_init(grpc_socket_vtable* impl);
+
+void grpc_custom_close_server_callback(grpc_tcp_listener* listener);
+
+grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket,
+                                          grpc_resource_quota* resource_quota,
+                                          char* peer_string);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_posix.cc b/third_party/grpc/src/core/lib/iomgr/tcp_posix.cc
new file mode 100644
index 0000000..c268c18
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_posix.cc
@@ -0,0 +1,1182 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_TCP
+
+#include "src/core/lib/iomgr/network_status_tracker.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/buffer_list.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+#ifdef GRPC_HAVE_MSG_NOSIGNAL
+#define SENDMSG_FLAGS MSG_NOSIGNAL
+#else
+#define SENDMSG_FLAGS 0
+#endif
+
+#ifdef GRPC_MSG_IOVLEN_TYPE
+typedef GRPC_MSG_IOVLEN_TYPE msg_iovlen_type;
+#else
+typedef size_t msg_iovlen_type;
+#endif
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+namespace {
+struct grpc_tcp {
+  grpc_endpoint base;
+  grpc_fd* em_fd;
+  int fd;
+  /* Used by the endpoint read function to distinguish the very first read call
+   * from the rest */
+  bool is_first_read;
+  double target_length;
+  double bytes_read_this_round;
+  gpr_refcount refcount;
+  gpr_atm shutdown_count;
+
+  int min_read_chunk_size;
+  int max_read_chunk_size;
+
+  /* garbage after the last read */
+  grpc_slice_buffer last_read_buffer;
+
+  grpc_slice_buffer* incoming_buffer;
+  grpc_slice_buffer* outgoing_buffer;
+  /** byte within outgoing_buffer->slices[0] to write next */
+  size_t outgoing_byte_idx;
+
+  grpc_closure* read_cb;
+  grpc_closure* write_cb;
+  grpc_closure* release_fd_cb;
+  int* release_fd;
+
+  grpc_closure read_done_closure;
+  grpc_closure write_done_closure;
+  grpc_closure error_closure;
+
+  char* peer_string;
+
+  grpc_resource_user* resource_user;
+  grpc_resource_user_slice_allocator slice_allocator;
+
+  grpc_core::TracedBuffer* tb_head; /* List of traced buffers */
+  gpr_mu tb_mu; /* Lock for access to list of traced buffers */
+
+  /* grpc_endpoint_write takes an argument which if non-null means that the
+   * transport layer wants the TCP layer to collect timestamps for this write.
+   * This arg is forwarded to the timestamps callback function when the ACK
+   * timestamp is received from the kernel. This arg is a (void *) which allows
+   * users of this API to pass in a pointer to any kind of structure. This
+   * structure could actually be a tag or any book-keeping object that the user
+   * can use to distinguish between different traced writes. The only
+   * requirement from the TCP endpoint layer is that this arg should be non-null
+   * if the user wants timestamps for the write. */
+  void* outgoing_buffer_arg;
+  /* A counter which starts at 0. It is initialized the first time the socket
+   * options for collecting timestamps are set, and is incremented with each
+   * byte sent. */
+  int bytes_counter;
+  bool socket_ts_enabled; /* True if timestamping options are set on the socket
+                           */
+  bool ts_capable;        /* Cache whether we can set timestamping options */
+  gpr_atm
+      stop_error_notification; /* Set to 1 if we do not want to be notified on
+                                  errors anymore */
+};
+
+struct backup_poller {
+  gpr_mu* pollset_mu;
+  grpc_closure run_poller;
+};
+
+}  // namespace
+
+#define BACKUP_POLLER_POLLSET(b) ((grpc_pollset*)((b) + 1))
+
+static gpr_atm g_uncovered_notifications_pending;
+static gpr_atm g_backup_poller; /* backup_poller* */
+
+static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error* error);
+static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error* error);
+static void tcp_drop_uncovered_then_handle_write(void* arg /* grpc_tcp */,
+                                                 grpc_error* error);
+
+static void done_poller(void* bp, grpc_error* error_ignored) {
+  backup_poller* p = static_cast<backup_poller*>(bp);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "BACKUP_POLLER:%p destroy", p);
+  }
+  grpc_pollset_destroy(BACKUP_POLLER_POLLSET(p));
+  gpr_free(p);
+}
+
+static void run_poller(void* bp, grpc_error* error_ignored) {
+  backup_poller* p = static_cast<backup_poller*>(bp);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "BACKUP_POLLER:%p run", p);
+  }
+  gpr_mu_lock(p->pollset_mu);
+  grpc_millis deadline = grpc_core::ExecCtx::Get()->Now() + 10 * GPR_MS_PER_SEC;
+  GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS();
+  GRPC_LOG_IF_ERROR(
+      "backup_poller:pollset_work",
+      grpc_pollset_work(BACKUP_POLLER_POLLSET(p), nullptr, deadline));
+  gpr_mu_unlock(p->pollset_mu);
+  /* last "uncovered" notification is the ref that keeps us polling, if we get
+   * there try a cas to release it */
+  if (gpr_atm_no_barrier_load(&g_uncovered_notifications_pending) == 1 &&
+      gpr_atm_full_cas(&g_uncovered_notifications_pending, 1, 0)) {
+    gpr_mu_lock(p->pollset_mu);
+    bool cas_ok = gpr_atm_full_cas(&g_backup_poller, (gpr_atm)p, 0);
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "BACKUP_POLLER:%p done cas_ok=%d", p, cas_ok);
+    }
+    gpr_mu_unlock(p->pollset_mu);
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "BACKUP_POLLER:%p shutdown", p);
+    }
+    grpc_pollset_shutdown(BACKUP_POLLER_POLLSET(p),
+                          GRPC_CLOSURE_INIT(&p->run_poller, done_poller, p,
+                                            grpc_schedule_on_exec_ctx));
+  } else {
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "BACKUP_POLLER:%p reschedule", p);
+    }
+    GRPC_CLOSURE_SCHED(&p->run_poller, GRPC_ERROR_NONE);
+  }
+}
+
+static void drop_uncovered(grpc_tcp* tcp) {
+  backup_poller* p = (backup_poller*)gpr_atm_acq_load(&g_backup_poller);
+  gpr_atm old_count =
+      gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, -1);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "BACKUP_POLLER:%p uncover cnt %d->%d", p,
+            static_cast<int>(old_count), static_cast<int>(old_count) - 1);
+  }
+  GPR_ASSERT(old_count != 1);
+}
+
+// gRPC API considers a Write operation to be done the moment it clears ‘flow
+// control’ i.e., not necessarily sent on the wire. This means that the
+// application MIGHT not call `grpc_completion_queue_next/pluck` in a timely
+// manner when its `Write()` API is acked.
+//
+// We need to ensure that the fd is 'covered' (i.e being monitored by some
+// polling thread and progress is made) and hence add it to a backup poller here
+static void cover_self(grpc_tcp* tcp) {
+  backup_poller* p;
+  gpr_atm old_count =
+      gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, 2);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "BACKUP_POLLER: cover cnt %d->%d",
+            static_cast<int>(old_count), 2 + static_cast<int>(old_count));
+  }
+  if (old_count == 0) {
+    GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED();
+    p = static_cast<backup_poller*>(
+        gpr_zalloc(sizeof(*p) + grpc_pollset_size()));
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "BACKUP_POLLER:%p create", p);
+    }
+    grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu);
+    gpr_atm_rel_store(&g_backup_poller, (gpr_atm)p);
+    GRPC_CLOSURE_SCHED(
+        GRPC_CLOSURE_INIT(&p->run_poller, run_poller, p,
+                          grpc_executor_scheduler(GRPC_EXECUTOR_LONG)),
+        GRPC_ERROR_NONE);
+  } else {
+    while ((p = (backup_poller*)gpr_atm_acq_load(&g_backup_poller)) ==
+           nullptr) {
+      // spin waiting for backup poller
+    }
+  }
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "BACKUP_POLLER:%p add %p", p, tcp);
+  }
+  grpc_pollset_add_fd(BACKUP_POLLER_POLLSET(p), tcp->em_fd);
+  if (old_count != 0) {
+    drop_uncovered(tcp);
+  }
+}
+
+static void notify_on_read(grpc_tcp* tcp) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p notify_on_read", tcp);
+  }
+  GRPC_CLOSURE_INIT(&tcp->read_done_closure, tcp_handle_read, tcp,
+                    grpc_schedule_on_exec_ctx);
+  grpc_fd_notify_on_read(tcp->em_fd, &tcp->read_done_closure);
+}
+
+static void notify_on_write(grpc_tcp* tcp) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p notify_on_write", tcp);
+  }
+  if (grpc_event_engine_run_in_background()) {
+    // If there is a polling engine always running in the background, there is
+    // no need to run the backup poller.
+    GRPC_CLOSURE_INIT(&tcp->write_done_closure, tcp_handle_write, tcp,
+                      grpc_schedule_on_exec_ctx);
+  } else {
+    cover_self(tcp);
+    GRPC_CLOSURE_INIT(&tcp->write_done_closure,
+                      tcp_drop_uncovered_then_handle_write, tcp,
+                      grpc_schedule_on_exec_ctx);
+  }
+  grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_done_closure);
+}
+
+static void tcp_drop_uncovered_then_handle_write(void* arg, grpc_error* error) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p got_write: %s", arg, grpc_error_string(error));
+  }
+  drop_uncovered(static_cast<grpc_tcp*>(arg));
+  tcp_handle_write(arg, error);
+}
+
+static void add_to_estimate(grpc_tcp* tcp, size_t bytes) {
+  tcp->bytes_read_this_round += static_cast<double>(bytes);
+}
+
+static void finish_estimate(grpc_tcp* tcp) {
+  /* If we read >80% of the target buffer in one read loop, increase the size
+     of the target buffer to either the amount read, or twice its previous
+     value */
+  if (tcp->bytes_read_this_round > tcp->target_length * 0.8) {
+    tcp->target_length =
+        GPR_MAX(2 * tcp->target_length, tcp->bytes_read_this_round);
+  } else {
+    tcp->target_length =
+        0.99 * tcp->target_length + 0.01 * tcp->bytes_read_this_round;
+  }
+  tcp->bytes_read_this_round = 0;
+}
+
+static size_t get_target_read_size(grpc_tcp* tcp) {
+  grpc_resource_quota* rq = grpc_resource_user_quota(tcp->resource_user);
+  double pressure = grpc_resource_quota_get_memory_pressure(rq);
+  double target =
+      tcp->target_length * (pressure > 0.8 ? (1.0 - pressure) / 0.2 : 1.0);
+  size_t sz = ((static_cast<size_t> GPR_CLAMP(target, tcp->min_read_chunk_size,
+                                              tcp->max_read_chunk_size)) +
+               255) &
+              ~static_cast<size_t>(255);
+  /* don't use more than 1/16th of the overall resource quota for a single read
+   * alloc */
+  size_t rqmax = grpc_resource_quota_peek_size(rq);
+  if (sz > rqmax / 16 && rqmax > 1024) {
+    sz = rqmax / 16;
+  }
+  return sz;
+}
+
+static grpc_error* tcp_annotate_error(grpc_error* src_error, grpc_tcp* tcp) {
+  return grpc_error_set_str(
+      grpc_error_set_int(
+          grpc_error_set_int(src_error, GRPC_ERROR_INT_FD, tcp->fd),
+          /* All tcp errors are marked with UNAVAILABLE so that application may
+           * choose to retry. */
+          GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE),
+      GRPC_ERROR_STR_TARGET_ADDRESS,
+      grpc_slice_from_copied_string(tcp->peer_string));
+}
+
+static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error* error);
+static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error* error);
+
+static void tcp_shutdown(grpc_endpoint* ep, grpc_error* why) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  grpc_fd_shutdown(tcp->em_fd, why);
+  grpc_resource_user_shutdown(tcp->resource_user);
+}
+
+static void tcp_free(grpc_tcp* tcp) {
+  grpc_fd_orphan(tcp->em_fd, tcp->release_fd_cb, tcp->release_fd,
+                 "tcp_unref_orphan");
+  grpc_slice_buffer_destroy_internal(&tcp->last_read_buffer);
+  grpc_resource_user_unref(tcp->resource_user);
+  gpr_free(tcp->peer_string);
+  gpr_mu_destroy(&tcp->tb_mu);
+  gpr_free(tcp);
+}
+
+#ifndef NDEBUG
+#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__)
+#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
+static void tcp_unref(grpc_tcp* tcp, const char* reason, const char* file,
+                      int line) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
+            val - 1);
+  }
+  if (gpr_unref(&tcp->refcount)) {
+    tcp_free(tcp);
+  }
+}
+
+static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
+                    int line) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
+            val + 1);
+  }
+  gpr_ref(&tcp->refcount);
+}
+#else
+#define TCP_UNREF(tcp, reason) tcp_unref((tcp))
+#define TCP_REF(tcp, reason) tcp_ref((tcp))
+static void tcp_unref(grpc_tcp* tcp) {
+  if (gpr_unref(&tcp->refcount)) {
+    tcp_free(tcp);
+  }
+}
+
+static void tcp_ref(grpc_tcp* tcp) { gpr_ref(&tcp->refcount); }
+#endif
+
+static void tcp_destroy(grpc_endpoint* ep) {
+  grpc_network_status_unregister_endpoint(ep);
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer);
+  if (grpc_event_engine_can_track_errors()) {
+    gpr_mu_lock(&tcp->tb_mu);
+    grpc_core::TracedBuffer::Shutdown(
+        &tcp->tb_head, tcp->outgoing_buffer_arg,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("endpoint destroyed"));
+    gpr_mu_unlock(&tcp->tb_mu);
+    tcp->outgoing_buffer_arg = nullptr;
+    gpr_atm_no_barrier_store(&tcp->stop_error_notification, true);
+    grpc_fd_set_error(tcp->em_fd);
+  }
+  TCP_UNREF(tcp, "destroy");
+}
+
+static void call_read_cb(grpc_tcp* tcp, grpc_error* error) {
+  grpc_closure* cb = tcp->read_cb;
+
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg);
+    size_t i;
+    const char* str = grpc_error_string(error);
+    gpr_log(GPR_INFO, "read: error=%s", str);
+
+    for (i = 0; i < tcp->incoming_buffer->count; i++) {
+      char* dump = grpc_dump_slice(tcp->incoming_buffer->slices[i],
+                                   GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump);
+      gpr_free(dump);
+    }
+  }
+
+  tcp->read_cb = nullptr;
+  tcp->incoming_buffer = nullptr;
+  GRPC_CLOSURE_SCHED(cb, error);
+}
+
+#define MAX_READ_IOVEC 4
+static void tcp_do_read(grpc_tcp* tcp) {
+  GPR_TIMER_SCOPE("tcp_do_read", 0);
+  struct msghdr msg;
+  struct iovec iov[MAX_READ_IOVEC];
+  ssize_t read_bytes;
+  size_t i;
+
+  GPR_ASSERT(tcp->incoming_buffer->count <= MAX_READ_IOVEC);
+
+  for (i = 0; i < tcp->incoming_buffer->count; i++) {
+    iov[i].iov_base = GRPC_SLICE_START_PTR(tcp->incoming_buffer->slices[i]);
+    iov[i].iov_len = GRPC_SLICE_LENGTH(tcp->incoming_buffer->slices[i]);
+  }
+
+  msg.msg_name = nullptr;
+  msg.msg_namelen = 0;
+  msg.msg_iov = iov;
+  msg.msg_iovlen = static_cast<msg_iovlen_type>(tcp->incoming_buffer->count);
+  msg.msg_control = nullptr;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  GRPC_STATS_INC_TCP_READ_OFFER(tcp->incoming_buffer->length);
+  GRPC_STATS_INC_TCP_READ_OFFER_IOV_SIZE(tcp->incoming_buffer->count);
+
+  do {
+    GPR_TIMER_SCOPE("recvmsg", 0);
+    GRPC_STATS_INC_SYSCALL_READ();
+    read_bytes = recvmsg(tcp->fd, &msg, 0);
+  } while (read_bytes < 0 && errno == EINTR);
+
+  if (read_bytes < 0) {
+    /* NB: After calling call_read_cb a parallel call of the read handler may
+     * be running. */
+    if (errno == EAGAIN) {
+      finish_estimate(tcp);
+      /* We've consumed the edge, request a new one */
+      notify_on_read(tcp);
+    } else {
+      grpc_slice_buffer_reset_and_unref_internal(tcp->incoming_buffer);
+      call_read_cb(tcp,
+                   tcp_annotate_error(GRPC_OS_ERROR(errno, "recvmsg"), tcp));
+      TCP_UNREF(tcp, "read");
+    }
+  } else if (read_bytes == 0) {
+    /* 0 read size ==> end of stream */
+    grpc_slice_buffer_reset_and_unref_internal(tcp->incoming_buffer);
+    call_read_cb(
+        tcp, tcp_annotate_error(
+                 GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), tcp));
+    TCP_UNREF(tcp, "read");
+  } else {
+    GRPC_STATS_INC_TCP_READ_SIZE(read_bytes);
+    add_to_estimate(tcp, static_cast<size_t>(read_bytes));
+    GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length);
+    if (static_cast<size_t>(read_bytes) == tcp->incoming_buffer->length) {
+      finish_estimate(tcp);
+    } else if (static_cast<size_t>(read_bytes) < tcp->incoming_buffer->length) {
+      grpc_slice_buffer_trim_end(
+          tcp->incoming_buffer,
+          tcp->incoming_buffer->length - static_cast<size_t>(read_bytes),
+          &tcp->last_read_buffer);
+    }
+    GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length);
+    call_read_cb(tcp, GRPC_ERROR_NONE);
+    TCP_UNREF(tcp, "read");
+  }
+}
+
+static void tcp_read_allocation_done(void* tcpp, grpc_error* error) {
+  grpc_tcp* tcp = static_cast<grpc_tcp*>(tcpp);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p read_allocation_done: %s", tcp,
+            grpc_error_string(error));
+  }
+  if (error != GRPC_ERROR_NONE) {
+    grpc_slice_buffer_reset_and_unref_internal(tcp->incoming_buffer);
+    grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer);
+    call_read_cb(tcp, GRPC_ERROR_REF(error));
+    TCP_UNREF(tcp, "read");
+  } else {
+    tcp_do_read(tcp);
+  }
+}
+
+static void tcp_continue_read(grpc_tcp* tcp) {
+  size_t target_read_size = get_target_read_size(tcp);
+  if (tcp->incoming_buffer->length < target_read_size / 2 &&
+      tcp->incoming_buffer->count < MAX_READ_IOVEC) {
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "TCP:%p alloc_slices", tcp);
+    }
+    grpc_resource_user_alloc_slices(&tcp->slice_allocator, target_read_size, 1,
+                                    tcp->incoming_buffer);
+  } else {
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "TCP:%p do_read", tcp);
+    }
+    tcp_do_read(tcp);
+  }
+}
+
+static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error* error) {
+  grpc_tcp* tcp = static_cast<grpc_tcp*>(arg);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p got_read: %s", tcp, grpc_error_string(error));
+  }
+
+  if (error != GRPC_ERROR_NONE) {
+    grpc_slice_buffer_reset_and_unref_internal(tcp->incoming_buffer);
+    grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer);
+    call_read_cb(tcp, GRPC_ERROR_REF(error));
+    TCP_UNREF(tcp, "read");
+  } else {
+    tcp_continue_read(tcp);
+  }
+}
+
+static void tcp_read(grpc_endpoint* ep, grpc_slice_buffer* incoming_buffer,
+                     grpc_closure* cb) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  GPR_ASSERT(tcp->read_cb == nullptr);
+  tcp->read_cb = cb;
+  tcp->incoming_buffer = incoming_buffer;
+  grpc_slice_buffer_reset_and_unref_internal(incoming_buffer);
+  grpc_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer);
+  TCP_REF(tcp, "read");
+  if (tcp->is_first_read) {
+    /* Endpoint read called for the very first time. Register read callback with
+     * the polling engine */
+    tcp->is_first_read = false;
+    notify_on_read(tcp);
+  } else {
+    /* Not the first time. We may or may not have more bytes available. In any
+     * case call tcp->read_done_closure (i.e tcp_handle_read()) which does the
+     * right thing (i.e calls tcp_do_read() which either reads the available
+     * bytes or calls notify_on_read() to be notified when new bytes become
+     * available */
+    GRPC_CLOSURE_SCHED(&tcp->read_done_closure, GRPC_ERROR_NONE);
+  }
+}
+
+/* A wrapper around sendmsg. It sends \a msg over \a fd and returns the number
+ * of bytes sent. */
+ssize_t tcp_send(int fd, const struct msghdr* msg) {
+  GPR_TIMER_SCOPE("sendmsg", 1);
+  ssize_t sent_length;
+  do {
+    /* TODO(klempner): Cork if this is a partial write */
+    GRPC_STATS_INC_SYSCALL_WRITE();
+    sent_length = sendmsg(fd, msg, SENDMSG_FLAGS);
+  } while (sent_length < 0 && errno == EINTR);
+  return sent_length;
+}
+
+/** This is to be called if outgoing_buffer_arg is not null. On linux platforms,
+ * this will call sendmsg with socket options set to collect timestamps inside
+ * the kernel. On return, sent_length is set to the return value of the sendmsg
+ * call. Returns false if setting the socket options failed. This is not
+ * implemented for non-linux platforms currently, and crashes out.
+ */
+static bool tcp_write_with_timestamps(grpc_tcp* tcp, struct msghdr* msg,
+                                      size_t sending_length,
+                                      ssize_t* sent_length);
+
+/** The callback function to be invoked when we get an error on the socket. */
+static void tcp_handle_error(void* arg /* grpc_tcp */, grpc_error* error);
+
+#ifdef GRPC_LINUX_ERRQUEUE
+static bool tcp_write_with_timestamps(grpc_tcp* tcp, struct msghdr* msg,
+                                      size_t sending_length,
+                                      ssize_t* sent_length) {
+  if (!tcp->socket_ts_enabled) {
+    uint32_t opt = grpc_core::kTimestampingSocketOptions;
+    if (setsockopt(tcp->fd, SOL_SOCKET, SO_TIMESTAMPING,
+                   static_cast<void*>(&opt), sizeof(opt)) != 0) {
+      grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer);
+      if (grpc_tcp_trace.enabled()) {
+        gpr_log(GPR_ERROR, "Failed to set timestamping options on the socket.");
+      }
+      return false;
+    }
+    tcp->bytes_counter = -1;
+    tcp->socket_ts_enabled = true;
+  }
+  /* Set control message to indicate that you want timestamps. */
+  union {
+    char cmsg_buf[CMSG_SPACE(sizeof(uint32_t))];
+    struct cmsghdr align;
+  } u;
+  cmsghdr* cmsg = reinterpret_cast<cmsghdr*>(u.cmsg_buf);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SO_TIMESTAMPING;
+  cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
+  *reinterpret_cast<int*>(CMSG_DATA(cmsg)) =
+      grpc_core::kTimestampingRecordingOptions;
+  msg->msg_control = u.cmsg_buf;
+  msg->msg_controllen = CMSG_SPACE(sizeof(uint32_t));
+
+  /* If there was an error on sendmsg the logic in tcp_flush will handle it. */
+  ssize_t length = tcp_send(tcp->fd, msg);
+  *sent_length = length;
+  /* Only save timestamps if all the bytes were taken by sendmsg. */
+  if (sending_length == static_cast<size_t>(length)) {
+    gpr_mu_lock(&tcp->tb_mu);
+    grpc_core::TracedBuffer::AddNewEntry(
+        &tcp->tb_head, static_cast<uint32_t>(tcp->bytes_counter + length),
+        tcp->outgoing_buffer_arg);
+    gpr_mu_unlock(&tcp->tb_mu);
+    tcp->outgoing_buffer_arg = nullptr;
+  }
+  return true;
+}
+
+/** Reads \a cmsg to derive timestamps from the control messages. If a valid
+ * timestamp is found, the traced buffer list is updated with this timestamp.
+ * The caller of this function should be looping on the control messages found
+ * in \a msg. \a cmsg should point to the control message that the caller wants
+ * processed.
+ * On return, a pointer to a control message is returned. On the next iteration,
+ * CMSG_NXTHDR(msg, ret_val) should be passed as \a cmsg. */
+struct cmsghdr* process_timestamp(grpc_tcp* tcp, msghdr* msg,
+                                  struct cmsghdr* cmsg) {
+  auto next_cmsg = CMSG_NXTHDR(msg, cmsg);
+  if (next_cmsg == nullptr) {
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_ERROR, "Received timestamp without extended error");
+    }
+    return cmsg;
+  }
+
+  if (!(next_cmsg->cmsg_level == SOL_IP || next_cmsg->cmsg_level == SOL_IPV6) ||
+      !(next_cmsg->cmsg_type == IP_RECVERR ||
+        next_cmsg->cmsg_type == IPV6_RECVERR)) {
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_ERROR, "Unexpected control message");
+    }
+    return cmsg;
+  }
+
+  auto tss =
+      reinterpret_cast<struct grpc_core::scm_timestamping*>(CMSG_DATA(cmsg));
+  auto serr = reinterpret_cast<struct sock_extended_err*>(CMSG_DATA(next_cmsg));
+  if (serr->ee_errno != ENOMSG ||
+      serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
+    gpr_log(GPR_ERROR, "Unexpected control message");
+    return cmsg;
+  }
+  /* The error handling can potentially be done on another thread so we need
+   * to protect the traced buffer list. A lock free list might be better. Using
+   * a simple mutex for now. */
+  gpr_mu_lock(&tcp->tb_mu);
+  grpc_core::TracedBuffer::ProcessTimestamp(&tcp->tb_head, serr, tss);
+  gpr_mu_unlock(&tcp->tb_mu);
+  return next_cmsg;
+}
+
+/** For linux platforms, reads the socket's error queue and processes error
+ * messages from the queue.
+ */
+static void process_errors(grpc_tcp* tcp) {
+  while (true) {
+    struct iovec iov;
+    iov.iov_base = nullptr;
+    iov.iov_len = 0;
+    struct msghdr msg;
+    msg.msg_name = nullptr;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 0;
+    msg.msg_flags = 0;
+
+    union {
+      char rbuf[1024 /*CMSG_SPACE(sizeof(scm_timestamping)) +
+                CMSG_SPACE(sizeof(sock_extended_err) + sizeof(sockaddr_in))*/];
+      struct cmsghdr align;
+    } aligned_buf;
+    memset(&aligned_buf, 0, sizeof(aligned_buf));
+
+    msg.msg_control = aligned_buf.rbuf;
+    msg.msg_controllen = sizeof(aligned_buf.rbuf);
+
+    int r, saved_errno;
+    do {
+      r = recvmsg(tcp->fd, &msg, MSG_ERRQUEUE);
+      saved_errno = errno;
+    } while (r < 0 && saved_errno == EINTR);
+
+    if (r == -1 && saved_errno == EAGAIN) {
+      return; /* No more errors to process */
+    }
+    if (r == -1) {
+      return;
+    }
+    if (grpc_tcp_trace.enabled()) {
+      if ((msg.msg_flags & MSG_CTRUNC) == 1) {
+        gpr_log(GPR_INFO, "Error message was truncated.");
+      }
+    }
+
+    if (msg.msg_controllen == 0) {
+      /* There was no control message found. It was probably spurious. */
+      return;
+    }
+    bool seen = false;
+    for (auto cmsg = CMSG_FIRSTHDR(&msg); cmsg && cmsg->cmsg_len;
+         cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+      if (cmsg->cmsg_level != SOL_SOCKET ||
+          cmsg->cmsg_type != SCM_TIMESTAMPING) {
+        /* Got a control message that is not a timestamp. Don't know how to
+         * handle this. */
+        if (grpc_tcp_trace.enabled()) {
+          gpr_log(GPR_INFO,
+                  "unknown control message cmsg_level:%d cmsg_type:%d",
+                  cmsg->cmsg_level, cmsg->cmsg_type);
+        }
+        return;
+      }
+      cmsg = process_timestamp(tcp, &msg, cmsg);
+      seen = true;
+    }
+    if (!seen) {
+      return;
+    }
+  }
+}
+
+static void tcp_handle_error(void* arg /* grpc_tcp */, grpc_error* error) {
+  grpc_tcp* tcp = static_cast<grpc_tcp*>(arg);
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p got_error: %s", tcp, grpc_error_string(error));
+  }
+
+  if (error != GRPC_ERROR_NONE ||
+      static_cast<bool>(gpr_atm_acq_load(&tcp->stop_error_notification))) {
+    /* We aren't going to register to hear on error anymore, so it is safe to
+     * unref. */
+    TCP_UNREF(tcp, "error-tracking");
+    return;
+  }
+
+  /* We are still interested in collecting timestamps, so let's try reading
+   * them. */
+  process_errors(tcp);
+  /* This might not a timestamps error. Set the read and write closures to be
+   * ready. */
+  grpc_fd_set_readable(tcp->em_fd);
+  grpc_fd_set_writable(tcp->em_fd);
+  GRPC_CLOSURE_INIT(&tcp->error_closure, tcp_handle_error, tcp,
+                    grpc_schedule_on_exec_ctx);
+  grpc_fd_notify_on_error(tcp->em_fd, &tcp->error_closure);
+}
+
+#else  /* GRPC_LINUX_ERRQUEUE */
+static bool tcp_write_with_timestamps(grpc_tcp* tcp, struct msghdr* msg,
+                                      size_t sending_length,
+                                      ssize_t* sent_length) {
+  gpr_log(GPR_ERROR, "Write with timestamps not supported for this platform");
+  GPR_ASSERT(0);
+  return false;
+}
+
+static void tcp_handle_error(void* arg /* grpc_tcp */, grpc_error* error) {
+  gpr_log(GPR_ERROR, "Error handling is not supported for this platform");
+  GPR_ASSERT(0);
+}
+#endif /* GRPC_LINUX_ERRQUEUE */
+
+/* If outgoing_buffer_arg is filled, shuts down the list early, so that any
+ * release operations needed can be performed on the arg */
+void tcp_shutdown_buffer_list(grpc_tcp* tcp) {
+  if (tcp->outgoing_buffer_arg) {
+    gpr_mu_lock(&tcp->tb_mu);
+    grpc_core::TracedBuffer::Shutdown(
+        &tcp->tb_head, tcp->outgoing_buffer_arg,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("TracedBuffer list shutdown"));
+    gpr_mu_unlock(&tcp->tb_mu);
+    tcp->outgoing_buffer_arg = nullptr;
+  }
+}
+
+/* returns true if done, false if pending; if returning true, *error is set */
+#if defined(IOV_MAX) && IOV_MAX < 1000
+#define MAX_WRITE_IOVEC IOV_MAX
+#else
+#define MAX_WRITE_IOVEC 1000
+#endif
+static bool tcp_flush(grpc_tcp* tcp, grpc_error** error) {
+  struct msghdr msg;
+  struct iovec iov[MAX_WRITE_IOVEC];
+  msg_iovlen_type iov_size;
+  ssize_t sent_length = 0;
+  size_t sending_length;
+  size_t trailing;
+  size_t unwind_slice_idx;
+  size_t unwind_byte_idx;
+
+  // We always start at zero, because we eagerly unref and trim the slice
+  // buffer as we write
+  size_t outgoing_slice_idx = 0;
+
+  for (;;) {
+    sending_length = 0;
+    unwind_slice_idx = outgoing_slice_idx;
+    unwind_byte_idx = tcp->outgoing_byte_idx;
+    for (iov_size = 0; outgoing_slice_idx != tcp->outgoing_buffer->count &&
+                       iov_size != MAX_WRITE_IOVEC;
+         iov_size++) {
+      iov[iov_size].iov_base =
+          GRPC_SLICE_START_PTR(
+              tcp->outgoing_buffer->slices[outgoing_slice_idx]) +
+          tcp->outgoing_byte_idx;
+      iov[iov_size].iov_len =
+          GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]) -
+          tcp->outgoing_byte_idx;
+      sending_length += iov[iov_size].iov_len;
+      outgoing_slice_idx++;
+      tcp->outgoing_byte_idx = 0;
+    }
+    GPR_ASSERT(iov_size > 0);
+
+    msg.msg_name = nullptr;
+    msg.msg_namelen = 0;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = iov_size;
+    msg.msg_flags = 0;
+    bool tried_sending_message = false;
+    if (tcp->outgoing_buffer_arg != nullptr) {
+      if (!tcp->ts_capable ||
+          !tcp_write_with_timestamps(tcp, &msg, sending_length, &sent_length)) {
+        /* We could not set socket options to collect Fathom timestamps.
+         * Fallback on writing without timestamps. */
+        tcp->ts_capable = false;
+        tcp_shutdown_buffer_list(tcp);
+      } else {
+        tried_sending_message = true;
+      }
+    }
+    if (!tried_sending_message) {
+      msg.msg_control = nullptr;
+      msg.msg_controllen = 0;
+
+      GRPC_STATS_INC_TCP_WRITE_SIZE(sending_length);
+      GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(iov_size);
+
+      sent_length = tcp_send(tcp->fd, &msg);
+    }
+
+    if (sent_length < 0) {
+      if (errno == EAGAIN) {
+        tcp->outgoing_byte_idx = unwind_byte_idx;
+        // unref all and forget about all slices that have been written to this
+        // point
+        for (size_t idx = 0; idx < unwind_slice_idx; ++idx) {
+          grpc_slice_unref_internal(
+              grpc_slice_buffer_take_first(tcp->outgoing_buffer));
+        }
+        return false;
+      } else if (errno == EPIPE) {
+        *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp);
+        grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer);
+        tcp_shutdown_buffer_list(tcp);
+        return true;
+      } else {
+        *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp);
+        grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer);
+        tcp_shutdown_buffer_list(tcp);
+        return true;
+      }
+    }
+
+    GPR_ASSERT(tcp->outgoing_byte_idx == 0);
+    tcp->bytes_counter += sent_length;
+    trailing = sending_length - static_cast<size_t>(sent_length);
+    while (trailing > 0) {
+      size_t slice_length;
+
+      outgoing_slice_idx--;
+      slice_length =
+          GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]);
+      if (slice_length > trailing) {
+        tcp->outgoing_byte_idx = slice_length - trailing;
+        break;
+      } else {
+        trailing -= slice_length;
+      }
+    }
+    if (outgoing_slice_idx == tcp->outgoing_buffer->count) {
+      *error = GRPC_ERROR_NONE;
+      grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer);
+      return true;
+    }
+  }
+}
+
+static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error* error) {
+  grpc_tcp* tcp = static_cast<grpc_tcp*>(arg);
+  grpc_closure* cb;
+
+  if (error != GRPC_ERROR_NONE) {
+    cb = tcp->write_cb;
+    tcp->write_cb = nullptr;
+    cb->cb(cb->cb_arg, error);
+    TCP_UNREF(tcp, "write");
+    return;
+  }
+
+  if (!tcp_flush(tcp, &error)) {
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "write: delayed");
+    }
+    notify_on_write(tcp);
+  } else {
+    cb = tcp->write_cb;
+    tcp->write_cb = nullptr;
+    if (grpc_tcp_trace.enabled()) {
+      const char* str = grpc_error_string(error);
+      gpr_log(GPR_INFO, "write: %s", str);
+    }
+    GRPC_CLOSURE_SCHED(cb, error);
+    TCP_UNREF(tcp, "write");
+  }
+}
+
+static void tcp_write(grpc_endpoint* ep, grpc_slice_buffer* buf,
+                      grpc_closure* cb, void* arg) {
+  GPR_TIMER_SCOPE("tcp_write", 0);
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  if (grpc_tcp_trace.enabled()) {
+    size_t i;
+
+    for (i = 0; i < buf->count; i++) {
+      char* data =
+          grpc_dump_slice(buf->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data);
+      gpr_free(data);
+    }
+  }
+
+  GPR_ASSERT(tcp->write_cb == nullptr);
+
+  tcp->outgoing_buffer_arg = arg;
+  if (buf->length == 0) {
+    GRPC_CLOSURE_SCHED(
+        cb, grpc_fd_is_shutdown(tcp->em_fd)
+                ? tcp_annotate_error(
+                      GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"), tcp)
+                : GRPC_ERROR_NONE);
+    tcp_shutdown_buffer_list(tcp);
+    return;
+  }
+  tcp->outgoing_buffer = buf;
+  tcp->outgoing_byte_idx = 0;
+  if (arg) {
+    GPR_ASSERT(grpc_event_engine_can_track_errors());
+  }
+
+  if (!tcp_flush(tcp, &error)) {
+    TCP_REF(tcp, "write");
+    tcp->write_cb = cb;
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "write: delayed");
+    }
+    notify_on_write(tcp);
+  } else {
+    if (grpc_tcp_trace.enabled()) {
+      const char* str = grpc_error_string(error);
+      gpr_log(GPR_INFO, "write: %s", str);
+    }
+    GRPC_CLOSURE_SCHED(cb, error);
+  }
+}
+
+static void tcp_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  grpc_pollset_add_fd(pollset, tcp->em_fd);
+}
+
+static void tcp_add_to_pollset_set(grpc_endpoint* ep,
+                                   grpc_pollset_set* pollset_set) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  grpc_pollset_set_add_fd(pollset_set, tcp->em_fd);
+}
+
+static void tcp_delete_from_pollset_set(grpc_endpoint* ep,
+                                        grpc_pollset_set* pollset_set) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  grpc_pollset_set_del_fd(pollset_set, tcp->em_fd);
+}
+
+static char* tcp_get_peer(grpc_endpoint* ep) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  return gpr_strdup(tcp->peer_string);
+}
+
+static int tcp_get_fd(grpc_endpoint* ep) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  return tcp->fd;
+}
+
+static grpc_resource_user* tcp_get_resource_user(grpc_endpoint* ep) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  return tcp->resource_user;
+}
+
+static bool tcp_can_track_err(grpc_endpoint* ep) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  if (!grpc_event_engine_can_track_errors()) {
+    return false;
+  }
+  struct sockaddr addr;
+  socklen_t len = sizeof(addr);
+  if (getsockname(tcp->fd, &addr, &len) < 0) {
+    return false;
+  }
+  if (addr.sa_family == AF_INET || addr.sa_family == AF_INET6) {
+    return true;
+  }
+  return false;
+}
+
+static const grpc_endpoint_vtable vtable = {tcp_read,
+                                            tcp_write,
+                                            tcp_add_to_pollset,
+                                            tcp_add_to_pollset_set,
+                                            tcp_delete_from_pollset_set,
+                                            tcp_shutdown,
+                                            tcp_destroy,
+                                            tcp_get_resource_user,
+                                            tcp_get_peer,
+                                            tcp_get_fd,
+                                            tcp_can_track_err};
+
+#define MAX_CHUNK_SIZE 32 * 1024 * 1024
+
+grpc_endpoint* grpc_tcp_create(grpc_fd* em_fd,
+                               const grpc_channel_args* channel_args,
+                               const char* peer_string) {
+  int tcp_read_chunk_size = GRPC_TCP_DEFAULT_READ_SLICE_SIZE;
+  int tcp_max_read_chunk_size = 4 * 1024 * 1024;
+  int tcp_min_read_chunk_size = 256;
+  grpc_resource_quota* resource_quota = grpc_resource_quota_create(nullptr);
+  if (channel_args != nullptr) {
+    for (size_t i = 0; i < channel_args->num_args; i++) {
+      if (0 ==
+          strcmp(channel_args->args[i].key, GRPC_ARG_TCP_READ_CHUNK_SIZE)) {
+        grpc_integer_options options = {tcp_read_chunk_size, 1, MAX_CHUNK_SIZE};
+        tcp_read_chunk_size =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+      } else if (0 == strcmp(channel_args->args[i].key,
+                             GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE)) {
+        grpc_integer_options options = {tcp_read_chunk_size, 1, MAX_CHUNK_SIZE};
+        tcp_min_read_chunk_size =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+      } else if (0 == strcmp(channel_args->args[i].key,
+                             GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE)) {
+        grpc_integer_options options = {tcp_read_chunk_size, 1, MAX_CHUNK_SIZE};
+        tcp_max_read_chunk_size =
+            grpc_channel_arg_get_integer(&channel_args->args[i], options);
+      } else if (0 ==
+                 strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
+        grpc_resource_quota_unref_internal(resource_quota);
+        resource_quota =
+            grpc_resource_quota_ref_internal(static_cast<grpc_resource_quota*>(
+                channel_args->args[i].value.pointer.p));
+      }
+    }
+  }
+
+  if (tcp_min_read_chunk_size > tcp_max_read_chunk_size) {
+    tcp_min_read_chunk_size = tcp_max_read_chunk_size;
+  }
+  tcp_read_chunk_size = GPR_CLAMP(tcp_read_chunk_size, tcp_min_read_chunk_size,
+                                  tcp_max_read_chunk_size);
+
+  grpc_tcp* tcp = static_cast<grpc_tcp*>(gpr_malloc(sizeof(grpc_tcp)));
+  tcp->base.vtable = &vtable;
+  tcp->peer_string = gpr_strdup(peer_string);
+  tcp->fd = grpc_fd_wrapped_fd(em_fd);
+  tcp->read_cb = nullptr;
+  tcp->write_cb = nullptr;
+  tcp->release_fd_cb = nullptr;
+  tcp->release_fd = nullptr;
+  tcp->incoming_buffer = nullptr;
+  tcp->target_length = static_cast<double>(tcp_read_chunk_size);
+  tcp->min_read_chunk_size = tcp_min_read_chunk_size;
+  tcp->max_read_chunk_size = tcp_max_read_chunk_size;
+  tcp->bytes_read_this_round = 0;
+  /* Will be set to false by the very first endpoint read function */
+  tcp->is_first_read = true;
+  tcp->bytes_counter = -1;
+  tcp->socket_ts_enabled = false;
+  tcp->ts_capable = true;
+  tcp->outgoing_buffer_arg = nullptr;
+  /* paired with unref in grpc_tcp_destroy */
+  gpr_ref_init(&tcp->refcount, 1);
+  gpr_atm_no_barrier_store(&tcp->shutdown_count, 0);
+  tcp->em_fd = em_fd;
+  grpc_slice_buffer_init(&tcp->last_read_buffer);
+  tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
+  grpc_resource_user_slice_allocator_init(
+      &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp);
+  /* Tell network status tracker about new endpoint */
+  grpc_network_status_register_endpoint(&tcp->base);
+  grpc_resource_quota_unref_internal(resource_quota);
+  gpr_mu_init(&tcp->tb_mu);
+  tcp->tb_head = nullptr;
+  /* Start being notified on errors if event engine can track errors. */
+  if (grpc_event_engine_can_track_errors()) {
+    /* Grab a ref to tcp so that we can safely access the tcp struct when
+     * processing errors. We unref when we no longer want to track errors
+     * separately. */
+    TCP_REF(tcp, "error-tracking");
+    gpr_atm_rel_store(&tcp->stop_error_notification, 0);
+    GRPC_CLOSURE_INIT(&tcp->error_closure, tcp_handle_error, tcp,
+                      grpc_schedule_on_exec_ctx);
+    grpc_fd_notify_on_error(tcp->em_fd, &tcp->error_closure);
+  }
+
+  return &tcp->base;
+}
+
+int grpc_tcp_fd(grpc_endpoint* ep) {
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  GPR_ASSERT(ep->vtable == &vtable);
+  return grpc_fd_wrapped_fd(tcp->em_fd);
+}
+
+void grpc_tcp_destroy_and_release_fd(grpc_endpoint* ep, int* fd,
+                                     grpc_closure* done) {
+  grpc_network_status_unregister_endpoint(ep);
+  grpc_tcp* tcp = reinterpret_cast<grpc_tcp*>(ep);
+  GPR_ASSERT(ep->vtable == &vtable);
+  tcp->release_fd = fd;
+  tcp->release_fd_cb = done;
+  grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer);
+  if (grpc_event_engine_can_track_errors()) {
+    /* Stop errors notification. */
+    gpr_mu_lock(&tcp->tb_mu);
+    grpc_core::TracedBuffer::Shutdown(
+        &tcp->tb_head, tcp->outgoing_buffer_arg,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("endpoint destroyed"));
+    gpr_mu_unlock(&tcp->tb_mu);
+    tcp->outgoing_buffer_arg = nullptr;
+    gpr_atm_no_barrier_store(&tcp->stop_error_notification, true);
+    grpc_fd_set_error(tcp->em_fd);
+  }
+  TCP_UNREF(tcp, "destroy");
+}
+
+#endif /* GRPC_POSIX_SOCKET_TCP */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_posix.h b/third_party/grpc/src/core/lib/iomgr/tcp_posix.h
new file mode 100644
index 0000000..eff825c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_posix.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TCP_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_TCP_POSIX_H
+/*
+   Low level TCP "bottom half" implementation, for use by transports built on
+   top of a TCP connection.
+
+   Note that this file does not (yet) include APIs for creating the socket in
+   the first place.
+
+   All calls passing slice transfer ownership of a slice refcount unless
+   otherwise specified.
+*/
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/buffer_list.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+/* Create a tcp endpoint given a file desciptor and a read slice size.
+   Takes ownership of fd. */
+grpc_endpoint* grpc_tcp_create(grpc_fd* fd, const grpc_channel_args* args,
+                               const char* peer_string);
+
+/* Return the tcp endpoint's fd, or -1 if this is not available. Does not
+   release the fd.
+   Requires: ep must be a tcp endpoint.
+ */
+int grpc_tcp_fd(grpc_endpoint* ep);
+
+/* Destroy the tcp endpoint without closing its fd. *fd will be set and done
+ * will be called when the endpoint is destroyed.
+ * Requires: ep must be a tcp endpoint and fd must not be NULL. */
+void grpc_tcp_destroy_and_release_fd(grpc_endpoint* ep, int* fd,
+                                     grpc_closure* done);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TCP_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server.cc
new file mode 100644
index 0000000..ea745f2
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server.cc
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/tcp_server.h"
+
+grpc_tcp_server_vtable* grpc_tcp_server_impl;
+
+grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete,
+                                   const grpc_channel_args* args,
+                                   grpc_tcp_server** server) {
+  return grpc_tcp_server_impl->create(shutdown_complete, args, server);
+}
+
+void grpc_tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets,
+                           size_t pollset_count,
+                           grpc_tcp_server_cb on_accept_cb, void* cb_arg) {
+  grpc_tcp_server_impl->start(server, pollsets, pollset_count, on_accept_cb,
+                              cb_arg);
+}
+
+grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s,
+                                     const grpc_resolved_address* addr,
+                                     int* out_port) {
+  return grpc_tcp_server_impl->add_port(s, addr, out_port);
+}
+
+unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s,
+                                       unsigned port_index) {
+  return grpc_tcp_server_impl->port_fd_count(s, port_index);
+}
+
+int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index,
+                            unsigned fd_index) {
+  return grpc_tcp_server_impl->port_fd(s, port_index, fd_index);
+}
+
+grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) {
+  return grpc_tcp_server_impl->ref(s);
+}
+
+void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s,
+                                           grpc_closure* shutdown_starting) {
+  grpc_tcp_server_impl->shutdown_starting_add(s, shutdown_starting);
+}
+
+void grpc_tcp_server_unref(grpc_tcp_server* s) {
+  grpc_tcp_server_impl->unref(s);
+}
+
+void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) {
+  grpc_tcp_server_impl->shutdown_listeners(s);
+}
+
+void grpc_set_tcp_server_impl(grpc_tcp_server_vtable* impl) {
+  grpc_tcp_server_impl = impl;
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server.h b/third_party/grpc/src/core/lib/iomgr/tcp_server.h
new file mode 100644
index 0000000..8fcbb2f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server.h
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
+#define GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+
+/* Forward decl of grpc_tcp_server */
+typedef struct grpc_tcp_server grpc_tcp_server;
+
+typedef struct grpc_tcp_server_acceptor {
+  /* grpc_tcp_server_cb functions share a ref on from_server that is valid
+     until the function returns. */
+  grpc_tcp_server* from_server;
+  /* Indices that may be passed to grpc_tcp_server_port_fd(). */
+  unsigned port_index;
+  unsigned fd_index;
+} grpc_tcp_server_acceptor;
+
+/* Called for newly connected TCP connections.
+   Takes ownership of acceptor. */
+typedef void (*grpc_tcp_server_cb)(void* arg, grpc_endpoint* ep,
+                                   grpc_pollset* accepting_pollset,
+                                   grpc_tcp_server_acceptor* acceptor);
+
+typedef struct grpc_tcp_server_vtable {
+  grpc_error* (*create)(grpc_closure* shutdown_complete,
+                        const grpc_channel_args* args,
+                        grpc_tcp_server** server);
+  void (*start)(grpc_tcp_server* server, grpc_pollset** pollsets,
+                size_t pollset_count, grpc_tcp_server_cb on_accept_cb,
+                void* cb_arg);
+  grpc_error* (*add_port)(grpc_tcp_server* s, const grpc_resolved_address* addr,
+                          int* out_port);
+  unsigned (*port_fd_count)(grpc_tcp_server* s, unsigned port_index);
+  int (*port_fd)(grpc_tcp_server* s, unsigned port_index, unsigned fd_index);
+  grpc_tcp_server* (*ref)(grpc_tcp_server* s);
+  void (*shutdown_starting_add)(grpc_tcp_server* s,
+                                grpc_closure* shutdown_starting);
+  void (*unref)(grpc_tcp_server* s);
+  void (*shutdown_listeners)(grpc_tcp_server* s);
+} grpc_tcp_server_vtable;
+
+/* Create a server, initially not bound to any ports. The caller owns one ref.
+   If shutdown_complete is not NULL, it will be used by
+   grpc_tcp_server_unref() when the ref count reaches zero. */
+grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete,
+                                   const grpc_channel_args* args,
+                                   grpc_tcp_server** server);
+
+/* Start listening to bound ports */
+void grpc_tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets,
+                           size_t pollset_count,
+                           grpc_tcp_server_cb on_accept_cb, void* cb_arg);
+
+/* Add a port to the server, returning the newly allocated port on success, or
+   -1 on failure.
+
+   The :: and 0.0.0.0 wildcard addresses are treated identically, accepting
+   both IPv4 and IPv6 connections, but :: is the preferred style.  This usually
+   creates one socket, but possibly two on systems which support IPv6,
+   but not dualstack sockets. */
+/* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle
+                  all of the multiple socket port matching logic in one place */
+grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s,
+                                     const grpc_resolved_address* addr,
+                                     int* out_port);
+
+/* Number of fds at the given port_index, or 0 if port_index is out of
+   bounds. */
+unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, unsigned port_index);
+
+/* Returns the file descriptor of the Mth (fd_index) listening socket of the Nth
+   (port_index) call to add_port() on this server, or -1 if the indices are out
+   of bounds. The file descriptor remains owned by the server, and will be
+   cleaned up when the ref count reaches zero. */
+int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index,
+                            unsigned fd_index);
+
+/* Ref s and return s. */
+grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s);
+
+/* shutdown_starting is called when ref count has reached zero and the server is
+   about to be destroyed. The server will be deleted after it returns. Calling
+   grpc_tcp_server_ref() from it has no effect. */
+void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s,
+                                           grpc_closure* shutdown_starting);
+
+/* If the refcount drops to zero, enqueue calls on exec_ctx to
+   shutdown_listeners and delete s. */
+void grpc_tcp_server_unref(grpc_tcp_server* s);
+
+/* Shutdown the fds of listeners. */
+void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s);
+
+void grpc_tcp_server_global_init();
+
+void grpc_set_tcp_server_impl(grpc_tcp_server_vtable* impl);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_custom.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_custom.cc
new file mode 100644
index 0000000..019b354
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_custom.cc
@@ -0,0 +1,472 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/tcp_custom.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+extern grpc_socket_vtable* grpc_custom_socket_vtable;
+
+/* one listening port */
+struct grpc_tcp_listener {
+  grpc_tcp_server* server;
+  unsigned port_index;
+  int port;
+
+  grpc_custom_socket* socket;
+
+  /* linked list */
+  struct grpc_tcp_listener* next;
+
+  bool closed;
+};
+
+struct grpc_tcp_server {
+  gpr_refcount refs;
+
+  /* Called whenever accept() succeeds on a server port. */
+  grpc_tcp_server_cb on_accept_cb;
+  void* on_accept_cb_arg;
+
+  int open_ports;
+
+  /* linked list of server ports */
+  grpc_tcp_listener* head;
+  grpc_tcp_listener* tail;
+
+  /* List of closures passed to shutdown_starting_add(). */
+  grpc_closure_list shutdown_starting;
+
+  /* shutdown callback */
+  grpc_closure* shutdown_complete;
+
+  bool shutdown;
+
+  grpc_resource_quota* resource_quota;
+};
+
+static grpc_error* tcp_server_create(grpc_closure* shutdown_complete,
+                                     const grpc_channel_args* args,
+                                     grpc_tcp_server** server) {
+  grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server));
+  s->resource_quota = grpc_resource_quota_create(nullptr);
+  for (size_t i = 0; i < (args == nullptr ? 0 : args->num_args); i++) {
+    if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) {
+      if (args->args[i].type == GRPC_ARG_POINTER) {
+        grpc_resource_quota_unref_internal(s->resource_quota);
+        s->resource_quota = grpc_resource_quota_ref_internal(
+            (grpc_resource_quota*)args->args[i].value.pointer.p);
+      } else {
+        grpc_resource_quota_unref_internal(s->resource_quota);
+        gpr_free(s);
+        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            GRPC_ARG_RESOURCE_QUOTA " must be a pointer to a buffer pool");
+      }
+    }
+  }
+  gpr_ref_init(&s->refs, 1);
+  s->on_accept_cb = nullptr;
+  s->on_accept_cb_arg = nullptr;
+  s->open_ports = 0;
+  s->head = nullptr;
+  s->tail = nullptr;
+  s->shutdown_starting.head = nullptr;
+  s->shutdown_starting.tail = nullptr;
+  s->shutdown_complete = shutdown_complete;
+  s->shutdown = false;
+  *server = s;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  gpr_ref(&s->refs);
+  return s;
+}
+
+static void tcp_server_shutdown_starting_add(grpc_tcp_server* s,
+                                             grpc_closure* shutdown_starting) {
+  grpc_closure_list_append(&s->shutdown_starting, shutdown_starting,
+                           GRPC_ERROR_NONE);
+}
+
+static void finish_shutdown(grpc_tcp_server* s) {
+  GPR_ASSERT(s->shutdown);
+  if (s->shutdown_complete != nullptr) {
+    GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE);
+  }
+
+  while (s->head) {
+    grpc_tcp_listener* sp = s->head;
+    s->head = sp->next;
+    sp->next = nullptr;
+    gpr_free(sp);
+  }
+  grpc_resource_quota_unref_internal(s->resource_quota);
+  gpr_free(s);
+}
+
+static void custom_close_callback(grpc_custom_socket* socket) {
+  grpc_tcp_listener* sp = socket->listener;
+  if (sp) {
+    grpc_core::ExecCtx exec_ctx;
+    sp->server->open_ports--;
+    if (sp->server->open_ports == 0 && sp->server->shutdown) {
+      finish_shutdown(sp->server);
+    }
+  }
+  socket->refs--;
+  if (socket->refs == 0) {
+    grpc_custom_socket_vtable->destroy(socket);
+    gpr_free(socket);
+  }
+}
+
+void grpc_custom_close_server_callback(grpc_tcp_listener* sp) {
+  if (sp) {
+    grpc_core::ExecCtx exec_ctx;
+    sp->server->open_ports--;
+    if (sp->server->open_ports == 0 && sp->server->shutdown) {
+      finish_shutdown(sp->server);
+    }
+  }
+}
+
+static void close_listener(grpc_tcp_listener* sp) {
+  grpc_custom_socket* socket = sp->socket;
+  if (!sp->closed) {
+    sp->closed = true;
+    grpc_custom_socket_vtable->close(socket, custom_close_callback);
+  }
+}
+
+static void tcp_server_destroy(grpc_tcp_server* s) {
+  int immediately_done = 0;
+  grpc_tcp_listener* sp;
+
+  GPR_ASSERT(!s->shutdown);
+  s->shutdown = true;
+
+  if (s->open_ports == 0) {
+    immediately_done = 1;
+  }
+  for (sp = s->head; sp; sp = sp->next) {
+    close_listener(sp);
+  }
+
+  if (immediately_done) {
+    finish_shutdown(s);
+  }
+}
+
+static void tcp_server_unref(grpc_tcp_server* s) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  if (gpr_unref(&s->refs)) {
+    /* Complete shutdown_starting work before destroying. */
+    grpc_core::ExecCtx exec_ctx;
+    GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting);
+    grpc_core::ExecCtx::Get()->Flush();
+    tcp_server_destroy(s);
+  }
+}
+
+static void finish_accept(grpc_tcp_listener* sp, grpc_custom_socket* socket) {
+  grpc_tcp_server_acceptor* acceptor =
+      (grpc_tcp_server_acceptor*)gpr_malloc(sizeof(*acceptor));
+  grpc_endpoint* ep = nullptr;
+  grpc_resolved_address peer_name;
+  char* peer_name_string;
+  grpc_error* err;
+
+  peer_name_string = nullptr;
+  memset(&peer_name, 0, sizeof(grpc_resolved_address));
+  peer_name.len = GRPC_MAX_SOCKADDR_SIZE;
+  err = grpc_custom_socket_vtable->getpeername(
+      socket, (grpc_sockaddr*)&peer_name.addr, (int*)&peer_name.len);
+  if (err == GRPC_ERROR_NONE) {
+    peer_name_string = grpc_sockaddr_to_uri(&peer_name);
+  } else {
+    GRPC_LOG_IF_ERROR("getpeername error", err);
+    GRPC_ERROR_UNREF(err);
+  }
+  if (grpc_tcp_trace.enabled()) {
+    if (peer_name_string) {
+      gpr_log(GPR_INFO, "SERVER_CONNECT: %p accepted connection: %s",
+              sp->server, peer_name_string);
+    } else {
+      gpr_log(GPR_INFO, "SERVER_CONNECT: %p accepted connection", sp->server);
+    }
+  }
+  ep = custom_tcp_endpoint_create(socket, sp->server->resource_quota,
+                                  peer_name_string);
+  acceptor->from_server = sp->server;
+  acceptor->port_index = sp->port_index;
+  acceptor->fd_index = 0;
+  sp->server->on_accept_cb(sp->server->on_accept_cb_arg, ep, nullptr, acceptor);
+  gpr_free(peer_name_string);
+}
+
+static void custom_accept_callback(grpc_custom_socket* socket,
+                                   grpc_custom_socket* client,
+                                   grpc_error* error);
+
+static void custom_accept_callback(grpc_custom_socket* socket,
+                                   grpc_custom_socket* client,
+                                   grpc_error* error) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_tcp_listener* sp = socket->listener;
+  if (error != GRPC_ERROR_NONE) {
+    if (!sp->closed) {
+      gpr_log(GPR_ERROR, "Accept failed: %s", grpc_error_string(error));
+    }
+    gpr_free(client);
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  finish_accept(sp, client);
+  if (!sp->closed) {
+    grpc_custom_socket* new_socket =
+        (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket));
+    new_socket->endpoint = nullptr;
+    new_socket->listener = nullptr;
+    new_socket->connector = nullptr;
+    new_socket->refs = 1;
+    grpc_custom_socket_vtable->accept(sp->socket, new_socket,
+                                      custom_accept_callback);
+  }
+}
+
+static grpc_error* add_socket_to_server(grpc_tcp_server* s,
+                                        grpc_custom_socket* socket,
+                                        const grpc_resolved_address* addr,
+                                        unsigned port_index,
+                                        grpc_tcp_listener** listener) {
+  grpc_tcp_listener* sp = nullptr;
+  int port = -1;
+  grpc_error* error;
+  grpc_resolved_address sockname_temp;
+
+  // The last argument to uv_tcp_bind is flags
+  error = grpc_custom_socket_vtable->bind(socket, (grpc_sockaddr*)addr->addr,
+                                          addr->len, 0);
+  if (error != GRPC_ERROR_NONE) {
+    return error;
+  }
+
+  error = grpc_custom_socket_vtable->listen(socket);
+  if (error != GRPC_ERROR_NONE) {
+    return error;
+  }
+
+  sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE;
+  error = grpc_custom_socket_vtable->getsockname(
+      socket, (grpc_sockaddr*)&sockname_temp.addr, (int*)&sockname_temp.len);
+  if (error != GRPC_ERROR_NONE) {
+    return error;
+  }
+
+  port = grpc_sockaddr_get_port(&sockname_temp);
+
+  GPR_ASSERT(port >= 0);
+  GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
+  sp = (grpc_tcp_listener*)gpr_zalloc(sizeof(grpc_tcp_listener));
+  sp->next = nullptr;
+  if (s->head == nullptr) {
+    s->head = sp;
+  } else {
+    s->tail->next = sp;
+  }
+  s->tail = sp;
+  sp->server = s;
+  sp->socket = socket;
+  sp->port = port;
+  sp->port_index = port_index;
+  sp->closed = false;
+  s->open_ports++;
+  *listener = sp;
+
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* tcp_server_add_port(grpc_tcp_server* s,
+                                       const grpc_resolved_address* addr,
+                                       int* port) {
+  // This function is mostly copied from tcp_server_windows.c
+  grpc_tcp_listener* sp = nullptr;
+  grpc_custom_socket* socket;
+  grpc_resolved_address addr6_v4mapped;
+  grpc_resolved_address wildcard;
+  grpc_resolved_address* allocated_addr = nullptr;
+  grpc_resolved_address sockname_temp;
+  unsigned port_index = 0;
+  grpc_error* error = GRPC_ERROR_NONE;
+  int family;
+
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+
+  if (s->tail != nullptr) {
+    port_index = s->tail->port_index + 1;
+  }
+
+  /* Check if this is a wildcard port, and if so, try to keep the port the same
+     as some previously created listener. */
+  if (grpc_sockaddr_get_port(addr) == 0) {
+    for (sp = s->head; sp; sp = sp->next) {
+      socket = sp->socket;
+      sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE;
+      if (nullptr == grpc_custom_socket_vtable->getsockname(
+                         socket, (grpc_sockaddr*)&sockname_temp.addr,
+                         (int*)&sockname_temp.len)) {
+        *port = grpc_sockaddr_get_port(&sockname_temp);
+        if (*port > 0) {
+          allocated_addr =
+              (grpc_resolved_address*)gpr_malloc(sizeof(grpc_resolved_address));
+          memcpy(allocated_addr, addr, sizeof(grpc_resolved_address));
+          grpc_sockaddr_set_port(allocated_addr, *port);
+          addr = allocated_addr;
+          break;
+        }
+      }
+    }
+  }
+
+  if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+    addr = &addr6_v4mapped;
+  }
+
+  /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
+  if (grpc_sockaddr_is_wildcard(addr, port)) {
+    grpc_sockaddr_make_wildcard6(*port, &wildcard);
+
+    addr = &wildcard;
+  }
+
+  if (grpc_tcp_trace.enabled()) {
+    char* port_string;
+    grpc_sockaddr_to_string(&port_string, addr, 0);
+    const char* str = grpc_error_string(error);
+    if (port_string) {
+      gpr_log(GPR_INFO, "SERVER %p add_port %s error=%s", s, port_string, str);
+      gpr_free(port_string);
+    } else {
+      gpr_log(GPR_INFO, "SERVER %p add_port error=%s", s, str);
+    }
+  }
+
+  family = grpc_sockaddr_get_family(addr);
+  socket = (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket));
+  socket->refs = 1;
+  socket->endpoint = nullptr;
+  socket->listener = nullptr;
+  socket->connector = nullptr;
+  grpc_custom_socket_vtable->init(socket, family);
+
+  if (error == GRPC_ERROR_NONE) {
+    error = add_socket_to_server(s, socket, addr, port_index, &sp);
+  }
+  gpr_free(allocated_addr);
+
+  if (error != GRPC_ERROR_NONE) {
+    grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+        "Failed to add port to server", &error, 1);
+    GRPC_ERROR_UNREF(error);
+    error = error_out;
+    *port = -1;
+  } else {
+    GPR_ASSERT(sp != nullptr);
+    *port = sp->port;
+  }
+  socket->listener = sp;
+  return error;
+}
+
+static void tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets,
+                             size_t pollset_count,
+                             grpc_tcp_server_cb on_accept_cb, void* cb_arg) {
+  grpc_tcp_listener* sp;
+  (void)pollsets;
+  (void)pollset_count;
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "SERVER_START %p", server);
+  }
+  GPR_ASSERT(on_accept_cb);
+  GPR_ASSERT(!server->on_accept_cb);
+  server->on_accept_cb = on_accept_cb;
+  server->on_accept_cb_arg = cb_arg;
+  for (sp = server->head; sp; sp = sp->next) {
+    grpc_custom_socket* new_socket =
+        (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket));
+    new_socket->endpoint = nullptr;
+    new_socket->listener = nullptr;
+    new_socket->connector = nullptr;
+    new_socket->refs = 1;
+    grpc_custom_socket_vtable->accept(sp->socket, new_socket,
+                                      custom_accept_callback);
+  }
+}
+
+static unsigned tcp_server_port_fd_count(grpc_tcp_server* s,
+                                         unsigned port_index) {
+  return 0;
+}
+
+static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index,
+                              unsigned fd_index) {
+  return -1;
+}
+
+static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {
+  for (grpc_tcp_listener* sp = s->head; sp; sp = sp->next) {
+    if (!sp->closed) {
+      sp->closed = true;
+      grpc_custom_socket_vtable->close(sp->socket, custom_close_callback);
+    }
+  }
+}
+
+grpc_tcp_server_vtable custom_tcp_server_vtable = {
+    tcp_server_create,
+    tcp_server_start,
+    tcp_server_add_port,
+    tcp_server_port_fd_count,
+    tcp_server_port_fd,
+    tcp_server_ref,
+    tcp_server_shutdown_starting_add,
+    tcp_server_unref,
+    tcp_server_shutdown_listeners};
+
+#ifdef GRPC_UV_TEST
+grpc_tcp_server_vtable* default_tcp_server_vtable = &custom_tcp_server_vtable;
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_posix.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_posix.cc
new file mode 100644
index 0000000..824db07
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_posix.cc
@@ -0,0 +1,562 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_TCP_SERVER
+
+#include "src/core/lib/iomgr/tcp_server.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/iomgr/tcp_server_utils_posix.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+
+static grpc_error* tcp_server_create(grpc_closure* shutdown_complete,
+                                     const grpc_channel_args* args,
+                                     grpc_tcp_server** server) {
+  grpc_tcp_server* s =
+      static_cast<grpc_tcp_server*>(gpr_zalloc(sizeof(grpc_tcp_server)));
+  s->so_reuseport = grpc_is_socket_reuse_port_supported();
+  s->expand_wildcard_addrs = false;
+  for (size_t i = 0; i < (args == nullptr ? 0 : args->num_args); i++) {
+    if (0 == strcmp(GRPC_ARG_ALLOW_REUSEPORT, args->args[i].key)) {
+      if (args->args[i].type == GRPC_ARG_INTEGER) {
+        s->so_reuseport = grpc_is_socket_reuse_port_supported() &&
+                          (args->args[i].value.integer != 0);
+      } else {
+        gpr_free(s);
+        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(GRPC_ARG_ALLOW_REUSEPORT
+                                                    " must be an integer");
+      }
+    } else if (0 == strcmp(GRPC_ARG_EXPAND_WILDCARD_ADDRS, args->args[i].key)) {
+      if (args->args[i].type == GRPC_ARG_INTEGER) {
+        s->expand_wildcard_addrs = (args->args[i].value.integer != 0);
+      } else {
+        gpr_free(s);
+        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            GRPC_ARG_EXPAND_WILDCARD_ADDRS " must be an integer");
+      }
+    }
+  }
+  gpr_ref_init(&s->refs, 1);
+  gpr_mu_init(&s->mu);
+  s->active_ports = 0;
+  s->destroyed_ports = 0;
+  s->shutdown = false;
+  s->shutdown_starting.head = nullptr;
+  s->shutdown_starting.tail = nullptr;
+  s->shutdown_complete = shutdown_complete;
+  s->on_accept_cb = nullptr;
+  s->on_accept_cb_arg = nullptr;
+  s->head = nullptr;
+  s->tail = nullptr;
+  s->nports = 0;
+  s->channel_args = grpc_channel_args_copy(args);
+  gpr_atm_no_barrier_store(&s->next_pollset_to_assign, 0);
+  *server = s;
+  return GRPC_ERROR_NONE;
+}
+
+static void finish_shutdown(grpc_tcp_server* s) {
+  gpr_mu_lock(&s->mu);
+  GPR_ASSERT(s->shutdown);
+  gpr_mu_unlock(&s->mu);
+  if (s->shutdown_complete != nullptr) {
+    GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE);
+  }
+
+  gpr_mu_destroy(&s->mu);
+
+  while (s->head) {
+    grpc_tcp_listener* sp = s->head;
+    s->head = sp->next;
+    gpr_free(sp);
+  }
+  grpc_channel_args_destroy(s->channel_args);
+
+  gpr_free(s);
+}
+
+static void destroyed_port(void* server, grpc_error* error) {
+  grpc_tcp_server* s = static_cast<grpc_tcp_server*>(server);
+  gpr_mu_lock(&s->mu);
+  s->destroyed_ports++;
+  if (s->destroyed_ports == s->nports) {
+    gpr_mu_unlock(&s->mu);
+    finish_shutdown(s);
+  } else {
+    GPR_ASSERT(s->destroyed_ports < s->nports);
+    gpr_mu_unlock(&s->mu);
+  }
+}
+
+/* called when all listening endpoints have been shutdown, so no further
+   events will be received on them - at this point it's safe to destroy
+   things */
+static void deactivated_all_ports(grpc_tcp_server* s) {
+  /* delete ALL the things */
+  gpr_mu_lock(&s->mu);
+
+  GPR_ASSERT(s->shutdown);
+
+  if (s->head) {
+    grpc_tcp_listener* sp;
+    for (sp = s->head; sp; sp = sp->next) {
+      grpc_unlink_if_unix_domain_socket(&sp->addr);
+      GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s,
+                        grpc_schedule_on_exec_ctx);
+      grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, nullptr,
+                     "tcp_listener_shutdown");
+    }
+    gpr_mu_unlock(&s->mu);
+  } else {
+    gpr_mu_unlock(&s->mu);
+    finish_shutdown(s);
+  }
+}
+
+static void tcp_server_destroy(grpc_tcp_server* s) {
+  gpr_mu_lock(&s->mu);
+
+  GPR_ASSERT(!s->shutdown);
+  s->shutdown = true;
+
+  /* shutdown all fd's */
+  if (s->active_ports) {
+    grpc_tcp_listener* sp;
+    for (sp = s->head; sp; sp = sp->next) {
+      grpc_fd_shutdown(
+          sp->emfd, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server destroyed"));
+    }
+    gpr_mu_unlock(&s->mu);
+  } else {
+    gpr_mu_unlock(&s->mu);
+    deactivated_all_ports(s);
+  }
+}
+
+/* event manager callback when reads are ready */
+static void on_read(void* arg, grpc_error* err) {
+  grpc_tcp_listener* sp = static_cast<grpc_tcp_listener*>(arg);
+  grpc_pollset* read_notifier_pollset;
+  if (err != GRPC_ERROR_NONE) {
+    goto error;
+  }
+
+  /* loop until accept4 returns EAGAIN, and then re-arm notification */
+  for (;;) {
+    grpc_resolved_address addr;
+    char* addr_str;
+    char* name;
+    memset(&addr, 0, sizeof(addr));
+    addr.len = static_cast<socklen_t>(sizeof(struct sockaddr_storage));
+    /* Note: If we ever decide to return this address to the user, remember to
+       strip off the ::ffff:0.0.0.0/96 prefix first. */
+    int fd = grpc_accept4(sp->fd, &addr, 1, 1);
+    if (fd < 0) {
+      switch (errno) {
+        case EINTR:
+          continue;
+        case EAGAIN:
+          grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
+          return;
+        default:
+          gpr_mu_lock(&sp->server->mu);
+          if (!sp->server->shutdown_listeners) {
+            gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno));
+          } else {
+            /* if we have shutdown listeners, accept4 could fail, and we
+               needn't notify users */
+          }
+          gpr_mu_unlock(&sp->server->mu);
+          goto error;
+      }
+    }
+
+    grpc_set_socket_no_sigpipe_if_possible(fd);
+
+    addr_str = grpc_sockaddr_to_uri(&addr);
+    gpr_asprintf(&name, "tcp-server-connection:%s", addr_str);
+
+    if (grpc_tcp_trace.enabled()) {
+      gpr_log(GPR_INFO, "SERVER_CONNECT: incoming connection: %s", addr_str);
+    }
+
+    grpc_fd* fdobj = grpc_fd_create(fd, name, true);
+
+    read_notifier_pollset =
+        sp->server->pollsets[static_cast<size_t>(gpr_atm_no_barrier_fetch_add(
+                                 &sp->server->next_pollset_to_assign, 1)) %
+                             sp->server->pollset_count];
+
+    grpc_pollset_add_fd(read_notifier_pollset, fdobj);
+
+    // Create acceptor.
+    grpc_tcp_server_acceptor* acceptor =
+        static_cast<grpc_tcp_server_acceptor*>(gpr_malloc(sizeof(*acceptor)));
+    acceptor->from_server = sp->server;
+    acceptor->port_index = sp->port_index;
+    acceptor->fd_index = sp->fd_index;
+
+    sp->server->on_accept_cb(
+        sp->server->on_accept_cb_arg,
+        grpc_tcp_create(fdobj, sp->server->channel_args, addr_str),
+        read_notifier_pollset, acceptor);
+
+    gpr_free(name);
+    gpr_free(addr_str);
+  }
+
+  GPR_UNREACHABLE_CODE(return );
+
+error:
+  gpr_mu_lock(&sp->server->mu);
+  if (0 == --sp->server->active_ports && sp->server->shutdown) {
+    gpr_mu_unlock(&sp->server->mu);
+    deactivated_all_ports(sp->server);
+  } else {
+    gpr_mu_unlock(&sp->server->mu);
+  }
+}
+
+/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
+static grpc_error* add_wildcard_addrs_to_server(grpc_tcp_server* s,
+                                                unsigned port_index,
+                                                int requested_port,
+                                                int* out_port) {
+  grpc_resolved_address wild4;
+  grpc_resolved_address wild6;
+  unsigned fd_index = 0;
+  grpc_dualstack_mode dsmode;
+  grpc_tcp_listener* sp = nullptr;
+  grpc_tcp_listener* sp2 = nullptr;
+  grpc_error* v6_err = GRPC_ERROR_NONE;
+  grpc_error* v4_err = GRPC_ERROR_NONE;
+  *out_port = -1;
+
+  if (grpc_tcp_server_have_ifaddrs() && s->expand_wildcard_addrs) {
+    return grpc_tcp_server_add_all_local_addrs(s, port_index, requested_port,
+                                               out_port);
+  }
+
+  grpc_sockaddr_make_wildcards(requested_port, &wild4, &wild6);
+  /* Try listening on IPv6 first. */
+  if ((v6_err = grpc_tcp_server_add_addr(s, &wild6, port_index, fd_index,
+                                         &dsmode, &sp)) == GRPC_ERROR_NONE) {
+    ++fd_index;
+    requested_port = *out_port = sp->port;
+    if (dsmode == GRPC_DSMODE_DUALSTACK || dsmode == GRPC_DSMODE_IPV4) {
+      return GRPC_ERROR_NONE;
+    }
+  }
+  /* If we got a v6-only socket or nothing, try adding 0.0.0.0. */
+  grpc_sockaddr_set_port(&wild4, requested_port);
+  if ((v4_err = grpc_tcp_server_add_addr(s, &wild4, port_index, fd_index,
+                                         &dsmode, &sp2)) == GRPC_ERROR_NONE) {
+    *out_port = sp2->port;
+    if (sp != nullptr) {
+      sp2->is_sibling = 1;
+      sp->sibling = sp2;
+    }
+  }
+  if (*out_port > 0) {
+    if (v6_err != GRPC_ERROR_NONE) {
+      gpr_log(GPR_INFO,
+              "Failed to add :: listener, "
+              "the environment may not support IPv6: %s",
+              grpc_error_string(v6_err));
+      GRPC_ERROR_UNREF(v6_err);
+    }
+    if (v4_err != GRPC_ERROR_NONE) {
+      gpr_log(GPR_INFO,
+              "Failed to add 0.0.0.0 listener, "
+              "the environment may not support IPv4: %s",
+              grpc_error_string(v4_err));
+      GRPC_ERROR_UNREF(v4_err);
+    }
+    return GRPC_ERROR_NONE;
+  } else {
+    grpc_error* root_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Failed to add any wildcard listeners");
+    GPR_ASSERT(v6_err != GRPC_ERROR_NONE && v4_err != GRPC_ERROR_NONE);
+    root_err = grpc_error_add_child(root_err, v6_err);
+    root_err = grpc_error_add_child(root_err, v4_err);
+    return root_err;
+  }
+}
+
+static grpc_error* clone_port(grpc_tcp_listener* listener, unsigned count) {
+  grpc_tcp_listener* sp = nullptr;
+  char* addr_str;
+  char* name;
+  grpc_error* err;
+
+  for (grpc_tcp_listener* l = listener->next; l && l->is_sibling; l = l->next) {
+    l->fd_index += count;
+  }
+
+  for (unsigned i = 0; i < count; i++) {
+    int fd = -1;
+    int port = -1;
+    grpc_dualstack_mode dsmode;
+    err = grpc_create_dualstack_socket(&listener->addr, SOCK_STREAM, 0, &dsmode,
+                                       &fd);
+    if (err != GRPC_ERROR_NONE) return err;
+    err = grpc_tcp_server_prepare_socket(listener->server, fd, &listener->addr,
+                                         true, &port);
+    if (err != GRPC_ERROR_NONE) return err;
+    listener->server->nports++;
+    grpc_sockaddr_to_string(&addr_str, &listener->addr, 1);
+    gpr_asprintf(&name, "tcp-server-listener:%s/clone-%d", addr_str, i);
+    sp = static_cast<grpc_tcp_listener*>(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, true);
+    memcpy(&sp->addr, &listener->addr, sizeof(grpc_resolved_address));
+    sp->port = port;
+    sp->port_index = listener->port_index;
+    sp->fd_index = listener->fd_index + count - i;
+    GPR_ASSERT(sp->emfd);
+    while (listener->server->tail->next != nullptr) {
+      listener->server->tail = listener->server->tail->next;
+    }
+    gpr_free(addr_str);
+    gpr_free(name);
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* tcp_server_add_port(grpc_tcp_server* s,
+                                       const grpc_resolved_address* addr,
+                                       int* out_port) {
+  grpc_tcp_listener* sp;
+  grpc_resolved_address sockname_temp;
+  grpc_resolved_address addr6_v4mapped;
+  int requested_port = grpc_sockaddr_get_port(addr);
+  unsigned port_index = 0;
+  grpc_dualstack_mode dsmode;
+  grpc_error* err;
+  *out_port = -1;
+  if (s->tail != nullptr) {
+    port_index = s->tail->port_index + 1;
+  }
+  grpc_unlink_if_unix_domain_socket(addr);
+
+  /* Check if this is a wildcard port, and if so, try to keep the port the same
+     as some previously created listener. */
+  if (requested_port == 0) {
+    for (sp = s->head; sp; sp = sp->next) {
+      sockname_temp.len =
+          static_cast<socklen_t>(sizeof(struct sockaddr_storage));
+      if (0 ==
+          getsockname(sp->fd,
+                      reinterpret_cast<grpc_sockaddr*>(&sockname_temp.addr),
+                      &sockname_temp.len)) {
+        int used_port = grpc_sockaddr_get_port(&sockname_temp);
+        if (used_port > 0) {
+          memcpy(&sockname_temp, addr, sizeof(grpc_resolved_address));
+          grpc_sockaddr_set_port(&sockname_temp, used_port);
+          requested_port = used_port;
+          addr = &sockname_temp;
+          break;
+        }
+      }
+    }
+  }
+  if (grpc_sockaddr_is_wildcard(addr, &requested_port)) {
+    return add_wildcard_addrs_to_server(s, port_index, requested_port,
+                                        out_port);
+  }
+  if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+    addr = &addr6_v4mapped;
+  }
+  if ((err = grpc_tcp_server_add_addr(s, addr, port_index, 0, &dsmode, &sp)) ==
+      GRPC_ERROR_NONE) {
+    *out_port = sp->port;
+  }
+  return err;
+}
+
+/* Return listener at port_index or NULL. Should only be called with s->mu
+   locked. */
+static grpc_tcp_listener* get_port_index(grpc_tcp_server* s,
+                                         unsigned port_index) {
+  unsigned num_ports = 0;
+  grpc_tcp_listener* sp;
+  for (sp = s->head; sp; sp = sp->next) {
+    if (!sp->is_sibling) {
+      if (++num_ports > port_index) {
+        return sp;
+      }
+    }
+  }
+  return nullptr;
+}
+
+unsigned tcp_server_port_fd_count(grpc_tcp_server* s, unsigned port_index) {
+  unsigned num_fds = 0;
+  gpr_mu_lock(&s->mu);
+  grpc_tcp_listener* sp = get_port_index(s, port_index);
+  for (; sp; sp = sp->sibling) {
+    ++num_fds;
+  }
+  gpr_mu_unlock(&s->mu);
+  return num_fds;
+}
+
+static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index,
+                              unsigned fd_index) {
+  gpr_mu_lock(&s->mu);
+  grpc_tcp_listener* sp = get_port_index(s, port_index);
+  for (; sp; sp = sp->sibling, --fd_index) {
+    if (fd_index == 0) {
+      gpr_mu_unlock(&s->mu);
+      return sp->fd;
+    }
+  }
+  gpr_mu_unlock(&s->mu);
+  return -1;
+}
+
+static void tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollsets,
+                             size_t pollset_count,
+                             grpc_tcp_server_cb on_accept_cb,
+                             void* on_accept_cb_arg) {
+  size_t i;
+  grpc_tcp_listener* sp;
+  GPR_ASSERT(on_accept_cb);
+  gpr_mu_lock(&s->mu);
+  GPR_ASSERT(!s->on_accept_cb);
+  GPR_ASSERT(s->active_ports == 0);
+  s->on_accept_cb = on_accept_cb;
+  s->on_accept_cb_arg = on_accept_cb_arg;
+  s->pollsets = pollsets;
+  s->pollset_count = pollset_count;
+  sp = s->head;
+  while (sp != nullptr) {
+    if (s->so_reuseport && !grpc_is_unix_socket(&sp->addr) &&
+        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++) {
+        grpc_pollset_add_fd(pollsets[i], sp->emfd);
+        GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp,
+                          grpc_schedule_on_exec_ctx);
+        grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
+        s->active_ports++;
+        sp = sp->next;
+      }
+    } else {
+      for (i = 0; i < pollset_count; i++) {
+        grpc_pollset_add_fd(pollsets[i], sp->emfd);
+      }
+      GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp,
+                        grpc_schedule_on_exec_ctx);
+      grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
+      s->active_ports++;
+      sp = sp->next;
+    }
+  }
+  gpr_mu_unlock(&s->mu);
+}
+
+grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) {
+  gpr_ref_non_zero(&s->refs);
+  return s;
+}
+
+static void tcp_server_shutdown_starting_add(grpc_tcp_server* s,
+                                             grpc_closure* shutdown_starting) {
+  gpr_mu_lock(&s->mu);
+  grpc_closure_list_append(&s->shutdown_starting, shutdown_starting,
+                           GRPC_ERROR_NONE);
+  gpr_mu_unlock(&s->mu);
+}
+
+static void tcp_server_unref(grpc_tcp_server* s) {
+  if (gpr_unref(&s->refs)) {
+    grpc_tcp_server_shutdown_listeners(s);
+    gpr_mu_lock(&s->mu);
+    GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting);
+    gpr_mu_unlock(&s->mu);
+    tcp_server_destroy(s);
+  }
+}
+
+static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {
+  gpr_mu_lock(&s->mu);
+  s->shutdown_listeners = true;
+  /* shutdown all fd's */
+  if (s->active_ports) {
+    grpc_tcp_listener* sp;
+    for (sp = s->head; sp; sp = sp->next) {
+      grpc_fd_shutdown(sp->emfd,
+                       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"));
+    }
+  }
+  gpr_mu_unlock(&s->mu);
+}
+
+grpc_tcp_server_vtable grpc_posix_tcp_server_vtable = {
+    tcp_server_create,
+    tcp_server_start,
+    tcp_server_add_port,
+    tcp_server_port_fd_count,
+    tcp_server_port_fd,
+    tcp_server_ref,
+    tcp_server_shutdown_starting_add,
+    tcp_server_unref,
+    tcp_server_shutdown_listeners};
+#endif /* GRPC_POSIX_SOCKET_TCP_SERVER */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix.h b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix.h
new file mode 100644
index 0000000..dd19909
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix.h
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+
+/* one listening port */
+typedef struct grpc_tcp_listener {
+  int fd;
+  grpc_fd* emfd;
+  grpc_tcp_server* server;
+  grpc_resolved_address addr;
+  int port;
+  unsigned port_index;
+  unsigned fd_index;
+  grpc_closure read_closure;
+  grpc_closure destroyed_closure;
+  struct grpc_tcp_listener* next;
+  /* 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;
+} grpc_tcp_listener;
+
+/* the overall server */
+struct grpc_tcp_server {
+  gpr_refcount refs;
+  /* Called whenever accept() succeeds on a server port. */
+  grpc_tcp_server_cb on_accept_cb;
+  void* on_accept_cb_arg;
+
+  gpr_mu mu;
+
+  /* active port count: how many ports are actually still listening */
+  size_t active_ports;
+  /* destroyed port count: how many ports are completely destroyed */
+  size_t destroyed_ports;
+
+  /* is this server shutting down? */
+  bool shutdown;
+  /* have listeners been shutdown? */
+  bool shutdown_listeners;
+  /* use SO_REUSEPORT */
+  bool so_reuseport;
+  /* expand wildcard addresses to a list of all local addresses */
+  bool expand_wildcard_addrs;
+
+  /* linked list of server ports */
+  grpc_tcp_listener* head;
+  grpc_tcp_listener* tail;
+  unsigned nports;
+
+  /* List of closures passed to shutdown_starting_add(). */
+  grpc_closure_list shutdown_starting;
+
+  /* shutdown callback */
+  grpc_closure* shutdown_complete;
+
+  /* all pollsets interested in new connections */
+  grpc_pollset** pollsets;
+  /* number of pollsets in the pollsets array */
+  size_t pollset_count;
+
+  /* next pollset to assign a channel to */
+  gpr_atm next_pollset_to_assign;
+
+  /* channel args for this server */
+  grpc_channel_args* channel_args;
+};
+
+/* If successful, add a listener to \a s for \a addr, set \a dsmode for the
+   socket, and return the \a listener. */
+grpc_error* grpc_tcp_server_add_addr(grpc_tcp_server* s,
+                                     const grpc_resolved_address* addr,
+                                     unsigned port_index, unsigned fd_index,
+                                     grpc_dualstack_mode* dsmode,
+                                     grpc_tcp_listener** listener);
+
+/* Get all addresses assigned to network interfaces on the machine and create a
+   listener for each. requested_port is the port to use for every listener, or 0
+   to select one random port that will be used for every listener. Set *out_port
+   to the port selected. Return GRPC_ERROR_NONE only if all listeners were
+   added. */
+grpc_error* grpc_tcp_server_add_all_local_addrs(grpc_tcp_server* s,
+                                                unsigned port_index,
+                                                int requested_port,
+                                                int* out_port);
+
+/* Prepare a recently-created socket for listening. */
+grpc_error* grpc_tcp_server_prepare_socket(grpc_tcp_server*, int fd,
+                                           const grpc_resolved_address* addr,
+                                           bool so_reuseport, int* port);
+/* Ruturn true if the platform supports ifaddrs */
+bool grpc_tcp_server_have_ifaddrs(void);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_common.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_common.cc
new file mode 100644
index 0000000..8d8d3f4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_common.cc
@@ -0,0 +1,223 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON
+
+#include "src/core/lib/iomgr/tcp_server_utils_posix.h"
+
+#include <errno.h>
+#include <limits.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/sync.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+
+#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
+
+static gpr_once s_init_max_accept_queue_size = GPR_ONCE_INIT;
+static int s_max_accept_queue_size;
+
+/* get max listen queue size on linux */
+static void init_max_accept_queue_size(void) {
+  int n = SOMAXCONN;
+  char buf[64];
+  FILE* fp = fopen("/proc/sys/net/core/somaxconn", "r");
+  if (fp == nullptr) {
+    /* 2.4 kernel. */
+    s_max_accept_queue_size = SOMAXCONN;
+    return;
+  }
+  if (fgets(buf, sizeof buf, fp)) {
+    char* end;
+    long i = strtol(buf, &end, 10);
+    if (i > 0 && i <= INT_MAX && end && *end == '\n') {
+      n = static_cast<int>(i);
+    }
+  }
+  fclose(fp);
+  s_max_accept_queue_size = n;
+
+  if (s_max_accept_queue_size < MIN_SAFE_ACCEPT_QUEUE_SIZE) {
+    gpr_log(GPR_INFO,
+            "Suspiciously small accept queue (%d) will probably lead to "
+            "connection drops",
+            s_max_accept_queue_size);
+  }
+}
+
+static int get_max_accept_queue_size(void) {
+  gpr_once_init(&s_init_max_accept_queue_size, init_max_accept_queue_size);
+  return s_max_accept_queue_size;
+}
+
+static grpc_error* add_socket_to_server(grpc_tcp_server* s, int fd,
+                                        const grpc_resolved_address* addr,
+                                        unsigned port_index, unsigned fd_index,
+                                        grpc_tcp_listener** listener) {
+  grpc_tcp_listener* sp = nullptr;
+  int port = -1;
+  char* addr_str;
+  char* name;
+
+  grpc_error* err =
+      grpc_tcp_server_prepare_socket(s, fd, addr, s->so_reuseport, &port);
+  if (err == GRPC_ERROR_NONE) {
+    GPR_ASSERT(port > 0);
+    grpc_sockaddr_to_string(&addr_str, addr, 1);
+    gpr_asprintf(&name, "tcp-server-listener:%s", addr_str);
+    gpr_mu_lock(&s->mu);
+    s->nports++;
+    GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
+    sp = static_cast<grpc_tcp_listener*>(gpr_malloc(sizeof(grpc_tcp_listener)));
+    sp->next = nullptr;
+    if (s->head == nullptr) {
+      s->head = sp;
+    } else {
+      s->tail->next = sp;
+    }
+    s->tail = sp;
+    sp->server = s;
+    sp->fd = fd;
+    sp->emfd = grpc_fd_create(fd, name, true);
+    memcpy(&sp->addr, addr, sizeof(grpc_resolved_address));
+    sp->port = port;
+    sp->port_index = port_index;
+    sp->fd_index = fd_index;
+    sp->is_sibling = 0;
+    sp->sibling = nullptr;
+    GPR_ASSERT(sp->emfd);
+    gpr_mu_unlock(&s->mu);
+    gpr_free(addr_str);
+    gpr_free(name);
+  }
+
+  *listener = sp;
+  return err;
+}
+
+/* If successful, add a listener to s for addr, set *dsmode for the socket, and
+   return the *listener. */
+grpc_error* grpc_tcp_server_add_addr(grpc_tcp_server* s,
+                                     const grpc_resolved_address* addr,
+                                     unsigned port_index, unsigned fd_index,
+                                     grpc_dualstack_mode* dsmode,
+                                     grpc_tcp_listener** listener) {
+  grpc_resolved_address addr4_copy;
+  int fd;
+  grpc_error* err =
+      grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, dsmode, &fd);
+  if (err != GRPC_ERROR_NONE) {
+    return err;
+  }
+  if (*dsmode == GRPC_DSMODE_IPV4 &&
+      grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) {
+    addr = &addr4_copy;
+  }
+  return add_socket_to_server(s, fd, addr, port_index, fd_index, listener);
+}
+
+/* Prepare a recently-created socket for listening. */
+grpc_error* grpc_tcp_server_prepare_socket(grpc_tcp_server* s, int fd,
+                                           const grpc_resolved_address* addr,
+                                           bool so_reuseport, int* port) {
+  grpc_resolved_address sockname_temp;
+  grpc_error* err = GRPC_ERROR_NONE;
+
+  GPR_ASSERT(fd >= 0);
+
+  if (so_reuseport && !grpc_is_unix_socket(addr)) {
+    err = grpc_set_socket_reuse_port(fd, 1);
+    if (err != GRPC_ERROR_NONE) goto error;
+  }
+
+  err = grpc_set_socket_nonblocking(fd, 1);
+  if (err != GRPC_ERROR_NONE) goto error;
+  err = grpc_set_socket_cloexec(fd, 1);
+  if (err != GRPC_ERROR_NONE) goto error;
+  if (!grpc_is_unix_socket(addr)) {
+    err = grpc_set_socket_low_latency(fd, 1);
+    if (err != GRPC_ERROR_NONE) goto error;
+    err = grpc_set_socket_reuse_addr(fd, 1);
+    if (err != GRPC_ERROR_NONE) goto error;
+    err = grpc_set_socket_tcp_user_timeout(fd, s->channel_args,
+                                           false /* is_client */);
+    if (err != GRPC_ERROR_NONE) goto error;
+  }
+  err = grpc_set_socket_no_sigpipe_if_possible(fd);
+  if (err != GRPC_ERROR_NONE) goto error;
+
+  if (s->channel_args) {
+    for (size_t i = 0; i < s->channel_args->num_args; i++) {
+      if (0 == strcmp(s->channel_args->args[i].key, GRPC_ARG_SOCKET_MUTATOR)) {
+        GPR_ASSERT(s->channel_args->args[i].type == GRPC_ARG_POINTER);
+        grpc_socket_mutator* mutator = static_cast<grpc_socket_mutator*>(
+            s->channel_args->args[i].value.pointer.p);
+        err = grpc_set_socket_with_mutator(fd, mutator);
+        if (err != GRPC_ERROR_NONE) goto error;
+      }
+    }
+  }
+
+  if (bind(fd, reinterpret_cast<grpc_sockaddr*>(const_cast<char*>(addr->addr)),
+           addr->len) < 0) {
+    err = GRPC_OS_ERROR(errno, "bind");
+    goto error;
+  }
+
+  if (listen(fd, get_max_accept_queue_size()) < 0) {
+    err = GRPC_OS_ERROR(errno, "listen");
+    goto error;
+  }
+
+  sockname_temp.len = static_cast<socklen_t>(sizeof(struct sockaddr_storage));
+
+  if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr),
+                  &sockname_temp.len) < 0) {
+    err = GRPC_OS_ERROR(errno, "getsockname");
+    goto error;
+  }
+
+  *port = grpc_sockaddr_get_port(&sockname_temp);
+  return GRPC_ERROR_NONE;
+
+error:
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
+  if (fd >= 0) {
+    close(fd);
+  }
+  grpc_error* ret =
+      grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Unable to configure socket", &err, 1),
+                         GRPC_ERROR_INT_FD, fd);
+  GRPC_ERROR_UNREF(err);
+  return ret;
+}
+
+#endif /* GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc
new file mode 100644
index 0000000..7fd86c5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc
@@ -0,0 +1,184 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_HAVE_IFADDRS
+
+#include "src/core/lib/iomgr/tcp_server_utils_posix.h"
+
+#include <errno.h>
+#include <ifaddrs.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+/* Return the listener in s with address addr or NULL. */
+static grpc_tcp_listener* find_listener_with_addr(grpc_tcp_server* s,
+                                                  grpc_resolved_address* addr) {
+  grpc_tcp_listener* l;
+  gpr_mu_lock(&s->mu);
+  for (l = s->head; l != nullptr; l = l->next) {
+    if (l->addr.len != addr->len) {
+      continue;
+    }
+    if (memcmp(l->addr.addr, addr->addr, addr->len) == 0) {
+      break;
+    }
+  }
+  gpr_mu_unlock(&s->mu);
+  return l;
+}
+
+/* Bind to "::" to get a port number not used by any address. */
+static grpc_error* get_unused_port(int* port) {
+  grpc_resolved_address wild;
+  grpc_sockaddr_make_wildcard6(0, &wild);
+  grpc_dualstack_mode dsmode;
+  int fd;
+  grpc_error* err =
+      grpc_create_dualstack_socket(&wild, SOCK_STREAM, 0, &dsmode, &fd);
+  if (err != GRPC_ERROR_NONE) {
+    return err;
+  }
+  if (dsmode == GRPC_DSMODE_IPV4) {
+    grpc_sockaddr_make_wildcard4(0, &wild);
+  }
+  if (bind(fd, reinterpret_cast<const grpc_sockaddr*>(wild.addr), wild.len) !=
+      0) {
+    err = GRPC_OS_ERROR(errno, "bind");
+    close(fd);
+    return err;
+  }
+  if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(wild.addr), &wild.len) !=
+      0) {
+    err = GRPC_OS_ERROR(errno, "getsockname");
+    close(fd);
+    return err;
+  }
+  close(fd);
+  *port = grpc_sockaddr_get_port(&wild);
+  return *port <= 0 ? GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad port")
+                    : GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_tcp_server_add_all_local_addrs(grpc_tcp_server* s,
+                                                unsigned port_index,
+                                                int requested_port,
+                                                int* out_port) {
+  struct ifaddrs* ifa = nullptr;
+  struct ifaddrs* ifa_it;
+  unsigned fd_index = 0;
+  grpc_tcp_listener* sp = nullptr;
+  grpc_error* err = GRPC_ERROR_NONE;
+  if (requested_port == 0) {
+    /* Note: There could be a race where some local addrs can listen on the
+       selected port and some can't. The sane way to handle this would be to
+       retry by recreating the whole grpc_tcp_server. Backing out individual
+       listeners and orphaning the FDs looks like too much trouble. */
+    if ((err = get_unused_port(&requested_port)) != GRPC_ERROR_NONE) {
+      return err;
+    } else if (requested_port <= 0) {
+      return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad get_unused_port()");
+    }
+    gpr_log(GPR_DEBUG, "Picked unused port %d", requested_port);
+  }
+  if (getifaddrs(&ifa) != 0 || ifa == nullptr) {
+    return GRPC_OS_ERROR(errno, "getifaddrs");
+  }
+  for (ifa_it = ifa; ifa_it != nullptr; ifa_it = ifa_it->ifa_next) {
+    grpc_resolved_address addr;
+    char* addr_str = nullptr;
+    grpc_dualstack_mode dsmode;
+    grpc_tcp_listener* new_sp = nullptr;
+    const char* ifa_name = (ifa_it->ifa_name ? ifa_it->ifa_name : "<unknown>");
+    if (ifa_it->ifa_addr == nullptr) {
+      continue;
+    } else if (ifa_it->ifa_addr->sa_family == AF_INET) {
+      addr.len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in));
+    } else if (ifa_it->ifa_addr->sa_family == AF_INET6) {
+      addr.len = static_cast<socklen_t>(sizeof(grpc_sockaddr_in6));
+    } else {
+      continue;
+    }
+    memcpy(addr.addr, ifa_it->ifa_addr, addr.len);
+    if (!grpc_sockaddr_set_port(&addr, requested_port)) {
+      /* Should never happen, because we check sa_family above. */
+      err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set port");
+      break;
+    }
+    if (grpc_sockaddr_to_string(&addr_str, &addr, 0) < 0) {
+      addr_str = gpr_strdup("<error>");
+    }
+    gpr_log(GPR_DEBUG,
+            "Adding local addr from interface %s flags 0x%x to server: %s",
+            ifa_name, ifa_it->ifa_flags, addr_str);
+    /* We could have multiple interfaces with the same address (e.g., bonding),
+       so look for duplicates. */
+    if (find_listener_with_addr(s, &addr) != nullptr) {
+      gpr_log(GPR_DEBUG, "Skipping duplicate addr %s on interface %s", addr_str,
+              ifa_name);
+      gpr_free(addr_str);
+      continue;
+    }
+    if ((err = grpc_tcp_server_add_addr(s, &addr, port_index, fd_index, &dsmode,
+                                        &new_sp)) != GRPC_ERROR_NONE) {
+      char* err_str = nullptr;
+      grpc_error* root_err;
+      if (gpr_asprintf(&err_str, "Failed to add listener: %s", addr_str) < 0) {
+        err_str = gpr_strdup("Failed to add listener");
+      }
+      root_err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_str);
+      gpr_free(err_str);
+      gpr_free(addr_str);
+      err = grpc_error_add_child(root_err, err);
+      break;
+    } else {
+      GPR_ASSERT(requested_port == new_sp->port);
+      ++fd_index;
+      if (sp != nullptr) {
+        new_sp->is_sibling = 1;
+        sp->sibling = new_sp;
+      }
+      sp = new_sp;
+    }
+    gpr_free(addr_str);
+  }
+  freeifaddrs(ifa);
+  if (err != GRPC_ERROR_NONE) {
+    return err;
+  } else if (sp == nullptr) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No local addresses");
+  } else {
+    *out_port = sp->port;
+    return GRPC_ERROR_NONE;
+  }
+}
+
+bool grpc_tcp_server_have_ifaddrs(void) { return true; }
+
+#endif /* GRPC_HAVE_IFADDRS */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc
new file mode 100644
index 0000000..86ee14f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#if defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS)
+
+#include "src/core/lib/iomgr/tcp_server_utils_posix.h"
+
+grpc_error* grpc_tcp_server_add_all_local_addrs(grpc_tcp_server* s,
+                                                unsigned port_index,
+                                                int requested_port,
+                                                int* out_port) {
+  return GRPC_ERROR_CREATE_FROM_STATIC_STRING("no ifaddrs available");
+}
+
+bool grpc_tcp_server_have_ifaddrs(void) { return false; }
+
+#endif /* defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS) */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_windows.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_windows.cc
new file mode 100644
index 0000000..b01afdc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_windows.cc
@@ -0,0 +1,560 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include <inttypes.h>
+#include <io.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_windows.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/pollset_windows.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/iomgr/tcp_windows.h"
+
+#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
+
+/* one listening port */
+typedef struct grpc_tcp_listener grpc_tcp_listener;
+struct grpc_tcp_listener {
+  /* This seemingly magic number comes from AcceptEx's documentation. each
+     address buffer needs to have at least 16 more bytes at their end. */
+  uint8_t addresses[(sizeof(grpc_sockaddr_in6) + 16) * 2];
+  /* This will hold the socket for the next accept. */
+  SOCKET new_socket;
+  /* The listener winsocket. */
+  grpc_winsocket* socket;
+  /* The actual TCP port number. */
+  int port;
+  unsigned port_index;
+  grpc_tcp_server* server;
+  /* The cached AcceptEx for that port. */
+  LPFN_ACCEPTEX AcceptEx;
+  int shutting_down;
+  int outstanding_calls;
+  /* closure for socket notification of accept being ready */
+  grpc_closure on_accept;
+  /* linked list */
+  struct grpc_tcp_listener* next;
+};
+
+/* the overall server */
+struct grpc_tcp_server {
+  gpr_refcount refs;
+  /* Called whenever accept() succeeds on a server port. */
+  grpc_tcp_server_cb on_accept_cb;
+  void* on_accept_cb_arg;
+
+  gpr_mu mu;
+
+  /* active port count: how many ports are actually still listening */
+  int active_ports;
+
+  /* linked list of server ports */
+  grpc_tcp_listener* head;
+  grpc_tcp_listener* tail;
+
+  /* List of closures passed to shutdown_starting_add(). */
+  grpc_closure_list shutdown_starting;
+
+  /* shutdown callback */
+  grpc_closure* shutdown_complete;
+
+  grpc_channel_args* channel_args;
+};
+
+/* Public function. Allocates the proper data structures to hold a
+   grpc_tcp_server. */
+static grpc_error* tcp_server_create(grpc_closure* shutdown_complete,
+                                     const grpc_channel_args* args,
+                                     grpc_tcp_server** server) {
+  grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server));
+  s->channel_args = grpc_channel_args_copy(args);
+  gpr_ref_init(&s->refs, 1);
+  gpr_mu_init(&s->mu);
+  s->active_ports = 0;
+  s->on_accept_cb = NULL;
+  s->on_accept_cb_arg = NULL;
+  s->head = NULL;
+  s->tail = NULL;
+  s->shutdown_starting.head = NULL;
+  s->shutdown_starting.tail = NULL;
+  s->shutdown_complete = shutdown_complete;
+  *server = s;
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_server(void* arg, grpc_error* error) {
+  grpc_tcp_server* s = (grpc_tcp_server*)arg;
+
+  /* Now that the accepts have been aborted, we can destroy the sockets.
+     The IOCP won't get notified on these, so we can flag them as already
+     closed by the system. */
+  while (s->head) {
+    grpc_tcp_listener* sp = s->head;
+    s->head = sp->next;
+    sp->next = NULL;
+    grpc_winsocket_destroy(sp->socket);
+    gpr_free(sp);
+  }
+  grpc_channel_args_destroy(s->channel_args);
+  gpr_mu_destroy(&s->mu);
+  gpr_free(s);
+}
+
+static void finish_shutdown_locked(grpc_tcp_server* s) {
+  if (s->shutdown_complete != NULL) {
+    GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE);
+  }
+
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_CREATE(destroy_server, s, grpc_schedule_on_exec_ctx),
+      GRPC_ERROR_NONE);
+}
+
+static grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) {
+  gpr_ref_non_zero(&s->refs);
+  return s;
+}
+
+static void tcp_server_shutdown_starting_add(grpc_tcp_server* s,
+                                             grpc_closure* shutdown_starting) {
+  gpr_mu_lock(&s->mu);
+  grpc_closure_list_append(&s->shutdown_starting, shutdown_starting,
+                           GRPC_ERROR_NONE);
+  gpr_mu_unlock(&s->mu);
+}
+
+static void tcp_server_destroy(grpc_tcp_server* s) {
+  grpc_tcp_listener* sp;
+  gpr_mu_lock(&s->mu);
+
+  /* First, shutdown all fd's. This will queue abortion calls for all
+     of the pending accepts due to the normal operation mechanism. */
+  if (s->active_ports == 0) {
+    finish_shutdown_locked(s);
+  } else {
+    for (sp = s->head; sp; sp = sp->next) {
+      sp->shutting_down = 1;
+      grpc_winsocket_shutdown(sp->socket);
+    }
+  }
+  gpr_mu_unlock(&s->mu);
+}
+
+static void tcp_server_unref(grpc_tcp_server* s) {
+  if (gpr_unref(&s->refs)) {
+    grpc_tcp_server_shutdown_listeners(s);
+    gpr_mu_lock(&s->mu);
+    GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting);
+    gpr_mu_unlock(&s->mu);
+    tcp_server_destroy(s);
+  }
+}
+
+/* Prepare (bind) a recently-created socket for listening. */
+static grpc_error* prepare_socket(SOCKET sock,
+                                  const grpc_resolved_address* addr,
+                                  int* port) {
+  grpc_resolved_address sockname_temp;
+  grpc_error* error = GRPC_ERROR_NONE;
+  int sockname_temp_len;
+
+  error = grpc_tcp_prepare_socket(sock);
+  if (error != GRPC_ERROR_NONE) {
+    goto failure;
+  }
+
+  if (bind(sock, (const grpc_sockaddr*)addr->addr, (int)addr->len) ==
+      SOCKET_ERROR) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "bind");
+    goto failure;
+  }
+
+  if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "listen");
+    goto failure;
+  }
+
+  sockname_temp_len = sizeof(struct sockaddr_storage);
+  if (getsockname(sock, (grpc_sockaddr*)sockname_temp.addr,
+                  &sockname_temp_len) == SOCKET_ERROR) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname");
+    goto failure;
+  }
+  sockname_temp.len = (size_t)sockname_temp_len;
+
+  *port = grpc_sockaddr_get_port(&sockname_temp);
+  return GRPC_ERROR_NONE;
+
+failure:
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  char* tgtaddr = grpc_sockaddr_to_uri(addr);
+  grpc_error_set_int(
+      grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                             "Failed to prepare server socket", &error, 1),
+                         GRPC_ERROR_STR_TARGET_ADDRESS,
+                         grpc_slice_from_copied_string(tgtaddr)),
+      GRPC_ERROR_INT_FD, (intptr_t)sock);
+  gpr_free(tgtaddr);
+  GRPC_ERROR_UNREF(error);
+  if (sock != INVALID_SOCKET) closesocket(sock);
+  return error;
+}
+
+static void decrement_active_ports_and_notify_locked(grpc_tcp_listener* sp) {
+  sp->shutting_down = 0;
+  GPR_ASSERT(sp->server->active_ports > 0);
+  if (0 == --sp->server->active_ports) {
+    finish_shutdown_locked(sp->server);
+  }
+}
+
+/* In order to do an async accept, we need to create a socket first which
+   will be the one assigned to the new incoming connection. */
+static grpc_error* start_accept_locked(grpc_tcp_listener* port) {
+  SOCKET sock = INVALID_SOCKET;
+  BOOL success;
+  DWORD addrlen = sizeof(grpc_sockaddr_in6) + 16;
+  DWORD bytes_received = 0;
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  if (port->shutting_down) {
+    return GRPC_ERROR_NONE;
+  }
+
+  sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+                   WSA_FLAG_OVERLAPPED);
+  if (sock == INVALID_SOCKET) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
+    goto failure;
+  }
+
+  error = grpc_tcp_prepare_socket(sock);
+  if (error != GRPC_ERROR_NONE) goto failure;
+
+  /* Start the "accept" asynchronously. */
+  success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0,
+                           addrlen, addrlen, &bytes_received,
+                           &port->socket->read_info.overlapped);
+
+  /* It is possible to get an accept immediately without delay. However, we
+     will still get an IOCP notification for it. So let's just ignore it. */
+  if (!success) {
+    int last_error = WSAGetLastError();
+    if (last_error != ERROR_IO_PENDING) {
+      error = GRPC_WSA_ERROR(last_error, "AcceptEx");
+      goto failure;
+    }
+  }
+
+  /* We're ready to do the accept. Calling grpc_socket_notify_on_read may
+     immediately process an accept that happened in the meantime. */
+  port->new_socket = sock;
+  grpc_socket_notify_on_read(port->socket, &port->on_accept);
+  port->outstanding_calls++;
+  return error;
+
+failure:
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  if (sock != INVALID_SOCKET) closesocket(sock);
+  return error;
+}
+
+/* Event manager callback when reads are ready. */
+static void on_accept(void* arg, grpc_error* error) {
+  grpc_tcp_listener* sp = (grpc_tcp_listener*)arg;
+  SOCKET sock = sp->new_socket;
+  grpc_winsocket_callback_info* info = &sp->socket->read_info;
+  grpc_endpoint* ep = NULL;
+  grpc_resolved_address peer_name;
+  char* peer_name_string;
+  char* fd_name;
+  DWORD transfered_bytes;
+  DWORD flags;
+  BOOL wsa_success;
+  int err;
+
+  gpr_mu_lock(&sp->server->mu);
+
+  peer_name.len = sizeof(struct sockaddr_storage);
+
+  /* The general mechanism for shutting down is to queue abortion calls. While
+     this is necessary in the read/write case, it's useless for the accept
+     case. We only need to adjust the pending callback count */
+  if (error != GRPC_ERROR_NONE) {
+    const char* msg = grpc_error_string(error);
+    gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", msg);
+
+    gpr_mu_unlock(&sp->server->mu);
+    return;
+  }
+
+  /* The IOCP notified us of a completed operation. Let's grab the results,
+     and act accordingly. */
+  transfered_bytes = 0;
+  wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
+                                       &transfered_bytes, FALSE, &flags);
+  if (!wsa_success) {
+    if (!sp->shutting_down) {
+      char* utf8_message = gpr_format_message(WSAGetLastError());
+      gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message);
+      gpr_free(utf8_message);
+    }
+    closesocket(sock);
+  } else {
+    if (!sp->shutting_down) {
+      peer_name_string = NULL;
+      err = setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
+                       (char*)&sp->socket->socket, sizeof(sp->socket->socket));
+      if (err) {
+        char* utf8_message = gpr_format_message(WSAGetLastError());
+        gpr_log(GPR_ERROR, "setsockopt error: %s", utf8_message);
+        gpr_free(utf8_message);
+      }
+      int peer_name_len = (int)peer_name.len;
+      err = getpeername(sock, (grpc_sockaddr*)peer_name.addr, &peer_name_len);
+      peer_name.len = (size_t)peer_name_len;
+      if (!err) {
+        peer_name_string = grpc_sockaddr_to_uri(&peer_name);
+      } else {
+        char* utf8_message = gpr_format_message(WSAGetLastError());
+        gpr_log(GPR_ERROR, "getpeername error: %s", utf8_message);
+        gpr_free(utf8_message);
+      }
+      gpr_asprintf(&fd_name, "tcp_server:%s", peer_name_string);
+      ep = grpc_tcp_create(grpc_winsocket_create(sock, fd_name),
+                           sp->server->channel_args, peer_name_string);
+      gpr_free(fd_name);
+      gpr_free(peer_name_string);
+    } else {
+      closesocket(sock);
+    }
+  }
+
+  /* The only time we should call our callback, is where we successfully
+     managed to accept a connection, and created an endpoint. */
+  if (ep) {
+    // Create acceptor.
+    grpc_tcp_server_acceptor* acceptor =
+        (grpc_tcp_server_acceptor*)gpr_malloc(sizeof(*acceptor));
+    acceptor->from_server = sp->server;
+    acceptor->port_index = sp->port_index;
+    acceptor->fd_index = 0;
+    sp->server->on_accept_cb(sp->server->on_accept_cb_arg, ep, NULL, acceptor);
+  }
+  /* As we were notified from the IOCP of one and exactly one accept,
+     the former socked we created has now either been destroy or assigned
+     to the new connection. We need to create a new one for the next
+     connection. */
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("start_accept", start_accept_locked(sp)));
+  if (0 == --sp->outstanding_calls) {
+    decrement_active_ports_and_notify_locked(sp);
+  }
+  gpr_mu_unlock(&sp->server->mu);
+}
+
+static grpc_error* add_socket_to_server(grpc_tcp_server* s, SOCKET sock,
+                                        const grpc_resolved_address* addr,
+                                        unsigned port_index,
+                                        grpc_tcp_listener** listener) {
+  grpc_tcp_listener* sp = NULL;
+  int port = -1;
+  int status;
+  GUID guid = WSAID_ACCEPTEX;
+  DWORD ioctl_num_bytes;
+  LPFN_ACCEPTEX AcceptEx;
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  /* We need to grab the AcceptEx pointer for that port, as it may be
+     interface-dependent. We'll cache it to avoid doing that again. */
+  status =
+      WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
+               &AcceptEx, sizeof(AcceptEx), &ioctl_num_bytes, NULL, NULL);
+
+  if (status != 0) {
+    char* utf8_message = gpr_format_message(WSAGetLastError());
+    gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message);
+    gpr_free(utf8_message);
+    closesocket(sock);
+    return NULL;
+  }
+
+  error = prepare_socket(sock, addr, &port);
+  if (error != GRPC_ERROR_NONE) {
+    return error;
+  }
+
+  GPR_ASSERT(port >= 0);
+  gpr_mu_lock(&s->mu);
+  GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
+  sp = (grpc_tcp_listener*)gpr_malloc(sizeof(grpc_tcp_listener));
+  sp->next = NULL;
+  if (s->head == NULL) {
+    s->head = sp;
+  } else {
+    s->tail->next = sp;
+  }
+  s->tail = sp;
+  sp->server = s;
+  sp->socket = grpc_winsocket_create(sock, "listener");
+  sp->shutting_down = 0;
+  sp->outstanding_calls = 0;
+  sp->AcceptEx = AcceptEx;
+  sp->new_socket = INVALID_SOCKET;
+  sp->port = port;
+  sp->port_index = port_index;
+  GRPC_CLOSURE_INIT(&sp->on_accept, on_accept, sp, grpc_schedule_on_exec_ctx);
+  GPR_ASSERT(sp->socket);
+  gpr_mu_unlock(&s->mu);
+  *listener = sp;
+
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* tcp_server_add_port(grpc_tcp_server* s,
+                                       const grpc_resolved_address* addr,
+                                       int* port) {
+  grpc_tcp_listener* sp = NULL;
+  SOCKET sock;
+  grpc_resolved_address addr6_v4mapped;
+  grpc_resolved_address wildcard;
+  grpc_resolved_address* allocated_addr = NULL;
+  grpc_resolved_address sockname_temp;
+  unsigned port_index = 0;
+  grpc_error* error = GRPC_ERROR_NONE;
+
+  if (s->tail != NULL) {
+    port_index = s->tail->port_index + 1;
+  }
+
+  /* Check if this is a wildcard port, and if so, try to keep the port the same
+     as some previously created listener. */
+  if (grpc_sockaddr_get_port(addr) == 0) {
+    for (sp = s->head; sp; sp = sp->next) {
+      int sockname_temp_len = sizeof(struct sockaddr_storage);
+      if (0 == getsockname(sp->socket->socket,
+                           (grpc_sockaddr*)sockname_temp.addr,
+                           &sockname_temp_len)) {
+        sockname_temp.len = (size_t)sockname_temp_len;
+        *port = grpc_sockaddr_get_port(&sockname_temp);
+        if (*port > 0) {
+          allocated_addr =
+              (grpc_resolved_address*)gpr_malloc(sizeof(grpc_resolved_address));
+          memcpy(allocated_addr, addr, sizeof(grpc_resolved_address));
+          grpc_sockaddr_set_port(allocated_addr, *port);
+          addr = allocated_addr;
+          break;
+        }
+      }
+    }
+  }
+
+  if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+    addr = &addr6_v4mapped;
+  }
+
+  /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
+  if (grpc_sockaddr_is_wildcard(addr, port)) {
+    grpc_sockaddr_make_wildcard6(*port, &wildcard);
+
+    addr = &wildcard;
+  }
+
+  sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+                   WSA_FLAG_OVERLAPPED);
+  if (sock == INVALID_SOCKET) {
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
+    goto done;
+  }
+
+  error = add_socket_to_server(s, sock, addr, port_index, &sp);
+
+done:
+  gpr_free(allocated_addr);
+
+  if (error != GRPC_ERROR_NONE) {
+    grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+        "Failed to add port to server", &error, 1);
+    GRPC_ERROR_UNREF(error);
+    error = error_out;
+    *port = -1;
+  } else {
+    GPR_ASSERT(sp != NULL);
+    *port = sp->port;
+  }
+  return error;
+}
+
+static void tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollset,
+                             size_t pollset_count,
+                             grpc_tcp_server_cb on_accept_cb,
+                             void* on_accept_cb_arg) {
+  grpc_tcp_listener* sp;
+  GPR_ASSERT(on_accept_cb);
+  gpr_mu_lock(&s->mu);
+  GPR_ASSERT(!s->on_accept_cb);
+  GPR_ASSERT(s->active_ports == 0);
+  s->on_accept_cb = on_accept_cb;
+  s->on_accept_cb_arg = on_accept_cb_arg;
+  for (sp = s->head; sp; sp = sp->next) {
+    GPR_ASSERT(GRPC_LOG_IF_ERROR("start_accept", start_accept_locked(sp)));
+    s->active_ports++;
+  }
+  gpr_mu_unlock(&s->mu);
+}
+
+static unsigned tcp_server_port_fd_count(grpc_tcp_server* s,
+                                         unsigned port_index) {
+  return 0;
+}
+
+static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index,
+                              unsigned fd_index) {
+  return -1;
+}
+
+static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {}
+
+grpc_tcp_server_vtable grpc_windows_tcp_server_vtable = {
+    tcp_server_create,
+    tcp_server_start,
+    tcp_server_add_port,
+    tcp_server_port_fd_count,
+    tcp_server_port_fd,
+    tcp_server_ref,
+    tcp_server_shutdown_starting_add,
+    tcp_server_unref,
+    tcp_server_shutdown_listeners};
+#endif /* GRPC_WINSOCK_SOCKET */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_uv.cc b/third_party/grpc/src/core/lib/iomgr/tcp_uv.cc
new file mode 100644
index 0000000..8d0e4a5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_uv.cc
@@ -0,0 +1,420 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_UV
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/network_status_tracker.h"
+#include "src/core/lib/iomgr/resolve_address_custom.h"
+#include "src/core/lib/iomgr/resource_quota.h"
+#include "src/core/lib/iomgr/tcp_custom.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+#include <uv.h>
+
+#define IGNORE_CONST(addr) ((grpc_sockaddr*)(uintptr_t)(addr))
+
+typedef struct uv_socket_t {
+  uv_connect_t connect_req;
+  uv_write_t write_req;
+  uv_shutdown_t shutdown_req;
+  uv_tcp_t* handle;
+  uv_buf_t* write_buffers;
+
+  char* read_buf;
+  size_t read_len;
+
+  bool pending_connection;
+  grpc_custom_socket* accept_socket;
+  grpc_error* accept_error;
+
+  grpc_custom_connect_callback connect_cb;
+  grpc_custom_write_callback write_cb;
+  grpc_custom_read_callback read_cb;
+  grpc_custom_accept_callback accept_cb;
+  grpc_custom_close_callback close_cb;
+
+} uv_socket_t;
+
+static grpc_error* tcp_error_create(const char* desc, int status) {
+  if (status == 0) {
+    return GRPC_ERROR_NONE;
+  }
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc);
+  /* All tcp errors are marked with UNAVAILABLE so that application may
+   * choose to retry. */
+  error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS,
+                             GRPC_STATUS_UNAVAILABLE);
+  return grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR,
+                            grpc_slice_from_static_string(uv_strerror(status)));
+}
+
+static void uv_socket_destroy(grpc_custom_socket* socket) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  gpr_free(uv_socket->handle);
+  gpr_free(uv_socket);
+}
+
+static void alloc_uv_buf(uv_handle_t* handle, size_t suggested_size,
+                         uv_buf_t* buf) {
+  uv_socket_t* uv_socket =
+      (uv_socket_t*)((grpc_custom_socket*)handle->data)->impl;
+  (void)suggested_size;
+  buf->base = uv_socket->read_buf;
+  buf->len = uv_socket->read_len;
+}
+
+static void uv_read_callback(uv_stream_t* stream, ssize_t nread,
+                             const uv_buf_t* buf) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (nread == 0) {
+    // Nothing happened. Wait for the next callback
+    return;
+  }
+  // TODO(murgatroid99): figure out what the return value here means
+  uv_read_stop(stream);
+  if (nread == UV_EOF) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF");
+  } else if (nread < 0) {
+    error = tcp_error_create("TCP Read failed", nread);
+  }
+  grpc_custom_socket* socket = (grpc_custom_socket*)stream->data;
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  uv_socket->read_cb(socket, (size_t)nread, error);
+}
+
+static void uv_close_callback(uv_handle_t* handle) {
+  grpc_custom_socket* socket = (grpc_custom_socket*)handle->data;
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  if (uv_socket->accept_socket) {
+    uv_socket->accept_cb(socket, uv_socket->accept_socket,
+                         GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket closed"));
+  }
+  uv_socket->close_cb(socket);
+}
+
+static void uv_socket_read(grpc_custom_socket* socket, char* buffer,
+                           size_t length, grpc_custom_read_callback read_cb) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  int status;
+  grpc_error* error;
+  uv_socket->read_cb = read_cb;
+  uv_socket->read_buf = buffer;
+  uv_socket->read_len = length;
+  // TODO(murgatroid99): figure out what the return value here means
+  status =
+      uv_read_start((uv_stream_t*)uv_socket->handle, (uv_alloc_cb)alloc_uv_buf,
+                    (uv_read_cb)uv_read_callback);
+  if (status != 0) {
+    error = tcp_error_create("TCP Read failed at start", status);
+    uv_socket->read_cb(socket, 0, error);
+  }
+}
+
+static void uv_write_callback(uv_write_t* req, int status) {
+  grpc_custom_socket* socket = (grpc_custom_socket*)req->data;
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  gpr_free(uv_socket->write_buffers);
+  uv_socket->write_cb(socket, tcp_error_create("TCP Write failed", status));
+}
+
+void uv_socket_write(grpc_custom_socket* socket,
+                     grpc_slice_buffer* write_slices,
+                     grpc_custom_write_callback write_cb) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  uv_socket->write_cb = write_cb;
+  uv_buf_t* uv_buffers;
+  uv_write_t* write_req;
+
+  uv_buffers = (uv_buf_t*)gpr_malloc(sizeof(uv_buf_t) * write_slices->count);
+  for (size_t i = 0; i < write_slices->count; i++) {
+    uv_buffers[i].base = (char*)GRPC_SLICE_START_PTR(write_slices->slices[i]);
+    uv_buffers[i].len = GRPC_SLICE_LENGTH(write_slices->slices[i]);
+  }
+
+  uv_socket->write_buffers = uv_buffers;
+  write_req = &uv_socket->write_req;
+  write_req->data = socket;
+  // TODO(murgatroid99): figure out what the return value here means
+  uv_write(write_req, (uv_stream_t*)uv_socket->handle, uv_buffers,
+           write_slices->count, uv_write_callback);
+}
+
+static void shutdown_callback(uv_shutdown_t* req, int status) {}
+
+static void uv_socket_shutdown(grpc_custom_socket* socket) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  uv_shutdown_t* req = &uv_socket->shutdown_req;
+  uv_shutdown(req, (uv_stream_t*)uv_socket->handle, shutdown_callback);
+}
+
+static void uv_socket_close(grpc_custom_socket* socket,
+                            grpc_custom_close_callback close_cb) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  uv_socket->close_cb = close_cb;
+  uv_close((uv_handle_t*)uv_socket->handle, uv_close_callback);
+}
+
+static grpc_error* uv_socket_init_helper(uv_socket_t* uv_socket, int domain) {
+  uv_tcp_t* tcp = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t));
+  uv_socket->handle = tcp;
+  int status = uv_tcp_init_ex(uv_default_loop(), tcp, (unsigned int)domain);
+  if (status != 0) {
+    return tcp_error_create("Failed to initialize UV tcp handle", status);
+  }
+#if defined(GPR_LINUX) && defined(SO_REUSEPORT)
+  if (domain == AF_INET || domain == AF_INET6) {
+    int enable = 1;
+    int fd;
+    uv_fileno((uv_handle_t*)tcp, &fd);
+    // TODO Handle error here.
+    setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable));
+  }
+#endif
+  uv_socket->write_buffers = nullptr;
+  uv_socket->read_len = 0;
+  uv_tcp_nodelay(uv_socket->handle, 1);
+  // Node uses a garbage collector to call destructors, so we don't
+  // want to hold the uv loop open with active gRPC objects.
+  uv_unref((uv_handle_t*)uv_socket->handle);
+  uv_socket->pending_connection = false;
+  uv_socket->accept_socket = nullptr;
+  uv_socket->accept_error = GRPC_ERROR_NONE;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* uv_socket_init(grpc_custom_socket* socket, int domain) {
+  uv_socket_t* uv_socket = (uv_socket_t*)gpr_malloc(sizeof(uv_socket_t));
+  grpc_error* error = uv_socket_init_helper(uv_socket, domain);
+  if (error != GRPC_ERROR_NONE) {
+    return error;
+  }
+  uv_socket->handle->data = socket;
+  socket->impl = uv_socket;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* uv_socket_getpeername(grpc_custom_socket* socket,
+                                         const grpc_sockaddr* addr,
+                                         int* addr_len) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  int err = uv_tcp_getpeername(uv_socket->handle,
+                               (struct sockaddr*)IGNORE_CONST(addr), addr_len);
+  return tcp_error_create("getpeername failed", err);
+}
+
+static grpc_error* uv_socket_getsockname(grpc_custom_socket* socket,
+                                         const grpc_sockaddr* addr,
+                                         int* addr_len) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  int err = uv_tcp_getsockname(uv_socket->handle,
+                               (struct sockaddr*)IGNORE_CONST(addr), addr_len);
+  return tcp_error_create("getsockname failed", err);
+}
+
+static void accept_new_connection(grpc_custom_socket* socket) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  if (!uv_socket->pending_connection || !uv_socket->accept_socket) {
+    return;
+  }
+  grpc_custom_socket* new_socket = uv_socket->accept_socket;
+  grpc_error* error = uv_socket->accept_error;
+  uv_socket->accept_socket = nullptr;
+  uv_socket->accept_error = GRPC_ERROR_NONE;
+  uv_socket->pending_connection = false;
+  if (uv_socket->accept_error != GRPC_ERROR_NONE) {
+    uv_stream_t dummy_handle;
+    uv_accept((uv_stream_t*)uv_socket->handle, &dummy_handle);
+    uv_socket->accept_cb(socket, new_socket, error);
+  } else {
+    uv_socket_t* uv_new_socket = (uv_socket_t*)gpr_malloc(sizeof(uv_socket_t));
+    uv_socket_init_helper(uv_new_socket, AF_UNSPEC);
+    // UV documentation says this is guaranteed to succeed
+    GPR_ASSERT(uv_accept((uv_stream_t*)uv_socket->handle,
+                         (uv_stream_t*)uv_new_socket->handle) == 0);
+    new_socket->impl = uv_new_socket;
+    uv_new_socket->handle->data = new_socket;
+    uv_socket->accept_cb(socket, new_socket, error);
+  }
+}
+
+static void uv_on_connect(uv_stream_t* server, int status) {
+  grpc_custom_socket* socket = (grpc_custom_socket*)server->data;
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  GPR_ASSERT(!uv_socket->pending_connection);
+  uv_socket->pending_connection = true;
+  if (status < 0) {
+    switch (status) {
+      case UV_EINTR:
+      case UV_EAGAIN:
+        return;
+      default:
+        uv_socket->accept_error = tcp_error_create("accept failed", status);
+    }
+  }
+  accept_new_connection(socket);
+}
+
+void uv_socket_accept(grpc_custom_socket* socket,
+                      grpc_custom_socket* new_socket,
+                      grpc_custom_accept_callback accept_cb) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  uv_socket->accept_cb = accept_cb;
+  GPR_ASSERT(uv_socket->accept_socket == nullptr);
+  uv_socket->accept_socket = new_socket;
+  accept_new_connection(socket);
+}
+
+static grpc_error* uv_socket_bind(grpc_custom_socket* socket,
+                                  const grpc_sockaddr* addr, size_t len,
+                                  int flags) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  int status =
+      uv_tcp_bind((uv_tcp_t*)uv_socket->handle, (struct sockaddr*)addr, 0);
+  return tcp_error_create("Failed to bind to port", status);
+}
+
+static grpc_error* uv_socket_listen(grpc_custom_socket* socket) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  int status =
+      uv_listen((uv_stream_t*)uv_socket->handle, SOMAXCONN, uv_on_connect);
+  return tcp_error_create("Failed to listen to port", status);
+}
+
+static void uv_tc_on_connect(uv_connect_t* req, int status) {
+  grpc_custom_socket* socket = (grpc_custom_socket*)req->data;
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  grpc_error* error;
+  if (status == UV_ECANCELED) {
+    // This should only happen if the handle is already closed
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timeout occurred");
+  } else {
+    error = tcp_error_create("Failed to connect to remote host", status);
+  }
+  uv_socket->connect_cb(socket, error);
+}
+
+static void uv_socket_connect(grpc_custom_socket* socket,
+                              const grpc_sockaddr* addr, size_t len,
+                              grpc_custom_connect_callback connect_cb) {
+  uv_socket_t* uv_socket = (uv_socket_t*)socket->impl;
+  uv_socket->connect_cb = connect_cb;
+  uv_socket->connect_req.data = socket;
+  int status = uv_tcp_connect(&uv_socket->connect_req, uv_socket->handle,
+                              (struct sockaddr*)addr, uv_tc_on_connect);
+  if (status != 0) {
+    // The callback will not be called
+    uv_socket->connect_cb(socket, tcp_error_create("connect failed", status));
+  }
+}
+
+static grpc_resolved_addresses* handle_addrinfo_result(
+    struct addrinfo* result) {
+  struct addrinfo* resp;
+  size_t i;
+  grpc_resolved_addresses* addresses =
+      (grpc_resolved_addresses*)gpr_malloc(sizeof(grpc_resolved_addresses));
+  addresses->naddrs = 0;
+  for (resp = result; resp != nullptr; resp = resp->ai_next) {
+    addresses->naddrs++;
+  }
+  addresses->addrs = (grpc_resolved_address*)gpr_malloc(
+      sizeof(grpc_resolved_address) * addresses->naddrs);
+  for (resp = result, i = 0; resp != nullptr; resp = resp->ai_next, i++) {
+    memcpy(&addresses->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
+    addresses->addrs[i].len = resp->ai_addrlen;
+  }
+  // addrinfo objects are allocated by libuv (e.g. in uv_getaddrinfo)
+  // and not by gpr_malloc
+  uv_freeaddrinfo(result);
+  return addresses;
+}
+
+static void uv_resolve_callback(uv_getaddrinfo_t* req, int status,
+                                struct addrinfo* res) {
+  grpc_custom_resolver* r = (grpc_custom_resolver*)req->data;
+  gpr_free(req);
+  grpc_resolved_addresses* result = nullptr;
+  if (status == 0) {
+    result = handle_addrinfo_result(res);
+  }
+  grpc_custom_resolve_callback(r, result,
+                               tcp_error_create("getaddrinfo failed", status));
+}
+
+static grpc_error* uv_resolve(char* host, char* port,
+                              grpc_resolved_addresses** result) {
+  int status;
+  uv_getaddrinfo_t req;
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;     /* ipv4 or ipv6 */
+  hints.ai_socktype = SOCK_STREAM; /* stream socket */
+  hints.ai_flags = AI_PASSIVE;     /* for wildcard IP address */
+  status = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints);
+  if (status != 0) {
+    *result = nullptr;
+  } else {
+    *result = handle_addrinfo_result(req.addrinfo);
+  }
+  return tcp_error_create("getaddrinfo failed", status);
+}
+
+static void uv_resolve_async(grpc_custom_resolver* r, char* host, char* port) {
+  int status;
+  uv_getaddrinfo_t* req =
+      (uv_getaddrinfo_t*)gpr_malloc(sizeof(uv_getaddrinfo_t));
+  req->data = r;
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = GRPC_AF_UNSPEC;     /* ipv4 or ipv6 */
+  hints.ai_socktype = GRPC_SOCK_STREAM; /* stream socket */
+  hints.ai_flags = GRPC_AI_PASSIVE;     /* for wildcard IP address */
+  status = uv_getaddrinfo(uv_default_loop(), req, uv_resolve_callback, host,
+                          port, &hints);
+  if (status != 0) {
+    gpr_free(req);
+    grpc_error* error = tcp_error_create("getaddrinfo failed", status);
+    grpc_custom_resolve_callback(r, NULL, error);
+  }
+}
+
+grpc_custom_resolver_vtable uv_resolver_vtable = {uv_resolve, uv_resolve_async};
+
+grpc_socket_vtable grpc_uv_socket_vtable = {
+    uv_socket_init,     uv_socket_connect,     uv_socket_destroy,
+    uv_socket_shutdown, uv_socket_close,       uv_socket_write,
+    uv_socket_read,     uv_socket_getpeername, uv_socket_getsockname,
+    uv_socket_bind,     uv_socket_listen,      uv_socket_accept};
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_windows.cc b/third_party/grpc/src/core/lib/iomgr/tcp_windows.cc
new file mode 100644
index 0000000..86ee101
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_windows.cc
@@ -0,0 +1,536 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+
+#include <limits.h>
+
+#include "src/core/lib/iomgr/network_status_tracker.h"
+#include "src/core/lib/iomgr/sockaddr_windows.h"
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_windows.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/iomgr/tcp_windows.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+#if defined(__MSYS__) && defined(GPR_ARCH_64)
+/* Nasty workaround for nasty bug when using the 64 bits msys compiler
+   in conjunction with Microsoft Windows headers. */
+#define GRPC_FIONBIO _IOW('f', 126, uint32_t)
+#else
+#define GRPC_FIONBIO FIONBIO
+#endif
+
+extern grpc_core::TraceFlag grpc_tcp_trace;
+
+grpc_error* grpc_tcp_set_non_block(SOCKET sock) {
+  int status;
+  uint32_t param = 1;
+  DWORD ret;
+  status = WSAIoctl(sock, GRPC_FIONBIO, &param, sizeof(param), NULL, 0, &ret,
+                    NULL, NULL);
+  return status == 0
+             ? GRPC_ERROR_NONE
+             : GRPC_WSA_ERROR(WSAGetLastError(), "WSAIoctl(GRPC_FIONBIO)");
+}
+
+static grpc_error* set_dualstack(SOCKET sock) {
+  int status;
+  unsigned long param = 0;
+  status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&param,
+                      sizeof(param));
+  return status == 0
+             ? GRPC_ERROR_NONE
+             : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)");
+}
+
+static grpc_error* enable_loopback_fast_path(SOCKET sock) {
+  int status;
+  uint32_t param = 1;
+  DWORD ret;
+  status = WSAIoctl(sock, /*SIO_LOOPBACK_FAST_PATH==*/_WSAIOW(IOC_VENDOR, 16),
+                    &param, sizeof(param), NULL, 0, &ret, 0, 0);
+  if (status == SOCKET_ERROR) {
+    status = WSAGetLastError();
+  }
+  return status == 0 || status == WSAEOPNOTSUPP
+             ? GRPC_ERROR_NONE
+             : GRPC_WSA_ERROR(status, "WSAIoctl(SIO_LOOPBACK_FAST_PATH)");
+}
+
+grpc_error* grpc_tcp_prepare_socket(SOCKET sock) {
+  grpc_error* err;
+  err = grpc_tcp_set_non_block(sock);
+  if (err != GRPC_ERROR_NONE) return err;
+  err = set_dualstack(sock);
+  if (err != GRPC_ERROR_NONE) return err;
+  err = enable_loopback_fast_path(sock);
+  if (err != GRPC_ERROR_NONE) return err;
+  return GRPC_ERROR_NONE;
+}
+
+typedef struct grpc_tcp {
+  /* This is our C++ class derivation emulation. */
+  grpc_endpoint base;
+  /* The one socket this endpoint is using. */
+  grpc_winsocket* socket;
+  /* Refcounting how many operations are in progress. */
+  gpr_refcount refcount;
+
+  grpc_closure on_read;
+  grpc_closure on_write;
+
+  grpc_closure* read_cb;
+  grpc_closure* write_cb;
+
+  /* garbage after the last read */
+  grpc_slice_buffer last_read_buffer;
+
+  grpc_slice_buffer* write_slices;
+  grpc_slice_buffer* read_slices;
+
+  grpc_resource_user* resource_user;
+
+  /* The IO Completion Port runs from another thread. We need some mechanism
+     to protect ourselves when requesting a shutdown. */
+  gpr_mu mu;
+  int shutting_down;
+  grpc_error* shutdown_error;
+
+  char* peer_string;
+} grpc_tcp;
+
+static void tcp_free(grpc_tcp* tcp) {
+  grpc_winsocket_destroy(tcp->socket);
+  gpr_mu_destroy(&tcp->mu);
+  gpr_free(tcp->peer_string);
+  grpc_slice_buffer_destroy_internal(&tcp->last_read_buffer);
+  grpc_resource_user_unref(tcp->resource_user);
+  if (tcp->shutting_down) GRPC_ERROR_UNREF(tcp->shutdown_error);
+  gpr_free(tcp);
+}
+
+#ifndef NDEBUG
+#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__)
+#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
+static void tcp_unref(grpc_tcp* tcp, const char* reason, const char* file,
+                      int line) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
+            val - 1);
+  }
+  if (gpr_unref(&tcp->refcount)) {
+    tcp_free(tcp);
+  }
+}
+
+static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
+                    int line) {
+  if (grpc_tcp_trace.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
+            val + 1);
+  }
+  gpr_ref(&tcp->refcount);
+}
+#else
+#define TCP_UNREF(tcp, reason) tcp_unref((tcp))
+#define TCP_REF(tcp, reason) tcp_ref((tcp))
+static void tcp_unref(grpc_tcp* tcp) {
+  if (gpr_unref(&tcp->refcount)) {
+    tcp_free(tcp);
+  }
+}
+
+static void tcp_ref(grpc_tcp* tcp) { gpr_ref(&tcp->refcount); }
+#endif
+
+/* Asynchronous callback from the IOCP, or the background thread. */
+static void on_read(void* tcpp, grpc_error* error) {
+  grpc_tcp* tcp = (grpc_tcp*)tcpp;
+  grpc_closure* cb = tcp->read_cb;
+  grpc_winsocket* socket = tcp->socket;
+  grpc_winsocket_callback_info* info = &socket->read_info;
+
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p on_read", tcp);
+  }
+
+  GRPC_ERROR_REF(error);
+
+  if (error == GRPC_ERROR_NONE) {
+    if (info->wsa_error != 0 && !tcp->shutting_down) {
+      char* utf8_message = gpr_format_message(info->wsa_error);
+      error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(utf8_message);
+      gpr_free(utf8_message);
+      grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices);
+    } else {
+      if (info->bytes_transfered != 0 && !tcp->shutting_down) {
+        GPR_ASSERT((size_t)info->bytes_transfered <= tcp->read_slices->length);
+        if (static_cast<size_t>(info->bytes_transfered) !=
+            tcp->read_slices->length) {
+          grpc_slice_buffer_trim_end(
+              tcp->read_slices,
+              tcp->read_slices->length -
+                  static_cast<size_t>(info->bytes_transfered),
+              &tcp->last_read_buffer);
+        }
+        GPR_ASSERT((size_t)info->bytes_transfered == tcp->read_slices->length);
+
+        if (grpc_tcp_trace.enabled()) {
+          size_t i;
+          for (i = 0; i < tcp->read_slices->count; i++) {
+            char* dump = grpc_dump_slice(tcp->read_slices->slices[i],
+                                         GPR_DUMP_HEX | GPR_DUMP_ASCII);
+            gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string,
+                    dump);
+            gpr_free(dump);
+          }
+        }
+      } else {
+        if (grpc_tcp_trace.enabled()) {
+          gpr_log(GPR_INFO, "TCP:%p unref read_slice", tcp);
+        }
+        grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices);
+        error = tcp->shutting_down
+                    ? GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                          "TCP stream shutting down", &tcp->shutdown_error, 1)
+                    : GRPC_ERROR_CREATE_FROM_STATIC_STRING("End of TCP stream");
+      }
+    }
+  }
+
+  tcp->read_cb = NULL;
+  TCP_UNREF(tcp, "read");
+  GRPC_CLOSURE_SCHED(cb, error);
+}
+
+#define DEFAULT_TARGET_READ_SIZE 8192
+#define MAX_WSABUF_COUNT 16
+static void win_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices,
+                     grpc_closure* cb) {
+  grpc_tcp* tcp = (grpc_tcp*)ep;
+  grpc_winsocket* handle = tcp->socket;
+  grpc_winsocket_callback_info* info = &handle->read_info;
+  int status;
+  DWORD bytes_read = 0;
+  DWORD flags = 0;
+  WSABUF buffers[MAX_WSABUF_COUNT];
+  size_t i;
+
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p win_read", tcp);
+  }
+
+  if (tcp->shutting_down) {
+    GRPC_CLOSURE_SCHED(
+        cb, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                "TCP socket is shutting down", &tcp->shutdown_error, 1));
+    return;
+  }
+
+  tcp->read_cb = cb;
+  tcp->read_slices = read_slices;
+  grpc_slice_buffer_reset_and_unref_internal(read_slices);
+  grpc_slice_buffer_swap(read_slices, &tcp->last_read_buffer);
+
+  if (tcp->read_slices->length < DEFAULT_TARGET_READ_SIZE / 2 &&
+      tcp->read_slices->count < MAX_WSABUF_COUNT) {
+    // TODO(jtattermusch): slice should be allocated using resource quota
+    grpc_slice_buffer_add(tcp->read_slices,
+                          GRPC_SLICE_MALLOC(DEFAULT_TARGET_READ_SIZE));
+  }
+
+  GPR_ASSERT(tcp->read_slices->count <= MAX_WSABUF_COUNT);
+  for (i = 0; i < tcp->read_slices->count; i++) {
+    buffers[i].len = (ULONG)GRPC_SLICE_LENGTH(
+        tcp->read_slices->slices[i]);  // we know slice size fits in 32bit.
+    buffers[i].buf = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[i]);
+  }
+
+  TCP_REF(tcp, "read");
+
+  /* First let's try a synchronous, non-blocking read. */
+  status = WSARecv(tcp->socket->socket, buffers, (DWORD)tcp->read_slices->count,
+                   &bytes_read, &flags, NULL, NULL);
+  info->wsa_error = status == 0 ? 0 : WSAGetLastError();
+
+  /* Did we get data immediately ? Yay. */
+  if (info->wsa_error != WSAEWOULDBLOCK) {
+    info->bytes_transfered = bytes_read;
+    GRPC_CLOSURE_SCHED(&tcp->on_read, GRPC_ERROR_NONE);
+    return;
+  }
+
+  /* Otherwise, let's retry, by queuing a read. */
+  memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED));
+  status = WSARecv(tcp->socket->socket, buffers, (DWORD)tcp->read_slices->count,
+                   &bytes_read, &flags, &info->overlapped, NULL);
+
+  if (status != 0) {
+    int wsa_error = WSAGetLastError();
+    if (wsa_error != WSA_IO_PENDING) {
+      info->wsa_error = wsa_error;
+      GRPC_CLOSURE_SCHED(&tcp->on_read,
+                         GRPC_WSA_ERROR(info->wsa_error, "WSARecv"));
+      return;
+    }
+  }
+
+  grpc_socket_notify_on_read(tcp->socket, &tcp->on_read);
+}
+
+/* Asynchronous callback from the IOCP, or the background thread. */
+static void on_write(void* tcpp, grpc_error* error) {
+  grpc_tcp* tcp = (grpc_tcp*)tcpp;
+  grpc_winsocket* handle = tcp->socket;
+  grpc_winsocket_callback_info* info = &handle->write_info;
+  grpc_closure* cb;
+
+  if (grpc_tcp_trace.enabled()) {
+    gpr_log(GPR_INFO, "TCP:%p on_write", tcp);
+  }
+
+  GRPC_ERROR_REF(error);
+
+  gpr_mu_lock(&tcp->mu);
+  cb = tcp->write_cb;
+  tcp->write_cb = NULL;
+  gpr_mu_unlock(&tcp->mu);
+
+  if (error == GRPC_ERROR_NONE) {
+    if (info->wsa_error != 0) {
+      error = GRPC_WSA_ERROR(info->wsa_error, "WSASend");
+    } else {
+      GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length);
+    }
+  }
+
+  TCP_UNREF(tcp, "write");
+  GRPC_CLOSURE_SCHED(cb, error);
+}
+
+/* Initiates a write. */
+static void win_write(grpc_endpoint* ep, grpc_slice_buffer* slices,
+                      grpc_closure* cb, void* arg) {
+  grpc_tcp* tcp = (grpc_tcp*)ep;
+  grpc_winsocket* socket = tcp->socket;
+  grpc_winsocket_callback_info* info = &socket->write_info;
+  unsigned i;
+  DWORD bytes_sent;
+  int status;
+  WSABUF local_buffers[MAX_WSABUF_COUNT];
+  WSABUF* allocated = NULL;
+  WSABUF* buffers = local_buffers;
+  size_t len;
+
+  if (grpc_tcp_trace.enabled()) {
+    size_t i;
+    for (i = 0; i < slices->count; i++) {
+      char* data =
+          grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data);
+      gpr_free(data);
+    }
+  }
+
+  if (tcp->shutting_down) {
+    GRPC_CLOSURE_SCHED(
+        cb, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                "TCP socket is shutting down", &tcp->shutdown_error, 1));
+    return;
+  }
+
+  tcp->write_cb = cb;
+  tcp->write_slices = slices;
+  GPR_ASSERT(tcp->write_slices->count <= UINT_MAX);
+  if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) {
+    buffers = (WSABUF*)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count);
+    allocated = buffers;
+  }
+
+  for (i = 0; i < tcp->write_slices->count; i++) {
+    len = GRPC_SLICE_LENGTH(tcp->write_slices->slices[i]);
+    GPR_ASSERT(len <= ULONG_MAX);
+    buffers[i].len = (ULONG)len;
+    buffers[i].buf = (char*)GRPC_SLICE_START_PTR(tcp->write_slices->slices[i]);
+  }
+
+  /* First, let's try a synchronous, non-blocking write. */
+  status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count,
+                   &bytes_sent, 0, NULL, NULL);
+  info->wsa_error = status == 0 ? 0 : WSAGetLastError();
+
+  /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy
+     connection that has its send queue filled up. But if we don't, then we can
+     avoid doing an async write operation at all. */
+  if (info->wsa_error != WSAEWOULDBLOCK) {
+    grpc_error* error = status == 0
+                            ? GRPC_ERROR_NONE
+                            : GRPC_WSA_ERROR(info->wsa_error, "WSASend");
+    GRPC_CLOSURE_SCHED(cb, error);
+    if (allocated) gpr_free(allocated);
+    return;
+  }
+
+  TCP_REF(tcp, "write");
+
+  /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same
+     operation, this time asynchronously. */
+  memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED));
+  status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count,
+                   &bytes_sent, 0, &socket->write_info.overlapped, NULL);
+  if (allocated) gpr_free(allocated);
+
+  if (status != 0) {
+    int wsa_error = WSAGetLastError();
+    if (wsa_error != WSA_IO_PENDING) {
+      TCP_UNREF(tcp, "write");
+      GRPC_CLOSURE_SCHED(cb, GRPC_WSA_ERROR(wsa_error, "WSASend"));
+      return;
+    }
+  }
+
+  /* As all is now setup, we can now ask for the IOCP notification. It may
+     trigger the callback immediately however, but no matter. */
+  grpc_socket_notify_on_write(socket, &tcp->on_write);
+}
+
+static void win_add_to_pollset(grpc_endpoint* ep, grpc_pollset* ps) {
+  grpc_tcp* tcp;
+  (void)ps;
+  tcp = (grpc_tcp*)ep;
+  grpc_iocp_add_socket(tcp->socket);
+}
+
+static void win_add_to_pollset_set(grpc_endpoint* ep, grpc_pollset_set* pss) {
+  grpc_tcp* tcp;
+  (void)pss;
+  tcp = (grpc_tcp*)ep;
+  grpc_iocp_add_socket(tcp->socket);
+}
+
+static void win_delete_from_pollset_set(grpc_endpoint* ep,
+                                        grpc_pollset_set* pss) {}
+
+/* Initiates a shutdown of the TCP endpoint. This will queue abort callbacks
+   for the potential read and write operations. It is up to the caller to
+   guarantee this isn't called in parallel to a read or write request, so
+   we're not going to protect against these. However the IO Completion Port
+   callback will happen from another thread, so we need to protect against
+   concurrent access of the data structure in that regard. */
+static void win_shutdown(grpc_endpoint* ep, grpc_error* why) {
+  grpc_tcp* tcp = (grpc_tcp*)ep;
+  gpr_mu_lock(&tcp->mu);
+  /* At that point, what may happen is that we're already inside the IOCP
+     callback. See the comments in on_read and on_write. */
+  if (!tcp->shutting_down) {
+    tcp->shutting_down = 1;
+    tcp->shutdown_error = why;
+  } else {
+    GRPC_ERROR_UNREF(why);
+  }
+  grpc_winsocket_shutdown(tcp->socket);
+  gpr_mu_unlock(&tcp->mu);
+  grpc_resource_user_shutdown(tcp->resource_user);
+}
+
+static void win_destroy(grpc_endpoint* ep) {
+  grpc_network_status_unregister_endpoint(ep);
+  grpc_tcp* tcp = (grpc_tcp*)ep;
+  grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer);
+  TCP_UNREF(tcp, "destroy");
+}
+
+static char* win_get_peer(grpc_endpoint* ep) {
+  grpc_tcp* tcp = (grpc_tcp*)ep;
+  return gpr_strdup(tcp->peer_string);
+}
+
+static grpc_resource_user* win_get_resource_user(grpc_endpoint* ep) {
+  grpc_tcp* tcp = (grpc_tcp*)ep;
+  return tcp->resource_user;
+}
+
+static int win_get_fd(grpc_endpoint* ep) { return -1; }
+
+static bool win_can_track_err(grpc_endpoint* ep) { return false; }
+
+static grpc_endpoint_vtable vtable = {win_read,
+                                      win_write,
+                                      win_add_to_pollset,
+                                      win_add_to_pollset_set,
+                                      win_delete_from_pollset_set,
+                                      win_shutdown,
+                                      win_destroy,
+                                      win_get_resource_user,
+                                      win_get_peer,
+                                      win_get_fd,
+                                      win_can_track_err};
+
+grpc_endpoint* grpc_tcp_create(grpc_winsocket* socket,
+                               grpc_channel_args* channel_args,
+                               const char* peer_string) {
+  grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL);
+  if (channel_args != NULL) {
+    for (size_t i = 0; i < channel_args->num_args; i++) {
+      if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
+        grpc_resource_quota_unref_internal(resource_quota);
+        resource_quota = grpc_resource_quota_ref_internal(
+            (grpc_resource_quota*)channel_args->args[i].value.pointer.p);
+      }
+    }
+  }
+  grpc_tcp* tcp = (grpc_tcp*)gpr_malloc(sizeof(grpc_tcp));
+  memset(tcp, 0, sizeof(grpc_tcp));
+  tcp->base.vtable = &vtable;
+  tcp->socket = socket;
+  gpr_mu_init(&tcp->mu);
+  gpr_ref_init(&tcp->refcount, 1);
+  GRPC_CLOSURE_INIT(&tcp->on_read, on_read, tcp, grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&tcp->on_write, on_write, tcp, grpc_schedule_on_exec_ctx);
+  tcp->peer_string = gpr_strdup(peer_string);
+  grpc_slice_buffer_init(&tcp->last_read_buffer);
+  tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
+  /* Tell network status tracking code about the new endpoint */
+  grpc_network_status_register_endpoint(&tcp->base);
+  grpc_resource_quota_unref_internal(resource_quota);
+
+  return &tcp->base;
+}
+
+#endif /* GRPC_WINSOCK_SOCKET */
diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_windows.h b/third_party/grpc/src/core/lib/iomgr/tcp_windows.h
new file mode 100644
index 0000000..04ef810
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/tcp_windows.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H
+/*
+   Low level TCP "bottom half" implementation, for use by transports built on
+   top of a TCP connection.
+
+   Note that this file does not (yet) include APIs for creating the socket in
+   the first place.
+
+   All calls passing slice transfer ownership of a slice refcount unless
+   otherwise specified.
+*/
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_WINSOCK_SOCKET
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/socket_windows.h"
+
+/* Create a tcp endpoint given a winsock handle.
+ * Takes ownership of the handle.
+ */
+grpc_endpoint* grpc_tcp_create(grpc_winsocket* socket,
+                               grpc_channel_args* channel_args,
+                               const char* peer_string);
+
+grpc_error* grpc_tcp_prepare_socket(SOCKET sock);
+
+grpc_error* grpc_tcp_set_non_block(SOCKET sock);
+
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.cc b/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.cc
new file mode 100644
index 0000000..6369e48
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.cc
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/time_averaged_stats.h"
+
+void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats,
+                                   double init_avg, double regress_weight,
+                                   double persistence_factor) {
+  stats->init_avg = init_avg;
+  stats->regress_weight = regress_weight;
+  stats->persistence_factor = persistence_factor;
+  stats->batch_total_value = 0;
+  stats->batch_num_samples = 0;
+  stats->aggregate_total_weight = 0;
+  stats->aggregate_weighted_avg = init_avg;
+}
+
+void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats,
+                                         double value) {
+  stats->batch_total_value += value;
+  ++stats->batch_num_samples;
+}
+
+double grpc_time_averaged_stats_update_average(
+    grpc_time_averaged_stats* stats) {
+  /* Start with the current batch: */
+  double weighted_sum = stats->batch_total_value;
+  double total_weight = stats->batch_num_samples;
+  if (stats->regress_weight > 0) {
+    /* Add in the regression towards init_avg_: */
+    weighted_sum += stats->regress_weight * stats->init_avg;
+    total_weight += stats->regress_weight;
+  }
+  if (stats->persistence_factor > 0) {
+    /* Add in the persistence: */
+    const double prev_sample_weight =
+        stats->persistence_factor * stats->aggregate_total_weight;
+    weighted_sum += prev_sample_weight * stats->aggregate_weighted_avg;
+    total_weight += prev_sample_weight;
+  }
+  stats->aggregate_weighted_avg =
+      (total_weight > 0) ? (weighted_sum / total_weight) : stats->init_avg;
+  stats->aggregate_total_weight = total_weight;
+  stats->batch_num_samples = 0;
+  stats->batch_total_value = 0;
+  return stats->aggregate_weighted_avg;
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.h b/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.h
new file mode 100644
index 0000000..8745f7f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H
+#define GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H
+
+/* This tracks a time-decaying weighted average.  It works by collecting
+   batches of samples and then mixing their average into a time-decaying
+   weighted mean.  It is designed for batch operations where we do many adds
+   before updating the average. */
+
+typedef struct {
+  /* The initial average value.  This is the reported average until the first
+     grpc_time_averaged_stats_update_average call.  If a positive regress_weight
+     is used, we also regress towards this value on each update. */
+  double init_avg;
+  /* The sample weight of "init_avg" that is mixed in with each call to
+     grpc_time_averaged_stats_update_average.  If the calls to
+     grpc_time_averaged_stats_add_sample stop, this will cause the average to
+     regress back to the mean.  This should be non-negative.  Set it to 0 to
+     disable the bias.  A value of 1 has the effect of adding in 1 bonus sample
+     with value init_avg to each sample period. */
+  double regress_weight;
+  /* This determines the rate of decay of the time-averaging from one period
+     to the next by scaling the aggregate_total_weight of samples from prior
+     periods when combining with the latest period.  It should be in the range
+     [0,1].  A higher value adapts more slowly.  With a value of 0.5, if the
+     batches each have k samples, the samples_in_avg_ will grow to 2 k, so the
+     weighting of the time average will eventually be 1/3 new batch and 2/3
+     old average. */
+  double persistence_factor;
+
+  /* The total value of samples since the last UpdateAverage(). */
+  double batch_total_value;
+  /* The number of samples since the last UpdateAverage(). */
+  double batch_num_samples;
+  /* The time-decayed sum of batch_num_samples_ over previous batches.  This is
+     the "weight" of the old aggregate_weighted_avg_ when updating the
+     average. */
+  double aggregate_total_weight;
+  /* A time-decayed average of the (batch_total_value_ / batch_num_samples_),
+     computed by decaying the samples_in_avg_ weight in the weighted average. */
+  double aggregate_weighted_avg;
+} grpc_time_averaged_stats;
+
+/* See the comments on the members above for an explanation of init_avg,
+   regress_weight, and persistence_factor. */
+void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats,
+                                   double init_avg, double regress_weight,
+                                   double persistence_factor);
+/* Add a sample to the current batch. */
+void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats,
+                                         double value);
+/* Complete a batch and compute the new estimate of the average sample
+   value. */
+double grpc_time_averaged_stats_update_average(grpc_time_averaged_stats* stats);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/timer.cc b/third_party/grpc/src/core/lib/iomgr/timer.cc
new file mode 100644
index 0000000..e647cde
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer.cc
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/timer_manager.h"
+
+grpc_timer_vtable* grpc_timer_impl;
+
+void grpc_set_timer_impl(grpc_timer_vtable* vtable) {
+  grpc_timer_impl = vtable;
+}
+
+void grpc_timer_init(grpc_timer* timer, grpc_millis deadline,
+                     grpc_closure* closure) {
+  grpc_timer_impl->init(timer, deadline, closure);
+}
+
+void grpc_timer_cancel(grpc_timer* timer) { grpc_timer_impl->cancel(timer); }
+
+grpc_timer_check_result grpc_timer_check(grpc_millis* next) {
+  return grpc_timer_impl->check(next);
+}
+
+void grpc_timer_list_init() { grpc_timer_impl->list_init(); }
+
+void grpc_timer_list_shutdown() { grpc_timer_impl->list_shutdown(); }
+
+void grpc_timer_consume_kick() { grpc_timer_impl->consume_kick(); }
diff --git a/third_party/grpc/src/core/lib/iomgr/timer.h b/third_party/grpc/src/core/lib/iomgr/timer.h
new file mode 100644
index 0000000..17e933b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer.h
@@ -0,0 +1,126 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TIMER_H
+#define GRPC_CORE_LIB_IOMGR_TIMER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/iomgr.h"
+
+typedef struct grpc_timer {
+  grpc_millis deadline;
+  uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */
+  bool pending;
+  struct grpc_timer* next;
+  struct grpc_timer* prev;
+  grpc_closure* closure;
+#ifndef NDEBUG
+  struct grpc_timer* hash_table_next;
+#endif
+
+  // Optional field used by custom timers
+  void* custom_timer;
+} grpc_timer;
+
+typedef enum {
+  GRPC_TIMERS_NOT_CHECKED,
+  GRPC_TIMERS_CHECKED_AND_EMPTY,
+  GRPC_TIMERS_FIRED,
+} grpc_timer_check_result;
+
+typedef struct grpc_timer_vtable {
+  void (*init)(grpc_timer* timer, grpc_millis, grpc_closure* closure);
+  void (*cancel)(grpc_timer* timer);
+
+  /* Internal API */
+  grpc_timer_check_result (*check)(grpc_millis* next);
+  void (*list_init)();
+  void (*list_shutdown)(void);
+  void (*consume_kick)(void);
+} grpc_timer_vtable;
+
+/* Initialize *timer. When expired or canceled, closure will be called with
+   error set to indicate if it expired (GRPC_ERROR_NONE) or was canceled
+   (GRPC_ERROR_CANCELLED). *closure is guaranteed to be called exactly once, and
+   application code should check the error to determine how it was invoked. The
+   application callback is also responsible for maintaining information about
+   when to free up any user-level state. Behavior is undefined for a deadline of
+   GRPC_MILLIS_INF_FUTURE. */
+void grpc_timer_init(grpc_timer* timer, grpc_millis deadline,
+                     grpc_closure* closure);
+
+/* Initialize *timer without setting it. This can later be passed through
+   the regular init or cancel */
+void grpc_timer_init_unset(grpc_timer* timer);
+
+/* Note that there is no timer destroy function. This is because the
+   timer is a one-time occurrence with a guarantee that the callback will
+   be called exactly once, either at expiration or cancellation. Thus, all
+   the internal timer event management state is destroyed just before
+   that callback is invoked. If the user has additional state associated with
+   the timer, the user is responsible for determining when it is safe to
+   destroy that state. */
+
+/* Cancel an *timer.
+   There are three cases:
+   1. We normally cancel the timer
+   2. The timer has already run
+   3. We can't cancel the timer because it is "in flight".
+
+   In all of these cases, the cancellation is still considered successful.
+   They are essentially distinguished in that the timer_cb will be run
+   exactly once from either the cancellation (with error GRPC_ERROR_CANCELLED)
+   or from the activation (with error GRPC_ERROR_NONE).
+
+   Note carefully that the callback function MAY occur in the same callstack
+   as grpc_timer_cancel. It's expected that most timers will be cancelled (their
+   primary use is to implement deadlines), and so this code is optimized such
+   that cancellation costs as little as possible. Making callbacks run inline
+   matches this aim.
+
+   Requires: cancel() must happen after init() on a given timer */
+void grpc_timer_cancel(grpc_timer* timer);
+
+/* iomgr internal api for dealing with timers */
+
+/* Check for timers to be run, and run them.
+   Return true if timer callbacks were executed.
+   If next is non-null, TRY to update *next with the next running timer
+   IF that timer occurs before *next current value.
+   *next is never guaranteed to be updated on any given execution; however,
+   with high probability at least one thread in the system will see an update
+   at any time slice. */
+grpc_timer_check_result grpc_timer_check(grpc_millis* next);
+void grpc_timer_list_init();
+void grpc_timer_list_shutdown();
+
+/* Consume a kick issued by grpc_kick_poller */
+void grpc_timer_consume_kick(void);
+
+/* the following must be implemented by each iomgr implementation */
+void grpc_kick_poller(void);
+
+/* Sets the timer implementation */
+void grpc_set_timer_impl(grpc_timer_vtable* vtable);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TIMER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_custom.cc b/third_party/grpc/src/core/lib/iomgr/timer_custom.cc
new file mode 100644
index 0000000..71d825f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_custom.cc
@@ -0,0 +1,93 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/timer_custom.h"
+
+static grpc_custom_timer_vtable* custom_timer_impl;
+
+void grpc_custom_timer_callback(grpc_custom_timer* t, grpc_error* error) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  grpc_core::ExecCtx exec_ctx;
+  grpc_timer* timer = t->original;
+  GPR_ASSERT(timer->pending);
+  timer->pending = 0;
+  GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE);
+  custom_timer_impl->stop(t);
+  gpr_free(t);
+}
+
+static void timer_init(grpc_timer* timer, grpc_millis deadline,
+                       grpc_closure* closure) {
+  uint64_t timeout;
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  grpc_millis now = grpc_core::ExecCtx::Get()->Now();
+  if (deadline <= grpc_core::ExecCtx::Get()->Now()) {
+    GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
+    timer->pending = false;
+    return;
+  } else {
+    timeout = deadline - now;
+  }
+  timer->pending = true;
+  timer->closure = closure;
+  grpc_custom_timer* timer_wrapper =
+      (grpc_custom_timer*)gpr_malloc(sizeof(grpc_custom_timer));
+  timer_wrapper->timeout_ms = timeout;
+  timer->custom_timer = (void*)timer_wrapper;
+  timer_wrapper->original = timer;
+  custom_timer_impl->start(timer_wrapper);
+}
+
+static void timer_cancel(grpc_timer* timer) {
+  GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
+  grpc_custom_timer* tw = (grpc_custom_timer*)timer->custom_timer;
+  if (timer->pending) {
+    timer->pending = 0;
+    GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_CANCELLED);
+    custom_timer_impl->stop(tw);
+    gpr_free(tw);
+  }
+}
+
+static grpc_timer_check_result timer_check(grpc_millis* next) {
+  return GRPC_TIMERS_NOT_CHECKED;
+}
+
+static void timer_list_init() {}
+static void timer_list_shutdown() {}
+
+static void timer_consume_kick(void) {}
+
+static grpc_timer_vtable custom_timer_vtable = {
+    timer_init,      timer_cancel,        timer_check,
+    timer_list_init, timer_list_shutdown, timer_consume_kick};
+
+void grpc_custom_timer_init(grpc_custom_timer_vtable* impl) {
+  custom_timer_impl = impl;
+  grpc_set_timer_impl(&custom_timer_vtable);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_custom.h b/third_party/grpc/src/core/lib/iomgr/timer_custom.h
new file mode 100644
index 0000000..bfea8ba
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_custom.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H
+#define GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/timer.h"
+
+typedef struct grpc_custom_timer {
+  // Implementation defined
+  void* timer;
+  uint64_t timeout_ms;
+
+  grpc_timer* original;
+} grpc_custom_timer;
+
+typedef struct grpc_custom_timer_vtable {
+  void (*start)(grpc_custom_timer* t);
+  void (*stop)(grpc_custom_timer* t);
+} grpc_custom_timer_vtable;
+
+void grpc_custom_timer_init(grpc_custom_timer_vtable* impl);
+
+void grpc_custom_timer_callback(grpc_custom_timer* t, grpc_error* error);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_generic.cc b/third_party/grpc/src/core/lib/iomgr/timer_generic.cc
new file mode 100644
index 0000000..6a925ad
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_generic.cc
@@ -0,0 +1,746 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <inttypes.h>
+
+#include "src/core/lib/iomgr/timer.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/spinlock.h"
+#include "src/core/lib/gpr/tls.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/time_averaged_stats.h"
+#include "src/core/lib/iomgr/timer_heap.h"
+
+#define INVALID_HEAP_INDEX 0xffffffffu
+
+#define ADD_DEADLINE_SCALE 0.33
+#define MIN_QUEUE_WINDOW_DURATION 0.01
+#define MAX_QUEUE_WINDOW_DURATION 1
+
+grpc_core::TraceFlag grpc_timer_trace(false, "timer");
+grpc_core::TraceFlag grpc_timer_check_trace(false, "timer_check");
+
+/* A "timer shard". Contains a 'heap' and a 'list' of timers. All timers with
+ * deadlines earlier than 'queue_deadline_cap' are maintained in the heap and
+ * others are maintained in the list (unordered). This helps to keep the number
+ * of elements in the heap low.
+ *
+ * The 'queue_deadline_cap' gets recomputed periodically based on the timer
+ * stats maintained in 'stats' and the relevant timers are then moved from the
+ * 'list' to 'heap'.
+ */
+typedef struct {
+  gpr_mu mu;
+  grpc_time_averaged_stats stats;
+  /* All and only timers with deadlines < this will be in the heap. */
+  grpc_millis queue_deadline_cap;
+  /* The deadline of the next timer due in this shard. */
+  grpc_millis min_deadline;
+  /* Index of this timer_shard in the g_shard_queue. */
+  uint32_t shard_queue_index;
+  /* This holds all timers with deadlines < queue_deadline_cap. Timers in this
+     list have the top bit of their deadline set to 0. */
+  grpc_timer_heap heap;
+  /* This holds timers whose deadline is >= queue_deadline_cap. */
+  grpc_timer list;
+} timer_shard;
+
+static size_t g_num_shards;
+
+/* Array of timer shards. Whenever a timer (grpc_timer *) is added, its address
+ * is hashed to select the timer shard to add the timer to */
+static timer_shard* g_shards;
+
+/* Maintains a sorted list of timer shards (sorted by their min_deadline, i.e
+ * the deadline of the next timer in each shard).
+ * Access to this is protected by g_shared_mutables.mu */
+static timer_shard** g_shard_queue;
+
+#ifndef NDEBUG
+
+/* == DEBUG ONLY: hash table for duplicate timer detection == */
+
+#define NUM_HASH_BUCKETS 1009 /* Prime number close to 1000 */
+
+static gpr_mu g_hash_mu[NUM_HASH_BUCKETS]; /* One mutex per bucket */
+static grpc_timer* g_timer_ht[NUM_HASH_BUCKETS] = {nullptr};
+
+static void init_timer_ht() {
+  for (int i = 0; i < NUM_HASH_BUCKETS; i++) {
+    gpr_mu_init(&g_hash_mu[i]);
+  }
+}
+
+static void destroy_timer_ht() {
+  for (int i = 0; i < NUM_HASH_BUCKETS; i++) {
+    gpr_mu_destroy(&g_hash_mu[i]);
+  }
+}
+
+static bool is_in_ht(grpc_timer* t) {
+  size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS);
+
+  gpr_mu_lock(&g_hash_mu[i]);
+  grpc_timer* p = g_timer_ht[i];
+  while (p != nullptr && p != t) {
+    p = p->hash_table_next;
+  }
+  gpr_mu_unlock(&g_hash_mu[i]);
+
+  return (p == t);
+}
+
+static void add_to_ht(grpc_timer* t) {
+  GPR_ASSERT(!t->hash_table_next);
+  size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS);
+
+  gpr_mu_lock(&g_hash_mu[i]);
+  grpc_timer* p = g_timer_ht[i];
+  while (p != nullptr && p != t) {
+    p = p->hash_table_next;
+  }
+
+  if (p == t) {
+    grpc_closure* c = t->closure;
+    gpr_log(GPR_ERROR,
+            "** Duplicate timer (%p) being added. Closure: (%p), created at: "
+            "(%s:%d), scheduled at: (%s:%d) **",
+            t, c, c->file_created, c->line_created, c->file_initiated,
+            c->line_initiated);
+    abort();
+  }
+
+  /* Timer not present in the bucket. Insert at head of the list */
+  t->hash_table_next = g_timer_ht[i];
+  g_timer_ht[i] = t;
+  gpr_mu_unlock(&g_hash_mu[i]);
+}
+
+static void remove_from_ht(grpc_timer* t) {
+  size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS);
+  bool removed = false;
+
+  gpr_mu_lock(&g_hash_mu[i]);
+  if (g_timer_ht[i] == t) {
+    g_timer_ht[i] = g_timer_ht[i]->hash_table_next;
+    removed = true;
+  } else if (g_timer_ht[i] != nullptr) {
+    grpc_timer* p = g_timer_ht[i];
+    while (p->hash_table_next != nullptr && p->hash_table_next != t) {
+      p = p->hash_table_next;
+    }
+
+    if (p->hash_table_next == t) {
+      p->hash_table_next = t->hash_table_next;
+      removed = true;
+    }
+  }
+  gpr_mu_unlock(&g_hash_mu[i]);
+
+  if (!removed) {
+    grpc_closure* c = t->closure;
+    gpr_log(GPR_ERROR,
+            "** Removing timer (%p) that is not added to hash table. Closure "
+            "(%p), created at: (%s:%d), scheduled at: (%s:%d) **",
+            t, c, c->file_created, c->line_created, c->file_initiated,
+            c->line_initiated);
+    abort();
+  }
+
+  t->hash_table_next = nullptr;
+}
+
+/* If a timer is added to a timer shard (either heap or a list), it must
+ * be pending. A timer is added to hash table only-if it is added to the
+ * timer shard.
+ * Therefore, if timer->pending is false, it cannot be in hash table */
+static void validate_non_pending_timer(grpc_timer* t) {
+  if (!t->pending && is_in_ht(t)) {
+    grpc_closure* c = t->closure;
+    gpr_log(GPR_ERROR,
+            "** gpr_timer_cancel() called on a non-pending timer (%p) which "
+            "is in the hash table. Closure: (%p), created at: (%s:%d), "
+            "scheduled at: (%s:%d) **",
+            t, c, c->file_created, c->line_created, c->file_initiated,
+            c->line_initiated);
+    abort();
+  }
+}
+
+#define INIT_TIMER_HASH_TABLE() init_timer_ht()
+#define DESTROY_TIMER_HASH_TABLE() destroy_timer_ht()
+#define ADD_TO_HASH_TABLE(t) add_to_ht((t))
+#define REMOVE_FROM_HASH_TABLE(t) remove_from_ht((t))
+#define VALIDATE_NON_PENDING_TIMER(t) validate_non_pending_timer((t))
+
+#else
+
+#define INIT_TIMER_HASH_TABLE()
+#define DESTROY_TIMER_HASH_TABLE()
+#define ADD_TO_HASH_TABLE(t)
+#define REMOVE_FROM_HASH_TABLE(t)
+#define VALIDATE_NON_PENDING_TIMER(t)
+
+#endif
+
+#if GPR_ARCH_64
+/* NOTE: TODO(sreek) - Currently the thread local storage support in grpc is
+   for intptr_t which means on 32-bit machines it is not wide enough to hold
+   grpc_millis which is 64-bit. Adding thread local support for 64 bit values
+   is a lot of work for very little gain. So we are currently restricting this
+   optimization to only 64 bit machines */
+
+/* Thread local variable that stores the deadline of the next timer the thread
+ * has last-seen. This is an optimization to prevent the thread from checking
+ * shared_mutables.min_timer (which requires acquiring shared_mutables.mu lock,
+ * an expensive operation) */
+GPR_TLS_DECL(g_last_seen_min_timer);
+#endif
+
+struct shared_mutables {
+  /* The deadline of the next timer due across all timer shards */
+  grpc_millis min_timer;
+  /* Allow only one run_some_expired_timers at once */
+  gpr_spinlock checker_mu;
+  bool initialized;
+  /* Protects g_shard_queue (and the shared_mutables struct itself) */
+  gpr_mu mu;
+} GPR_ALIGN_STRUCT(GPR_CACHELINE_SIZE);
+
+static struct shared_mutables g_shared_mutables;
+
+static grpc_millis saturating_add(grpc_millis a, grpc_millis b) {
+  if (a > GRPC_MILLIS_INF_FUTURE - b) {
+    return GRPC_MILLIS_INF_FUTURE;
+  }
+  return a + b;
+}
+
+static grpc_timer_check_result run_some_expired_timers(grpc_millis now,
+                                                       grpc_millis* next,
+                                                       grpc_error* error);
+
+static grpc_millis compute_min_deadline(timer_shard* shard) {
+  return grpc_timer_heap_is_empty(&shard->heap)
+             ? saturating_add(shard->queue_deadline_cap, 1)
+             : grpc_timer_heap_top(&shard->heap)->deadline;
+}
+
+static void timer_list_init() {
+  uint32_t i;
+
+  g_num_shards = GPR_CLAMP(2 * gpr_cpu_num_cores(), 1, 32);
+  g_shards =
+      static_cast<timer_shard*>(gpr_zalloc(g_num_shards * sizeof(*g_shards)));
+  g_shard_queue = static_cast<timer_shard**>(
+      gpr_zalloc(g_num_shards * sizeof(*g_shard_queue)));
+
+  g_shared_mutables.initialized = true;
+  g_shared_mutables.checker_mu = GPR_SPINLOCK_INITIALIZER;
+  gpr_mu_init(&g_shared_mutables.mu);
+  g_shared_mutables.min_timer = grpc_core::ExecCtx::Get()->Now();
+
+#if GPR_ARCH_64
+  gpr_tls_init(&g_last_seen_min_timer);
+  gpr_tls_set(&g_last_seen_min_timer, 0);
+#endif
+
+  for (i = 0; i < g_num_shards; i++) {
+    timer_shard* shard = &g_shards[i];
+    gpr_mu_init(&shard->mu);
+    grpc_time_averaged_stats_init(&shard->stats, 1.0 / ADD_DEADLINE_SCALE, 0.1,
+                                  0.5);
+    shard->queue_deadline_cap = g_shared_mutables.min_timer;
+    shard->shard_queue_index = i;
+    grpc_timer_heap_init(&shard->heap);
+    shard->list.next = shard->list.prev = &shard->list;
+    shard->min_deadline = compute_min_deadline(shard);
+    g_shard_queue[i] = shard;
+  }
+
+  INIT_TIMER_HASH_TABLE();
+}
+
+static void timer_list_shutdown() {
+  size_t i;
+  run_some_expired_timers(
+      GRPC_MILLIS_INF_FUTURE, nullptr,
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timer list shutdown"));
+  for (i = 0; i < g_num_shards; i++) {
+    timer_shard* shard = &g_shards[i];
+    gpr_mu_destroy(&shard->mu);
+    grpc_timer_heap_destroy(&shard->heap);
+  }
+  gpr_mu_destroy(&g_shared_mutables.mu);
+
+#if GPR_ARCH_64
+  gpr_tls_destroy(&g_last_seen_min_timer);
+#endif
+
+  gpr_free(g_shards);
+  gpr_free(g_shard_queue);
+  g_shared_mutables.initialized = false;
+
+  DESTROY_TIMER_HASH_TABLE();
+}
+
+/* returns true if the first element in the list */
+static void list_join(grpc_timer* head, grpc_timer* timer) {
+  timer->next = head;
+  timer->prev = head->prev;
+  timer->next->prev = timer->prev->next = timer;
+}
+
+static void list_remove(grpc_timer* timer) {
+  timer->next->prev = timer->prev;
+  timer->prev->next = timer->next;
+}
+
+static void swap_adjacent_shards_in_queue(uint32_t first_shard_queue_index) {
+  timer_shard* temp;
+  temp = g_shard_queue[first_shard_queue_index];
+  g_shard_queue[first_shard_queue_index] =
+      g_shard_queue[first_shard_queue_index + 1];
+  g_shard_queue[first_shard_queue_index + 1] = temp;
+  g_shard_queue[first_shard_queue_index]->shard_queue_index =
+      first_shard_queue_index;
+  g_shard_queue[first_shard_queue_index + 1]->shard_queue_index =
+      first_shard_queue_index + 1;
+}
+
+static void note_deadline_change(timer_shard* shard) {
+  while (shard->shard_queue_index > 0 &&
+         shard->min_deadline <
+             g_shard_queue[shard->shard_queue_index - 1]->min_deadline) {
+    swap_adjacent_shards_in_queue(shard->shard_queue_index - 1);
+  }
+  while (shard->shard_queue_index < g_num_shards - 1 &&
+         shard->min_deadline >
+             g_shard_queue[shard->shard_queue_index + 1]->min_deadline) {
+    swap_adjacent_shards_in_queue(shard->shard_queue_index);
+  }
+}
+
+void grpc_timer_init_unset(grpc_timer* timer) { timer->pending = false; }
+
+static void timer_init(grpc_timer* timer, grpc_millis deadline,
+                       grpc_closure* closure) {
+  int is_first_timer = 0;
+  timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)];
+  timer->closure = closure;
+  timer->deadline = deadline;
+
+#ifndef NDEBUG
+  timer->hash_table_next = nullptr;
+#endif
+
+  if (grpc_timer_trace.enabled()) {
+    gpr_log(GPR_INFO, "TIMER %p: SET %" PRId64 " now %" PRId64 " call %p[%p]",
+            timer, deadline, grpc_core::ExecCtx::Get()->Now(), closure,
+            closure->cb);
+  }
+
+  if (!g_shared_mutables.initialized) {
+    timer->pending = false;
+    GRPC_CLOSURE_SCHED(timer->closure,
+                       GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                           "Attempt to create timer before initialization"));
+    return;
+  }
+
+  gpr_mu_lock(&shard->mu);
+  timer->pending = true;
+  grpc_millis now = grpc_core::ExecCtx::Get()->Now();
+  if (deadline <= now) {
+    timer->pending = false;
+    GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE);
+    gpr_mu_unlock(&shard->mu);
+    /* early out */
+    return;
+  }
+
+  grpc_time_averaged_stats_add_sample(
+      &shard->stats, static_cast<double>(deadline - now) / 1000.0);
+
+  ADD_TO_HASH_TABLE(timer);
+
+  if (deadline < shard->queue_deadline_cap) {
+    is_first_timer = grpc_timer_heap_add(&shard->heap, timer);
+  } else {
+    timer->heap_index = INVALID_HEAP_INDEX;
+    list_join(&shard->list, timer);
+  }
+  if (grpc_timer_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "  .. add to shard %d with queue_deadline_cap=%" PRId64
+            " => is_first_timer=%s",
+            static_cast<int>(shard - g_shards), shard->queue_deadline_cap,
+            is_first_timer ? "true" : "false");
+  }
+  gpr_mu_unlock(&shard->mu);
+
+  /* Deadline may have decreased, we need to adjust the master queue.  Note
+     that there is a potential racy unlocked region here.  There could be a
+     reordering of multiple grpc_timer_init calls, at this point, but the < test
+     below should ensure that we err on the side of caution.  There could
+     also be a race with grpc_timer_check, which might beat us to the lock.  In
+     that case, it is possible that the timer that we added will have already
+     run by the time we hold the lock, but that too is a safe error.
+     Finally, it's possible that the grpc_timer_check that intervened failed to
+     trigger the new timer because the min_deadline hadn't yet been reduced.
+     In that case, the timer will simply have to wait for the next
+     grpc_timer_check. */
+  if (is_first_timer) {
+    gpr_mu_lock(&g_shared_mutables.mu);
+    if (grpc_timer_trace.enabled()) {
+      gpr_log(GPR_INFO, "  .. old shard min_deadline=%" PRId64,
+              shard->min_deadline);
+    }
+    if (deadline < shard->min_deadline) {
+      grpc_millis old_min_deadline = g_shard_queue[0]->min_deadline;
+      shard->min_deadline = deadline;
+      note_deadline_change(shard);
+      if (shard->shard_queue_index == 0 && deadline < old_min_deadline) {
+#if GPR_ARCH_64
+        // TODO: sreek - Using c-style cast here. static_cast<> gives an error
+        // (on mac platforms complaining that gpr_atm* is (long *) while
+        // (&g_shared_mutables.min_timer) is a (long long *). The cast should be
+        // safe since we know that both are pointer types and 64-bit wide.
+        gpr_atm_no_barrier_store((gpr_atm*)(&g_shared_mutables.min_timer),
+                                 deadline);
+#else
+        // On 32-bit systems, gpr_atm_no_barrier_store does not work on 64-bit
+        // types (like grpc_millis). So all reads and writes to
+        // g_shared_mutables.min_timer varialbe under g_shared_mutables.mu
+        g_shared_mutables.min_timer = deadline;
+#endif
+        grpc_kick_poller();
+      }
+    }
+    gpr_mu_unlock(&g_shared_mutables.mu);
+  }
+}
+
+static void timer_consume_kick(void) {
+#if GPR_ARCH_64
+  /* Force re-evaluation of last seen min */
+  gpr_tls_set(&g_last_seen_min_timer, 0);
+#endif
+}
+
+static void timer_cancel(grpc_timer* timer) {
+  if (!g_shared_mutables.initialized) {
+    /* must have already been cancelled, also the shard mutex is invalid */
+    return;
+  }
+
+  timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)];
+  gpr_mu_lock(&shard->mu);
+  if (grpc_timer_trace.enabled()) {
+    gpr_log(GPR_INFO, "TIMER %p: CANCEL pending=%s", timer,
+            timer->pending ? "true" : "false");
+  }
+
+  if (timer->pending) {
+    REMOVE_FROM_HASH_TABLE(timer);
+
+    GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_CANCELLED);
+    timer->pending = false;
+    if (timer->heap_index == INVALID_HEAP_INDEX) {
+      list_remove(timer);
+    } else {
+      grpc_timer_heap_remove(&shard->heap, timer);
+    }
+  } else {
+    VALIDATE_NON_PENDING_TIMER(timer);
+  }
+  gpr_mu_unlock(&shard->mu);
+}
+
+/* Rebalances the timer shard by computing a new 'queue_deadline_cap' and moving
+   all relevant timers in shard->list (i.e timers with deadlines earlier than
+   'queue_deadline_cap') into into shard->heap.
+   Returns 'true' if shard->heap has atleast ONE element
+   REQUIRES: shard->mu locked */
+static bool refill_heap(timer_shard* shard, grpc_millis now) {
+  /* Compute the new queue window width and bound by the limits: */
+  double computed_deadline_delta =
+      grpc_time_averaged_stats_update_average(&shard->stats) *
+      ADD_DEADLINE_SCALE;
+  double deadline_delta =
+      GPR_CLAMP(computed_deadline_delta, MIN_QUEUE_WINDOW_DURATION,
+                MAX_QUEUE_WINDOW_DURATION);
+  grpc_timer *timer, *next;
+
+  /* Compute the new cap and put all timers under it into the queue: */
+  shard->queue_deadline_cap =
+      saturating_add(GPR_MAX(now, shard->queue_deadline_cap),
+                     static_cast<grpc_millis>(deadline_delta * 1000.0));
+
+  if (grpc_timer_check_trace.enabled()) {
+    gpr_log(GPR_INFO, "  .. shard[%d]->queue_deadline_cap --> %" PRId64,
+            static_cast<int>(shard - g_shards), shard->queue_deadline_cap);
+  }
+  for (timer = shard->list.next; timer != &shard->list; timer = next) {
+    next = timer->next;
+
+    if (timer->deadline < shard->queue_deadline_cap) {
+      if (grpc_timer_check_trace.enabled()) {
+        gpr_log(GPR_INFO, "  .. add timer with deadline %" PRId64 " to heap",
+                timer->deadline);
+      }
+      list_remove(timer);
+      grpc_timer_heap_add(&shard->heap, timer);
+    }
+  }
+  return !grpc_timer_heap_is_empty(&shard->heap);
+}
+
+/* This pops the next non-cancelled timer with deadline <= now from the
+   queue, or returns NULL if there isn't one.
+   REQUIRES: shard->mu locked */
+static grpc_timer* pop_one(timer_shard* shard, grpc_millis now) {
+  grpc_timer* timer;
+  for (;;) {
+    if (grpc_timer_check_trace.enabled()) {
+      gpr_log(GPR_INFO, "  .. shard[%d]: heap_empty=%s",
+              static_cast<int>(shard - g_shards),
+              grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false");
+    }
+    if (grpc_timer_heap_is_empty(&shard->heap)) {
+      if (now < shard->queue_deadline_cap) return nullptr;
+      if (!refill_heap(shard, now)) return nullptr;
+    }
+    timer = grpc_timer_heap_top(&shard->heap);
+    if (grpc_timer_check_trace.enabled()) {
+      gpr_log(GPR_INFO,
+              "  .. check top timer deadline=%" PRId64 " now=%" PRId64,
+              timer->deadline, now);
+    }
+    if (timer->deadline > now) return nullptr;
+    if (grpc_timer_trace.enabled()) {
+      gpr_log(GPR_INFO, "TIMER %p: FIRE %" PRId64 "ms late via %s scheduler",
+              timer, now - timer->deadline,
+              timer->closure->scheduler->vtable->name);
+    }
+    timer->pending = false;
+    grpc_timer_heap_pop(&shard->heap);
+    return timer;
+  }
+}
+
+/* REQUIRES: shard->mu unlocked */
+static size_t pop_timers(timer_shard* shard, grpc_millis now,
+                         grpc_millis* new_min_deadline, grpc_error* error) {
+  size_t n = 0;
+  grpc_timer* timer;
+  gpr_mu_lock(&shard->mu);
+  while ((timer = pop_one(shard, now))) {
+    REMOVE_FROM_HASH_TABLE(timer);
+    GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_REF(error));
+    n++;
+  }
+  *new_min_deadline = compute_min_deadline(shard);
+  gpr_mu_unlock(&shard->mu);
+  if (grpc_timer_check_trace.enabled()) {
+    gpr_log(GPR_INFO, "  .. shard[%d] popped %" PRIdPTR,
+            static_cast<int>(shard - g_shards), n);
+  }
+  return n;
+}
+
+static grpc_timer_check_result run_some_expired_timers(grpc_millis now,
+                                                       grpc_millis* next,
+                                                       grpc_error* error) {
+  grpc_timer_check_result result = GRPC_TIMERS_NOT_CHECKED;
+
+#if GPR_ARCH_64
+  // TODO: sreek - Using c-style cast here. static_cast<> gives an error (on
+  // mac platforms complaining that gpr_atm* is (long *) while
+  // (&g_shared_mutables.min_timer) is a (long long *). The cast should be
+  // safe since we know that both are pointer types and 64-bit wide
+  grpc_millis min_timer = static_cast<grpc_millis>(
+      gpr_atm_no_barrier_load((gpr_atm*)(&g_shared_mutables.min_timer)));
+  gpr_tls_set(&g_last_seen_min_timer, min_timer);
+#else
+  // On 32-bit systems, gpr_atm_no_barrier_load does not work on 64-bit types
+  // (like grpc_millis). So all reads and writes to g_shared_mutables.min_timer
+  // are done under g_shared_mutables.mu
+  gpr_mu_lock(&g_shared_mutables.mu);
+  grpc_millis min_timer = g_shared_mutables.min_timer;
+  gpr_mu_unlock(&g_shared_mutables.mu);
+#endif
+  if (now < min_timer) {
+    if (next != nullptr) *next = GPR_MIN(*next, min_timer);
+    return GRPC_TIMERS_CHECKED_AND_EMPTY;
+  }
+
+  if (gpr_spinlock_trylock(&g_shared_mutables.checker_mu)) {
+    gpr_mu_lock(&g_shared_mutables.mu);
+    result = GRPC_TIMERS_CHECKED_AND_EMPTY;
+
+    if (grpc_timer_check_trace.enabled()) {
+      gpr_log(GPR_INFO, "  .. shard[%d]->min_deadline = %" PRId64,
+              static_cast<int>(g_shard_queue[0] - g_shards),
+              g_shard_queue[0]->min_deadline);
+    }
+
+    while (g_shard_queue[0]->min_deadline < now ||
+           (now != GRPC_MILLIS_INF_FUTURE &&
+            g_shard_queue[0]->min_deadline == now)) {
+      grpc_millis new_min_deadline;
+
+      /* For efficiency, we pop as many available timers as we can from the
+         shard.  This may violate perfect timer deadline ordering, but that
+         shouldn't be a big deal because we don't make ordering guarantees. */
+      if (pop_timers(g_shard_queue[0], now, &new_min_deadline, error) > 0) {
+        result = GRPC_TIMERS_FIRED;
+      }
+
+      if (grpc_timer_check_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "  .. result --> %d"
+                ", shard[%d]->min_deadline %" PRId64 " --> %" PRId64
+                ", now=%" PRId64,
+                result, static_cast<int>(g_shard_queue[0] - g_shards),
+                g_shard_queue[0]->min_deadline, new_min_deadline, now);
+      }
+
+      /* An grpc_timer_init() on the shard could intervene here, adding a new
+         timer that is earlier than new_min_deadline.  However,
+         grpc_timer_init() will block on the master_lock before it can call
+         set_min_deadline, so this one will complete first and then the Addtimer
+         will reduce the min_deadline (perhaps unnecessarily). */
+      g_shard_queue[0]->min_deadline = new_min_deadline;
+      note_deadline_change(g_shard_queue[0]);
+    }
+
+    if (next) {
+      *next = GPR_MIN(*next, g_shard_queue[0]->min_deadline);
+    }
+
+#if GPR_ARCH_64
+    // TODO: sreek - Using c-style cast here. static_cast<> gives an error (on
+    // mac platforms complaining that gpr_atm* is (long *) while
+    // (&g_shared_mutables.min_timer) is a (long long *). The cast should be
+    // safe since we know that both are pointer types and 64-bit wide
+    gpr_atm_no_barrier_store((gpr_atm*)(&g_shared_mutables.min_timer),
+                             g_shard_queue[0]->min_deadline);
+#else
+    // On 32-bit systems, gpr_atm_no_barrier_store does not work on 64-bit
+    // types (like grpc_millis). So all reads and writes to
+    // g_shared_mutables.min_timer are done under g_shared_mutables.mu
+    g_shared_mutables.min_timer = g_shard_queue[0]->min_deadline;
+#endif
+    gpr_mu_unlock(&g_shared_mutables.mu);
+    gpr_spinlock_unlock(&g_shared_mutables.checker_mu);
+  }
+
+  GRPC_ERROR_UNREF(error);
+
+  return result;
+}
+
+static grpc_timer_check_result timer_check(grpc_millis* next) {
+  // prelude
+  grpc_millis now = grpc_core::ExecCtx::Get()->Now();
+
+#if GPR_ARCH_64
+  /* fetch from a thread-local first: this avoids contention on a globally
+     mutable cacheline in the common case */
+  grpc_millis min_timer = gpr_tls_get(&g_last_seen_min_timer);
+#else
+  // On 32-bit systems, we currently do not have thread local support for 64-bit
+  // types. In this case, directly read from g_shared_mutables.min_timer.
+  // Also, note that on 32-bit systems, gpr_atm_no_barrier_store does not work
+  // on 64-bit types (like grpc_millis). So all reads and writes to
+  // g_shared_mutables.min_timer are done under g_shared_mutables.mu
+  gpr_mu_lock(&g_shared_mutables.mu);
+  grpc_millis min_timer = g_shared_mutables.min_timer;
+  gpr_mu_unlock(&g_shared_mutables.mu);
+#endif
+
+  if (now < min_timer) {
+    if (next != nullptr) {
+      *next = GPR_MIN(*next, min_timer);
+    }
+    if (grpc_timer_check_trace.enabled()) {
+      gpr_log(GPR_INFO, "TIMER CHECK SKIP: now=%" PRId64 " min_timer=%" PRId64,
+              now, min_timer);
+    }
+    return GRPC_TIMERS_CHECKED_AND_EMPTY;
+  }
+
+  grpc_error* shutdown_error =
+      now != GRPC_MILLIS_INF_FUTURE
+          ? GRPC_ERROR_NONE
+          : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system");
+
+  // tracing
+  if (grpc_timer_check_trace.enabled()) {
+    char* next_str;
+    if (next == nullptr) {
+      next_str = gpr_strdup("NULL");
+    } else {
+      gpr_asprintf(&next_str, "%" PRId64, *next);
+    }
+#if GPR_ARCH_64
+    gpr_log(GPR_INFO,
+            "TIMER CHECK BEGIN: now=%" PRId64 " next=%s tls_min=%" PRId64
+            " glob_min=%" PRId64,
+            now, next_str, min_timer,
+            static_cast<grpc_millis>(gpr_atm_no_barrier_load(
+                (gpr_atm*)(&g_shared_mutables.min_timer))));
+#else
+    gpr_log(GPR_INFO, "TIMER CHECK BEGIN: now=%" PRId64 " next=%s min=%" PRId64,
+            now, next_str, min_timer);
+#endif
+    gpr_free(next_str);
+  }
+  // actual code
+  grpc_timer_check_result r =
+      run_some_expired_timers(now, next, shutdown_error);
+  // tracing
+  if (grpc_timer_check_trace.enabled()) {
+    char* next_str;
+    if (next == nullptr) {
+      next_str = gpr_strdup("NULL");
+    } else {
+      gpr_asprintf(&next_str, "%" PRId64, *next);
+    }
+    gpr_log(GPR_INFO, "TIMER CHECK END: r=%d; next=%s", r, next_str);
+    gpr_free(next_str);
+  }
+  return r;
+}
+
+grpc_timer_vtable grpc_generic_timer_vtable = {
+    timer_init,      timer_cancel,        timer_check,
+    timer_list_init, timer_list_shutdown, timer_consume_kick};
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_generic.h b/third_party/grpc/src/core/lib/iomgr/timer_generic.h
new file mode 100644
index 0000000..97a4513
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_generic.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H
+#define GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+struct grpc_timer {
+  gpr_atm deadline;
+  uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */
+  bool pending;
+  struct grpc_timer* next;
+  struct grpc_timer* prev;
+  grpc_closure* closure;
+#ifndef NDEBUG
+  struct grpc_timer* hash_table_next;
+#endif
+};
+
+#endif /* GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_heap.cc b/third_party/grpc/src/core/lib/iomgr/timer_heap.cc
new file mode 100644
index 0000000..2c6a599
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_heap.cc
@@ -0,0 +1,135 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include "src/core/lib/iomgr/timer_heap.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+/* Adjusts a heap so as to move a hole at position i closer to the root,
+   until a suitable position is found for element t. Then, copies t into that
+   position. This functor is called each time immediately after modifying a
+   value in the underlying container, with the offset of the modified element as
+   its argument. */
+static void adjust_upwards(grpc_timer** first, uint32_t i, grpc_timer* t) {
+  while (i > 0) {
+    uint32_t parent = static_cast<uint32_t>((static_cast<int>(i) - 1) / 2);
+    if (first[parent]->deadline <= t->deadline) break;
+    first[i] = first[parent];
+    first[i]->heap_index = i;
+    i = parent;
+  }
+  first[i] = t;
+  t->heap_index = i;
+}
+
+/* Adjusts a heap so as to move a hole at position i farther away from the root,
+   until a suitable position is found for element t.  Then, copies t into that
+   position. */
+static void adjust_downwards(grpc_timer** first, uint32_t i, uint32_t length,
+                             grpc_timer* t) {
+  for (;;) {
+    uint32_t left_child = 1u + 2u * i;
+    if (left_child >= length) break;
+    uint32_t right_child = left_child + 1;
+    uint32_t next_i = right_child < length && first[left_child]->deadline >
+                                                  first[right_child]->deadline
+                          ? right_child
+                          : left_child;
+    if (t->deadline <= first[next_i]->deadline) break;
+    first[i] = first[next_i];
+    first[i]->heap_index = i;
+    i = next_i;
+  }
+  first[i] = t;
+  t->heap_index = i;
+}
+
+#define SHRINK_MIN_ELEMS 8
+#define SHRINK_FULLNESS_FACTOR 2
+
+static void maybe_shrink(grpc_timer_heap* heap) {
+  if (heap->timer_count >= 8 &&
+      heap->timer_count <= heap->timer_capacity / SHRINK_FULLNESS_FACTOR / 2) {
+    heap->timer_capacity = heap->timer_count * SHRINK_FULLNESS_FACTOR;
+    heap->timers = static_cast<grpc_timer**>(
+        gpr_realloc(heap->timers, heap->timer_capacity * sizeof(grpc_timer*)));
+  }
+}
+
+static void note_changed_priority(grpc_timer_heap* heap, grpc_timer* timer) {
+  uint32_t i = timer->heap_index;
+  uint32_t parent = static_cast<uint32_t>((static_cast<int>(i) - 1) / 2);
+  if (heap->timers[parent]->deadline > timer->deadline) {
+    adjust_upwards(heap->timers, i, timer);
+  } else {
+    adjust_downwards(heap->timers, i, heap->timer_count, timer);
+  }
+}
+
+void grpc_timer_heap_init(grpc_timer_heap* heap) {
+  memset(heap, 0, sizeof(*heap));
+}
+
+void grpc_timer_heap_destroy(grpc_timer_heap* heap) { gpr_free(heap->timers); }
+
+bool grpc_timer_heap_add(grpc_timer_heap* heap, grpc_timer* timer) {
+  if (heap->timer_count == heap->timer_capacity) {
+    heap->timer_capacity =
+        GPR_MAX(heap->timer_capacity + 1, heap->timer_capacity * 3 / 2);
+    heap->timers = static_cast<grpc_timer**>(
+        gpr_realloc(heap->timers, heap->timer_capacity * sizeof(grpc_timer*)));
+  }
+  timer->heap_index = heap->timer_count;
+  adjust_upwards(heap->timers, heap->timer_count, timer);
+  heap->timer_count++;
+  return timer->heap_index == 0;
+}
+
+void grpc_timer_heap_remove(grpc_timer_heap* heap, grpc_timer* timer) {
+  uint32_t i = timer->heap_index;
+  if (i == heap->timer_count - 1) {
+    heap->timer_count--;
+    maybe_shrink(heap);
+    return;
+  }
+  heap->timers[i] = heap->timers[heap->timer_count - 1];
+  heap->timers[i]->heap_index = i;
+  heap->timer_count--;
+  maybe_shrink(heap);
+  note_changed_priority(heap, heap->timers[i]);
+}
+
+bool grpc_timer_heap_is_empty(grpc_timer_heap* heap) {
+  return heap->timer_count == 0;
+}
+
+grpc_timer* grpc_timer_heap_top(grpc_timer_heap* heap) {
+  return heap->timers[0];
+}
+
+void grpc_timer_heap_pop(grpc_timer_heap* heap) {
+  grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap));
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_heap.h b/third_party/grpc/src/core/lib/iomgr/timer_heap.h
new file mode 100644
index 0000000..7b983e7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_heap.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H
+#define GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/timer.h"
+
+typedef struct {
+  grpc_timer** timers;
+  uint32_t timer_count;
+  uint32_t timer_capacity;
+} grpc_timer_heap;
+
+/* return true if the new timer is the first timer in the heap */
+bool grpc_timer_heap_add(grpc_timer_heap* heap, grpc_timer* timer);
+
+void grpc_timer_heap_init(grpc_timer_heap* heap);
+void grpc_timer_heap_destroy(grpc_timer_heap* heap);
+
+void grpc_timer_heap_remove(grpc_timer_heap* heap, grpc_timer* timer);
+grpc_timer* grpc_timer_heap_top(grpc_timer_heap* heap);
+void grpc_timer_heap_pop(grpc_timer_heap* heap);
+
+bool grpc_timer_heap_is_empty(grpc_timer_heap* heap);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_manager.cc b/third_party/grpc/src/core/lib/iomgr/timer_manager.cc
new file mode 100644
index 0000000..cb12329
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_manager.cc
@@ -0,0 +1,376 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <inttypes.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/timer_manager.h"
+
+struct completed_thread {
+  grpc_core::Thread thd;
+  completed_thread* next;
+};
+
+extern grpc_core::TraceFlag grpc_timer_check_trace;
+
+// global mutex
+static gpr_mu g_mu;
+// are we multi-threaded
+static bool g_threaded;
+// cv to wait until a thread is needed
+static gpr_cv g_cv_wait;
+// cv for notification when threading ends
+static gpr_cv g_cv_shutdown;
+// number of threads in the system
+static int g_thread_count;
+// number of threads sitting around waiting
+static int g_waiter_count;
+// linked list of threads that have completed (and need joining)
+static completed_thread* g_completed_threads;
+// was the manager kicked by the timer system
+static bool g_kicked;
+// is there a thread waiting until the next timer should fire?
+static bool g_has_timed_waiter;
+// the deadline of the current timed waiter thread (only relevant if
+// g_has_timed_waiter is true)
+static grpc_millis g_timed_waiter_deadline;
+// generation counter to track which thread is waiting for the next timer
+static uint64_t g_timed_waiter_generation;
+
+static void timer_thread(void* completed_thread_ptr);
+
+// For debug of the timer manager crash only.
+// TODO (mxyan): remove after bug is fixed.
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+extern int64_t g_timer_manager_init_count;
+extern int64_t g_timer_manager_shutdown_count;
+extern int64_t g_fork_count;
+extern int64_t g_next_value;
+#endif  // GRPC_DEBUG_TIMER_MANAGER
+
+static void gc_completed_threads(void) {
+  if (g_completed_threads != nullptr) {
+    completed_thread* to_gc = g_completed_threads;
+    g_completed_threads = nullptr;
+    gpr_mu_unlock(&g_mu);
+    while (to_gc != nullptr) {
+      to_gc->thd.Join();
+      completed_thread* next = to_gc->next;
+      gpr_free(to_gc);
+      to_gc = next;
+    }
+    gpr_mu_lock(&g_mu);
+  }
+}
+
+static void start_timer_thread_and_unlock(void) {
+  GPR_ASSERT(g_threaded);
+  ++g_waiter_count;
+  ++g_thread_count;
+  gpr_mu_unlock(&g_mu);
+  if (grpc_timer_check_trace.enabled()) {
+    gpr_log(GPR_INFO, "Spawn timer thread");
+  }
+  completed_thread* ct =
+      static_cast<completed_thread*>(gpr_malloc(sizeof(*ct)));
+  ct->thd = grpc_core::Thread("grpc_global_timer", timer_thread, ct);
+  ct->thd.Start();
+}
+
+void grpc_timer_manager_tick() {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_timer_check(nullptr);
+}
+
+static void run_some_timers() {
+  // if there's something to execute...
+  gpr_mu_lock(&g_mu);
+  // remove a waiter from the pool, and start another thread if necessary
+  --g_waiter_count;
+  if (g_waiter_count == 0 && g_threaded) {
+    // The number of timer threads is always increasing until all the threads
+    // are stopped. In rare cases, if a large number of timers fire
+    // simultaneously, we may end up using a large number of threads.
+    start_timer_thread_and_unlock();
+  } else {
+    // if there's no thread waiting with a timeout, kick an existing untimed
+    // waiter so that the next deadline is not missed
+    if (!g_has_timed_waiter) {
+      if (grpc_timer_check_trace.enabled()) {
+        gpr_log(GPR_INFO, "kick untimed waiter");
+      }
+      gpr_cv_signal(&g_cv_wait);
+    }
+    gpr_mu_unlock(&g_mu);
+  }
+  // without our lock, flush the exec_ctx
+  if (grpc_timer_check_trace.enabled()) {
+    gpr_log(GPR_INFO, "flush exec_ctx");
+  }
+  grpc_core::ExecCtx::Get()->Flush();
+  gpr_mu_lock(&g_mu);
+  // garbage collect any threads hanging out that are dead
+  gc_completed_threads();
+  // get ready to wait again
+  ++g_waiter_count;
+  gpr_mu_unlock(&g_mu);
+}
+
+// wait until 'next' (or forever if there is already a timed waiter in the pool)
+// returns true if the thread should continue executing (false if it should
+// shutdown)
+static bool wait_until(grpc_millis next) {
+  gpr_mu_lock(&g_mu);
+  // if we're not threaded anymore, leave
+  if (!g_threaded) {
+    gpr_mu_unlock(&g_mu);
+    return false;
+  }
+
+  // If g_kicked is true at this point, it means there was a kick from the timer
+  // system that the timer-manager threads here missed. We cannot trust 'next'
+  // here any longer (since there might be an earlier deadline). So if g_kicked
+  // is true at this point, we should quickly exit this and get the next
+  // deadline from the timer system
+
+  if (!g_kicked) {
+    // if there's no timed waiter, we should become one: that waiter waits
+    // only until the next timer should expire. All other timers wait forever
+    //
+    // 'g_timed_waiter_generation' is a global generation counter. The idea here
+    // is that the thread becoming a timed-waiter increments and stores this
+    // global counter locally in 'my_timed_waiter_generation' before going to
+    // sleep. After waking up, if my_timed_waiter_generation ==
+    // g_timed_waiter_generation, it can be sure that it was the timed_waiter
+    // thread (and that no other thread took over while this was asleep)
+    //
+    // Initialize my_timed_waiter_generation to some value that is NOT equal to
+    // g_timed_waiter_generation
+    uint64_t my_timed_waiter_generation = g_timed_waiter_generation - 1;
+
+    /* If there's no timed waiter, we should become one: that waiter waits only
+       until the next timer should expire. All other timer threads wait forever
+       unless their 'next' is earlier than the current timed-waiter's deadline
+       (in which case the thread with earlier 'next' takes over as the new timed
+       waiter) */
+    if (next != GRPC_MILLIS_INF_FUTURE) {
+      if (!g_has_timed_waiter || (next < g_timed_waiter_deadline)) {
+        my_timed_waiter_generation = ++g_timed_waiter_generation;
+        g_has_timed_waiter = true;
+        g_timed_waiter_deadline = next;
+
+        if (grpc_timer_check_trace.enabled()) {
+          grpc_millis wait_time = next - grpc_core::ExecCtx::Get()->Now();
+          gpr_log(GPR_INFO, "sleep for a %" PRId64 " milliseconds", wait_time);
+        }
+      } else {  // g_timed_waiter == true && next >= g_timed_waiter_deadline
+        next = GRPC_MILLIS_INF_FUTURE;
+      }
+    }
+
+    if (grpc_timer_check_trace.enabled() && next == GRPC_MILLIS_INF_FUTURE) {
+      gpr_log(GPR_INFO, "sleep until kicked");
+    }
+
+      // For debug of the timer manager crash only.
+      // TODO (mxyan): remove after bug is fixed.
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+    g_next_value = next;
+#endif
+    gpr_cv_wait(&g_cv_wait, &g_mu,
+                grpc_millis_to_timespec(next, GPR_CLOCK_MONOTONIC));
+
+    if (grpc_timer_check_trace.enabled()) {
+      gpr_log(GPR_INFO, "wait ended: was_timed:%d kicked:%d",
+              my_timed_waiter_generation == g_timed_waiter_generation,
+              g_kicked);
+    }
+    // if this was the timed waiter, then we need to check timers, and flag
+    // that there's now no timed waiter... we'll look for a replacement if
+    // there's work to do after checking timers (code above)
+    if (my_timed_waiter_generation == g_timed_waiter_generation) {
+      g_has_timed_waiter = false;
+      g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE;
+    }
+  }
+
+  // if this was a kick from the timer system, consume it (and don't stop
+  // this thread yet)
+  if (g_kicked) {
+    grpc_timer_consume_kick();
+    g_kicked = false;
+  }
+
+  gpr_mu_unlock(&g_mu);
+  return true;
+}
+
+static void timer_main_loop() {
+  for (;;) {
+    grpc_millis next = GRPC_MILLIS_INF_FUTURE;
+    grpc_core::ExecCtx::Get()->InvalidateNow();
+
+    // check timer state, updates next to the next time to run a check
+    switch (grpc_timer_check(&next)) {
+      case GRPC_TIMERS_FIRED:
+        run_some_timers();
+        break;
+      case GRPC_TIMERS_NOT_CHECKED:
+        /* This case only happens under contention, meaning more than one timer
+           manager thread checked timers concurrently.
+
+           If that happens, we're guaranteed that some other thread has just
+           checked timers, and this will avalanche into some other thread seeing
+           empty timers and doing a timed sleep.
+
+           Consequently, we can just sleep forever here and be happy at some
+           saved wakeup cycles. */
+        if (grpc_timer_check_trace.enabled()) {
+          gpr_log(GPR_INFO, "timers not checked: expect another thread to");
+        }
+        next = GRPC_MILLIS_INF_FUTURE;
+      // fallthrough
+      case GRPC_TIMERS_CHECKED_AND_EMPTY:
+        if (!wait_until(next)) {
+          return;
+        }
+        break;
+    }
+  }
+}
+
+static void timer_thread_cleanup(completed_thread* ct) {
+  gpr_mu_lock(&g_mu);
+  // terminate the thread: drop the waiter count, thread count, and let whomever
+  // stopped the threading stuff know that we're done
+  --g_waiter_count;
+  --g_thread_count;
+  if (0 == g_thread_count) {
+    gpr_cv_signal(&g_cv_shutdown);
+  }
+  ct->next = g_completed_threads;
+  g_completed_threads = ct;
+  gpr_mu_unlock(&g_mu);
+  if (grpc_timer_check_trace.enabled()) {
+    gpr_log(GPR_INFO, "End timer thread");
+  }
+}
+
+static void timer_thread(void* completed_thread_ptr) {
+  // this threads exec_ctx: we try to run things through to completion here
+  // since it's easy to spin up new threads
+  grpc_core::ExecCtx exec_ctx(GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD);
+  timer_main_loop();
+
+  timer_thread_cleanup(static_cast<completed_thread*>(completed_thread_ptr));
+}
+
+static void start_threads(void) {
+  gpr_mu_lock(&g_mu);
+  if (!g_threaded) {
+    g_threaded = true;
+    start_timer_thread_and_unlock();
+  } else {
+    gpr_mu_unlock(&g_mu);
+  }
+}
+
+void grpc_timer_manager_init(void) {
+  gpr_mu_init(&g_mu);
+  gpr_cv_init(&g_cv_wait);
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+  // For debug of the timer manager crash only.
+  // TODO (mxyan): remove after bug is fixed.
+  g_timer_manager_init_count++;
+#endif
+  gpr_cv_init(&g_cv_shutdown);
+  g_threaded = false;
+  g_thread_count = 0;
+  g_waiter_count = 0;
+  g_completed_threads = nullptr;
+
+  g_has_timed_waiter = false;
+  g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE;
+
+  start_threads();
+}
+
+static void stop_threads(void) {
+  gpr_mu_lock(&g_mu);
+  if (grpc_timer_check_trace.enabled()) {
+    gpr_log(GPR_INFO, "stop timer threads: threaded=%d", g_threaded);
+  }
+  if (g_threaded) {
+    g_threaded = false;
+    gpr_cv_broadcast(&g_cv_wait);
+    if (grpc_timer_check_trace.enabled()) {
+      gpr_log(GPR_INFO, "num timer threads: %d", g_thread_count);
+    }
+    while (g_thread_count > 0) {
+      gpr_cv_wait(&g_cv_shutdown, &g_mu, gpr_inf_future(GPR_CLOCK_MONOTONIC));
+      if (grpc_timer_check_trace.enabled()) {
+        gpr_log(GPR_INFO, "num timer threads: %d", g_thread_count);
+      }
+      gc_completed_threads();
+    }
+  }
+  gpr_mu_unlock(&g_mu);
+}
+
+void grpc_timer_manager_shutdown(void) {
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+  // For debug of the timer manager crash only.
+  // TODO (mxyan): remove after bug is fixed.
+  g_timer_manager_shutdown_count++;
+#endif
+  stop_threads();
+
+  gpr_mu_destroy(&g_mu);
+  gpr_cv_destroy(&g_cv_wait);
+  gpr_cv_destroy(&g_cv_shutdown);
+}
+
+void grpc_timer_manager_set_threading(bool threaded) {
+#ifdef GRPC_DEBUG_TIMER_MANAGER
+  // For debug of the timer manager crash only.
+  // TODO (mxyan): remove after bug is fixed.
+  g_fork_count++;
+#endif
+  if (threaded) {
+    start_threads();
+  } else {
+    stop_threads();
+  }
+}
+
+void grpc_kick_poller(void) {
+  gpr_mu_lock(&g_mu);
+  g_kicked = true;
+  g_has_timed_waiter = false;
+  g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE;
+  ++g_timed_waiter_generation;
+  gpr_cv_signal(&g_cv_wait);
+  gpr_mu_unlock(&g_mu);
+}
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_manager.h b/third_party/grpc/src/core/lib/iomgr/timer_manager.h
new file mode 100644
index 0000000..00dcdc4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_manager.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H
+#define GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+/* Timer Manager tries to keep only one thread waiting for the next timeout at
+   all times, and thus effectively preventing the thundering herd problem. */
+
+void grpc_timer_manager_init(void);
+void grpc_timer_manager_shutdown(void);
+
+/* enable/disable threading - must be called after grpc_timer_manager_init and
+ * before grpc_timer_manager_shutdown */
+void grpc_timer_manager_set_threading(bool enabled);
+/* explicitly perform one tick of the timer system - for when threading is
+ * disabled */
+void grpc_timer_manager_tick(void);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/timer_uv.cc b/third_party/grpc/src/core/lib/iomgr/timer_uv.cc
new file mode 100644
index 0000000..8b7c82e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/timer_uv.cc
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_UV
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/iomgr_custom.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/iomgr/timer_custom.h"
+
+#include <uv.h>
+
+static void timer_close_callback(uv_handle_t* handle) { gpr_free(handle); }
+
+static void stop_uv_timer(uv_timer_t* handle) {
+  uv_timer_stop(handle);
+  uv_unref((uv_handle_t*)handle);
+  uv_close((uv_handle_t*)handle, timer_close_callback);
+}
+
+void run_expired_timer(uv_timer_t* handle) {
+  grpc_custom_timer* timer_wrapper = (grpc_custom_timer*)handle->data;
+  grpc_custom_timer_callback(timer_wrapper, GRPC_ERROR_NONE);
+}
+
+static void timer_start(grpc_custom_timer* t) {
+  uv_timer_t* uv_timer;
+  uv_timer = (uv_timer_t*)gpr_malloc(sizeof(uv_timer_t));
+  uv_timer_init(uv_default_loop(), uv_timer);
+  uv_timer->data = t;
+  t->timer = (void*)uv_timer;
+  uv_timer_start(uv_timer, run_expired_timer, t->timeout_ms, 0);
+  // Node uses a garbage collector to call destructors, so we don't
+  // want to hold the uv loop open with active gRPC objects.
+  uv_unref((uv_handle_t*)uv_timer);
+}
+
+static void timer_stop(grpc_custom_timer* t) {
+  stop_uv_timer((uv_timer_t*)t->timer);
+}
+
+grpc_custom_timer_vtable uv_timer_vtable = {timer_start, timer_stop};
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/udp_server.cc b/third_party/grpc/src/core/lib/iomgr/udp_server.cc
new file mode 100644
index 0000000..3dd7cab
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/udp_server.cc
@@ -0,0 +1,746 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/udp_server.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_factory_posix.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+
+/* A listener which implements basic features of Listening on a port for
+ * I/O events*/
+class GrpcUdpListener {
+ public:
+  GrpcUdpListener(grpc_udp_server* server, int fd,
+                  const grpc_resolved_address* addr);
+  ~GrpcUdpListener();
+
+  /* Called when grpc server starts to listening on the grpc_fd. */
+  void StartListening(grpc_pollset** pollsets, size_t pollset_count,
+                      GrpcUdpHandlerFactory* handler_factory);
+
+  /* Called when data is available to read from the socket.
+   * Return true if there is more data to read from fd. */
+  void OnRead(grpc_error* error, void* do_read_arg);
+
+  /* Called when the socket is writeable. The given closure should be scheduled
+   * when the socket becomes blocked next time. */
+  void OnCanWrite(grpc_error* error, void* do_write_arg);
+
+  /* Called when the grpc_fd is about to be orphaned (and the FD closed). */
+  void OnFdAboutToOrphan();
+
+  /* Called to orphan fd of this listener.*/
+  void OrphanFd();
+
+  /* Called when this listener is going to be destroyed. */
+  void OnDestroy();
+
+  int fd() const { return fd_; }
+
+ protected:
+  grpc_fd* emfd() const { return emfd_; }
+
+  gpr_mu* mutex() { return &mutex_; }
+
+ private:
+  /* event manager callback when reads are ready */
+  static void on_read(void* arg, grpc_error* error);
+  static void on_write(void* arg, grpc_error* error);
+
+  static void do_read(void* arg, grpc_error* error);
+  static void do_write(void* arg, grpc_error* error);
+  // Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback
+  // interface.
+  static void fd_notify_on_write_wrapper(void* arg, grpc_error* error);
+
+  static void shutdown_fd(void* args, grpc_error* error);
+
+  int fd_;
+  grpc_fd* emfd_;
+  grpc_udp_server* server_;
+  grpc_resolved_address addr_;
+  grpc_closure read_closure_;
+  grpc_closure write_closure_;
+  // To be called when corresponding QuicGrpcServer closes all active
+  // connections.
+  grpc_closure orphan_fd_closure_;
+  grpc_closure destroyed_closure_;
+  // To be scheduled on another thread to actually read/write.
+  grpc_closure do_read_closure_;
+  grpc_closure do_write_closure_;
+  grpc_closure notify_on_write_closure_;
+  // True if orphan_cb is trigered.
+  bool orphan_notified_;
+  // True if grpc_fd_notify_on_write() is called after on_write() call.
+  bool notify_on_write_armed_;
+  // True if fd has been shutdown.
+  bool already_shutdown_;
+  // Object actually handles I/O events. Assigned in StartListening().
+  GrpcUdpHandler* udp_handler_ = nullptr;
+  // To be notified on destruction.
+  GrpcUdpHandlerFactory* handler_factory_ = nullptr;
+  // Required to access above fields.
+  gpr_mu mutex_;
+};
+
+GrpcUdpListener::GrpcUdpListener(grpc_udp_server* server, int fd,
+                                 const grpc_resolved_address* addr)
+    : fd_(fd),
+      server_(server),
+      orphan_notified_(false),
+      already_shutdown_(false) {
+  char* addr_str;
+  char* name;
+  grpc_sockaddr_to_string(&addr_str, addr, 1);
+  gpr_asprintf(&name, "udp-server-listener:%s", addr_str);
+  gpr_free(addr_str);
+  emfd_ = grpc_fd_create(fd, name, true);
+  memcpy(&addr_, addr, sizeof(grpc_resolved_address));
+  GPR_ASSERT(emfd_);
+  gpr_free(name);
+  gpr_mu_init(&mutex_);
+}
+
+GrpcUdpListener::~GrpcUdpListener() { gpr_mu_destroy(&mutex_); }
+
+/* the overall server */
+struct grpc_udp_server {
+  gpr_mu mu;
+
+  /* factory to use for creating and binding sockets, or NULL */
+  grpc_socket_factory* socket_factory;
+
+  /* active port count: how many ports are actually still listening */
+  size_t active_ports;
+  /* destroyed port count: how many ports are completely destroyed */
+  size_t destroyed_ports;
+
+  /* is this server shutting down? (boolean) */
+  int shutdown;
+
+  /* An array of listeners */
+  grpc_core::InlinedVector<GrpcUdpListener, 16> listeners;
+
+  /* factory for use to create udp listeners */
+  GrpcUdpHandlerFactory* handler_factory;
+
+  /* shutdown callback */
+  grpc_closure* shutdown_complete;
+
+  /* all pollsets interested in new connections */
+  grpc_pollset** pollsets;
+  /* number of pollsets in the pollsets array */
+  size_t pollset_count;
+  /* opaque object to pass to callbacks */
+  void* user_data;
+
+  /* latch has_so_reuseport during server creation */
+  bool so_reuseport;
+};
+
+static grpc_socket_factory* get_socket_factory(const grpc_channel_args* args) {
+  if (args) {
+    const grpc_arg* arg = grpc_channel_args_find(args, GRPC_ARG_SOCKET_FACTORY);
+    if (arg) {
+      GPR_ASSERT(arg->type == GRPC_ARG_POINTER);
+      return static_cast<grpc_socket_factory*>(arg->value.pointer.p);
+    }
+  }
+  return nullptr;
+}
+
+grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args) {
+  grpc_udp_server* s = grpc_core::New<grpc_udp_server>();
+  gpr_mu_init(&s->mu);
+  s->socket_factory = get_socket_factory(args);
+  if (s->socket_factory) {
+    grpc_socket_factory_ref(s->socket_factory);
+  }
+  s->active_ports = 0;
+  s->destroyed_ports = 0;
+  s->shutdown = 0;
+  s->so_reuseport = grpc_is_socket_reuse_port_supported();
+  return s;
+}
+
+// static
+void GrpcUdpListener::shutdown_fd(void* args, grpc_error* error) {
+  if (args == nullptr) {
+    // No-op if shutdown args are null.
+    return;
+  }
+  auto sp = static_cast<GrpcUdpListener*>(args);
+  gpr_mu_lock(sp->mutex());
+  gpr_log(GPR_DEBUG, "shutdown fd %d", sp->fd_);
+  grpc_fd_shutdown(sp->emfd_, GRPC_ERROR_REF(error));
+  sp->already_shutdown_ = true;
+  if (!sp->notify_on_write_armed_) {
+    // Re-arm write notification to notify listener with error. This is
+    // necessary to decrement active_ports.
+    sp->notify_on_write_armed_ = true;
+    grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_);
+  }
+  gpr_mu_unlock(sp->mutex());
+}
+
+static void finish_shutdown(grpc_udp_server* s) {
+  if (s->shutdown_complete != nullptr) {
+    GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE);
+  }
+
+  gpr_mu_destroy(&s->mu);
+
+  gpr_log(GPR_DEBUG, "Destroy all listeners.");
+  for (size_t i = 0; i < s->listeners.size(); ++i) {
+    s->listeners[i].OnDestroy();
+  }
+
+  if (s->socket_factory) {
+    grpc_socket_factory_unref(s->socket_factory);
+  }
+
+  grpc_core::Delete(s);
+}
+
+static void destroyed_port(void* server, grpc_error* error) {
+  grpc_udp_server* s = static_cast<grpc_udp_server*>(server);
+  gpr_mu_lock(&s->mu);
+  s->destroyed_ports++;
+  if (s->destroyed_ports == s->listeners.size()) {
+    gpr_mu_unlock(&s->mu);
+    finish_shutdown(s);
+  } else {
+    gpr_mu_unlock(&s->mu);
+  }
+}
+
+/* called when all listening endpoints have been shutdown, so no further
+   events will be received on them - at this point it's safe to destroy
+   things */
+static void deactivated_all_ports(grpc_udp_server* s) {
+  /* delete ALL the things */
+  gpr_mu_lock(&s->mu);
+
+  GPR_ASSERT(s->shutdown);
+
+  if (s->listeners.size() == 0) {
+    gpr_mu_unlock(&s->mu);
+    finish_shutdown(s);
+    return;
+  }
+  for (size_t i = 0; i < s->listeners.size(); ++i) {
+    s->listeners[i].OrphanFd();
+  }
+  gpr_mu_unlock(&s->mu);
+}
+
+void GrpcUdpListener::OrphanFd() {
+  gpr_log(GPR_DEBUG, "Orphan fd %d, emfd %p", fd_, emfd_);
+  grpc_unlink_if_unix_domain_socket(&addr_);
+
+  GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_,
+                    grpc_schedule_on_exec_ctx);
+  /* Because at this point, all listening sockets have been shutdown already, no
+   * need to call OnFdAboutToOrphan() to notify the handler again. */
+  grpc_fd_orphan(emfd_, &destroyed_closure_, nullptr, "udp_listener_shutdown");
+}
+
+void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) {
+  gpr_mu_lock(&s->mu);
+
+  GPR_ASSERT(!s->shutdown);
+  s->shutdown = 1;
+
+  s->shutdown_complete = on_done;
+
+  gpr_log(GPR_DEBUG, "start to destroy udp_server");
+  /* shutdown all fd's */
+  if (s->active_ports) {
+    for (size_t i = 0; i < s->listeners.size(); ++i) {
+      GrpcUdpListener* sp = &s->listeners[i];
+      sp->OnFdAboutToOrphan();
+    }
+    gpr_mu_unlock(&s->mu);
+  } else {
+    gpr_mu_unlock(&s->mu);
+    deactivated_all_ports(s);
+  }
+}
+
+void GrpcUdpListener::OnFdAboutToOrphan() {
+  gpr_mu_lock(&mutex_);
+  grpc_unlink_if_unix_domain_socket(&addr_);
+
+  GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_,
+                    grpc_schedule_on_exec_ctx);
+  if (!orphan_notified_ && udp_handler_ != nullptr) {
+    /* Singals udp_handler that the FD is about to be closed and
+     * should no longer be used. */
+    GRPC_CLOSURE_INIT(&orphan_fd_closure_, shutdown_fd, this,
+                      grpc_schedule_on_exec_ctx);
+    gpr_log(GPR_DEBUG, "fd %d about to be orphaned", fd_);
+    udp_handler_->OnFdAboutToOrphan(&orphan_fd_closure_, server_->user_data);
+    orphan_notified_ = true;
+  }
+  gpr_mu_unlock(&mutex_);
+}
+
+static int bind_socket(grpc_socket_factory* socket_factory, int sockfd,
+                       const grpc_resolved_address* addr) {
+  return (socket_factory != nullptr)
+             ? grpc_socket_factory_bind(socket_factory, sockfd, addr)
+             : bind(sockfd,
+                    reinterpret_cast<grpc_sockaddr*>(
+                        const_cast<char*>(addr->addr)),
+                    addr->len);
+}
+
+/* Prepare a recently-created socket for listening. */
+static int prepare_socket(grpc_socket_factory* socket_factory, int fd,
+                          const grpc_resolved_address* addr, int rcv_buf_size,
+                          int snd_buf_size, bool so_reuseport) {
+  grpc_resolved_address sockname_temp;
+  grpc_sockaddr* addr_ptr =
+      reinterpret_cast<grpc_sockaddr*>(const_cast<char*>(addr->addr));
+
+  if (fd < 0) {
+    goto error;
+  }
+
+  if (grpc_set_socket_nonblocking(fd, 1) != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR, "Unable to set nonblocking %d: %s", fd, strerror(errno));
+    goto error;
+  }
+  if (grpc_set_socket_cloexec(fd, 1) != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR, "Unable to set cloexec %d: %s", fd, strerror(errno));
+    goto error;
+  }
+
+  if (grpc_set_socket_ip_pktinfo_if_possible(fd) != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR, "Unable to set ip_pktinfo.");
+    goto error;
+  } else if (addr_ptr->sa_family == AF_INET6) {
+    if (grpc_set_socket_ipv6_recvpktinfo_if_possible(fd) != GRPC_ERROR_NONE) {
+      gpr_log(GPR_ERROR, "Unable to set ipv6_recvpktinfo.");
+      goto error;
+    }
+  }
+
+  if (grpc_set_socket_sndbuf(fd, snd_buf_size) != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR, "Failed to set send buffer size to %d bytes",
+            snd_buf_size);
+    goto error;
+  }
+
+  if (grpc_set_socket_rcvbuf(fd, rcv_buf_size) != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR, "Failed to set receive buffer size to %d bytes",
+            rcv_buf_size);
+    goto error;
+  }
+
+  {
+    int get_overflow = 1;
+    if (0 != setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow,
+                        sizeof(get_overflow))) {
+      gpr_log(GPR_INFO, "Failed to set socket overflow support");
+    }
+  }
+
+  if (so_reuseport && !grpc_is_unix_socket(addr) &&
+      grpc_set_socket_reuse_port(fd, 1) != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR, "Failed to set SO_REUSEPORT for fd %d", fd);
+    goto error;
+  }
+
+  if (bind_socket(socket_factory, fd, addr) < 0) {
+    char* addr_str;
+    grpc_sockaddr_to_string(&addr_str, addr, 0);
+    gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno));
+    gpr_free(addr_str);
+    goto error;
+  }
+
+  sockname_temp.len = static_cast<socklen_t>(sizeof(struct sockaddr_storage));
+
+  if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr),
+                  &sockname_temp.len) < 0) {
+    gpr_log(GPR_ERROR, "Unable to get the address socket %d is bound to: %s",
+            fd, strerror(errno));
+    goto error;
+  }
+
+  return grpc_sockaddr_get_port(&sockname_temp);
+
+error:
+  if (fd >= 0) {
+    close(fd);
+  }
+  return -1;
+}
+
+// static
+void GrpcUdpListener::do_read(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  /* TODO: the reason we hold server->mu here is merely to prevent fd
+   * shutdown while we are reading. However, it blocks do_write(). Switch to
+   * read lock if available. */
+  gpr_mu_lock(sp->mutex());
+  /* Tell the registered callback that data is available to read. */
+  if (!sp->already_shutdown_ && sp->udp_handler_->Read()) {
+    /* There maybe more packets to read. Schedule read_more_cb_ closure to run
+     * after finishing this event loop. */
+    GRPC_CLOSURE_SCHED(&sp->do_read_closure_, GRPC_ERROR_NONE);
+  } else {
+    /* Finish reading all the packets, re-arm the notification event so we can
+     * get another chance to read. Or fd already shutdown, re-arm to get a
+     * notification with shutdown error. */
+    grpc_fd_notify_on_read(sp->emfd_, &sp->read_closure_);
+  }
+  gpr_mu_unlock(sp->mutex());
+}
+
+// static
+void GrpcUdpListener::on_read(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  sp->OnRead(error, arg);
+}
+
+void GrpcUdpListener::OnRead(grpc_error* error, void* do_read_arg) {
+  if (error != GRPC_ERROR_NONE) {
+    gpr_mu_lock(&server_->mu);
+    if (0 == --server_->active_ports && server_->shutdown) {
+      gpr_mu_unlock(&server_->mu);
+      deactivated_all_ports(server_);
+    } else {
+      gpr_mu_unlock(&server_->mu);
+    }
+    return;
+  }
+
+  /* Read once. If there is more data to read, off load the work to another
+   * thread to finish. */
+  if (udp_handler_->Read()) {
+    /* There maybe more packets to read. Schedule read_more_cb_ closure to run
+     * after finishing this event loop. */
+    GRPC_CLOSURE_INIT(&do_read_closure_, do_read, do_read_arg,
+                      grpc_executor_scheduler(GRPC_EXECUTOR_LONG));
+    GRPC_CLOSURE_SCHED(&do_read_closure_, GRPC_ERROR_NONE);
+  } else {
+    /* Finish reading all the packets, re-arm the notification event so we can
+     * get another chance to read. Or fd already shutdown, re-arm to get a
+     * notification with shutdown error. */
+    grpc_fd_notify_on_read(emfd_, &read_closure_);
+  }
+}
+
+// static
+// Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback interface.
+void GrpcUdpListener::fd_notify_on_write_wrapper(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  gpr_mu_lock(sp->mutex());
+  if (!sp->notify_on_write_armed_) {
+    grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_);
+    sp->notify_on_write_armed_ = true;
+  }
+  gpr_mu_unlock(sp->mutex());
+}
+
+// static
+void GrpcUdpListener::do_write(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  gpr_mu_lock(sp->mutex());
+  if (sp->already_shutdown_) {
+    // If fd has been shutdown, don't write any more and re-arm notification.
+    grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_);
+  } else {
+    sp->notify_on_write_armed_ = false;
+    /* Tell the registered callback that the socket is writeable. */
+    GPR_ASSERT(error == GRPC_ERROR_NONE);
+    GRPC_CLOSURE_INIT(&sp->notify_on_write_closure_, fd_notify_on_write_wrapper,
+                      arg, grpc_schedule_on_exec_ctx);
+    sp->udp_handler_->OnCanWrite(sp->server_->user_data,
+                                 &sp->notify_on_write_closure_);
+  }
+  gpr_mu_unlock(sp->mutex());
+}
+
+// static
+void GrpcUdpListener::on_write(void* arg, grpc_error* error) {
+  GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg);
+  sp->OnCanWrite(error, arg);
+}
+
+void GrpcUdpListener::OnCanWrite(grpc_error* error, void* do_write_arg) {
+  if (error != GRPC_ERROR_NONE) {
+    gpr_mu_lock(&server_->mu);
+    if (0 == --server_->active_ports && server_->shutdown) {
+      gpr_mu_unlock(&server_->mu);
+      deactivated_all_ports(server_);
+    } else {
+      gpr_mu_unlock(&server_->mu);
+    }
+    return;
+  }
+
+  /* Schedule actual write in another thread. */
+  GRPC_CLOSURE_INIT(&do_write_closure_, do_write, do_write_arg,
+                    grpc_executor_scheduler(GRPC_EXECUTOR_LONG));
+
+  GRPC_CLOSURE_SCHED(&do_write_closure_, GRPC_ERROR_NONE);
+}
+
+static int add_socket_to_server(grpc_udp_server* s, int fd,
+                                const grpc_resolved_address* addr,
+                                int rcv_buf_size, int snd_buf_size) {
+  gpr_log(GPR_DEBUG, "add socket %d to server", fd);
+
+  int port = prepare_socket(s->socket_factory, fd, addr, rcv_buf_size,
+                            snd_buf_size, s->so_reuseport);
+  if (port >= 0) {
+    gpr_mu_lock(&s->mu);
+    s->listeners.emplace_back(s, fd, addr);
+    gpr_log(GPR_DEBUG,
+            "add socket %d to server for port %d, %zu listener(s) in total", fd,
+            port, s->listeners.size());
+    gpr_mu_unlock(&s->mu);
+  }
+  return port;
+}
+
+int grpc_udp_server_add_port(grpc_udp_server* s,
+                             const grpc_resolved_address* addr,
+                             int rcv_buf_size, int snd_buf_size,
+                             GrpcUdpHandlerFactory* handler_factory,
+                             size_t num_listeners) {
+  if (num_listeners > 1 && !s->so_reuseport) {
+    gpr_log(GPR_ERROR,
+            "Try to have multiple listeners on same port, but SO_REUSEPORT is "
+            "not supported. Only create 1 listener.");
+  }
+  char* addr_str;
+  grpc_sockaddr_to_string(&addr_str, addr, 1);
+  gpr_log(GPR_DEBUG, "add address: %s to server", addr_str);
+  gpr_free(addr_str);
+
+  int allocated_port1 = -1;
+  int allocated_port2 = -1;
+  int fd;
+  grpc_dualstack_mode dsmode;
+  grpc_resolved_address addr6_v4mapped;
+  grpc_resolved_address wild4;
+  grpc_resolved_address wild6;
+  grpc_resolved_address addr4_copy;
+  grpc_resolved_address* allocated_addr = nullptr;
+  grpc_resolved_address sockname_temp;
+  int port = 0;
+
+  /* Check if this is a wildcard port, and if so, try to keep the port the same
+     as some previously created listener. */
+  if (grpc_sockaddr_get_port(addr) == 0) {
+    /* Loop through existing listeners to find the port in use. */
+    for (size_t i = 0; i < s->listeners.size(); ++i) {
+      sockname_temp.len =
+          static_cast<socklen_t>(sizeof(struct sockaddr_storage));
+      if (0 == getsockname(s->listeners[i].fd(),
+                           reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr),
+                           &sockname_temp.len)) {
+        port = grpc_sockaddr_get_port(&sockname_temp);
+        if (port > 0) {
+          /* Found such a port, update |addr| to reflects this port. */
+          allocated_addr = static_cast<grpc_resolved_address*>(
+              gpr_malloc(sizeof(grpc_resolved_address)));
+          memcpy(allocated_addr, addr, sizeof(grpc_resolved_address));
+          grpc_sockaddr_set_port(allocated_addr, port);
+          addr = allocated_addr;
+          break;
+        }
+      }
+    }
+  }
+
+  if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+    addr = &addr6_v4mapped;
+  }
+
+  s->handler_factory = handler_factory;
+  for (size_t i = 0; i < num_listeners; ++i) {
+    /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
+    if (grpc_sockaddr_is_wildcard(addr, &port)) {
+      grpc_sockaddr_make_wildcards(port, &wild4, &wild6);
+
+      /* Try listening on IPv6 first. */
+      addr = &wild6;
+      // TODO(rjshade): Test and propagate the returned grpc_error*:
+      GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory(
+          s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd));
+      allocated_port1 =
+          add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size);
+      if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
+        if (port == 0) {
+          /* This is the first time to bind to |addr|. If its port is still
+           * wildcard port, update |addr| with the ephermeral port returned by
+           * kernel. Thus |addr| can have a specific port in following
+           * iterations. */
+          grpc_sockaddr_set_port(addr, allocated_port1);
+          port = allocated_port1;
+        } else if (allocated_port1 >= 0) {
+          /* The following sucessfully created socket should have same port as
+           * the first one. */
+          GPR_ASSERT(port == allocated_port1);
+        }
+        /* A dualstack socket is created, no need to create corresponding IPV4
+         * socket. */
+        continue;
+      }
+
+      /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
+      if (port == 0 && allocated_port1 > 0) {
+        /* |port| hasn't been assigned to an emphemeral port yet, |wild4| must
+         * have a wildcard port. Update it with the emphemeral port created
+         * during binding.*/
+        grpc_sockaddr_set_port(&wild4, allocated_port1);
+        port = allocated_port1;
+      }
+      /* |wild4| should have been updated with an emphemeral port by now. Use
+       * this IPV4 address to create a IPV4 socket. */
+      addr = &wild4;
+    }
+
+    // TODO(rjshade): Test and propagate the returned grpc_error*:
+    GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory(
+        s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd));
+    if (fd < 0) {
+      gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+    }
+    if (dsmode == GRPC_DSMODE_IPV4 &&
+        grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) {
+      addr = &addr4_copy;
+    }
+    allocated_port2 =
+        add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size);
+    if (port == 0) {
+      /* Update |addr| with the ephermeral port returned by kernel. So |addr|
+       * can have a specific port in following iterations. */
+      grpc_sockaddr_set_port(addr, allocated_port2);
+      port = allocated_port2;
+    } else if (allocated_port2 >= 0) {
+      GPR_ASSERT(port == allocated_port2);
+    }
+  }
+
+  gpr_free(allocated_addr);
+  return port;
+}
+
+int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index) {
+  if (port_index >= s->listeners.size()) {
+    return -1;
+  }
+
+  return s->listeners[port_index].fd();
+}
+
+void grpc_udp_server_start(grpc_udp_server* s, grpc_pollset** pollsets,
+                           size_t pollset_count, void* user_data) {
+  gpr_log(GPR_DEBUG, "grpc_udp_server_start");
+  gpr_mu_lock(&s->mu);
+  GPR_ASSERT(s->active_ports == 0);
+  s->pollsets = pollsets;
+  s->user_data = user_data;
+
+  for (size_t i = 0; i < s->listeners.size(); ++i) {
+    s->listeners[i].StartListening(pollsets, pollset_count, s->handler_factory);
+  }
+
+  gpr_mu_unlock(&s->mu);
+}
+
+void GrpcUdpListener::StartListening(grpc_pollset** pollsets,
+                                     size_t pollset_count,
+                                     GrpcUdpHandlerFactory* handler_factory) {
+  gpr_mu_lock(&mutex_);
+  handler_factory_ = handler_factory;
+  udp_handler_ = handler_factory->CreateUdpHandler(emfd_, server_->user_data);
+  for (size_t i = 0; i < pollset_count; i++) {
+    grpc_pollset_add_fd(pollsets[i], emfd_);
+  }
+  GRPC_CLOSURE_INIT(&read_closure_, on_read, this, grpc_schedule_on_exec_ctx);
+  grpc_fd_notify_on_read(emfd_, &read_closure_);
+
+  GRPC_CLOSURE_INIT(&write_closure_, on_write, this, grpc_schedule_on_exec_ctx);
+  notify_on_write_armed_ = true;
+  grpc_fd_notify_on_write(emfd_, &write_closure_);
+
+  /* Registered for both read and write callbacks: increment active_ports
+   * twice to account for this, and delay free-ing of memory until both
+   * on_read and on_write have fired. */
+  server_->active_ports += 2;
+  gpr_mu_unlock(&mutex_);
+}
+
+void GrpcUdpListener::OnDestroy() {
+  if (udp_handler_ != nullptr) {
+    handler_factory_->DestroyUdpHandler(udp_handler_);
+  }
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/udp_server.h b/third_party/grpc/src/core/lib/iomgr/udp_server.h
new file mode 100644
index 0000000..3656791
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/udp_server.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_UDP_SERVER_H
+#define GRPC_CORE_LIB_IOMGR_UDP_SERVER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+
+/* Forward decl of struct grpc_server */
+/* This is not typedef'ed to avoid a typedef-redefinition error */
+struct grpc_server;
+
+/* Forward decl of grpc_udp_server */
+typedef struct grpc_udp_server grpc_udp_server;
+
+/* An interface associated with a socket. udp server delivers I/O event on that
+ * socket to the subclass of this interface which is created through
+ * GrpcUdpHandlerFactory.
+ * Its implementation should do the real IO work, e.g. read packet and write. */
+class GrpcUdpHandler {
+ public:
+  GrpcUdpHandler(grpc_fd* emfd, void* user_data) {}
+  virtual ~GrpcUdpHandler() {}
+
+  // Interfaces to be implemented by subclasses to do the actual setup/tear down
+  // or I/O.
+
+  // Called when data is available to read from the socket. Returns true if
+  // there is more data to read after this call.
+  virtual bool Read() GRPC_ABSTRACT;
+  // Called when socket becomes write unblocked. The given closure should be
+  // scheduled when the socket becomes blocked next time.
+  virtual void OnCanWrite(void* user_data,
+                          grpc_closure* notify_on_write_closure) GRPC_ABSTRACT;
+  // Called before the gRPC FD is orphaned. Notify udp server to continue
+  // orphaning fd by scheduling the given closure, afterwards the associated fd
+  // will be closed.
+  virtual void OnFdAboutToOrphan(grpc_closure* orphan_fd_closure,
+                                 void* user_data) GRPC_ABSTRACT;
+
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+class GrpcUdpHandlerFactory {
+ public:
+  virtual ~GrpcUdpHandlerFactory() {}
+  /* Called when start to listen on a socket.
+   * Return an instance of the implementation of GrpcUdpHandler interface which
+   * will process I/O events for this socket from now on. */
+  virtual GrpcUdpHandler* CreateUdpHandler(grpc_fd* emfd,
+                                           void* user_data) GRPC_ABSTRACT;
+  virtual void DestroyUdpHandler(GrpcUdpHandler* handler) GRPC_ABSTRACT;
+
+  GRPC_ABSTRACT_BASE_CLASS
+};
+
+/* Create a server, initially not bound to any ports */
+grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args);
+
+/* Start listening to bound ports. user_data is passed to callbacks. */
+void grpc_udp_server_start(grpc_udp_server* udp_server, grpc_pollset** pollsets,
+                           size_t pollset_count, void* user_data);
+
+int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index);
+
+/* Add a port to the server, returning port number on success, or negative
+   on failure.
+
+   Create |num_listeners| sockets for given address to listen on using
+   SO_REUSEPORT if supported.
+
+   The :: and 0.0.0.0 wildcard addresses are treated identically, accepting
+   both IPv4 and IPv6 connections, but :: is the preferred style. This usually
+   creates |num_listeners| sockets, but possibly 2 * |num_listeners| on systems
+   which support IPv6, but not dualstack sockets. */
+
+/* TODO(ctiller): deprecate this, and make grpc_udp_server_add_ports to handle
+                  all of the multiple socket port matching logic in one place */
+int grpc_udp_server_add_port(grpc_udp_server* s,
+                             const grpc_resolved_address* addr,
+                             int rcv_buf_size, int snd_buf_size,
+                             GrpcUdpHandlerFactory* handler_factory,
+                             size_t num_listeners);
+
+void grpc_udp_server_destroy(grpc_udp_server* server, grpc_closure* on_done);
+
+#endif /* GRPC_CORE_LIB_IOMGR_UDP_SERVER_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.cc b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.cc
new file mode 100644
index 0000000..22fcaf5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.cc
@@ -0,0 +1,104 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_HAVE_UNIX_SOCKET
+
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+void grpc_create_socketpair_if_unix(int sv[2]) {
+  GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+}
+
+grpc_error* grpc_resolve_unix_domain_address(const char* name,
+                                             grpc_resolved_addresses** addrs) {
+  struct sockaddr_un* un;
+  if (strlen(name) >
+      GPR_ARRAY_SIZE(((struct sockaddr_un*)nullptr)->sun_path) - 1) {
+    char* err_msg;
+    grpc_error* err;
+    gpr_asprintf(&err_msg,
+                 "Path name should not have more than %" PRIuPTR " characters.",
+                 GPR_ARRAY_SIZE(un->sun_path) - 1);
+    err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg);
+    gpr_free(err_msg);
+    return err;
+  }
+  *addrs = static_cast<grpc_resolved_addresses*>(
+      gpr_malloc(sizeof(grpc_resolved_addresses)));
+  (*addrs)->naddrs = 1;
+  (*addrs)->addrs = static_cast<grpc_resolved_address*>(
+      gpr_malloc(sizeof(grpc_resolved_address)));
+  un = reinterpret_cast<struct sockaddr_un*>((*addrs)->addrs->addr);
+  un->sun_family = AF_UNIX;
+  strncpy(un->sun_path, name, sizeof(un->sun_path));
+  (*addrs)->addrs->len =
+      static_cast<socklen_t>(strlen(un->sun_path) + sizeof(un->sun_family) + 1);
+  return GRPC_ERROR_NONE;
+}
+
+int grpc_is_unix_socket(const grpc_resolved_address* resolved_addr) {
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  return addr->sa_family == AF_UNIX;
+}
+
+void grpc_unlink_if_unix_domain_socket(
+    const grpc_resolved_address* resolved_addr) {
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  if (addr->sa_family != AF_UNIX) {
+    return;
+  }
+  struct sockaddr_un* un = reinterpret_cast<struct sockaddr_un*>(
+      const_cast<char*>(resolved_addr->addr));
+  struct stat st;
+
+  if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {
+    unlink(un->sun_path);
+  }
+}
+
+char* grpc_sockaddr_to_uri_unix_if_possible(
+    const grpc_resolved_address* resolved_addr) {
+  const grpc_sockaddr* addr =
+      reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
+  if (addr->sa_family != AF_UNIX) {
+    return nullptr;
+  }
+
+  char* result;
+  gpr_asprintf(&result, "unix:%s", ((struct sockaddr_un*)addr)->sun_path);
+  return result;
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.h b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.h
new file mode 100644
index 0000000..917d032
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/iomgr/resolve_address.h"
+
+void grpc_create_socketpair_if_unix(int sv[2]);
+
+grpc_error* grpc_resolve_unix_domain_address(
+    const char* name, grpc_resolved_addresses** addresses);
+
+int grpc_is_unix_socket(const grpc_resolved_address* resolved_addr);
+
+void grpc_unlink_if_unix_domain_socket(
+    const grpc_resolved_address* resolved_addr);
+
+char* grpc_sockaddr_to_uri_unix_if_possible(
+    const grpc_resolved_address* resolved_addr);
+
+#endif /* GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix_noop.cc b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix_noop.cc
new file mode 100644
index 0000000..dfab3e0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix_noop.cc
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+
+#ifndef GRPC_HAVE_UNIX_SOCKET
+
+#include <grpc/support/log.h>
+
+void grpc_create_socketpair_if_unix(int sv[2]) {
+  // TODO: Either implement this for the non-Unix socket case or make
+  // sure that it is never called in any such case. Until then, leave an
+  // assertion to notify if this gets called inadvertently
+  GPR_ASSERT(0);
+}
+
+grpc_error* grpc_resolve_unix_domain_address(
+    const char* name, grpc_resolved_addresses** addresses) {
+  *addresses = NULL;
+  return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+      "Unix domain sockets are not supported on Windows");
+}
+
+int grpc_is_unix_socket(const grpc_resolved_address* addr) { return false; }
+
+void grpc_unlink_if_unix_domain_socket(const grpc_resolved_address* addr) {}
+
+char* grpc_sockaddr_to_uri_unix_if_possible(const grpc_resolved_address* addr) {
+  return NULL;
+}
+
+#endif
diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.cc
new file mode 100644
index 0000000..74faa63
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.cc
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_WAKEUP_FD
+
+#include "src/core/lib/iomgr/wakeup_fd_cv.h"
+
+#include <errno.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/gpr/useful.h"
+#include "src/core/lib/gprpp/thd.h"
+
+#define MAX_TABLE_RESIZE 256
+
+extern grpc_cv_fd_table g_cvfds;
+
+static grpc_error* cv_fd_init(grpc_wakeup_fd* fd_info) {
+  unsigned int i, newsize;
+  int idx;
+  gpr_mu_lock(&g_cvfds.mu);
+  if (!g_cvfds.free_fds) {
+    newsize = GPR_MIN(g_cvfds.size * 2, g_cvfds.size + MAX_TABLE_RESIZE);
+    g_cvfds.cvfds = static_cast<grpc_fd_node*>(
+        gpr_realloc(g_cvfds.cvfds, sizeof(grpc_fd_node) * newsize));
+    for (i = g_cvfds.size; i < newsize; i++) {
+      g_cvfds.cvfds[i].is_set = 0;
+      g_cvfds.cvfds[i].cvs = nullptr;
+      g_cvfds.cvfds[i].next_free = g_cvfds.free_fds;
+      g_cvfds.free_fds = &g_cvfds.cvfds[i];
+    }
+    g_cvfds.size = newsize;
+  }
+
+  idx = static_cast<int>(g_cvfds.free_fds - g_cvfds.cvfds);
+  g_cvfds.free_fds = g_cvfds.free_fds->next_free;
+  g_cvfds.cvfds[idx].cvs = nullptr;
+  g_cvfds.cvfds[idx].is_set = 0;
+  fd_info->read_fd = GRPC_IDX_TO_FD(idx);
+  fd_info->write_fd = -1;
+  gpr_mu_unlock(&g_cvfds.mu);
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* cv_fd_wakeup(grpc_wakeup_fd* fd_info) {
+  grpc_cv_node* cvn;
+  gpr_mu_lock(&g_cvfds.mu);
+  g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].is_set = 1;
+  cvn = g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].cvs;
+  while (cvn) {
+    gpr_cv_signal(cvn->cv);
+    cvn = cvn->next;
+  }
+  gpr_mu_unlock(&g_cvfds.mu);
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* cv_fd_consume(grpc_wakeup_fd* fd_info) {
+  gpr_mu_lock(&g_cvfds.mu);
+  g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].is_set = 0;
+  gpr_mu_unlock(&g_cvfds.mu);
+  return GRPC_ERROR_NONE;
+}
+
+static void cv_fd_destroy(grpc_wakeup_fd* fd_info) {
+  if (fd_info->read_fd == 0) {
+    return;
+  }
+  gpr_mu_lock(&g_cvfds.mu);
+  // Assert that there are no active pollers
+  GPR_ASSERT(!g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].cvs);
+  g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].next_free = g_cvfds.free_fds;
+  g_cvfds.free_fds = &g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)];
+  gpr_mu_unlock(&g_cvfds.mu);
+}
+
+static int cv_check_availability(void) { return 1; }
+
+const grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable = {
+    cv_fd_init, cv_fd_consume, cv_fd_wakeup, cv_fd_destroy,
+    cv_check_availability};
+
+#endif /* GRPC_POSIX_WAKUP_FD */
diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.h b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.h
new file mode 100644
index 0000000..86365f0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/*
+ * wakeup_fd_cv uses condition variables to implement wakeup fds.
+ *
+ * It is intended for use only in cases when eventfd() and pipe() are not
+ * available.  It can only be used with the "poll" engine.
+ *
+ * Implementation:
+ * A global table of cv wakeup fds is mantained.  A cv wakeup fd is a negative
+ * file descriptor.  poll() is then run in a background thread with only the
+ * real socket fds while we wait on a condition variable trigged by either the
+ * poll() completion or a wakeup_fd() call.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H
+#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/ev_posix.h"
+
+#define GRPC_FD_TO_IDX(fd) (-(fd)-1)
+#define GRPC_IDX_TO_FD(idx) (-(idx)-1)
+
+typedef struct grpc_cv_node {
+  gpr_cv* cv;
+  struct grpc_cv_node* next;
+  struct grpc_cv_node* prev;
+} grpc_cv_node;
+
+typedef struct grpc_fd_node {
+  int is_set;
+  grpc_cv_node* cvs;
+  struct grpc_fd_node* next_free;
+} grpc_fd_node;
+
+typedef struct grpc_cv_fd_table {
+  gpr_mu mu;
+  gpr_refcount pollcount;
+  gpr_cv shutdown_cv;
+  grpc_fd_node* cvfds;
+  grpc_fd_node* free_fds;
+  unsigned int size;
+  grpc_poll_function_type poll;
+} grpc_cv_fd_table;
+
+extern const grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable;
+
+#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_eventfd.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_eventfd.cc
new file mode 100644
index 0000000..d68c9ad
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_eventfd.cc
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_LINUX_EVENTFD
+
+#include <errno.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/profiling/timers.h"
+
+static grpc_error* eventfd_create(grpc_wakeup_fd* fd_info) {
+  fd_info->read_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+  fd_info->write_fd = -1;
+  if (fd_info->read_fd < 0) {
+    return GRPC_OS_ERROR(errno, "eventfd");
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* eventfd_consume(grpc_wakeup_fd* fd_info) {
+  eventfd_t value;
+  int err;
+  do {
+    err = eventfd_read(fd_info->read_fd, &value);
+  } while (err < 0 && errno == EINTR);
+  if (err < 0 && errno != EAGAIN) {
+    return GRPC_OS_ERROR(errno, "eventfd_read");
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* eventfd_wakeup(grpc_wakeup_fd* fd_info) {
+  GPR_TIMER_SCOPE("eventfd_wakeup", 0);
+  int err;
+  do {
+    err = eventfd_write(fd_info->read_fd, 1);
+  } while (err < 0 && errno == EINTR);
+  if (err < 0) {
+    return GRPC_OS_ERROR(errno, "eventfd_write");
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static void eventfd_destroy(grpc_wakeup_fd* fd_info) {
+  if (fd_info->read_fd != 0) close(fd_info->read_fd);
+}
+
+static int eventfd_check_availability(void) {
+  const int efd = eventfd(0, 0);
+  const int is_available = efd >= 0;
+  if (is_available) close(efd);
+  return is_available;
+}
+
+const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
+    eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy,
+    eventfd_check_availability};
+
+#endif /* GRPC_LINUX_EVENTFD */
diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_nospecial.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_nospecial.cc
new file mode 100644
index 0000000..6477892
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_nospecial.cc
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/*
+ * This is a dummy file to provide an invalid specialized_wakeup_fd_vtable on
+ * systems without anything better than pipe.
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_NO_SPECIAL_WAKEUP_FD
+
+#include <stddef.h>
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+
+static int check_availability_invalid(void) { return 0; }
+
+const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
+    nullptr, nullptr, nullptr, nullptr, check_availability_invalid};
+
+#endif /* GRPC_POSIX_NO_SPECIAL_WAKEUP_FD */
diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.cc
new file mode 100644
index 0000000..cb17390
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.cc
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_WAKEUP_FD
+
+#include "src/core/lib/iomgr/wakeup_fd_pipe.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+static grpc_error* pipe_init(grpc_wakeup_fd* fd_info) {
+  int pipefd[2];
+  int r = pipe(pipefd);
+  if (0 != r) {
+    gpr_log(GPR_ERROR, "pipe creation failed (%d): %s", errno, strerror(errno));
+    return GRPC_OS_ERROR(errno, "pipe");
+  }
+  grpc_error* err;
+  err = grpc_set_socket_nonblocking(pipefd[0], 1);
+  if (err != GRPC_ERROR_NONE) return err;
+  err = grpc_set_socket_nonblocking(pipefd[1], 1);
+  if (err != GRPC_ERROR_NONE) return err;
+  fd_info->read_fd = pipefd[0];
+  fd_info->write_fd = pipefd[1];
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* pipe_consume(grpc_wakeup_fd* fd_info) {
+  char buf[128];
+  ssize_t r;
+
+  for (;;) {
+    r = read(fd_info->read_fd, buf, sizeof(buf));
+    if (r > 0) continue;
+    if (r == 0) return GRPC_ERROR_NONE;
+    switch (errno) {
+      case EAGAIN:
+        return GRPC_ERROR_NONE;
+      case EINTR:
+        continue;
+      default:
+        return GRPC_OS_ERROR(errno, "read");
+    }
+  }
+}
+
+static grpc_error* pipe_wakeup(grpc_wakeup_fd* fd_info) {
+  char c = 0;
+  while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR)
+    ;
+  return GRPC_ERROR_NONE;
+}
+
+static void pipe_destroy(grpc_wakeup_fd* fd_info) {
+  if (fd_info->read_fd != 0) close(fd_info->read_fd);
+  if (fd_info->write_fd != 0) close(fd_info->write_fd);
+}
+
+static int pipe_check_availability(void) {
+  grpc_wakeup_fd fd;
+  fd.read_fd = fd.write_fd = -1;
+
+  if (pipe_init(&fd) == GRPC_ERROR_NONE) {
+    pipe_destroy(&fd);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable = {
+    pipe_init, pipe_consume, pipe_wakeup, pipe_destroy,
+    pipe_check_availability};
+
+#endif /* GPR_POSIX_WAKUP_FD */
diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.h b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.h
new file mode 100644
index 0000000..1756976
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H
+#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+
+extern const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable;
+
+#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H */
diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.cc
new file mode 100644
index 0000000..b5b8b37
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.cc
@@ -0,0 +1,87 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_POSIX_WAKEUP_FD
+
+#include <stddef.h>
+#include "src/core/lib/iomgr/wakeup_fd_cv.h"
+#include "src/core/lib/iomgr/wakeup_fd_pipe.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+
+static const grpc_wakeup_fd_vtable* wakeup_fd_vtable = nullptr;
+
+int grpc_allow_specialized_wakeup_fd = 1;
+int grpc_allow_pipe_wakeup_fd = 1;
+
+int has_real_wakeup_fd = 1;
+int cv_wakeup_fds_enabled = 0;
+
+void grpc_wakeup_fd_global_init(void) {
+  if (grpc_allow_specialized_wakeup_fd &&
+      grpc_specialized_wakeup_fd_vtable.check_availability()) {
+    wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable;
+  } else if (grpc_allow_pipe_wakeup_fd &&
+             grpc_pipe_wakeup_fd_vtable.check_availability()) {
+    wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable;
+  } else {
+    has_real_wakeup_fd = 0;
+  }
+}
+
+void grpc_wakeup_fd_global_destroy(void) { wakeup_fd_vtable = nullptr; }
+
+int grpc_has_wakeup_fd(void) { return has_real_wakeup_fd; }
+
+int grpc_cv_wakeup_fds_enabled(void) { return cv_wakeup_fds_enabled; }
+
+void grpc_enable_cv_wakeup_fds(int enable) { cv_wakeup_fds_enabled = enable; }
+
+grpc_error* grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info) {
+  if (cv_wakeup_fds_enabled) {
+    return grpc_cv_wakeup_fd_vtable.init(fd_info);
+  }
+  return wakeup_fd_vtable->init(fd_info);
+}
+
+grpc_error* grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info) {
+  if (cv_wakeup_fds_enabled) {
+    return grpc_cv_wakeup_fd_vtable.consume(fd_info);
+  }
+  return wakeup_fd_vtable->consume(fd_info);
+}
+
+grpc_error* grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info) {
+  if (cv_wakeup_fds_enabled) {
+    return grpc_cv_wakeup_fd_vtable.wakeup(fd_info);
+  }
+  return wakeup_fd_vtable->wakeup(fd_info);
+}
+
+void grpc_wakeup_fd_destroy(grpc_wakeup_fd* fd_info) {
+  if (cv_wakeup_fds_enabled) {
+    grpc_cv_wakeup_fd_vtable.destroy(fd_info);
+  } else {
+    wakeup_fd_vtable->destroy(fd_info);
+  }
+}
+
+#endif /* GRPC_POSIX_WAKEUP_FD */
diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.h b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.h
new file mode 100644
index 0000000..670c319
--- /dev/null
+++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.h
@@ -0,0 +1,96 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/*
+ * wakeup_fd abstracts the concept of a file descriptor for the purpose of
+ * waking up a thread in select()/poll()/epoll_wait()/etc.
+
+ * The poll() family of system calls provide a way for a thread to block until
+ * there is activity on one (or more) of a set of file descriptors. An
+ * application may wish to wake up this thread to do non file related work. The
+ * typical way to do this is to add a pipe to the set of file descriptors, then
+ * write to the pipe to wake up the thread in poll().
+ *
+ * Linux has a lighter weight eventfd specifically designed for this purpose.
+ * wakeup_fd abstracts the difference between the two.
+ *
+ * Setup:
+ * 1. Before calling anything, call global_init() at least once.
+ * 1. Call grpc_wakeup_fd_init() to set up a wakeup_fd.
+ * 2. Add the result of GRPC_WAKEUP_FD_FD to the set of monitored file
+ *    descriptors for the poll() style API you are using. Monitor the file
+ *    descriptor for readability.
+ * 3. To tear down, call grpc_wakeup_fd_destroy(). This closes the underlying
+ *    file descriptor.
+ *
+ * Usage:
+ * 1. To wake up a polling thread, call grpc_wakeup_fd_wakeup() on a wakeup_fd
+ *    it is monitoring.
+ * 2. If the polling thread was awakened by a wakeup_fd event, call
+ *    grpc_wakeup_fd_consume_wakeup() on it.
+ */
+#ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/error.h"
+
+void grpc_wakeup_fd_global_init(void);
+void grpc_wakeup_fd_global_destroy(void);
+
+/* Force using the fallback implementation. This is intended for testing
+ * purposes only.*/
+void grpc_wakeup_fd_global_init_force_fallback(void);
+
+int grpc_has_wakeup_fd(void);
+int grpc_cv_wakeup_fds_enabled(void);
+void grpc_enable_cv_wakeup_fds(int enable);
+
+typedef struct grpc_wakeup_fd grpc_wakeup_fd;
+
+typedef struct grpc_wakeup_fd_vtable {
+  grpc_error* (*init)(grpc_wakeup_fd* fd_info);
+  grpc_error* (*consume)(grpc_wakeup_fd* fd_info);
+  grpc_error* (*wakeup)(grpc_wakeup_fd* fd_info);
+  void (*destroy)(grpc_wakeup_fd* fd_info);
+  /* Must be called before calling any other functions */
+  int (*check_availability)(void);
+} grpc_wakeup_fd_vtable;
+
+struct grpc_wakeup_fd {
+  int read_fd;
+  int write_fd;
+};
+
+extern int grpc_allow_specialized_wakeup_fd;
+extern int grpc_allow_pipe_wakeup_fd;
+
+#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd)
+
+grpc_error* grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info) GRPC_MUST_USE_RESULT;
+grpc_error* grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info)
+    GRPC_MUST_USE_RESULT;
+grpc_error* grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info) GRPC_MUST_USE_RESULT;
+void grpc_wakeup_fd_destroy(grpc_wakeup_fd* fd_info);
+
+/* Defined in some specialized implementation's .c file, or by
+ * wakeup_fd_nospecial.c if no such implementation exists. */
+extern const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable;
+
+#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H */
diff --git a/third_party/grpc/src/core/lib/json/json.cc b/third_party/grpc/src/core/lib/json/json.cc
new file mode 100644
index 0000000..e78b73c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/json/json.cc
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/json/json.h"
+
+grpc_json* grpc_json_create(grpc_json_type type) {
+  grpc_json* json = static_cast<grpc_json*>(gpr_zalloc(sizeof(*json)));
+  json->type = type;
+
+  return json;
+}
+
+void grpc_json_destroy(grpc_json* json) {
+  while (json->child) {
+    grpc_json_destroy(json->child);
+  }
+
+  if (json->next) {
+    json->next->prev = json->prev;
+  }
+
+  if (json->prev) {
+    json->prev->next = json->next;
+  } else if (json->parent) {
+    json->parent->child = json->next;
+  }
+
+  if (json->owns_value) {
+    gpr_free((void*)json->value);
+  }
+
+  gpr_free(json);
+}
+
+grpc_json* grpc_json_link_child(grpc_json* parent, grpc_json* child,
+                                grpc_json* sibling) {
+  // link child up to parent
+  child->parent = parent;
+  // first child case.
+  if (parent->child == nullptr) {
+    GPR_ASSERT(sibling == nullptr);
+    parent->child = child;
+    return child;
+  }
+  if (sibling == nullptr) {
+    sibling = parent->child;
+  }
+  // always find the right most sibling.
+  while (sibling->next != nullptr) {
+    sibling = sibling->next;
+  }
+  sibling->next = child;
+  return child;
+}
+
+grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent,
+                                  const char* key, const char* value,
+                                  grpc_json_type type, bool owns_value) {
+  grpc_json* child = grpc_json_create(type);
+  grpc_json_link_child(parent, child, sibling);
+  child->owns_value = owns_value;
+  child->value = value;
+  child->key = key;
+  return child;
+}
+
+grpc_json* grpc_json_add_number_string_child(grpc_json* parent, grpc_json* it,
+                                             const char* name, int64_t num) {
+  char* num_str;
+  gpr_asprintf(&num_str, "%" PRId64, num);
+  return grpc_json_create_child(it, parent, name, num_str, GRPC_JSON_STRING,
+                                true);
+}
diff --git a/third_party/grpc/src/core/lib/json/json.h b/third_party/grpc/src/core/lib/json/json.h
new file mode 100644
index 0000000..8173845
--- /dev/null
+++ b/third_party/grpc/src/core/lib/json/json.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_JSON_JSON_H
+#define GRPC_CORE_LIB_JSON_JSON_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "src/core/lib/json/json_common.h"
+
+/* A tree-like structure to hold json values. The key and value pointers
+ * are not owned by it.
+ */
+typedef struct grpc_json {
+  struct grpc_json* next;
+  struct grpc_json* prev;
+  struct grpc_json* child;
+  struct grpc_json* parent;
+
+  grpc_json_type type;
+  const char* key;
+  const char* value;
+
+  /* if set, destructor will free value */
+  bool owns_value;
+} grpc_json;
+
+/* The next two functions are going to parse the input string, and
+ * modify it in the process, in order to use its space to store
+ * all of the keys and values for the returned object tree.
+ *
+ * They assume UTF-8 input stream, and will output UTF-8 encoded
+ * strings in the tree. The input stream's UTF-8 isn't validated,
+ * as in, what you input is what you get as an output.
+ *
+ * All the keys and values in the grpc_json objects will be strings
+ * pointing at your input buffer.
+ *
+ * Delete the allocated tree afterward using grpc_json_destroy().
+ */
+grpc_json* grpc_json_parse_string_with_len(char* input, size_t size);
+grpc_json* grpc_json_parse_string(char* input);
+
+/* This function will create a new string using gpr_realloc, and will
+ * deserialize the grpc_json tree into it. It'll be zero-terminated,
+ * but will be allocated in chunks of 256 bytes.
+ *
+ * The indent parameter controls the way the output is formatted.
+ * If indent is 0, then newlines will be suppressed as well, and the
+ * output will be condensed at its maximum.
+ */
+char* grpc_json_dump_to_string(grpc_json* json, int indent);
+
+/* Use these to create or delete a grpc_json object.
+ * Deletion is recursive. We will not attempt to free any of the strings
+ * in any of the objects of that tree, unless the boolean, owns_value,
+ * is true.
+ */
+grpc_json* grpc_json_create(grpc_json_type type);
+void grpc_json_destroy(grpc_json* json);
+
+/* Links the child json object into the parent's json tree. If the parent
+ * already has children, then passing in the most recently added child as the
+ * sibling parameter is an optimization. For if sibling is NULL, this function
+ * will manually traverse the tree in order to find the right most sibling.
+ */
+grpc_json* grpc_json_link_child(grpc_json* parent, grpc_json* child,
+                                grpc_json* sibling);
+
+/* Creates a child json object into the parent's json tree then links it in
+ * as described above. */
+grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent,
+                                  const char* key, const char* value,
+                                  grpc_json_type type, bool owns_value);
+
+/* Creates a child json string object from the integer num, then links the
+   json object into the parent's json tree */
+grpc_json* grpc_json_add_number_string_child(grpc_json* parent, grpc_json* it,
+                                             const char* name, int64_t num);
+
+#endif /* GRPC_CORE_LIB_JSON_JSON_H */
diff --git a/third_party/grpc/src/core/lib/json/json_common.h b/third_party/grpc/src/core/lib/json/json_common.h
new file mode 100644
index 0000000..bfa2ba3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/json/json_common.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_JSON_JSON_COMMON_H
+#define GRPC_CORE_LIB_JSON_JSON_COMMON_H
+
+/* The various json types. */
+typedef enum {
+  GRPC_JSON_OBJECT,
+  GRPC_JSON_ARRAY,
+  GRPC_JSON_STRING,
+  GRPC_JSON_NUMBER,
+  GRPC_JSON_TRUE,
+  GRPC_JSON_FALSE,
+  GRPC_JSON_NULL,
+  GRPC_JSON_TOP_LEVEL
+} grpc_json_type;
+
+#endif /* GRPC_CORE_LIB_JSON_JSON_COMMON_H */
diff --git a/third_party/grpc/src/core/lib/json/json_reader.cc b/third_party/grpc/src/core/lib/json/json_reader.cc
new file mode 100644
index 0000000..819572e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/json/json_reader.cc
@@ -0,0 +1,663 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/json/json_reader.h"
+
+static void json_reader_string_clear(grpc_json_reader* reader) {
+  reader->vtable->string_clear(reader->userdata);
+}
+
+static void json_reader_string_add_char(grpc_json_reader* reader, uint32_t c) {
+  reader->vtable->string_add_char(reader->userdata, c);
+}
+
+static void json_reader_string_add_utf32(grpc_json_reader* reader,
+                                         uint32_t utf32) {
+  reader->vtable->string_add_utf32(reader->userdata, utf32);
+}
+
+static uint32_t grpc_json_reader_read_char(grpc_json_reader* reader) {
+  return reader->vtable->read_char(reader->userdata);
+}
+
+static void json_reader_container_begins(grpc_json_reader* reader,
+                                         grpc_json_type type) {
+  reader->vtable->container_begins(reader->userdata, type);
+}
+
+static grpc_json_type grpc_json_reader_container_ends(
+    grpc_json_reader* reader) {
+  return reader->vtable->container_ends(reader->userdata);
+}
+
+static void json_reader_set_key(grpc_json_reader* reader) {
+  reader->vtable->set_key(reader->userdata);
+}
+
+static void json_reader_set_string(grpc_json_reader* reader) {
+  reader->vtable->set_string(reader->userdata);
+}
+
+static int json_reader_set_number(grpc_json_reader* reader) {
+  return reader->vtable->set_number(reader->userdata);
+}
+
+static void json_reader_set_true(grpc_json_reader* reader) {
+  reader->vtable->set_true(reader->userdata);
+}
+
+static void json_reader_set_false(grpc_json_reader* reader) {
+  reader->vtable->set_false(reader->userdata);
+}
+
+static void json_reader_set_null(grpc_json_reader* reader) {
+  reader->vtable->set_null(reader->userdata);
+}
+
+/* Call this function to initialize the reader structure. */
+void grpc_json_reader_init(grpc_json_reader* reader,
+                           grpc_json_reader_vtable* vtable, void* userdata) {
+  memset(reader, 0, sizeof(*reader));
+  reader->vtable = vtable;
+  reader->userdata = userdata;
+  json_reader_string_clear(reader);
+  reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
+}
+
+int grpc_json_reader_is_complete(grpc_json_reader* reader) {
+  return ((reader->depth == 0) &&
+          ((reader->state == GRPC_JSON_STATE_END) ||
+           (reader->state == GRPC_JSON_STATE_VALUE_END)));
+}
+
+grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader) {
+  uint32_t c, success;
+
+  /* This state-machine is a strict implementation of ECMA-404 */
+  for (;;) {
+    c = grpc_json_reader_read_char(reader);
+    switch (c) {
+      /* Let's process the error cases first. */
+      case GRPC_JSON_READ_CHAR_ERROR:
+        return GRPC_JSON_READ_ERROR;
+
+      case GRPC_JSON_READ_CHAR_EAGAIN:
+        return GRPC_JSON_EAGAIN;
+
+      case GRPC_JSON_READ_CHAR_EOF:
+        if (grpc_json_reader_is_complete(reader)) {
+          return GRPC_JSON_DONE;
+        } else {
+          return GRPC_JSON_PARSE_ERROR;
+        }
+        break;
+
+      /* Processing whitespaces. */
+      case ' ':
+      case '\t':
+      case '\n':
+      case '\r':
+        switch (reader->state) {
+          case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
+          case GRPC_JSON_STATE_OBJECT_KEY_END:
+          case GRPC_JSON_STATE_VALUE_BEGIN:
+          case GRPC_JSON_STATE_VALUE_END:
+          case GRPC_JSON_STATE_END:
+            break;
+
+          case GRPC_JSON_STATE_OBJECT_KEY_STRING:
+          case GRPC_JSON_STATE_VALUE_STRING:
+            if (c != ' ') return GRPC_JSON_PARSE_ERROR;
+            if (reader->unicode_high_surrogate != 0)
+              return GRPC_JSON_PARSE_ERROR;
+            json_reader_string_add_char(reader, c);
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NUMBER:
+          case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
+          case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
+          case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
+            success = static_cast<uint32_t>(json_reader_set_number(reader));
+            if (!success) return GRPC_JSON_PARSE_ERROR;
+            json_reader_string_clear(reader);
+            reader->state = GRPC_JSON_STATE_VALUE_END;
+            break;
+
+          default:
+            return GRPC_JSON_PARSE_ERROR;
+        }
+        break;
+
+      /* Value, object or array terminations. */
+      case ',':
+      case '}':
+      case ']':
+        switch (reader->state) {
+          case GRPC_JSON_STATE_OBJECT_KEY_STRING:
+          case GRPC_JSON_STATE_VALUE_STRING:
+            if (reader->unicode_high_surrogate != 0) {
+              return GRPC_JSON_PARSE_ERROR;
+            }
+            json_reader_string_add_char(reader, c);
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NUMBER:
+          case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
+          case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
+          case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
+            if (reader->depth == 0) {
+              return GRPC_JSON_PARSE_ERROR;
+            } else if ((c == '}') && !reader->in_object) {
+              return GRPC_JSON_PARSE_ERROR;
+            } else if ((c == ']') && !reader->in_array) {
+              return GRPC_JSON_PARSE_ERROR;
+            }
+            success = static_cast<uint32_t>(json_reader_set_number(reader));
+            if (!success) return GRPC_JSON_PARSE_ERROR;
+            json_reader_string_clear(reader);
+            reader->state = GRPC_JSON_STATE_VALUE_END;
+            /* The missing break here is intentional. */
+            /* fallthrough */
+
+          case GRPC_JSON_STATE_VALUE_END:
+          case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
+          case GRPC_JSON_STATE_VALUE_BEGIN:
+            if (c == ',') {
+              if (reader->state != GRPC_JSON_STATE_VALUE_END) {
+                return GRPC_JSON_PARSE_ERROR;
+              }
+              if (reader->in_object) {
+                reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN;
+              } else if (reader->in_array) {
+                reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
+              } else {
+                return GRPC_JSON_PARSE_ERROR;
+              }
+            } else {
+              if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR;
+              if ((c == '}') && !reader->in_object) {
+                return GRPC_JSON_PARSE_ERROR;
+              }
+              if ((c == '}') &&
+                  (reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) &&
+                  !reader->container_just_begun) {
+                return GRPC_JSON_PARSE_ERROR;
+              }
+              if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR;
+              if ((c == ']') &&
+                  (reader->state == GRPC_JSON_STATE_VALUE_BEGIN) &&
+                  !reader->container_just_begun) {
+                return GRPC_JSON_PARSE_ERROR;
+              }
+              reader->state = GRPC_JSON_STATE_VALUE_END;
+              switch (grpc_json_reader_container_ends(reader)) {
+                case GRPC_JSON_OBJECT:
+                  reader->in_object = 1;
+                  reader->in_array = 0;
+                  break;
+                case GRPC_JSON_ARRAY:
+                  reader->in_object = 0;
+                  reader->in_array = 1;
+                  break;
+                case GRPC_JSON_TOP_LEVEL:
+                  GPR_ASSERT(reader->depth == 0);
+                  reader->in_object = 0;
+                  reader->in_array = 0;
+                  reader->state = GRPC_JSON_STATE_END;
+                  break;
+                default:
+                  GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR);
+              }
+            }
+            break;
+
+          default:
+            return GRPC_JSON_PARSE_ERROR;
+        }
+        break;
+
+      /* In-string escaping. */
+      case '\\':
+        switch (reader->state) {
+          case GRPC_JSON_STATE_OBJECT_KEY_STRING:
+            reader->escaped_string_was_key = 1;
+            reader->state = GRPC_JSON_STATE_STRING_ESCAPE;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_STRING:
+            reader->escaped_string_was_key = 0;
+            reader->state = GRPC_JSON_STATE_STRING_ESCAPE;
+            break;
+
+          /* This is the \\ case. */
+          case GRPC_JSON_STATE_STRING_ESCAPE:
+            if (reader->unicode_high_surrogate != 0)
+              return GRPC_JSON_PARSE_ERROR;
+            json_reader_string_add_char(reader, '\\');
+            if (reader->escaped_string_was_key) {
+              reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
+            } else {
+              reader->state = GRPC_JSON_STATE_VALUE_STRING;
+            }
+            break;
+
+          default:
+            return GRPC_JSON_PARSE_ERROR;
+        }
+        break;
+
+      default:
+        reader->container_just_begun = 0;
+        switch (reader->state) {
+          case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
+            if (c != '"') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
+            break;
+
+          case GRPC_JSON_STATE_OBJECT_KEY_STRING:
+            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);
+              json_reader_string_clear(reader);
+            } else {
+              if (c < 32) return GRPC_JSON_PARSE_ERROR;
+              json_reader_string_add_char(reader, c);
+            }
+            break;
+
+          case GRPC_JSON_STATE_VALUE_STRING:
+            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);
+              json_reader_string_clear(reader);
+            } else {
+              if (c < 32) return GRPC_JSON_PARSE_ERROR;
+              json_reader_string_add_char(reader, c);
+            }
+            break;
+
+          case GRPC_JSON_STATE_OBJECT_KEY_END:
+            if (c != ':') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_BEGIN:
+            switch (c) {
+              case 't':
+                reader->state = GRPC_JSON_STATE_VALUE_TRUE_R;
+                break;
+
+              case 'f':
+                reader->state = GRPC_JSON_STATE_VALUE_FALSE_A;
+                break;
+
+              case 'n':
+                reader->state = GRPC_JSON_STATE_VALUE_NULL_U;
+                break;
+
+              case '"':
+                reader->state = GRPC_JSON_STATE_VALUE_STRING;
+                break;
+
+              case '0':
+                json_reader_string_add_char(reader, c);
+                reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO;
+                break;
+
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+              case '8':
+              case '9':
+              case '-':
+                json_reader_string_add_char(reader, c);
+                reader->state = GRPC_JSON_STATE_VALUE_NUMBER;
+                break;
+
+              case '{':
+                reader->container_just_begun = 1;
+                json_reader_container_begins(reader, GRPC_JSON_OBJECT);
+                reader->depth++;
+                reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN;
+                reader->in_object = 1;
+                reader->in_array = 0;
+                break;
+
+              case '[':
+                reader->container_just_begun = 1;
+                json_reader_container_begins(reader, GRPC_JSON_ARRAY);
+                reader->depth++;
+                reader->in_object = 0;
+                reader->in_array = 1;
+                break;
+              default:
+                return GRPC_JSON_PARSE_ERROR;
+            }
+            break;
+
+          case GRPC_JSON_STATE_STRING_ESCAPE:
+            if (reader->escaped_string_was_key) {
+              reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
+            } else {
+              reader->state = GRPC_JSON_STATE_VALUE_STRING;
+            }
+            if (reader->unicode_high_surrogate && c != 'u') {
+              return GRPC_JSON_PARSE_ERROR;
+            }
+            switch (c) {
+              case '"':
+              case '/':
+                json_reader_string_add_char(reader, c);
+                break;
+              case 'b':
+                json_reader_string_add_char(reader, '\b');
+                break;
+              case 'f':
+                json_reader_string_add_char(reader, '\f');
+                break;
+              case 'n':
+                json_reader_string_add_char(reader, '\n');
+                break;
+              case 'r':
+                json_reader_string_add_char(reader, '\r');
+                break;
+              case 't':
+                json_reader_string_add_char(reader, '\t');
+                break;
+              case 'u':
+                reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1;
+                reader->unicode_char = 0;
+                break;
+              default:
+                return GRPC_JSON_PARSE_ERROR;
+            }
+            break;
+
+          case GRPC_JSON_STATE_STRING_ESCAPE_U1:
+          case GRPC_JSON_STATE_STRING_ESCAPE_U2:
+          case GRPC_JSON_STATE_STRING_ESCAPE_U3:
+          case GRPC_JSON_STATE_STRING_ESCAPE_U4:
+            if ((c >= '0') && (c <= '9')) {
+              c -= '0';
+            } else if ((c >= 'A') && (c <= 'F')) {
+              c -= 'A' - 10;
+            } else if ((c >= 'a') && (c <= 'f')) {
+              c -= 'a' - 10;
+            } else {
+              return GRPC_JSON_PARSE_ERROR;
+            }
+            reader->unicode_char =
+                static_cast<uint16_t>(reader->unicode_char << 4);
+            reader->unicode_char =
+                static_cast<uint16_t>(reader->unicode_char | c);
+
+            switch (reader->state) {
+              case GRPC_JSON_STATE_STRING_ESCAPE_U1:
+                reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2;
+                break;
+              case GRPC_JSON_STATE_STRING_ESCAPE_U2:
+                reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3;
+                break;
+              case GRPC_JSON_STATE_STRING_ESCAPE_U3:
+                reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4;
+                break;
+              case GRPC_JSON_STATE_STRING_ESCAPE_U4:
+                /* See grpc_json_writer_escape_string to have a description
+                 * of what's going on here.
+                 */
+                if ((reader->unicode_char & 0xfc00) == 0xd800) {
+                  /* high surrogate utf-16 */
+                  if (reader->unicode_high_surrogate != 0)
+                    return GRPC_JSON_PARSE_ERROR;
+                  reader->unicode_high_surrogate = reader->unicode_char;
+                } else if ((reader->unicode_char & 0xfc00) == 0xdc00) {
+                  /* low surrogate utf-16 */
+                  uint32_t utf32;
+                  if (reader->unicode_high_surrogate == 0)
+                    return GRPC_JSON_PARSE_ERROR;
+                  utf32 = 0x10000;
+                  utf32 += static_cast<uint32_t>(
+                      (reader->unicode_high_surrogate - 0xd800) * 0x400);
+                  utf32 += static_cast<uint32_t>(reader->unicode_char - 0xdc00);
+                  json_reader_string_add_utf32(reader, utf32);
+                  reader->unicode_high_surrogate = 0;
+                } else {
+                  /* anything else */
+                  if (reader->unicode_high_surrogate != 0)
+                    return GRPC_JSON_PARSE_ERROR;
+                  json_reader_string_add_utf32(reader, reader->unicode_char);
+                }
+                if (reader->escaped_string_was_key) {
+                  reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
+                } else {
+                  reader->state = GRPC_JSON_STATE_VALUE_STRING;
+                }
+                break;
+              default:
+                GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR);
+            }
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NUMBER:
+            json_reader_string_add_char(reader, c);
+            switch (c) {
+              case '0':
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+              case '8':
+              case '9':
+                break;
+              case 'e':
+              case 'E':
+                reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E;
+                break;
+              case '.':
+                reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT;
+                break;
+              default:
+                return GRPC_JSON_PARSE_ERROR;
+            }
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
+            json_reader_string_add_char(reader, c);
+            switch (c) {
+              case '0':
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+              case '8':
+              case '9':
+                break;
+              case 'e':
+              case 'E':
+                reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E;
+                break;
+              default:
+                return GRPC_JSON_PARSE_ERROR;
+            }
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
+            if (c != '.') return GRPC_JSON_PARSE_ERROR;
+            json_reader_string_add_char(reader, c);
+            reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NUMBER_DOT:
+            json_reader_string_add_char(reader, c);
+            switch (c) {
+              case '0':
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+              case '8':
+              case '9':
+                reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL;
+                break;
+              default:
+                return GRPC_JSON_PARSE_ERROR;
+            }
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NUMBER_E:
+            json_reader_string_add_char(reader, c);
+            switch (c) {
+              case '0':
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+              case '8':
+              case '9':
+              case '+':
+              case '-':
+                reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM;
+                break;
+              default:
+                return GRPC_JSON_PARSE_ERROR;
+            }
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
+            json_reader_string_add_char(reader, c);
+            switch (c) {
+              case '0':
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+              case '8':
+              case '9':
+                break;
+              default:
+                return GRPC_JSON_PARSE_ERROR;
+            }
+            break;
+
+          case GRPC_JSON_STATE_VALUE_TRUE_R:
+            if (c != 'r') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_VALUE_TRUE_U;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_TRUE_U:
+            if (c != 'u') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_VALUE_TRUE_E;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_TRUE_E:
+            if (c != 'e') return GRPC_JSON_PARSE_ERROR;
+            json_reader_set_true(reader);
+            reader->state = GRPC_JSON_STATE_VALUE_END;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_FALSE_A:
+            if (c != 'a') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_VALUE_FALSE_L;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_FALSE_L:
+            if (c != 'l') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_VALUE_FALSE_S;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_FALSE_S:
+            if (c != 's') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_VALUE_FALSE_E;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_FALSE_E:
+            if (c != 'e') return GRPC_JSON_PARSE_ERROR;
+            json_reader_set_false(reader);
+            reader->state = GRPC_JSON_STATE_VALUE_END;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NULL_U:
+            if (c != 'u') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_VALUE_NULL_L1;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NULL_L1:
+            if (c != 'l') return GRPC_JSON_PARSE_ERROR;
+            reader->state = GRPC_JSON_STATE_VALUE_NULL_L2;
+            break;
+
+          case GRPC_JSON_STATE_VALUE_NULL_L2:
+            if (c != 'l') return GRPC_JSON_PARSE_ERROR;
+            json_reader_set_null(reader);
+            reader->state = GRPC_JSON_STATE_VALUE_END;
+            break;
+
+          /* All of the VALUE_END cases are handled in the specialized case
+           * above. */
+          case GRPC_JSON_STATE_VALUE_END:
+            switch (c) {
+              case ',':
+              case '}':
+              case ']':
+                GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR);
+                break;
+
+              default:
+                return GRPC_JSON_PARSE_ERROR;
+            }
+            break;
+
+          case GRPC_JSON_STATE_END:
+            return GRPC_JSON_PARSE_ERROR;
+        }
+    }
+  }
+
+  GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR);
+}
diff --git a/third_party/grpc/src/core/lib/json/json_reader.h b/third_party/grpc/src/core/lib/json/json_reader.h
new file mode 100644
index 0000000..78f7ad9
--- /dev/null
+++ b/third_party/grpc/src/core/lib/json/json_reader.h
@@ -0,0 +1,146 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_JSON_JSON_READER_H
+#define GRPC_CORE_LIB_JSON_JSON_READER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/json/json_common.h"
+
+typedef enum {
+  GRPC_JSON_STATE_OBJECT_KEY_BEGIN,
+  GRPC_JSON_STATE_OBJECT_KEY_STRING,
+  GRPC_JSON_STATE_OBJECT_KEY_END,
+  GRPC_JSON_STATE_VALUE_BEGIN,
+  GRPC_JSON_STATE_VALUE_STRING,
+  GRPC_JSON_STATE_STRING_ESCAPE,
+  GRPC_JSON_STATE_STRING_ESCAPE_U1,
+  GRPC_JSON_STATE_STRING_ESCAPE_U2,
+  GRPC_JSON_STATE_STRING_ESCAPE_U3,
+  GRPC_JSON_STATE_STRING_ESCAPE_U4,
+  GRPC_JSON_STATE_VALUE_NUMBER,
+  GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL,
+  GRPC_JSON_STATE_VALUE_NUMBER_ZERO,
+  GRPC_JSON_STATE_VALUE_NUMBER_DOT,
+  GRPC_JSON_STATE_VALUE_NUMBER_E,
+  GRPC_JSON_STATE_VALUE_NUMBER_EPM,
+  GRPC_JSON_STATE_VALUE_TRUE_R,
+  GRPC_JSON_STATE_VALUE_TRUE_U,
+  GRPC_JSON_STATE_VALUE_TRUE_E,
+  GRPC_JSON_STATE_VALUE_FALSE_A,
+  GRPC_JSON_STATE_VALUE_FALSE_L,
+  GRPC_JSON_STATE_VALUE_FALSE_S,
+  GRPC_JSON_STATE_VALUE_FALSE_E,
+  GRPC_JSON_STATE_VALUE_NULL_U,
+  GRPC_JSON_STATE_VALUE_NULL_L1,
+  GRPC_JSON_STATE_VALUE_NULL_L2,
+  GRPC_JSON_STATE_VALUE_END,
+  GRPC_JSON_STATE_END
+} grpc_json_reader_state;
+
+enum {
+  /* The first non-unicode value is 0x110000. But let's pick
+   * a value high enough to start our error codes from. These
+   * values are safe to return from the read_char function.
+   */
+  GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0,
+  GRPC_JSON_READ_CHAR_EAGAIN,
+  GRPC_JSON_READ_CHAR_ERROR
+};
+
+struct grpc_json_reader;
+
+typedef struct grpc_json_reader_vtable {
+  /* Clears your internal string scratchpad. */
+  void (*string_clear)(void* userdata);
+  /* Adds a char to the string scratchpad. */
+  void (*string_add_char)(void* userdata, uint32_t c);
+  /* Adds a utf32 char to the string scratchpad. */
+  void (*string_add_utf32)(void* userdata, uint32_t c);
+  /* Reads a character from your input. May be utf-8, 16 or 32. */
+  uint32_t (*read_char)(void* userdata);
+  /* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */
+  void (*container_begins)(void* userdata, grpc_json_type type);
+  /* Ends the current container. Must return the type of its parent. */
+  grpc_json_type (*container_ends)(void* userdata);
+  /* Your internal string scratchpad is an object's key. */
+  void (*set_key)(void* userdata);
+  /* Your internal string scratchpad is a string value. */
+  void (*set_string)(void* userdata);
+  /* Your internal string scratchpad is a numerical value. Return 1 if valid. */
+  int (*set_number)(void* userdata);
+  /* Sets the values true, false or null. */
+  void (*set_true)(void* userdata);
+  void (*set_false)(void* userdata);
+  void (*set_null)(void* userdata);
+} grpc_json_reader_vtable;
+
+typedef struct grpc_json_reader {
+  /* That structure is fully private, and initialized by grpc_json_reader_init.
+   * The definition is public so you can put it on your stack.
+   */
+
+  void* userdata;
+  grpc_json_reader_vtable* vtable;
+  int depth;
+  int in_object;
+  int in_array;
+  int escaped_string_was_key;
+  int container_just_begun;
+  uint16_t unicode_char, unicode_high_surrogate;
+  grpc_json_reader_state state;
+} grpc_json_reader;
+
+/* The return type of the parser. */
+typedef enum {
+  GRPC_JSON_DONE,          /* The parser finished successfully. */
+  GRPC_JSON_EAGAIN,        /* The parser yields to get more data. */
+  GRPC_JSON_READ_ERROR,    /* The parser passes through a read error. */
+  GRPC_JSON_PARSE_ERROR,   /* The parser found an error in the json stream. */
+  GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */
+} grpc_json_reader_status;
+
+/* Call this function to start parsing the input. It will return the following:
+ *    . GRPC_JSON_DONE if the input got eof, and the parsing finished
+ *      successfully.
+ *    . GRPC_JSON_EAGAIN if the read_char function returned again. Call the
+ *      parser again as needed. It is okay to call the parser in polling mode,
+ *      although a bit dull.
+ *    . GRPC_JSON_READ_ERROR if the read_char function returned an error. The
+ *      state isn't broken however, and the function can be called again if the
+ *      error has been corrected. But please use the EAGAIN feature instead for
+ *      consistency.
+ *    . GRPC_JSON_PARSE_ERROR if the input was somehow invalid.
+ *    . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid
+ *      internal state.
+ */
+grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader);
+
+/* Call this function to initialize the reader structure. */
+void grpc_json_reader_init(grpc_json_reader* reader,
+                           grpc_json_reader_vtable* vtable, void* userdata);
+
+/* You may call this from the read_char callback if you don't know where is the
+ * end of your input stream, and you'd like the json reader to hint you that it
+ * has completed reading its input, so you can return an EOF to it. Note that
+ * there might still be trailing whitespaces after that point.
+ */
+int grpc_json_reader_is_complete(grpc_json_reader* reader);
+
+#endif /* GRPC_CORE_LIB_JSON_JSON_READER_H */
diff --git a/third_party/grpc/src/core/lib/json/json_string.cc b/third_party/grpc/src/core/lib/json/json_string.cc
new file mode 100644
index 0000000..4f9175b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/json/json_string.cc
@@ -0,0 +1,367 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/json/json_reader.h"
+#include "src/core/lib/json/json_writer.h"
+
+/* The json reader will construct a bunch of grpc_json objects and
+ * link them all up together in a tree-like structure that will represent
+ * the json data in memory.
+ *
+ * It also uses its own input as a scratchpad to store all of the decoded,
+ * unescaped strings. So we need to keep track of all these pointers in
+ * that opaque structure the reader will carry for us.
+ *
+ * Note that this works because the act of parsing json always reduces its
+ * input size, and never expands it.
+ */
+typedef struct {
+  grpc_json* top;
+  grpc_json* current_container;
+  grpc_json* current_value;
+  uint8_t* input;
+  uint8_t* key;
+  uint8_t* string;
+  uint8_t* string_ptr;
+  size_t remaining_input;
+} json_reader_userdata;
+
+/* This json writer will put everything in a big string.
+ * The point is that we allocate that string in chunks of 256 bytes.
+ */
+typedef struct {
+  char* output;
+  size_t free_space;
+  size_t string_len;
+  size_t allocated;
+} json_writer_userdata;
+
+/* This function checks if there's enough space left in the output buffer,
+ * and will enlarge it if necessary. We're only allocating chunks of 256
+ * bytes at a time (or multiples thereof).
+ */
+static void json_writer_output_check(void* userdata, size_t needed) {
+  json_writer_userdata* state = static_cast<json_writer_userdata*>(userdata);
+  if (state->free_space >= needed) return;
+  needed -= state->free_space;
+  /* Round up by 256 bytes. */
+  needed = (needed + 0xff) & ~0xffU;
+  state->output =
+      static_cast<char*>(gpr_realloc(state->output, state->allocated + needed));
+  state->free_space += needed;
+  state->allocated += needed;
+}
+
+/* These are needed by the writer's implementation. */
+static void json_writer_output_char(void* userdata, char c) {
+  json_writer_userdata* state = static_cast<json_writer_userdata*>(userdata);
+  json_writer_output_check(userdata, 1);
+  state->output[state->string_len++] = c;
+  state->free_space--;
+}
+
+static void json_writer_output_string_with_len(void* userdata, const char* str,
+                                               size_t len) {
+  json_writer_userdata* state = static_cast<json_writer_userdata*>(userdata);
+  json_writer_output_check(userdata, len);
+  memcpy(state->output + state->string_len, str, len);
+  state->string_len += len;
+  state->free_space -= len;
+}
+
+static void json_writer_output_string(void* userdata, const char* str) {
+  size_t len = strlen(str);
+  json_writer_output_string_with_len(userdata, str, len);
+}
+
+/* The reader asks us to clear our scratchpad. In our case, we'll simply mark
+ * the end of the current string, and advance our output pointer.
+ */
+static void json_reader_string_clear(void* userdata) {
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+  if (state->string) {
+    GPR_ASSERT(state->string_ptr < state->input);
+    *state->string_ptr++ = 0;
+  }
+  state->string = state->string_ptr;
+}
+
+static void json_reader_string_add_char(void* userdata, uint32_t c) {
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+  GPR_ASSERT(state->string_ptr < state->input);
+  GPR_ASSERT(c <= 0xff);
+  *state->string_ptr++ = static_cast<uint8_t>(c);
+}
+
+/* We are converting a UTF-32 character into UTF-8 here,
+ * as described by RFC3629.
+ */
+static void json_reader_string_add_utf32(void* userdata, uint32_t c) {
+  if (c <= 0x7f) {
+    json_reader_string_add_char(userdata, c);
+  } else if (c <= 0x7ff) {
+    uint32_t b1 = 0xc0 | ((c >> 6) & 0x1f);
+    uint32_t b2 = 0x80 | (c & 0x3f);
+    json_reader_string_add_char(userdata, b1);
+    json_reader_string_add_char(userdata, b2);
+  } else if (c <= 0xffff) {
+    uint32_t b1 = 0xe0 | ((c >> 12) & 0x0f);
+    uint32_t b2 = 0x80 | ((c >> 6) & 0x3f);
+    uint32_t b3 = 0x80 | (c & 0x3f);
+    json_reader_string_add_char(userdata, b1);
+    json_reader_string_add_char(userdata, b2);
+    json_reader_string_add_char(userdata, b3);
+  } else if (c <= 0x1fffff) {
+    uint32_t b1 = 0xf0 | ((c >> 18) & 0x07);
+    uint32_t b2 = 0x80 | ((c >> 12) & 0x3f);
+    uint32_t b3 = 0x80 | ((c >> 6) & 0x3f);
+    uint32_t b4 = 0x80 | (c & 0x3f);
+    json_reader_string_add_char(userdata, b1);
+    json_reader_string_add_char(userdata, b2);
+    json_reader_string_add_char(userdata, b3);
+    json_reader_string_add_char(userdata, b4);
+  }
+}
+
+/* We consider that the input may be a zero-terminated string. So we
+ * can end up hitting eof before the end of the alleged string length.
+ */
+static uint32_t json_reader_read_char(void* userdata) {
+  uint32_t r;
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+
+  if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF;
+
+  r = *state->input++;
+  state->remaining_input--;
+
+  if (r == 0) {
+    state->remaining_input = 0;
+    return GRPC_JSON_READ_CHAR_EOF;
+  }
+
+  return r;
+}
+
+/* Helper function to create a new grpc_json object and link it into
+ * our tree-in-progress inside our opaque structure.
+ */
+static grpc_json* json_create_and_link(void* userdata, grpc_json_type type) {
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+  grpc_json* json = grpc_json_create(type);
+
+  json->parent = state->current_container;
+  json->prev = state->current_value;
+  state->current_value = json;
+
+  if (json->prev) {
+    json->prev->next = json;
+  }
+  if (json->parent) {
+    if (!json->parent->child) {
+      json->parent->child = json;
+    }
+    if (json->parent->type == GRPC_JSON_OBJECT) {
+      json->key = reinterpret_cast<char*>(state->key);
+    }
+  }
+  if (!state->top) {
+    state->top = json;
+  }
+
+  return json;
+}
+
+static void json_reader_container_begins(void* userdata, grpc_json_type type) {
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+  grpc_json* container;
+
+  GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT);
+
+  container = json_create_and_link(userdata, type);
+  state->current_container = container;
+  state->current_value = nullptr;
+}
+
+/* It's important to remember that the reader is mostly stateless, so it
+ * isn't trying to remember what the container was prior the one that just
+ * ends. Since we're keeping track of these for our own purpose, we are
+ * able to return that information back, which is useful for it to validate
+ * the input json stream.
+ *
+ * Also note that if we're at the top of the tree, and the last container
+ * ends, we have to return GRPC_JSON_TOP_LEVEL.
+ */
+static grpc_json_type json_reader_container_ends(void* userdata) {
+  grpc_json_type container_type = GRPC_JSON_TOP_LEVEL;
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+
+  GPR_ASSERT(state->current_container);
+
+  state->current_value = state->current_container;
+  state->current_container = state->current_container->parent;
+
+  if (state->current_container) {
+    container_type = state->current_container->type;
+  }
+
+  return container_type;
+}
+
+/* The next 3 functions basically are the reader asking us to use our string
+ * scratchpad for one of these 3 purposes.
+ *
+ * Note that in the set_number case, we're not going to try interpreting it.
+ * We'll keep it as a string, and leave it to the caller to evaluate it.
+ */
+static void json_reader_set_key(void* userdata) {
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+  state->key = state->string;
+}
+
+static void json_reader_set_string(void* userdata) {
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+  grpc_json* json = json_create_and_link(userdata, GRPC_JSON_STRING);
+  json->value = reinterpret_cast<char*>(state->string);
+}
+
+static int json_reader_set_number(void* userdata) {
+  json_reader_userdata* state = static_cast<json_reader_userdata*>(userdata);
+  grpc_json* json = json_create_and_link(userdata, GRPC_JSON_NUMBER);
+  json->value = reinterpret_cast<char*>(state->string);
+  return 1;
+}
+
+/* The object types true, false and null are self-sufficient, and don't need
+ * any more information beside their type.
+ */
+static void json_reader_set_true(void* userdata) {
+  json_create_and_link(userdata, GRPC_JSON_TRUE);
+}
+
+static void json_reader_set_false(void* userdata) {
+  json_create_and_link(userdata, GRPC_JSON_FALSE);
+}
+
+static void json_reader_set_null(void* userdata) {
+  json_create_and_link(userdata, GRPC_JSON_NULL);
+}
+
+static grpc_json_reader_vtable reader_vtable = {
+    json_reader_string_clear,     json_reader_string_add_char,
+    json_reader_string_add_utf32, json_reader_read_char,
+    json_reader_container_begins, json_reader_container_ends,
+    json_reader_set_key,          json_reader_set_string,
+    json_reader_set_number,       json_reader_set_true,
+    json_reader_set_false,        json_reader_set_null};
+
+/* And finally, let's define our public API. */
+grpc_json* grpc_json_parse_string_with_len(char* input, size_t size) {
+  grpc_json_reader reader;
+  json_reader_userdata state;
+  grpc_json* json = nullptr;
+  grpc_json_reader_status status;
+
+  if (!input) return nullptr;
+
+  state.top = state.current_container = state.current_value = nullptr;
+  state.string = state.key = nullptr;
+  state.string_ptr = state.input = reinterpret_cast<uint8_t*>(input);
+  state.remaining_input = size;
+  grpc_json_reader_init(&reader, &reader_vtable, &state);
+
+  status = grpc_json_reader_run(&reader);
+  json = state.top;
+
+  if ((status != GRPC_JSON_DONE) && json) {
+    grpc_json_destroy(json);
+    json = nullptr;
+  }
+
+  return json;
+}
+
+#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff
+
+grpc_json* grpc_json_parse_string(char* input) {
+  return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH);
+}
+
+static void json_dump_recursive(grpc_json_writer* writer, grpc_json* json,
+                                int in_object) {
+  while (json) {
+    if (in_object) grpc_json_writer_object_key(writer, json->key);
+
+    switch (json->type) {
+      case GRPC_JSON_OBJECT:
+      case GRPC_JSON_ARRAY:
+        grpc_json_writer_container_begins(writer, json->type);
+        if (json->child)
+          json_dump_recursive(writer, json->child,
+                              json->type == GRPC_JSON_OBJECT);
+        grpc_json_writer_container_ends(writer, json->type);
+        break;
+      case GRPC_JSON_STRING:
+        grpc_json_writer_value_string(writer, json->value);
+        break;
+      case GRPC_JSON_NUMBER:
+        grpc_json_writer_value_raw(writer, json->value);
+        break;
+      case GRPC_JSON_TRUE:
+        grpc_json_writer_value_raw_with_len(writer, "true", 4);
+        break;
+      case GRPC_JSON_FALSE:
+        grpc_json_writer_value_raw_with_len(writer, "false", 5);
+        break;
+      case GRPC_JSON_NULL:
+        grpc_json_writer_value_raw_with_len(writer, "null", 4);
+        break;
+      default:
+        GPR_UNREACHABLE_CODE(abort());
+    }
+    json = json->next;
+  }
+}
+
+static grpc_json_writer_vtable writer_vtable = {
+    json_writer_output_char, json_writer_output_string,
+    json_writer_output_string_with_len};
+
+char* grpc_json_dump_to_string(grpc_json* json, int indent) {
+  grpc_json_writer writer;
+  json_writer_userdata state;
+
+  state.output = nullptr;
+  state.free_space = state.string_len = state.allocated = 0;
+  grpc_json_writer_init(&writer, indent, &writer_vtable, &state);
+
+  json_dump_recursive(&writer, json, 0);
+
+  json_writer_output_char(&state, 0);
+
+  return state.output;
+}
diff --git a/third_party/grpc/src/core/lib/json/json_writer.cc b/third_party/grpc/src/core/lib/json/json_writer.cc
new file mode 100644
index 0000000..7bbdccc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/json/json_writer.cc
@@ -0,0 +1,245 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include "src/core/lib/json/json_writer.h"
+
+static void json_writer_output_char(grpc_json_writer* writer, char c) {
+  writer->vtable->output_char(writer->userdata, c);
+}
+
+static void json_writer_output_string(grpc_json_writer* writer,
+                                      const char* str) {
+  writer->vtable->output_string(writer->userdata, str);
+}
+
+static void json_writer_output_string_with_len(grpc_json_writer* writer,
+                                               const char* str, size_t len) {
+  writer->vtable->output_string_with_len(writer->userdata, str, len);
+}
+
+void grpc_json_writer_init(grpc_json_writer* writer, int indent,
+                           grpc_json_writer_vtable* vtable, void* userdata) {
+  memset(writer, 0, sizeof(*writer));
+  writer->container_empty = 1;
+  writer->indent = indent;
+  writer->vtable = vtable;
+  writer->userdata = userdata;
+}
+
+static void json_writer_output_indent(grpc_json_writer* writer) {
+  static const char spacesstr[] =
+      "                "
+      "                "
+      "                "
+      "                ";
+
+  unsigned spaces = static_cast<unsigned>(writer->depth * writer->indent);
+
+  if (writer->indent == 0) return;
+
+  if (writer->got_key) {
+    json_writer_output_char(writer, ' ');
+    return;
+  }
+
+  while (spaces >= (sizeof(spacesstr) - 1)) {
+    json_writer_output_string_with_len(writer, spacesstr,
+                                       sizeof(spacesstr) - 1);
+    spaces -= static_cast<unsigned>(sizeof(spacesstr) - 1);
+  }
+
+  if (spaces == 0) return;
+
+  json_writer_output_string_with_len(
+      writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces);
+}
+
+static void json_writer_value_end(grpc_json_writer* writer) {
+  if (writer->container_empty) {
+    writer->container_empty = 0;
+    if ((writer->indent == 0) || (writer->depth == 0)) return;
+    json_writer_output_char(writer, '\n');
+  } else {
+    json_writer_output_char(writer, ',');
+    if (writer->indent == 0) return;
+    json_writer_output_char(writer, '\n');
+  }
+}
+
+static void json_writer_escape_utf16(grpc_json_writer* writer, uint16_t utf16) {
+  static const char hex[] = "0123456789abcdef";
+
+  json_writer_output_string_with_len(writer, "\\u", 2);
+  json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]);
+  json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]);
+  json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]);
+  json_writer_output_char(writer, hex[(utf16)&0x0f]);
+}
+
+static void json_writer_escape_string(grpc_json_writer* writer,
+                                      const char* string) {
+  json_writer_output_char(writer, '"');
+
+  for (;;) {
+    uint8_t c = static_cast<uint8_t>(*string++);
+    if (c == 0) {
+      break;
+    } else if ((c >= 32) && (c <= 126)) {
+      if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\');
+      json_writer_output_char(writer, static_cast<char>(c));
+    } else if ((c < 32) || (c == 127)) {
+      switch (c) {
+        case '\b':
+          json_writer_output_string_with_len(writer, "\\b", 2);
+          break;
+        case '\f':
+          json_writer_output_string_with_len(writer, "\\f", 2);
+          break;
+        case '\n':
+          json_writer_output_string_with_len(writer, "\\n", 2);
+          break;
+        case '\r':
+          json_writer_output_string_with_len(writer, "\\r", 2);
+          break;
+        case '\t':
+          json_writer_output_string_with_len(writer, "\\t", 2);
+          break;
+        default:
+          json_writer_escape_utf16(writer, c);
+          break;
+      }
+    } else {
+      uint32_t utf32 = 0;
+      int extra = 0;
+      int i;
+      int valid = 1;
+      if ((c & 0xe0) == 0xc0) {
+        utf32 = c & 0x1f;
+        extra = 1;
+      } else if ((c & 0xf0) == 0xe0) {
+        utf32 = c & 0x0f;
+        extra = 2;
+      } else if ((c & 0xf8) == 0xf0) {
+        utf32 = c & 0x07;
+        extra = 3;
+      } else {
+        break;
+      }
+      for (i = 0; i < extra; i++) {
+        utf32 <<= 6;
+        c = static_cast<uint8_t>(*string++);
+        /* Breaks out and bail on any invalid UTF-8 sequence, including \0. */
+        if ((c & 0xc0) != 0x80) {
+          valid = 0;
+          break;
+        }
+        utf32 |= c & 0x3f;
+      }
+      if (!valid) break;
+      /* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam.
+       * Any other range is technically reserved for future usage, so if we
+       * don't want the software to break in the future, we have to allow
+       * anything else. The first non-unicode character is 0x110000. */
+      if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) || (utf32 >= 0x110000))
+        break;
+      if (utf32 >= 0x10000) {
+        /* If utf32 contains a character that is above 0xffff, it needs to be
+         * broken down into a utf-16 surrogate pair. A surrogate pair is first
+         * a high surrogate, followed by a low surrogate. Each surrogate holds
+         * 10 bits of usable data, thus allowing a total of 20 bits of data.
+         * The high surrogate marker is 0xd800, while the low surrogate marker
+         * is 0xdc00. The low 10 bits of each will be the usable data.
+         *
+         * After re-combining the 20 bits of data, one has to add 0x10000 to
+         * the resulting value, in order to obtain the original character.
+         * This is obviously because the range 0x0000 - 0xffff can be written
+         * without any special trick.
+         *
+         * Since 0x10ffff is the highest allowed character, we're working in
+         * the range 0x00000 - 0xfffff after we decrement it by 0x10000.
+         * That range is exactly 20 bits.
+         */
+        utf32 -= 0x10000;
+        json_writer_escape_utf16(writer,
+                                 static_cast<uint16_t>(0xd800 | (utf32 >> 10)));
+        json_writer_escape_utf16(
+            writer, static_cast<uint16_t>(0xdc00 | (utf32 & 0x3ff)));
+      } else {
+        json_writer_escape_utf16(writer, static_cast<uint16_t>(utf32));
+      }
+    }
+  }
+
+  json_writer_output_char(writer, '"');
+}
+
+void grpc_json_writer_container_begins(grpc_json_writer* writer,
+                                       grpc_json_type type) {
+  if (!writer->got_key) json_writer_value_end(writer);
+  json_writer_output_indent(writer);
+  json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '[');
+  writer->container_empty = 1;
+  writer->got_key = 0;
+  writer->depth++;
+}
+
+void grpc_json_writer_container_ends(grpc_json_writer* writer,
+                                     grpc_json_type type) {
+  if (writer->indent && !writer->container_empty)
+    json_writer_output_char(writer, '\n');
+  writer->depth--;
+  if (!writer->container_empty) json_writer_output_indent(writer);
+  json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']');
+  writer->container_empty = 0;
+  writer->got_key = 0;
+}
+
+void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) {
+  json_writer_value_end(writer);
+  json_writer_output_indent(writer);
+  json_writer_escape_string(writer, string);
+  json_writer_output_char(writer, ':');
+  writer->got_key = 1;
+}
+
+void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string) {
+  if (!writer->got_key) json_writer_value_end(writer);
+  json_writer_output_indent(writer);
+  json_writer_output_string(writer, string);
+  writer->got_key = 0;
+}
+
+void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer,
+                                         const char* string, size_t len) {
+  if (!writer->got_key) json_writer_value_end(writer);
+  json_writer_output_indent(writer);
+  json_writer_output_string_with_len(writer, string, len);
+  writer->got_key = 0;
+}
+
+void grpc_json_writer_value_string(grpc_json_writer* writer,
+                                   const char* string) {
+  if (!writer->got_key) json_writer_value_end(writer);
+  json_writer_output_indent(writer);
+  json_writer_escape_string(writer, string);
+  writer->got_key = 0;
+}
diff --git a/third_party/grpc/src/core/lib/json/json_writer.h b/third_party/grpc/src/core/lib/json/json_writer.h
new file mode 100644
index 0000000..ba0bedd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/json/json_writer.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* The idea of the writer is basically symmetrical of the reader. While the
+ * reader emits various calls to your code, the writer takes basically the
+ * same calls and emit json out of it. It doesn't try to make any check on
+ * the order of the calls you do on it. Meaning you can theorically force
+ * it to generate invalid json.
+ *
+ * Also, unlike the reader, the writer expects UTF-8 encoded input strings.
+ * These strings will be UTF-8 validated, and any invalid character will
+ * cut the conversion short, before any invalid UTF-8 sequence, thus forming
+ * a valid UTF-8 string overall.
+ */
+
+#ifndef GRPC_CORE_LIB_JSON_JSON_WRITER_H
+#define GRPC_CORE_LIB_JSON_JSON_WRITER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+
+#include "src/core/lib/json/json_common.h"
+
+typedef struct grpc_json_writer_vtable {
+  /* Adds a character to the output stream. */
+  void (*output_char)(void* userdata, char);
+  /* Adds a zero-terminated string to the output stream. */
+  void (*output_string)(void* userdata, const char* str);
+  /* Adds a fixed-length string to the output stream. */
+  void (*output_string_with_len)(void* userdata, const char* str, size_t len);
+
+} grpc_json_writer_vtable;
+
+typedef struct grpc_json_writer {
+  void* userdata;
+  grpc_json_writer_vtable* vtable;
+  int indent;
+  int depth;
+  int container_empty;
+  int got_key;
+} grpc_json_writer;
+
+/* Call this to initialize your writer structure. The indent parameter is
+ * specifying the number of spaces to use for indenting the output. If you
+ * use indent=0, then the output will not have any newlines either, thus
+ * emitting a condensed json output.
+ */
+void grpc_json_writer_init(grpc_json_writer* writer, int indent,
+                           grpc_json_writer_vtable* vtable, void* userdata);
+
+/* Signals the beginning of a container. */
+void grpc_json_writer_container_begins(grpc_json_writer* writer,
+                                       grpc_json_type type);
+/* Signals the end of a container. */
+void grpc_json_writer_container_ends(grpc_json_writer* writer,
+                                     grpc_json_type type);
+/* Writes down an object key for the next value. */
+void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string);
+/* Sets a raw value. Useful for numbers. */
+void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string);
+/* Sets a raw value with its length. Useful for values like true or false. */
+void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer,
+                                         const char* string, size_t len);
+/* Sets a string value. It'll be escaped, and utf-8 validated. */
+void grpc_json_writer_value_string(grpc_json_writer* writer,
+                                   const char* string);
+
+#endif /* GRPC_CORE_LIB_JSON_JSON_WRITER_H */
diff --git a/third_party/grpc/src/core/lib/profiling/basic_timers.cc b/third_party/grpc/src/core/lib/profiling/basic_timers.cc
new file mode 100644
index 0000000..b19ad9f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/profiling/basic_timers.cc
@@ -0,0 +1,287 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/profiling/timers.h"
+
+#ifdef GRPC_BASIC_PROFILER
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/lib/gpr/env.h"
+
+typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type;
+
+typedef struct gpr_timer_entry {
+  gpr_timespec tm;
+  const char* tagstr;
+  const char* file;
+  short line;
+  char type;
+  uint8_t important;
+  int thd;
+} gpr_timer_entry;
+
+#define MAX_COUNT 1000000
+
+typedef struct gpr_timer_log {
+  size_t num_entries;
+  struct gpr_timer_log* next;
+  struct gpr_timer_log* prev;
+  gpr_timer_entry log[MAX_COUNT];
+} gpr_timer_log;
+
+typedef struct gpr_timer_log_list {
+  gpr_timer_log* head;
+  /* valid iff head!=NULL */
+  gpr_timer_log* tail;
+} gpr_timer_log_list;
+
+static __thread gpr_timer_log* g_thread_log;
+static gpr_once g_once_init = GPR_ONCE_INIT;
+static FILE* output_file;
+static const char* output_filename_or_null = NULL;
+static pthread_mutex_t g_mu;
+static pthread_cond_t g_cv;
+static gpr_timer_log_list g_in_progress_logs;
+static gpr_timer_log_list g_done_logs;
+static int g_shutdown;
+static pthread_t g_writing_thread;
+static __thread int g_thread_id;
+static int g_next_thread_id;
+static int g_writing_enabled = 1;
+
+static const char* output_filename() {
+  if (output_filename_or_null == NULL) {
+    output_filename_or_null = gpr_getenv("LATENCY_TRACE");
+    if (output_filename_or_null == NULL ||
+        strlen(output_filename_or_null) == 0) {
+      output_filename_or_null = "latency_trace.txt";
+    }
+  }
+  return output_filename_or_null;
+}
+
+static int timer_log_push_back(gpr_timer_log_list* list, gpr_timer_log* log) {
+  if (list->head == NULL) {
+    list->head = list->tail = log;
+    log->next = log->prev = NULL;
+    return 1;
+  } else {
+    log->prev = list->tail;
+    log->next = NULL;
+    list->tail->next = log;
+    list->tail = log;
+    return 0;
+  }
+}
+
+static gpr_timer_log* timer_log_pop_front(gpr_timer_log_list* list) {
+  gpr_timer_log* out = list->head;
+  if (out != NULL) {
+    list->head = out->next;
+    if (list->head != NULL) {
+      list->head->prev = NULL;
+    } else {
+      list->tail = NULL;
+    }
+  }
+  return out;
+}
+
+static void timer_log_remove(gpr_timer_log_list* list, gpr_timer_log* log) {
+  if (log->prev == NULL) {
+    list->head = log->next;
+    if (list->head != NULL) {
+      list->head->prev = NULL;
+    }
+  } else {
+    log->prev->next = log->next;
+  }
+  if (log->next == NULL) {
+    list->tail = log->prev;
+    if (list->tail != NULL) {
+      list->tail->next = NULL;
+    }
+  } else {
+    log->next->prev = log->prev;
+  }
+}
+
+static void write_log(gpr_timer_log* log) {
+  size_t i;
+  if (output_file == NULL) {
+    output_file = fopen(output_filename(), "w");
+  }
+  for (i = 0; i < log->num_entries; i++) {
+    gpr_timer_entry* entry = &(log->log[i]);
+    if (gpr_time_cmp(entry->tm, gpr_time_0(entry->tm.clock_type)) < 0) {
+      entry->tm = gpr_time_0(entry->tm.clock_type);
+    }
+    fprintf(output_file,
+            "{\"t\": %" PRId64
+            ".%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": "
+            "\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n",
+            entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type,
+            entry->tagstr, entry->file, entry->line, entry->important);
+  }
+}
+
+static void* writing_thread(void* unused) {
+  gpr_timer_log* log;
+  pthread_mutex_lock(&g_mu);
+  for (;;) {
+    while ((log = timer_log_pop_front(&g_done_logs)) == NULL && !g_shutdown) {
+      pthread_cond_wait(&g_cv, &g_mu);
+    }
+    if (log != NULL) {
+      pthread_mutex_unlock(&g_mu);
+      write_log(log);
+      free(log);
+      pthread_mutex_lock(&g_mu);
+    }
+    if (g_shutdown) {
+      pthread_mutex_unlock(&g_mu);
+      return NULL;
+    }
+  }
+}
+
+static void flush_logs(gpr_timer_log_list* list) {
+  gpr_timer_log* log;
+  while ((log = timer_log_pop_front(list)) != NULL) {
+    write_log(log);
+    free(log);
+  }
+}
+
+static void finish_writing(void) {
+  pthread_mutex_lock(&g_mu);
+  g_shutdown = 1;
+  pthread_cond_signal(&g_cv);
+  pthread_mutex_unlock(&g_mu);
+  pthread_join(g_writing_thread, NULL);
+
+  gpr_log(GPR_INFO, "flushing logs");
+
+  pthread_mutex_lock(&g_mu);
+  flush_logs(&g_done_logs);
+  flush_logs(&g_in_progress_logs);
+  pthread_mutex_unlock(&g_mu);
+
+  if (output_file) {
+    fclose(output_file);
+  }
+}
+
+void gpr_timers_set_log_filename(const char* filename) {
+  output_filename_or_null = filename;
+}
+
+static void init_output() {
+  pthread_attr_t attr;
+  pthread_attr_init(&attr);
+  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+  pthread_create(&g_writing_thread, &attr, &writing_thread, NULL);
+  pthread_attr_destroy(&attr);
+
+  atexit(finish_writing);
+}
+
+static void rotate_log() {
+  /* Using malloc here, as this code could end up being called by gpr_malloc */
+  gpr_timer_log* log = static_cast<gpr_timer_log*>(malloc(sizeof(*log)));
+  gpr_once_init(&g_once_init, init_output);
+  log->num_entries = 0;
+  pthread_mutex_lock(&g_mu);
+  if (g_thread_log != NULL) {
+    timer_log_remove(&g_in_progress_logs, g_thread_log);
+    if (timer_log_push_back(&g_done_logs, g_thread_log)) {
+      pthread_cond_signal(&g_cv);
+    }
+  } else {
+    g_thread_id = g_next_thread_id++;
+  }
+  timer_log_push_back(&g_in_progress_logs, log);
+  pthread_mutex_unlock(&g_mu);
+  g_thread_log = log;
+}
+
+static void gpr_timers_log_add(const char* tagstr, marker_type type,
+                               int important, const char* file, int line) {
+  gpr_timer_entry* entry;
+
+  if (!g_writing_enabled) {
+    return;
+  }
+
+  if (g_thread_log == NULL || g_thread_log->num_entries == MAX_COUNT) {
+    rotate_log();
+  }
+
+  entry = &g_thread_log->log[g_thread_log->num_entries++];
+
+  entry->tm = gpr_now(GPR_CLOCK_PRECISE);
+  entry->tagstr = tagstr;
+  entry->type = type;
+  entry->file = file;
+  entry->line = (short)line;
+  entry->important = important != 0;
+  entry->thd = g_thread_id;
+}
+
+/* Latency profiler API implementation. */
+void gpr_timer_add_mark(const char* tagstr, int important, const char* file,
+                        int line) {
+  gpr_timers_log_add(tagstr, MARK, important, file, line);
+}
+
+void gpr_timer_begin(const char* tagstr, int important, const char* file,
+                     int line) {
+  gpr_timers_log_add(tagstr, BEGIN, important, file, line);
+}
+
+void gpr_timer_end(const char* tagstr, int important, const char* file,
+                   int line) {
+  gpr_timers_log_add(tagstr, END, important, file, line);
+}
+
+void gpr_timer_set_enabled(int enabled) { g_writing_enabled = enabled; }
+
+/* Basic profiler specific API functions. */
+void gpr_timers_global_init(void) {}
+
+void gpr_timers_global_destroy(void) {}
+
+#else  /* !GRPC_BASIC_PROFILER */
+void gpr_timers_global_init(void) {}
+
+void gpr_timers_global_destroy(void) {}
+
+void gpr_timers_set_log_filename(const char* filename) {}
+
+void gpr_timer_set_enabled(int enabled) {}
+#endif /* GRPC_BASIC_PROFILER */
diff --git a/third_party/grpc/src/core/lib/profiling/stap_probes.d b/third_party/grpc/src/core/lib/profiling/stap_probes.d
new file mode 100644
index 0000000..153de91
--- /dev/null
+++ b/third_party/grpc/src/core/lib/profiling/stap_probes.d
@@ -0,0 +1,7 @@
+provider _stap {
+	probe add_mark(int tag);
+	probe add_important_mark(int tag);
+	probe timing_ns_begin(int tag);
+	probe timing_ns_end(int tag);
+};
+
diff --git a/third_party/grpc/src/core/lib/profiling/stap_timers.cc b/third_party/grpc/src/core/lib/profiling/stap_timers.cc
new file mode 100644
index 0000000..5ee1c43
--- /dev/null
+++ b/third_party/grpc/src/core/lib/profiling/stap_timers.cc
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_STAP_PROFILER
+
+#include "src/core/lib/profiling/timers.h"
+
+#include <sys/sdt.h>
+/* Generated from src/core/profiling/stap_probes.d */
+#include "src/core/lib/profiling/stap_probes.h"
+
+/* Latency profiler API implementation. */
+void gpr_timer_add_mark(int tag, const char* tagstr, void* id, const char* file,
+                        int line) {
+  _STAP_ADD_MARK(tag);
+}
+
+void gpr_timer_add_important_mark(int tag, const char* tagstr, void* id,
+                                  const char* file, int line) {
+  _STAP_ADD_IMPORTANT_MARK(tag);
+}
+
+void gpr_timer_begin(int tag, const char* tagstr, void* id, const char* file,
+                     int line) {
+  _STAP_TIMING_NS_BEGIN(tag);
+}
+
+void gpr_timer_end(int tag, const char* tagstr, void* id, const char* file,
+                   int line) {
+  _STAP_TIMING_NS_END(tag);
+}
+
+#endif /* GRPC_STAP_PROFILER */
diff --git a/third_party/grpc/src/core/lib/profiling/timers.h b/third_party/grpc/src/core/lib/profiling/timers.h
new file mode 100644
index 0000000..7ff7278
--- /dev/null
+++ b/third_party/grpc/src/core/lib/profiling/timers.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_PROFILING_TIMERS_H
+#define GRPC_CORE_LIB_PROFILING_TIMERS_H
+
+void gpr_timers_global_init(void);
+void gpr_timers_global_destroy(void);
+
+void gpr_timer_add_mark(const char* tagstr, int important, const char* file,
+                        int line);
+void gpr_timer_begin(const char* tagstr, int important, const char* file,
+                     int line);
+void gpr_timer_end(const char* tagstr, int important, const char* file,
+                   int line);
+
+void gpr_timers_set_log_filename(const char* filename);
+
+void gpr_timer_set_enabled(int enabled);
+
+#if !(defined(GRPC_STAP_PROFILER) + defined(GRPC_BASIC_PROFILER) + \
+      defined(GRPC_CUSTOM_PROFILER))
+/* No profiling. No-op all the things. */
+#define GPR_TIMER_MARK(tag, important) \
+  do {                                 \
+  } while (0)
+
+#define GPR_TIMER_SCOPE(tag, important) \
+  do {                                  \
+  } while (0)
+
+#else /* at least one profiler requested... */
+/* ... hopefully only one. */
+#if defined(GRPC_STAP_PROFILER) && defined(GRPC_BASIC_PROFILER)
+#error "GRPC_STAP_PROFILER and GRPC_BASIC_PROFILER are mutually exclusive."
+#endif
+#if defined(GRPC_STAP_PROFILER) && defined(GRPC_CUSTOM_PROFILER)
+#error "GRPC_STAP_PROFILER and GRPC_CUSTOM_PROFILER are mutually exclusive."
+#endif
+#if defined(GRPC_CUSTOM_PROFILER) && defined(GRPC_BASIC_PROFILER)
+#error "GRPC_CUSTOM_PROFILER and GRPC_BASIC_PROFILER are mutually exclusive."
+#endif
+
+/* Generic profiling interface. */
+#define GPR_TIMER_MARK(tag, important) \
+  gpr_timer_add_mark(tag, important, __FILE__, __LINE__);
+
+#ifdef GRPC_STAP_PROFILER
+/* Empty placeholder for now. */
+#endif /* GRPC_STAP_PROFILER */
+
+#ifdef GRPC_BASIC_PROFILER
+/* Empty placeholder for now. */
+#endif /* GRPC_BASIC_PROFILER */
+
+namespace grpc {
+class ProfileScope {
+ public:
+  ProfileScope(const char* desc, bool important, const char* file, int line)
+      : desc_(desc) {
+    gpr_timer_begin(desc_, important ? 1 : 0, file, line);
+  }
+  ~ProfileScope() { gpr_timer_end(desc_, 0, "n/a", 0); }
+
+ private:
+  const char* const desc_;
+};
+}  // namespace grpc
+
+#define GPR_TIMER_SCOPE_NAME_INTERNAL(prefix, line) prefix##line
+#define GPR_TIMER_SCOPE_NAME(prefix, line) \
+  GPR_TIMER_SCOPE_NAME_INTERNAL(prefix, line)
+#define GPR_TIMER_SCOPE(tag, important)                                 \
+  ::grpc::ProfileScope GPR_TIMER_SCOPE_NAME(_profile_scope_, __LINE__)( \
+      (tag), (important), __FILE__, __LINE__)
+
+#endif /* at least one profiler requested. */
+
+#endif /* GRPC_CORE_LIB_PROFILING_TIMERS_H */
diff --git a/third_party/grpc/src/core/lib/security/context/security_context.cc b/third_party/grpc/src/core/lib/security/context/security_context.cc
new file mode 100644
index 0000000..8443ee0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/context/security_context.cc
@@ -0,0 +1,322 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+
+#include <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount(
+    false, "auth_context_refcount");
+
+/* --- grpc_call --- */
+
+grpc_call_error grpc_call_set_credentials(grpc_call* call,
+                                          grpc_call_credentials* creds) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_client_security_context* ctx = nullptr;
+  GRPC_API_TRACE("grpc_call_set_credentials(call=%p, creds=%p)", 2,
+                 (call, creds));
+  if (!grpc_call_is_client(call)) {
+    gpr_log(GPR_ERROR, "Method is client-side only.");
+    return GRPC_CALL_ERROR_NOT_ON_SERVER;
+  }
+  ctx = static_cast<grpc_client_security_context*>(
+      grpc_call_context_get(call, GRPC_CONTEXT_SECURITY));
+  if (ctx == nullptr) {
+    ctx = grpc_client_security_context_create(grpc_call_get_arena(call), creds);
+    grpc_call_context_set(call, GRPC_CONTEXT_SECURITY, ctx,
+                          grpc_client_security_context_destroy);
+  } else {
+    ctx->creds = creds != nullptr ? creds->Ref() : nullptr;
+  }
+
+  return GRPC_CALL_OK;
+}
+
+grpc_auth_context* grpc_call_auth_context(grpc_call* call) {
+  void* sec_ctx = grpc_call_context_get(call, GRPC_CONTEXT_SECURITY);
+  GRPC_API_TRACE("grpc_call_auth_context(call=%p)", 1, (call));
+  if (sec_ctx == nullptr) return nullptr;
+  if (grpc_call_is_client(call)) {
+    auto* sc = static_cast<grpc_client_security_context*>(sec_ctx);
+    if (sc->auth_context == nullptr) {
+      return nullptr;
+    } else {
+      return sc->auth_context
+          ->Ref(DEBUG_LOCATION, "grpc_call_auth_context client")
+          .release();
+    }
+  } else {
+    auto* sc = static_cast<grpc_server_security_context*>(sec_ctx);
+    if (sc->auth_context == nullptr) {
+      return nullptr;
+    } else {
+      return sc->auth_context
+          ->Ref(DEBUG_LOCATION, "grpc_call_auth_context server")
+          .release();
+    }
+  }
+}
+
+void grpc_auth_context_release(grpc_auth_context* context) {
+  GRPC_API_TRACE("grpc_auth_context_release(context=%p)", 1, (context));
+  if (context == nullptr) return;
+  context->Unref(DEBUG_LOCATION, "grpc_auth_context_unref");
+}
+
+/* --- grpc_client_security_context --- */
+grpc_client_security_context::~grpc_client_security_context() {
+  auth_context.reset(DEBUG_LOCATION, "client_security_context");
+  if (extension.instance != nullptr && extension.destroy != nullptr) {
+    extension.destroy(extension.instance);
+  }
+}
+
+grpc_client_security_context* grpc_client_security_context_create(
+    gpr_arena* arena, grpc_call_credentials* creds) {
+  return new (gpr_arena_alloc(arena, sizeof(grpc_client_security_context)))
+      grpc_client_security_context(creds != nullptr ? creds->Ref() : nullptr);
+}
+
+void grpc_client_security_context_destroy(void* ctx) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_client_security_context* c =
+      static_cast<grpc_client_security_context*>(ctx);
+  c->~grpc_client_security_context();
+}
+
+/* --- grpc_server_security_context --- */
+grpc_server_security_context::~grpc_server_security_context() {
+  auth_context.reset(DEBUG_LOCATION, "server_security_context");
+  if (extension.instance != nullptr && extension.destroy != nullptr) {
+    extension.destroy(extension.instance);
+  }
+}
+
+grpc_server_security_context* grpc_server_security_context_create(
+    gpr_arena* arena) {
+  return new (gpr_arena_alloc(arena, sizeof(grpc_server_security_context)))
+      grpc_server_security_context();
+}
+
+void grpc_server_security_context_destroy(void* ctx) {
+  grpc_server_security_context* c =
+      static_cast<grpc_server_security_context*>(ctx);
+  c->~grpc_server_security_context();
+}
+
+/* --- grpc_auth_context --- */
+
+static grpc_auth_property_iterator empty_iterator = {nullptr, 0, nullptr};
+
+const char* grpc_auth_context_peer_identity_property_name(
+    const grpc_auth_context* ctx) {
+  GRPC_API_TRACE("grpc_auth_context_peer_identity_property_name(ctx=%p)", 1,
+                 (ctx));
+  return ctx->peer_identity_property_name();
+}
+
+int grpc_auth_context_set_peer_identity_property_name(grpc_auth_context* ctx,
+                                                      const char* name) {
+  grpc_auth_property_iterator it =
+      grpc_auth_context_find_properties_by_name(ctx, name);
+  const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it);
+  GRPC_API_TRACE(
+      "grpc_auth_context_set_peer_identity_property_name(ctx=%p, name=%s)", 2,
+      (ctx, name));
+  if (prop == nullptr) {
+    gpr_log(GPR_ERROR, "Property name %s not found in auth context.",
+            name != nullptr ? name : "NULL");
+    return 0;
+  }
+  ctx->set_peer_identity_property_name(prop->name);
+  return 1;
+}
+
+int grpc_auth_context_peer_is_authenticated(const grpc_auth_context* ctx) {
+  GRPC_API_TRACE("grpc_auth_context_peer_is_authenticated(ctx=%p)", 1, (ctx));
+  return ctx->is_authenticated();
+}
+
+grpc_auth_property_iterator grpc_auth_context_property_iterator(
+    const grpc_auth_context* ctx) {
+  grpc_auth_property_iterator it = empty_iterator;
+  GRPC_API_TRACE("grpc_auth_context_property_iterator(ctx=%p)", 1, (ctx));
+  if (ctx == nullptr) return it;
+  it.ctx = ctx;
+  return it;
+}
+
+const grpc_auth_property* grpc_auth_property_iterator_next(
+    grpc_auth_property_iterator* it) {
+  GRPC_API_TRACE("grpc_auth_property_iterator_next(it=%p)", 1, (it));
+  if (it == nullptr || it->ctx == nullptr) return nullptr;
+  while (it->index == it->ctx->properties().count) {
+    if (it->ctx->chained() == nullptr) return nullptr;
+    it->ctx = it->ctx->chained();
+    it->index = 0;
+  }
+  if (it->name == nullptr) {
+    return &it->ctx->properties().array[it->index++];
+  } else {
+    while (it->index < it->ctx->properties().count) {
+      const grpc_auth_property* prop =
+          &it->ctx->properties().array[it->index++];
+      GPR_ASSERT(prop->name != nullptr);
+      if (strcmp(it->name, prop->name) == 0) {
+        return prop;
+      }
+    }
+    /* We could not find the name, try another round. */
+    return grpc_auth_property_iterator_next(it);
+  }
+}
+
+grpc_auth_property_iterator grpc_auth_context_find_properties_by_name(
+    const grpc_auth_context* ctx, const char* name) {
+  grpc_auth_property_iterator it = empty_iterator;
+  GRPC_API_TRACE("grpc_auth_context_find_properties_by_name(ctx=%p, name=%s)",
+                 2, (ctx, name));
+  if (ctx == nullptr || name == nullptr) return empty_iterator;
+  it.ctx = ctx;
+  it.name = name;
+  return it;
+}
+
+grpc_auth_property_iterator grpc_auth_context_peer_identity(
+    const grpc_auth_context* ctx) {
+  GRPC_API_TRACE("grpc_auth_context_peer_identity(ctx=%p)", 1, (ctx));
+  if (ctx == nullptr) return empty_iterator;
+  return grpc_auth_context_find_properties_by_name(
+      ctx, ctx->peer_identity_property_name());
+}
+
+void grpc_auth_context::ensure_capacity() {
+  if (properties_.count == properties_.capacity) {
+    properties_.capacity =
+        GPR_MAX(properties_.capacity + 8, properties_.capacity * 2);
+    properties_.array = static_cast<grpc_auth_property*>(gpr_realloc(
+        properties_.array, properties_.capacity * sizeof(grpc_auth_property)));
+  }
+}
+
+void grpc_auth_context::add_property(const char* name, const char* value,
+                                     size_t value_length) {
+  ensure_capacity();
+  grpc_auth_property* prop = &properties_.array[properties_.count++];
+  prop->name = gpr_strdup(name);
+  prop->value = static_cast<char*>(gpr_malloc(value_length + 1));
+  memcpy(prop->value, value, value_length);
+  prop->value[value_length] = '\0';
+  prop->value_length = value_length;
+}
+
+void grpc_auth_context_add_property(grpc_auth_context* ctx, const char* name,
+                                    const char* value, size_t value_length) {
+  GRPC_API_TRACE(
+      "grpc_auth_context_add_property(ctx=%p, name=%s, value=%*.*s, "
+      "value_length=%lu)",
+      6,
+      (ctx, name, (int)value_length, (int)value_length, value,
+       (unsigned long)value_length));
+  ctx->add_property(name, value, value_length);
+}
+
+void grpc_auth_context::add_cstring_property(const char* name,
+                                             const char* value) {
+  ensure_capacity();
+  grpc_auth_property* prop = &properties_.array[properties_.count++];
+  prop->name = gpr_strdup(name);
+  prop->value = gpr_strdup(value);
+  prop->value_length = strlen(value);
+}
+
+void grpc_auth_context_add_cstring_property(grpc_auth_context* ctx,
+                                            const char* name,
+                                            const char* value) {
+  GRPC_API_TRACE(
+      "grpc_auth_context_add_cstring_property(ctx=%p, name=%s, value=%s)", 3,
+      (ctx, name, value));
+  ctx->add_cstring_property(name, value);
+}
+
+void grpc_auth_property_reset(grpc_auth_property* property) {
+  gpr_free(property->name);
+  gpr_free(property->value);
+  memset(property, 0, sizeof(grpc_auth_property));
+}
+
+static void auth_context_pointer_arg_destroy(void* p) {
+  if (p != nullptr) {
+    static_cast<grpc_auth_context*>(p)->Unref(DEBUG_LOCATION,
+                                              "auth_context_pointer_arg");
+  }
+}
+
+static void* auth_context_pointer_arg_copy(void* p) {
+  auto* ctx = static_cast<grpc_auth_context*>(p);
+  return ctx == nullptr
+             ? nullptr
+             : ctx->Ref(DEBUG_LOCATION, "auth_context_pointer_arg").release();
+}
+
+static int auth_context_pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); }
+
+static const grpc_arg_pointer_vtable auth_context_pointer_vtable = {
+    auth_context_pointer_arg_copy, auth_context_pointer_arg_destroy,
+    auth_context_pointer_cmp};
+
+grpc_arg grpc_auth_context_to_arg(grpc_auth_context* p) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_AUTH_CONTEXT_ARG, p,
+                                         &auth_context_pointer_vtable);
+}
+
+grpc_auth_context* grpc_auth_context_from_arg(const grpc_arg* arg) {
+  if (strcmp(arg->key, GRPC_AUTH_CONTEXT_ARG) != 0) return nullptr;
+  if (arg->type != GRPC_ARG_POINTER) {
+    gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type,
+            GRPC_AUTH_CONTEXT_ARG);
+    return nullptr;
+  }
+  return static_cast<grpc_auth_context*>(arg->value.pointer.p);
+}
+
+grpc_auth_context* grpc_find_auth_context_in_args(
+    const grpc_channel_args* args) {
+  size_t i;
+  if (args == nullptr) return nullptr;
+  for (i = 0; i < args->num_args; i++) {
+    grpc_auth_context* p = grpc_auth_context_from_arg(&args->args[i]);
+    if (p != nullptr) return p;
+  }
+  return nullptr;
+}
diff --git a/third_party/grpc/src/core/lib/security/context/security_context.h b/third_party/grpc/src/core/lib/security/context/security_context.h
new file mode 100644
index 0000000..b43ee5e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/context/security_context.h
@@ -0,0 +1,151 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H
+#define GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/security/credentials/credentials.h"
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount;
+
+struct gpr_arena;
+
+/* --- grpc_auth_context ---
+
+   High level authentication context object. Can optionally be chained. */
+
+/* Property names are always NULL terminated. */
+
+struct grpc_auth_property_array {
+  grpc_auth_property* array = nullptr;
+  size_t count = 0;
+  size_t capacity = 0;
+};
+
+void grpc_auth_property_reset(grpc_auth_property* property);
+
+// This type is forward declared as a C struct and we cannot define it as a
+// class. Otherwise, compiler will complain about type mismatch due to
+// -Wmismatched-tags.
+struct grpc_auth_context
+    : public grpc_core::RefCounted<grpc_auth_context,
+                                   grpc_core::NonPolymorphicRefCount> {
+ public:
+  explicit grpc_auth_context(
+      grpc_core::RefCountedPtr<grpc_auth_context> chained)
+      : grpc_core::RefCounted<grpc_auth_context,
+                              grpc_core::NonPolymorphicRefCount>(
+            &grpc_trace_auth_context_refcount),
+        chained_(std::move(chained)) {
+    if (chained_ != nullptr) {
+      peer_identity_property_name_ = chained_->peer_identity_property_name_;
+    }
+  }
+
+  ~grpc_auth_context() {
+    chained_.reset(DEBUG_LOCATION, "chained");
+    if (properties_.array != nullptr) {
+      for (size_t i = 0; i < properties_.count; i++) {
+        grpc_auth_property_reset(&properties_.array[i]);
+      }
+      gpr_free(properties_.array);
+    }
+  }
+
+  const grpc_auth_context* chained() const { return chained_.get(); }
+  const grpc_auth_property_array& properties() const { return properties_; }
+
+  bool is_authenticated() const {
+    return peer_identity_property_name_ != nullptr;
+  }
+  const char* peer_identity_property_name() const {
+    return peer_identity_property_name_;
+  }
+  void set_peer_identity_property_name(const char* name) {
+    peer_identity_property_name_ = name;
+  }
+
+  void ensure_capacity();
+  void add_property(const char* name, const char* value, size_t value_length);
+  void add_cstring_property(const char* name, const char* value);
+
+ private:
+  grpc_core::RefCountedPtr<grpc_auth_context> chained_;
+  grpc_auth_property_array properties_;
+  const char* peer_identity_property_name_ = nullptr;
+};
+
+/* --- 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. */
+
+struct grpc_security_context_extension {
+  void* instance = nullptr;
+  void (*destroy)(void*) = nullptr;
+};
+
+/* --- grpc_client_security_context ---
+
+   Internal client-side security context. */
+
+struct grpc_client_security_context {
+  explicit grpc_client_security_context(
+      grpc_core::RefCountedPtr<grpc_call_credentials> creds)
+      : creds(std::move(creds)) {}
+  ~grpc_client_security_context();
+
+  grpc_core::RefCountedPtr<grpc_call_credentials> creds;
+  grpc_core::RefCountedPtr<grpc_auth_context> auth_context;
+  grpc_security_context_extension extension;
+};
+
+grpc_client_security_context* grpc_client_security_context_create(
+    gpr_arena* arena, grpc_call_credentials* creds);
+void grpc_client_security_context_destroy(void* ctx);
+
+/* --- grpc_server_security_context ---
+
+   Internal server-side security context. */
+
+struct grpc_server_security_context {
+  grpc_server_security_context() = default;
+  ~grpc_server_security_context();
+
+  grpc_core::RefCountedPtr<grpc_auth_context> auth_context;
+  grpc_security_context_extension extension;
+};
+
+grpc_server_security_context* grpc_server_security_context_create(
+    gpr_arena* arena);
+void grpc_server_security_context_destroy(void* ctx);
+
+/* --- Channel args for auth context --- */
+#define GRPC_AUTH_CONTEXT_ARG "grpc.auth_context"
+
+grpc_arg grpc_auth_context_to_arg(grpc_auth_context* c);
+grpc_auth_context* grpc_auth_context_from_arg(const grpc_arg* arg);
+grpc_auth_context* grpc_find_auth_context_in_args(
+    const grpc_channel_args* args);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.cc
new file mode 100644
index 0000000..0654649
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.cc
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/alts/alts_credentials.h"
+
+#include <cstring>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+#include "src/core/lib/security/security_connector/alts/alts_security_connector.h"
+
+#define GRPC_CREDENTIALS_TYPE_ALTS "Alts"
+#define GRPC_ALTS_HANDSHAKER_SERVICE_URL "metadata.google.internal:8080"
+
+grpc_alts_credentials::grpc_alts_credentials(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url)
+    : grpc_channel_credentials(GRPC_CREDENTIALS_TYPE_ALTS),
+      options_(grpc_alts_credentials_options_copy(options)),
+      handshaker_service_url_(handshaker_service_url == nullptr
+                                  ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL)
+                                  : gpr_strdup(handshaker_service_url)) {}
+
+grpc_alts_credentials::~grpc_alts_credentials() {
+  grpc_alts_credentials_options_destroy(options_);
+  gpr_free(handshaker_service_url_);
+}
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_alts_credentials::create_security_connector(
+    grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+    const char* target_name, const grpc_channel_args* args,
+    grpc_channel_args** new_args) {
+  return grpc_alts_channel_security_connector_create(
+      this->Ref(), std::move(call_creds), target_name);
+}
+
+grpc_alts_server_credentials::grpc_alts_server_credentials(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url)
+    : grpc_server_credentials(GRPC_CREDENTIALS_TYPE_ALTS),
+      options_(grpc_alts_credentials_options_copy(options)),
+      handshaker_service_url_(handshaker_service_url == nullptr
+                                  ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL)
+                                  : gpr_strdup(handshaker_service_url)) {}
+
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_alts_server_credentials::create_security_connector() {
+  return grpc_alts_server_security_connector_create(this->Ref());
+}
+
+grpc_alts_server_credentials::~grpc_alts_server_credentials() {
+  grpc_alts_credentials_options_destroy(options_);
+  gpr_free(handshaker_service_url_);
+}
+
+grpc_channel_credentials* grpc_alts_credentials_create_customized(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url, bool enable_untrusted_alts) {
+  if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) {
+    return nullptr;
+  }
+  return grpc_core::New<grpc_alts_credentials>(options, handshaker_service_url);
+}
+
+grpc_server_credentials* grpc_alts_server_credentials_create_customized(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url, bool enable_untrusted_alts) {
+  if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) {
+    return nullptr;
+  }
+  return grpc_core::New<grpc_alts_server_credentials>(options,
+                                                      handshaker_service_url);
+}
+
+grpc_channel_credentials* grpc_alts_credentials_create(
+    const grpc_alts_credentials_options* options) {
+  return grpc_alts_credentials_create_customized(
+      options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false);
+}
+
+grpc_server_credentials* grpc_alts_server_credentials_create(
+    const grpc_alts_credentials_options* options) {
+  return grpc_alts_server_credentials_create_customized(
+      options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false);
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.h b/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.h
new file mode 100644
index 0000000..cc6d522
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.h
@@ -0,0 +1,109 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+#include "src/core/lib/security/credentials/credentials.h"
+
+/* Main struct for grpc ALTS channel credential. */
+class grpc_alts_credentials final : public grpc_channel_credentials {
+ public:
+  grpc_alts_credentials(const grpc_alts_credentials_options* options,
+                        const char* handshaker_service_url);
+  ~grpc_alts_credentials() override;
+
+  grpc_core::RefCountedPtr<grpc_channel_security_connector>
+  create_security_connector(
+      grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+      const char* target_name, const grpc_channel_args* args,
+      grpc_channel_args** new_args) override;
+
+  const grpc_alts_credentials_options* options() const { return options_; }
+  grpc_alts_credentials_options* mutable_options() { return options_; }
+  const char* handshaker_service_url() const { return handshaker_service_url_; }
+
+ private:
+  grpc_alts_credentials_options* options_;
+  char* handshaker_service_url_;
+};
+
+/* Main struct for grpc ALTS server credential. */
+class grpc_alts_server_credentials final : public grpc_server_credentials {
+ public:
+  grpc_alts_server_credentials(const grpc_alts_credentials_options* options,
+                               const char* handshaker_service_url);
+  ~grpc_alts_server_credentials() override;
+
+  grpc_core::RefCountedPtr<grpc_server_security_connector>
+  create_security_connector() override;
+
+  const grpc_alts_credentials_options* options() const { return options_; }
+  grpc_alts_credentials_options* mutable_options() { return options_; }
+  const char* handshaker_service_url() const { return handshaker_service_url_; }
+
+ private:
+  grpc_alts_credentials_options* options_;
+  char* handshaker_service_url_;
+};
+
+/**
+ * This method creates an ALTS channel credential object with customized
+ * information provided by caller.
+ *
+ * - options: grpc ALTS credentials options instance for client.
+ * - handshaker_service_url: address of ALTS handshaker service in the format of
+ *   "host:port". If it's nullptr, the address of default metadata server will
+ *   be used.
+ * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted
+ *   mode. This mode can be enabled when we are sure ALTS is running on GCP or
+ * for testing purpose.
+ *
+ * It returns nullptr if the flag is disabled AND ALTS is not running on GCP.
+ * Otherwise, it returns the created credential object.
+ */
+
+grpc_channel_credentials* grpc_alts_credentials_create_customized(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url, bool enable_untrusted_alts);
+
+/**
+ * This method creates an ALTS server credential object with customized
+ * information provided by caller.
+ *
+ * - options: grpc ALTS credentials options instance for server.
+ * - handshaker_service_url: address of ALTS handshaker service in the format of
+ *   "host:port". If it's nullptr, the address of default metadata server will
+ *   be used.
+ * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted
+ *   mode. This mode can be enabled when we are sure ALTS is running on GCP or
+ * for testing purpose.
+ *
+ * It returns nullptr if the flag is disabled and ALTS is not running on GCP.
+ * Otherwise, it returns the created credential object.
+ */
+grpc_server_credentials* grpc_alts_server_credentials_create_customized(
+    const grpc_alts_credentials_options* options,
+    const char* handshaker_service_url, bool enable_untrusted_alts);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.cc b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.cc
new file mode 100644
index 0000000..9680787
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.cc
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+const size_t kBiosDataBufferSize = 256;
+
+static char* trim(const char* src) {
+  if (src == nullptr) {
+    return nullptr;
+  }
+  char* des = nullptr;
+  size_t start = 0, end = strlen(src) - 1;
+  /* find the last character that is not a whitespace. */
+  while (end != 0 && isspace(src[end])) {
+    end--;
+  }
+  /* find the first character that is not a whitespace. */
+  while (start < strlen(src) && isspace(src[start])) {
+    start++;
+  }
+  if (start <= end) {
+    des = static_cast<char*>(
+        gpr_zalloc(sizeof(char) * (end - start + 2 /* '\0' */)));
+    memcpy(des, src + start, end - start + 1);
+  }
+  return des;
+}
+
+namespace grpc_core {
+namespace internal {
+
+char* read_bios_file(const char* bios_file) {
+  FILE* fp = fopen(bios_file, "r");
+  if (!fp) {
+    gpr_log(GPR_ERROR, "BIOS data file cannot be opened.");
+    return nullptr;
+  }
+  char buf[kBiosDataBufferSize + 1];
+  size_t ret = fread(buf, sizeof(char), kBiosDataBufferSize, fp);
+  buf[ret] = '\0';
+  char* trimmed_buf = trim(buf);
+  fclose(fp);
+  return trimmed_buf;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.h b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.h
new file mode 100644
index 0000000..aea4cea
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H
+
+namespace grpc_core {
+namespace internal {
+
+/**
+ * This method is a helper function that reads a file containing system bios
+ * data. Exposed for testing only.
+ *
+ * - bios_file: a file containing BIOS data used to determine GCE tenancy
+ *   information.
+ *
+ * It returns a buffer containing the data read from the file.
+ */
+char* read_bios_file(const char* bios_file);
+
+/**
+ * This method checks if system BIOS data contains Google-specific phrases.
+ * Exposed for testing only.
+ *
+ * - bios_data: a buffer containing system BIOS data.
+ *
+ * It returns true if the BIOS data contains Google-specific phrases, and false
+ * otherwise.
+ */
+bool check_bios_data(const char* bios_data);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+/**
+ * This method checks if a VM (Windows or Linux) is running within Google
+ * compute Engine (GCE) or not. It returns true if the VM is running in GCE and
+ * false otherwise.
+ */
+bool grpc_alts_is_running_on_gcp();
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc
new file mode 100644
index 0000000..8454fd7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/sync.h>
+
+#include <string.h>
+
+#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google"
+#define GRPC_ALTS_EXPECT_NAME_GCE "Google Compute Engine"
+#define GRPC_ALTS_PRODUCT_NAME_FILE "/sys/class/dmi/id/product_name"
+
+static bool g_compute_engine_detection_done = false;
+static bool g_is_on_compute_engine = false;
+static gpr_mu g_mu;
+static gpr_once g_once = GPR_ONCE_INIT;
+
+namespace grpc_core {
+namespace internal {
+
+bool check_bios_data(const char* bios_data_file) {
+  char* bios_data = read_bios_file(bios_data_file);
+  bool result =
+      bios_data && ((!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE)) ||
+                    (!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GCE)));
+  gpr_free(bios_data);
+  return result;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+static void init_mu(void) { gpr_mu_init(&g_mu); }
+
+bool grpc_alts_is_running_on_gcp() {
+  gpr_once_init(&g_once, init_mu);
+  gpr_mu_lock(&g_mu);
+  if (!g_compute_engine_detection_done) {
+    g_is_on_compute_engine =
+        grpc_core::internal::check_bios_data(GRPC_ALTS_PRODUCT_NAME_FILE);
+    g_compute_engine_detection_done = true;
+  }
+  gpr_mu_unlock(&g_mu);
+  return g_is_on_compute_engine;
+}
+
+#endif  // GPR_LINUX
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc
new file mode 100644
index 0000000..d97681b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#if !defined(GPR_LINUX) && !defined(GPR_WINDOWS)
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#include <grpc/support/log.h>
+
+bool grpc_alts_is_running_on_gcp() {
+  gpr_log(GPR_ERROR,
+          "Platforms other than Linux and Windows are not supported");
+  return false;
+}
+
+#endif  // !defined(LINUX) && !defined(GPR_WINDOWS)
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc
new file mode 100644
index 0000000..55efe0e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+
+#include <shellapi.h>
+#include <stdio.h>
+#include <tchar.h>
+#include <windows.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google"
+#define GRPC_ALTS_WINDOWS_CHECK_COMMAND "powershell.exe"
+#define GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS \
+  "(Get-WmiObject -Class Win32_BIOS).Manufacturer"
+#define GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE "windows_bios.data"
+
+const size_t kBiosDataBufferSize = 256;
+
+static bool g_compute_engine_detection_done = false;
+static bool g_is_on_compute_engine = false;
+static gpr_mu g_mu;
+static gpr_once g_once = GPR_ONCE_INIT;
+
+namespace grpc_core {
+namespace internal {
+
+bool check_bios_data(const char* bios_data_file) {
+  char* bios_data = read_bios_file(bios_data_file);
+  bool result = !strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE);
+  remove(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE);
+  gpr_free(bios_data);
+  return result;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+static void init_mu(void) { gpr_mu_init(&g_mu); }
+
+static bool run_powershell() {
+  SECURITY_ATTRIBUTES sa;
+  sa.nLength = sizeof(sa);
+  sa.lpSecurityDescriptor = NULL;
+  sa.bInheritHandle = TRUE;
+  HANDLE h = CreateFile(_T(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE), GENERIC_WRITE,
+                        FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, OPEN_ALWAYS,
+                        FILE_ATTRIBUTE_NORMAL, NULL);
+  if (h == INVALID_HANDLE_VALUE) {
+    gpr_log(GPR_ERROR, "CreateFile failed (%d).", GetLastError());
+    return false;
+  }
+  PROCESS_INFORMATION pi;
+  STARTUPINFO si;
+  DWORD flags = CREATE_NO_WINDOW;
+  ZeroMemory(&pi, sizeof(pi));
+  ZeroMemory(&si, sizeof(si));
+  si.cb = sizeof(si);
+  si.dwFlags |= STARTF_USESTDHANDLES;
+  si.hStdInput = NULL;
+  si.hStdError = h;
+  si.hStdOutput = h;
+  TCHAR cmd[kBiosDataBufferSize];
+  _sntprintf(cmd, kBiosDataBufferSize, _T("%s %s"),
+             _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND),
+             _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS));
+  if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si,
+                     &pi)) {
+    gpr_log(GPR_ERROR, "CreateProcess failed (%d).\n", GetLastError());
+    return false;
+  }
+  WaitForSingleObject(pi.hProcess, INFINITE);
+  CloseHandle(pi.hProcess);
+  CloseHandle(pi.hThread);
+  CloseHandle(h);
+  return true;
+}
+
+bool grpc_alts_is_running_on_gcp() {
+  gpr_once_init(&g_once, init_mu);
+  gpr_mu_lock(&g_mu);
+  if (!g_compute_engine_detection_done) {
+    g_is_on_compute_engine =
+        run_powershell() &&
+        grpc_core::internal::check_bios_data(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE);
+    g_compute_engine_detection_done = true;
+  }
+  gpr_mu_unlock(&g_mu);
+  return g_is_on_compute_engine;
+}
+
+#endif  // GPR_WINDOWS
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc
new file mode 100644
index 0000000..118d18d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+static grpc_alts_credentials_options* alts_client_options_copy(
+    const grpc_alts_credentials_options* options);
+
+static void alts_client_options_destroy(grpc_alts_credentials_options* options);
+
+static target_service_account* target_service_account_create(
+    const char* service_account) {
+  if (service_account == nullptr) {
+    return nullptr;
+  }
+  auto* sa = static_cast<target_service_account*>(
+      gpr_zalloc(sizeof(target_service_account)));
+  sa->data = gpr_strdup(service_account);
+  return sa;
+}
+
+void grpc_alts_credentials_client_options_add_target_service_account(
+    grpc_alts_credentials_options* options, const char* service_account) {
+  if (options == nullptr || service_account == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to "
+        "grpc_alts_credentials_client_options_add_target_service_account()");
+    return;
+  }
+  auto client_options =
+      reinterpret_cast<grpc_alts_credentials_client_options*>(options);
+  target_service_account* node = target_service_account_create(service_account);
+  node->next = client_options->target_account_list_head;
+  client_options->target_account_list_head = node;
+}
+
+static void target_service_account_destroy(
+    target_service_account* service_account) {
+  if (service_account == nullptr) {
+    return;
+  }
+  gpr_free(service_account->data);
+  gpr_free(service_account);
+}
+
+static const grpc_alts_credentials_options_vtable vtable = {
+    alts_client_options_copy, alts_client_options_destroy};
+
+grpc_alts_credentials_options* grpc_alts_credentials_client_options_create(
+    void) {
+  auto client_options = static_cast<grpc_alts_credentials_client_options*>(
+      gpr_zalloc(sizeof(grpc_alts_credentials_client_options)));
+  client_options->base.vtable = &vtable;
+  return &client_options->base;
+}
+
+static grpc_alts_credentials_options* alts_client_options_copy(
+    const grpc_alts_credentials_options* options) {
+  if (options == nullptr) {
+    return nullptr;
+  }
+  grpc_alts_credentials_options* new_options =
+      grpc_alts_credentials_client_options_create();
+  auto new_client_options =
+      reinterpret_cast<grpc_alts_credentials_client_options*>(new_options);
+  /* Copy target service accounts. */
+  target_service_account* prev = nullptr;
+  auto node =
+      (reinterpret_cast<const grpc_alts_credentials_client_options*>(options))
+          ->target_account_list_head;
+  while (node != nullptr) {
+    target_service_account* new_node =
+        target_service_account_create(node->data);
+    if (prev == nullptr) {
+      new_client_options->target_account_list_head = new_node;
+    } else {
+      prev->next = new_node;
+    }
+    prev = new_node;
+    node = node->next;
+  }
+  /* Copy rpc protocol versions. */
+  grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions,
+                                      &new_options->rpc_versions);
+  return new_options;
+}
+
+static void alts_client_options_destroy(
+    grpc_alts_credentials_options* options) {
+  if (options == nullptr) {
+    return;
+  }
+  auto* client_options =
+      reinterpret_cast<grpc_alts_credentials_client_options*>(options);
+  target_service_account* node = client_options->target_account_list_head;
+  while (node != nullptr) {
+    target_service_account* next_node = node->next;
+    target_service_account_destroy(node);
+    node = next_node;
+  }
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc
new file mode 100644
index 0000000..d428171
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+grpc_alts_credentials_options* grpc_alts_credentials_options_copy(
+    const grpc_alts_credentials_options* options) {
+  if (options != nullptr && options->vtable != nullptr &&
+      options->vtable->copy != nullptr) {
+    return options->vtable->copy(options);
+  }
+  /* An error occurred. */
+  gpr_log(GPR_ERROR,
+          "Invalid arguments to grpc_alts_credentials_options_copy()");
+  return nullptr;
+}
+
+void grpc_alts_credentials_options_destroy(
+    grpc_alts_credentials_options* options) {
+  if (options != nullptr) {
+    if (options->vtable != nullptr && options->vtable->destruct != nullptr) {
+      options->vtable->destruct(options);
+    }
+    gpr_free(options);
+  }
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h
new file mode 100644
index 0000000..320af71
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+/* V-table for grpc_alts_credentials_options */
+typedef struct grpc_alts_credentials_options_vtable {
+  grpc_alts_credentials_options* (*copy)(
+      const grpc_alts_credentials_options* options);
+  void (*destruct)(grpc_alts_credentials_options* options);
+} grpc_alts_credentials_options_vtable;
+
+struct grpc_alts_credentials_options {
+  const struct grpc_alts_credentials_options_vtable* vtable;
+  grpc_gcp_rpc_protocol_versions rpc_versions;
+};
+
+typedef struct target_service_account {
+  struct target_service_account* next;
+  char* data;
+} target_service_account;
+
+/**
+ * Main struct for ALTS client credentials options. The options contain a
+ * a list of target service accounts (if specified) used for secure naming
+ * check.
+ */
+typedef struct grpc_alts_credentials_client_options {
+  grpc_alts_credentials_options base;
+  target_service_account* target_account_list_head;
+} grpc_alts_credentials_client_options;
+
+/**
+ * Main struct for ALTS server credentials options. The options currently
+ * do not contain any server-specific fields.
+ */
+typedef struct grpc_alts_credentials_server_options {
+  grpc_alts_credentials_options base;
+} grpc_alts_credentials_server_options;
+
+/**
+ * This method performs a deep copy on grpc_alts_credentials_options instance.
+ *
+ * - options: a grpc_alts_credentials_options instance that needs to be copied.
+ *
+ * It returns a new grpc_alts_credentials_options instance on success and NULL
+ * on failure.
+ */
+grpc_alts_credentials_options* grpc_alts_credentials_options_copy(
+    const grpc_alts_credentials_options* options);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc
new file mode 100644
index 0000000..1a59c45
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+static grpc_alts_credentials_options* alts_server_options_copy(
+    const grpc_alts_credentials_options* options);
+
+static void alts_server_options_destroy(
+    grpc_alts_credentials_options* options) {}
+
+static const grpc_alts_credentials_options_vtable vtable = {
+    alts_server_options_copy, alts_server_options_destroy};
+
+grpc_alts_credentials_options* grpc_alts_credentials_server_options_create(
+    void) {
+  grpc_alts_credentials_server_options* server_options =
+      static_cast<grpc_alts_credentials_server_options*>(
+          gpr_zalloc(sizeof(*server_options)));
+  server_options->base.vtable = &vtable;
+  return &server_options->base;
+}
+
+static grpc_alts_credentials_options* alts_server_options_copy(
+    const grpc_alts_credentials_options* options) {
+  if (options == nullptr) {
+    return nullptr;
+  }
+  grpc_alts_credentials_options* new_options =
+      grpc_alts_credentials_server_options_create();
+  /* Copy rpc protocol versions. */
+  grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions,
+                                      &new_options->rpc_versions);
+  return new_options;
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.cc
new file mode 100644
index 0000000..586bbed
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.cc
@@ -0,0 +1,209 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/composite/composite_credentials.h"
+
+#include <cstring>
+#include <new>
+
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+/* -- Composite call credentials. -- */
+
+static void composite_call_metadata_cb(void* arg, grpc_error* error);
+
+namespace {
+struct grpc_composite_call_credentials_metadata_context {
+  grpc_composite_call_credentials_metadata_context(
+      grpc_composite_call_credentials* composite_creds,
+      grpc_polling_entity* pollent, grpc_auth_metadata_context auth_md_context,
+      grpc_credentials_mdelem_array* md_array,
+      grpc_closure* on_request_metadata)
+      : composite_creds(composite_creds),
+        pollent(pollent),
+        auth_md_context(auth_md_context),
+        md_array(md_array),
+        on_request_metadata(on_request_metadata) {
+    GRPC_CLOSURE_INIT(&internal_on_request_metadata, composite_call_metadata_cb,
+                      this, grpc_schedule_on_exec_ctx);
+  }
+
+  grpc_composite_call_credentials* composite_creds;
+  size_t creds_index = 0;
+  grpc_polling_entity* pollent;
+  grpc_auth_metadata_context auth_md_context;
+  grpc_credentials_mdelem_array* md_array;
+  grpc_closure* on_request_metadata;
+  grpc_closure internal_on_request_metadata;
+};
+}  // namespace
+
+static void composite_call_metadata_cb(void* arg, grpc_error* error) {
+  grpc_composite_call_credentials_metadata_context* ctx =
+      static_cast<grpc_composite_call_credentials_metadata_context*>(arg);
+  if (error == GRPC_ERROR_NONE) {
+    const grpc_composite_call_credentials::CallCredentialsList& inner =
+        ctx->composite_creds->inner();
+    /* See if we need to get some more metadata. */
+    if (ctx->creds_index < inner.size()) {
+      if (inner[ctx->creds_index++]->get_request_metadata(
+              ctx->pollent, ctx->auth_md_context, ctx->md_array,
+              &ctx->internal_on_request_metadata, &error)) {
+        // Synchronous response, so call ourselves recursively.
+        composite_call_metadata_cb(arg, error);
+        GRPC_ERROR_UNREF(error);
+      }
+      return;
+    }
+    // We're done!
+  }
+  GRPC_CLOSURE_SCHED(ctx->on_request_metadata, GRPC_ERROR_REF(error));
+  gpr_free(ctx);
+}
+
+bool grpc_composite_call_credentials::get_request_metadata(
+    grpc_polling_entity* pollent, grpc_auth_metadata_context auth_md_context,
+    grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
+    grpc_error** error) {
+  grpc_composite_call_credentials_metadata_context* ctx;
+  ctx = grpc_core::New<grpc_composite_call_credentials_metadata_context>(
+      this, pollent, auth_md_context, md_array, on_request_metadata);
+  bool synchronous = true;
+  const CallCredentialsList& inner = ctx->composite_creds->inner();
+  while (ctx->creds_index < inner.size()) {
+    if (inner[ctx->creds_index++]->get_request_metadata(
+            ctx->pollent, ctx->auth_md_context, ctx->md_array,
+            &ctx->internal_on_request_metadata, error)) {
+      if (*error != GRPC_ERROR_NONE) break;
+    } else {
+      synchronous = false;  // Async return.
+      break;
+    }
+  }
+  if (synchronous) grpc_core::Delete(ctx);
+  return synchronous;
+}
+
+void grpc_composite_call_credentials::cancel_get_request_metadata(
+    grpc_credentials_mdelem_array* md_array, grpc_error* error) {
+  for (size_t i = 0; i < inner_.size(); ++i) {
+    inner_[i]->cancel_get_request_metadata(md_array, GRPC_ERROR_REF(error));
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+static size_t get_creds_array_size(const grpc_call_credentials* creds,
+                                   bool is_composite) {
+  return is_composite
+             ? static_cast<const grpc_composite_call_credentials*>(creds)
+                   ->inner()
+                   .size()
+             : 1;
+}
+
+void grpc_composite_call_credentials::push_to_inner(
+    grpc_core::RefCountedPtr<grpc_call_credentials> creds, bool is_composite) {
+  if (!is_composite) {
+    inner_.push_back(std::move(creds));
+    return;
+  }
+  auto composite_creds =
+      static_cast<grpc_composite_call_credentials*>(creds.get());
+  for (size_t i = 0; i < composite_creds->inner().size(); ++i) {
+    inner_.push_back(std::move(composite_creds->inner_[i]));
+  }
+}
+
+grpc_composite_call_credentials::grpc_composite_call_credentials(
+    grpc_core::RefCountedPtr<grpc_call_credentials> creds1,
+    grpc_core::RefCountedPtr<grpc_call_credentials> creds2)
+    : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) {
+  const bool creds1_is_composite =
+      strcmp(creds1->type(), GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0;
+  const bool creds2_is_composite =
+      strcmp(creds2->type(), GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0;
+  const size_t size = get_creds_array_size(creds1.get(), creds1_is_composite) +
+                      get_creds_array_size(creds2.get(), creds2_is_composite);
+  inner_.reserve(size);
+  push_to_inner(std::move(creds1), creds1_is_composite);
+  push_to_inner(std::move(creds2), creds2_is_composite);
+}
+
+static grpc_core::RefCountedPtr<grpc_call_credentials>
+composite_call_credentials_create(
+    grpc_core::RefCountedPtr<grpc_call_credentials> creds1,
+    grpc_core::RefCountedPtr<grpc_call_credentials> creds2) {
+  return grpc_core::MakeRefCounted<grpc_composite_call_credentials>(
+      std::move(creds1), std::move(creds2));
+}
+
+grpc_call_credentials* grpc_composite_call_credentials_create(
+    grpc_call_credentials* creds1, grpc_call_credentials* creds2,
+    void* reserved) {
+  GRPC_API_TRACE(
+      "grpc_composite_call_credentials_create(creds1=%p, creds2=%p, "
+      "reserved=%p)",
+      3, (creds1, creds2, reserved));
+  GPR_ASSERT(reserved == nullptr);
+  GPR_ASSERT(creds1 != nullptr);
+  GPR_ASSERT(creds2 != nullptr);
+
+  return composite_call_credentials_create(creds1->Ref(), creds2->Ref())
+      .release();
+}
+
+/* -- Composite channel credentials. -- */
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_composite_channel_credentials::create_security_connector(
+    grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+    const char* target, const grpc_channel_args* args,
+    grpc_channel_args** new_args) {
+  GPR_ASSERT(inner_creds_ != nullptr && call_creds_ != nullptr);
+  /* If we are passed a call_creds, create a call composite to pass it
+     downstream. */
+  if (call_creds != nullptr) {
+    return inner_creds_->create_security_connector(
+        composite_call_credentials_create(call_creds_, std::move(call_creds)),
+        target, args, new_args);
+  } else {
+    return inner_creds_->create_security_connector(call_creds_, target, args,
+                                                   new_args);
+  }
+}
+
+grpc_channel_credentials* grpc_composite_channel_credentials_create(
+    grpc_channel_credentials* channel_creds, grpc_call_credentials* call_creds,
+    void* reserved) {
+  GPR_ASSERT(channel_creds != nullptr && call_creds != nullptr &&
+             reserved == nullptr);
+  GRPC_API_TRACE(
+      "grpc_composite_channel_credentials_create(channel_creds=%p, "
+      "call_creds=%p, reserved=%p)",
+      3, (channel_creds, call_creds, reserved));
+  return grpc_core::New<grpc_composite_channel_credentials>(
+      channel_creds->Ref(), call_creds->Ref());
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.h b/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.h
new file mode 100644
index 0000000..7a1c7d5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/credentials/credentials.h"
+
+/* -- Composite channel credentials. -- */
+
+class grpc_composite_channel_credentials : public grpc_channel_credentials {
+ public:
+  grpc_composite_channel_credentials(
+      grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+      grpc_core::RefCountedPtr<grpc_call_credentials> call_creds)
+      : grpc_channel_credentials(channel_creds->type()),
+        inner_creds_(std::move(channel_creds)),
+        call_creds_(std::move(call_creds)) {}
+
+  ~grpc_composite_channel_credentials() override = default;
+
+  grpc_core::RefCountedPtr<grpc_channel_credentials>
+  duplicate_without_call_credentials() override {
+    return inner_creds_;
+  }
+
+  grpc_core::RefCountedPtr<grpc_channel_security_connector>
+  create_security_connector(
+      grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+      const char* target, const grpc_channel_args* args,
+      grpc_channel_args** new_args) override;
+
+  const grpc_channel_credentials* inner_creds() const {
+    return inner_creds_.get();
+  }
+  const grpc_call_credentials* call_creds() const { return call_creds_.get(); }
+  grpc_call_credentials* mutable_call_creds() { return call_creds_.get(); }
+
+ private:
+  grpc_core::RefCountedPtr<grpc_channel_credentials> inner_creds_;
+  grpc_core::RefCountedPtr<grpc_call_credentials> call_creds_;
+};
+
+/* -- Composite call credentials. -- */
+
+class grpc_composite_call_credentials : public grpc_call_credentials {
+ public:
+  using CallCredentialsList =
+      grpc_core::InlinedVector<grpc_core::RefCountedPtr<grpc_call_credentials>,
+                               2>;
+
+  grpc_composite_call_credentials(
+      grpc_core::RefCountedPtr<grpc_call_credentials> creds1,
+      grpc_core::RefCountedPtr<grpc_call_credentials> creds2);
+  ~grpc_composite_call_credentials() override = default;
+
+  bool get_request_metadata(grpc_polling_entity* pollent,
+                            grpc_auth_metadata_context context,
+                            grpc_credentials_mdelem_array* md_array,
+                            grpc_closure* on_request_metadata,
+                            grpc_error** error) override;
+
+  void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array,
+                                   grpc_error* error) override;
+
+  const CallCredentialsList& inner() const { return inner_; }
+
+ private:
+  void push_to_inner(grpc_core::RefCountedPtr<grpc_call_credentials> creds,
+                     bool is_composite);
+
+  CallCredentialsList inner_;
+};
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/credentials/credentials.cc b/third_party/grpc/src/core/lib/security/credentials/credentials.cc
new file mode 100644
index 0000000..90452d6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/credentials.cc
@@ -0,0 +1,162 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+/* -- Common. -- */
+
+void grpc_channel_credentials_release(grpc_channel_credentials* creds) {
+  GRPC_API_TRACE("grpc_channel_credentials_release(creds=%p)", 1, (creds));
+  grpc_core::ExecCtx exec_ctx;
+  if (creds) creds->Unref();
+}
+
+void grpc_call_credentials_release(grpc_call_credentials* creds) {
+  GRPC_API_TRACE("grpc_call_credentials_release(creds=%p)", 1, (creds));
+  grpc_core::ExecCtx exec_ctx;
+  if (creds) creds->Unref();
+}
+
+static void credentials_pointer_arg_destroy(void* p) {
+  static_cast<grpc_channel_credentials*>(p)->Unref();
+}
+
+static void* credentials_pointer_arg_copy(void* p) {
+  return static_cast<grpc_channel_credentials*>(p)->Ref().release();
+}
+
+static int credentials_pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); }
+
+static const grpc_arg_pointer_vtable credentials_pointer_vtable = {
+    credentials_pointer_arg_copy, credentials_pointer_arg_destroy,
+    credentials_pointer_cmp};
+
+grpc_arg grpc_channel_credentials_to_arg(
+    grpc_channel_credentials* credentials) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_ARG_CHANNEL_CREDENTIALS,
+                                         credentials,
+                                         &credentials_pointer_vtable);
+}
+
+grpc_channel_credentials* grpc_channel_credentials_from_arg(
+    const grpc_arg* arg) {
+  if (strcmp(arg->key, GRPC_ARG_CHANNEL_CREDENTIALS)) return nullptr;
+  if (arg->type != GRPC_ARG_POINTER) {
+    gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type,
+            GRPC_ARG_CHANNEL_CREDENTIALS);
+    return nullptr;
+  }
+  return static_cast<grpc_channel_credentials*>(arg->value.pointer.p);
+}
+
+grpc_channel_credentials* grpc_channel_credentials_find_in_args(
+    const grpc_channel_args* args) {
+  size_t i;
+  if (args == nullptr) return nullptr;
+  for (i = 0; i < args->num_args; i++) {
+    grpc_channel_credentials* credentials =
+        grpc_channel_credentials_from_arg(&args->args[i]);
+    if (credentials != nullptr) return credentials;
+  }
+  return nullptr;
+}
+
+void grpc_server_credentials_release(grpc_server_credentials* creds) {
+  GRPC_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (creds));
+  grpc_core::ExecCtx exec_ctx;
+  if (creds) creds->Unref();
+}
+
+void grpc_server_credentials::set_auth_metadata_processor(
+    const grpc_auth_metadata_processor& processor) {
+  GRPC_API_TRACE(
+      "grpc_server_credentials_set_auth_metadata_processor("
+      "creds=%p, "
+      "processor=grpc_auth_metadata_processor { process: %p, state: %p })",
+      3, (this, (void*)(intptr_t)processor.process, processor.state));
+  DestroyProcessor();
+  processor_ = processor;
+}
+
+void grpc_server_credentials_set_auth_metadata_processor(
+    grpc_server_credentials* creds, grpc_auth_metadata_processor processor) {
+  GPR_DEBUG_ASSERT(creds != nullptr);
+  creds->set_auth_metadata_processor(processor);
+}
+
+static void server_credentials_pointer_arg_destroy(void* p) {
+  static_cast<grpc_server_credentials*>(p)->Unref();
+}
+
+static void* server_credentials_pointer_arg_copy(void* p) {
+  return static_cast<grpc_server_credentials*>(p)->Ref().release();
+}
+
+static int server_credentials_pointer_cmp(void* a, void* b) {
+  return GPR_ICMP(a, b);
+}
+
+static const grpc_arg_pointer_vtable cred_ptr_vtable = {
+    server_credentials_pointer_arg_copy, server_credentials_pointer_arg_destroy,
+    server_credentials_pointer_cmp};
+
+grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials* p) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_SERVER_CREDENTIALS_ARG, p,
+                                         &cred_ptr_vtable);
+}
+
+grpc_server_credentials* grpc_server_credentials_from_arg(const grpc_arg* arg) {
+  if (strcmp(arg->key, GRPC_SERVER_CREDENTIALS_ARG) != 0) return nullptr;
+  if (arg->type != GRPC_ARG_POINTER) {
+    gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type,
+            GRPC_SERVER_CREDENTIALS_ARG);
+    return nullptr;
+  }
+  return static_cast<grpc_server_credentials*>(arg->value.pointer.p);
+}
+
+grpc_server_credentials* grpc_find_server_credentials_in_args(
+    const grpc_channel_args* args) {
+  size_t i;
+  if (args == nullptr) return nullptr;
+  for (i = 0; i < args->num_args; i++) {
+    grpc_server_credentials* p =
+        grpc_server_credentials_from_arg(&args->args[i]);
+    if (p != nullptr) return p;
+  }
+  return nullptr;
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/credentials.h b/third_party/grpc/src/core/lib/security/credentials/credentials.h
new file mode 100644
index 0000000..4091ef3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/credentials.h
@@ -0,0 +1,271 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+#include <grpc/support/sync.h>
+#include "src/core/lib/transport/metadata_batch.h"
+
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
+
+struct grpc_http_response;
+
+/* --- Constants. --- */
+
+typedef enum {
+  GRPC_CREDENTIALS_OK = 0,
+  GRPC_CREDENTIALS_ERROR
+} grpc_credentials_status;
+
+#define GRPC_FAKE_TRANSPORT_SECURITY_TYPE "fake"
+
+#define GRPC_CHANNEL_CREDENTIALS_TYPE_SSL "Ssl"
+#define GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY \
+  "FakeTransportSecurity"
+#define GRPC_CHANNEL_CREDENTIALS_TYPE_GOOGLE_DEFAULT "GoogleDefault"
+
+#define GRPC_CALL_CREDENTIALS_TYPE_OAUTH2 "Oauth2"
+#define GRPC_CALL_CREDENTIALS_TYPE_JWT "Jwt"
+#define GRPC_CALL_CREDENTIALS_TYPE_IAM "Iam"
+#define GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE "Composite"
+
+#define GRPC_AUTHORIZATION_METADATA_KEY "authorization"
+#define GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY \
+  "x-goog-iam-authorization-token"
+#define GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY "x-goog-iam-authority-selector"
+
+#define GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS 60
+
+#define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata.google.internal"
+#define GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH \
+  "/computeMetadata/v1/instance/service-accounts/default/token"
+
+#define GRPC_GOOGLE_OAUTH2_SERVICE_HOST "www.googleapis.com"
+#define GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH "/oauth2/v3/token"
+
+#define GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX                         \
+  "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&" \
+  "assertion="
+
+#define GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING \
+  "client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token"
+
+/* --- Google utils --- */
+
+/* It is the caller's responsibility to gpr_free the result if not NULL. */
+char* grpc_get_well_known_google_credentials_file_path(void);
+
+/* Implementation function for the different platforms. */
+char* grpc_get_well_known_google_credentials_file_path_impl(void);
+
+/* Override for testing only. Not thread-safe */
+typedef char* (*grpc_well_known_credentials_path_getter)(void);
+void grpc_override_well_known_credentials_path_getter(
+    grpc_well_known_credentials_path_getter getter);
+
+/* --- grpc_channel_credentials. --- */
+
+#define GRPC_ARG_CHANNEL_CREDENTIALS "grpc.channel_credentials"
+
+// This type is forward declared as a C struct and we cannot define it as a
+// class. Otherwise, compiler will complain about type mismatch due to
+// -Wmismatched-tags.
+struct grpc_channel_credentials
+    : grpc_core::RefCounted<grpc_channel_credentials> {
+ public:
+  explicit grpc_channel_credentials(const char* type) : type_(type) {}
+  virtual ~grpc_channel_credentials() = default;
+
+  // Creates a security connector for the channel. May also create new channel
+  // args for the channel to be used in place of the passed in const args if
+  // returned non NULL. In that case the caller is responsible for destroying
+  // new_args after channel creation.
+  virtual grpc_core::RefCountedPtr<grpc_channel_security_connector>
+  create_security_connector(
+      grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+      const char* target, const grpc_channel_args* args,
+      grpc_channel_args** new_args) {
+    // Tell clang-tidy that call_creds cannot be passed as const-ref.
+    call_creds.reset();
+    GRPC_ABSTRACT;
+  }
+
+  // Creates a version of the channel credentials without any attached call
+  // credentials. This can be used in order to open a channel to a non-trusted
+  // gRPC load balancer.
+  virtual grpc_core::RefCountedPtr<grpc_channel_credentials>
+  duplicate_without_call_credentials() {
+    // By default we just increment the refcount.
+    return Ref();
+  }
+
+  const char* type() const { return type_; }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ private:
+  const char* type_;
+};
+
+/* Util to encapsulate the channel credentials in a channel arg. */
+grpc_arg grpc_channel_credentials_to_arg(grpc_channel_credentials* credentials);
+
+/* Util to get the channel credentials from a channel arg. */
+grpc_channel_credentials* grpc_channel_credentials_from_arg(
+    const grpc_arg* arg);
+
+/* Util to find the channel credentials from channel args. */
+grpc_channel_credentials* grpc_channel_credentials_find_in_args(
+    const grpc_channel_args* args);
+
+/* --- grpc_credentials_mdelem_array. --- */
+
+typedef struct {
+  grpc_mdelem* md = nullptr;
+  size_t size = 0;
+} grpc_credentials_mdelem_array;
+
+/// Takes a new ref to \a md.
+void grpc_credentials_mdelem_array_add(grpc_credentials_mdelem_array* list,
+                                       grpc_mdelem md);
+
+/// Appends all elements from \a src to \a dst, taking a new ref to each one.
+void grpc_credentials_mdelem_array_append(grpc_credentials_mdelem_array* dst,
+                                          grpc_credentials_mdelem_array* src);
+
+void grpc_credentials_mdelem_array_destroy(grpc_credentials_mdelem_array* list);
+
+/* --- grpc_call_credentials. --- */
+
+// This type is forward declared as a C struct and we cannot define it as a
+// class. Otherwise, compiler will complain about type mismatch due to
+// -Wmismatched-tags.
+struct grpc_call_credentials
+    : public grpc_core::RefCounted<grpc_call_credentials> {
+ public:
+  explicit grpc_call_credentials(const char* type) : type_(type) {}
+  virtual ~grpc_call_credentials() = default;
+
+  // Returns true if completed synchronously, in which case \a error will
+  // be set to indicate the result.  Otherwise, \a on_request_metadata will
+  // be invoked asynchronously when complete.  \a md_array will be populated
+  // with the resulting metadata once complete.
+  virtual bool get_request_metadata(grpc_polling_entity* pollent,
+                                    grpc_auth_metadata_context context,
+                                    grpc_credentials_mdelem_array* md_array,
+                                    grpc_closure* on_request_metadata,
+                                    grpc_error** error) GRPC_ABSTRACT;
+
+  // Cancels a pending asynchronous operation started by
+  // grpc_call_credentials_get_request_metadata() with the corresponding
+  // value of \a md_array.
+  virtual void cancel_get_request_metadata(
+      grpc_credentials_mdelem_array* md_array, grpc_error* error) GRPC_ABSTRACT;
+
+  const char* type() const { return type_; }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ private:
+  const char* type_;
+};
+
+/* Metadata-only credentials with the specified key and value where
+   asynchronicity can be simulated for testing. */
+grpc_call_credentials* grpc_md_only_test_credentials_create(
+    const char* md_key, const char* md_value, bool is_async);
+
+/* --- grpc_server_credentials. --- */
+
+// This type is forward declared as a C struct and we cannot define it as a
+// class. Otherwise, compiler will complain about type mismatch due to
+// -Wmismatched-tags.
+struct grpc_server_credentials
+    : public grpc_core::RefCounted<grpc_server_credentials> {
+ public:
+  explicit grpc_server_credentials(const char* type) : type_(type) {}
+
+  virtual ~grpc_server_credentials() { DestroyProcessor(); }
+
+  virtual grpc_core::RefCountedPtr<grpc_server_security_connector>
+  create_security_connector() GRPC_ABSTRACT;
+
+  const char* type() const { return type_; }
+
+  const grpc_auth_metadata_processor& auth_metadata_processor() const {
+    return processor_;
+  }
+  void set_auth_metadata_processor(
+      const grpc_auth_metadata_processor& processor);
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ private:
+  void DestroyProcessor() {
+    if (processor_.destroy != nullptr && processor_.state != nullptr) {
+      processor_.destroy(processor_.state);
+    }
+  }
+
+  const char* type_;
+  grpc_auth_metadata_processor processor_ =
+      grpc_auth_metadata_processor();  // Zero-initialize the C struct.
+};
+
+#define GRPC_SERVER_CREDENTIALS_ARG "grpc.server_credentials"
+
+grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials* c);
+grpc_server_credentials* grpc_server_credentials_from_arg(const grpc_arg* arg);
+grpc_server_credentials* grpc_find_server_credentials_in_args(
+    const grpc_channel_args* args);
+
+/* -- Credentials Metadata Request. -- */
+
+struct grpc_credentials_metadata_request {
+  explicit grpc_credentials_metadata_request(
+      grpc_core::RefCountedPtr<grpc_call_credentials> creds)
+      : creds(std::move(creds)) {}
+  ~grpc_credentials_metadata_request() {
+    grpc_http_response_destroy(&response);
+  }
+
+  grpc_core::RefCountedPtr<grpc_call_credentials> creds;
+  grpc_http_response response;
+};
+
+inline grpc_credentials_metadata_request*
+grpc_credentials_metadata_request_create(
+    grpc_core::RefCountedPtr<grpc_call_credentials> creds) {
+  return grpc_core::New<grpc_credentials_metadata_request>(std::move(creds));
+}
+
+inline void grpc_credentials_metadata_request_destroy(
+    grpc_credentials_metadata_request* r) {
+  grpc_core::Delete(r);
+}
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/credentials_metadata.cc b/third_party/grpc/src/core/lib/security/credentials/credentials_metadata.cc
new file mode 100644
index 0000000..703de4a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/credentials_metadata.cc
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+#include <grpc/support/alloc.h>
+
+#include <string.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+
+static void mdelem_list_ensure_capacity(grpc_credentials_mdelem_array* list,
+                                        size_t additional_space_needed) {
+  size_t target_size = list->size + additional_space_needed;
+  // Find the next power of two greater than the target size (i.e.,
+  // whenever we add more space, we double what we already have).
+  size_t new_size = 2;
+  while (new_size < target_size) {
+    new_size *= 2;
+  }
+  list->md = static_cast<grpc_mdelem*>(
+      gpr_realloc(list->md, sizeof(grpc_mdelem) * new_size));
+}
+
+void grpc_credentials_mdelem_array_add(grpc_credentials_mdelem_array* list,
+                                       grpc_mdelem md) {
+  mdelem_list_ensure_capacity(list, 1);
+  list->md[list->size++] = GRPC_MDELEM_REF(md);
+}
+
+void grpc_credentials_mdelem_array_append(grpc_credentials_mdelem_array* dst,
+                                          grpc_credentials_mdelem_array* src) {
+  mdelem_list_ensure_capacity(dst, src->size);
+  for (size_t i = 0; i < src->size; ++i) {
+    dst->md[dst->size++] = GRPC_MDELEM_REF(src->md[i]);
+  }
+}
+
+void grpc_credentials_mdelem_array_destroy(
+    grpc_credentials_mdelem_array* list) {
+  for (size_t i = 0; i < list->size; ++i) {
+    GRPC_MDELEM_UNREF(list->md[i]);
+  }
+  gpr_free(list->md);
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.cc
new file mode 100644
index 0000000..337dd76
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.cc
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/fake/fake_credentials.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"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/security/security_connector/fake/fake_security_connector.h"
+
+/* -- Fake transport security credentials. -- */
+
+namespace {
+class grpc_fake_channel_credentials final : public grpc_channel_credentials {
+ public:
+  grpc_fake_channel_credentials()
+      : grpc_channel_credentials(
+            GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY) {}
+  ~grpc_fake_channel_credentials() override = default;
+
+  grpc_core::RefCountedPtr<grpc_channel_security_connector>
+  create_security_connector(
+      grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+      const char* target, const grpc_channel_args* args,
+      grpc_channel_args** new_args) override {
+    return grpc_fake_channel_security_connector_create(
+        this->Ref(), std::move(call_creds), target, args);
+  }
+};
+
+class grpc_fake_server_credentials final : public grpc_server_credentials {
+ public:
+  grpc_fake_server_credentials()
+      : grpc_server_credentials(
+            GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY) {}
+  ~grpc_fake_server_credentials() override = default;
+
+  grpc_core::RefCountedPtr<grpc_server_security_connector>
+  create_security_connector() override {
+    return grpc_fake_server_security_connector_create(this->Ref());
+  }
+};
+}  // namespace
+
+grpc_channel_credentials* grpc_fake_transport_security_credentials_create() {
+  return grpc_core::New<grpc_fake_channel_credentials>();
+}
+
+grpc_server_credentials*
+grpc_fake_transport_security_server_credentials_create() {
+  return grpc_core::New<grpc_fake_server_credentials>();
+}
+
+grpc_arg grpc_fake_transport_expected_targets_arg(char* expected_targets) {
+  return grpc_channel_arg_string_create(
+      (char*)GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS, expected_targets);
+}
+
+const char* grpc_fake_transport_get_expected_targets(
+    const grpc_channel_args* args) {
+  const grpc_arg* expected_target_arg =
+      grpc_channel_args_find(args, GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS);
+  return grpc_channel_arg_get_string(expected_target_arg);
+}
+
+/* -- Metadata-only test credentials. -- */
+
+bool grpc_md_only_test_credentials::get_request_metadata(
+    grpc_polling_entity* pollent, grpc_auth_metadata_context context,
+    grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
+    grpc_error** error) {
+  grpc_credentials_mdelem_array_add(md_array, md_);
+  if (is_async_) {
+    GRPC_CLOSURE_SCHED(on_request_metadata, GRPC_ERROR_NONE);
+    return false;
+  }
+  return true;
+}
+
+void grpc_md_only_test_credentials::cancel_get_request_metadata(
+    grpc_credentials_mdelem_array* md_array, grpc_error* error) {
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_call_credentials* grpc_md_only_test_credentials_create(
+    const char* md_key, const char* md_value, bool is_async) {
+  return grpc_core::New<grpc_md_only_test_credentials>(md_key, md_value,
+                                                       is_async);
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.h b/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.h
new file mode 100644
index 0000000..b7f6a19
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.h
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \
+  "grpc.fake_security.expected_targets"
+
+/* -- Fake transport security credentials. -- */
+
+/* Creates a fake transport security credentials object for testing. */
+grpc_channel_credentials* grpc_fake_transport_security_credentials_create(void);
+
+/* Creates a fake server transport security credentials object for testing. */
+grpc_server_credentials* grpc_fake_transport_security_server_credentials_create(
+    void);
+
+/* Used to verify the target names given to the fake transport security
+ * connector.
+ *
+ * The syntax of \a expected_targets by example:
+ * For LB channels:
+ *     "backend_target_1,backend_target_2,...;lb_target_1,lb_target_2,..."
+ * For regular channels:
+ *     "backend_taget_1,backend_target_2,..."
+ *
+ * That is to say, LB channels have a heading list of LB targets separated from
+ * the list of backend targets by a semicolon. For non-LB channels, only the
+ * latter is present. */
+grpc_arg grpc_fake_transport_expected_targets_arg(char* expected_targets);
+
+/* Return the value associated with the expected targets channel arg or NULL */
+const char* grpc_fake_transport_get_expected_targets(
+    const grpc_channel_args* args);
+
+/* --  Metadata-only Test credentials. -- */
+
+class grpc_md_only_test_credentials : public grpc_call_credentials {
+ public:
+  grpc_md_only_test_credentials(const char* md_key, const char* md_value,
+                                bool is_async)
+      : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2),
+        md_(grpc_mdelem_from_slices(grpc_slice_from_copied_string(md_key),
+                                    grpc_slice_from_copied_string(md_value))),
+        is_async_(is_async) {}
+  ~grpc_md_only_test_credentials() override { GRPC_MDELEM_UNREF(md_); }
+
+  bool get_request_metadata(grpc_polling_entity* pollent,
+                            grpc_auth_metadata_context context,
+                            grpc_credentials_mdelem_array* md_array,
+                            grpc_closure* on_request_metadata,
+                            grpc_error** error) override;
+
+  void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array,
+                                   grpc_error* error) override;
+
+ private:
+  grpc_mdelem md_;
+  bool is_async_;
+};
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/google_default/credentials_generic.cc b/third_party/grpc/src/core/lib/security/credentials/google_default/credentials_generic.cc
new file mode 100644
index 0000000..10ff0f6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/google_default/credentials_generic.cc
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+
+char* grpc_get_well_known_google_credentials_file_path_impl(void) {
+  char* result = nullptr;
+  char* base = gpr_getenv(GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR);
+  if (base == nullptr) {
+    gpr_log(GPR_ERROR, "Could not get " GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR
+                       " environment variable.");
+    return nullptr;
+  }
+  gpr_asprintf(&result, "%s/%s", base, GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX);
+  gpr_free(base);
+  return result;
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.cc
new file mode 100644
index 0000000..a86a17d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.cc
@@ -0,0 +1,374 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/load_file.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/security/credentials/alts/alts_credentials.h"
+#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
+#include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
+#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
+#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/api_trace.h"
+
+/* -- Constants. -- */
+
+#define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal"
+
+/* -- Default credentials. -- */
+
+/* A sticky bit that will be set only if the result of metadata server detection
+ * is positive. We do not set the bit if the result is negative. Because it
+ * means the detection is done via network test that is unreliable and the
+ * unreliable result should not be referred by successive calls. */
+static int g_metadata_server_available = 0;
+static int g_is_on_gce = 0;
+static gpr_mu g_state_mu;
+/* Protect a metadata_server_detector instance that can be modified by more than
+ * one gRPC threads */
+static gpr_mu* g_polling_mu;
+static gpr_once g_once = GPR_ONCE_INIT;
+static grpc_core::internal::grpc_gce_tenancy_checker g_gce_tenancy_checker =
+    grpc_alts_is_running_on_gcp;
+
+static void init_default_credentials(void) { gpr_mu_init(&g_state_mu); }
+
+typedef struct {
+  grpc_polling_entity pollent;
+  int is_done;
+  int success;
+  grpc_http_response response;
+} metadata_server_detector;
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_google_default_channel_credentials::create_security_connector(
+    grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+    const char* target, const grpc_channel_args* args,
+    grpc_channel_args** new_args) {
+  bool is_grpclb_load_balancer = grpc_channel_arg_get_bool(
+      grpc_channel_args_find(args, GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER),
+      false);
+  bool is_backend_from_grpclb_load_balancer = grpc_channel_arg_get_bool(
+      grpc_channel_args_find(
+          args, GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER),
+      false);
+  bool use_alts =
+      is_grpclb_load_balancer || is_backend_from_grpclb_load_balancer;
+  /* Return failure if ALTS is selected but not running on GCE. */
+  if (use_alts && !g_is_on_gce) {
+    gpr_log(GPR_ERROR, "ALTS is selected, but not running on GCE.");
+    return nullptr;
+  }
+
+  grpc_core::RefCountedPtr<grpc_channel_security_connector> sc =
+      use_alts ? alts_creds_->create_security_connector(call_creds, target,
+                                                        args, new_args)
+               : ssl_creds_->create_security_connector(call_creds, target, args,
+                                                       new_args);
+  /* grpclb-specific channel args are removed from the channel args set
+   * to ensure backends and fallback adresses will have the same set of channel
+   * args. By doing that, it guarantees the connections to backends will not be
+   * torn down and re-connected when switching in and out of fallback mode.
+   */
+  if (use_alts) {
+    static const char* args_to_remove[] = {
+        GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER,
+        GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER,
+    };
+    *new_args = grpc_channel_args_copy_and_add_and_remove(
+        args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), nullptr, 0);
+  }
+  return sc;
+}
+
+static void on_metadata_server_detection_http_response(void* user_data,
+                                                       grpc_error* error) {
+  metadata_server_detector* detector =
+      static_cast<metadata_server_detector*>(user_data);
+  if (error == GRPC_ERROR_NONE && detector->response.status == 200 &&
+      detector->response.hdr_count > 0) {
+    /* Internet providers can return a generic response to all requests, so
+       it is necessary to check that metadata header is present also. */
+    size_t i;
+    for (i = 0; i < detector->response.hdr_count; i++) {
+      grpc_http_header* header = &detector->response.hdrs[i];
+      if (strcmp(header->key, "Metadata-Flavor") == 0 &&
+          strcmp(header->value, "Google") == 0) {
+        detector->success = 1;
+        break;
+      }
+    }
+  }
+  gpr_mu_lock(g_polling_mu);
+  detector->is_done = 1;
+  GRPC_LOG_IF_ERROR(
+      "Pollset kick",
+      grpc_pollset_kick(grpc_polling_entity_pollset(&detector->pollent),
+                        nullptr));
+  gpr_mu_unlock(g_polling_mu);
+}
+
+static void destroy_pollset(void* p, grpc_error* e) {
+  grpc_pollset_destroy(static_cast<grpc_pollset*>(p));
+}
+
+static int is_metadata_server_reachable() {
+  metadata_server_detector detector;
+  grpc_httpcli_request request;
+  grpc_httpcli_context context;
+  grpc_closure destroy_closure;
+  /* The http call is local. If it takes more than one sec, it is for sure not
+     on compute engine. */
+  grpc_millis max_detection_delay = GPR_MS_PER_SEC;
+  grpc_pollset* pollset =
+      static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
+  grpc_pollset_init(pollset, &g_polling_mu);
+  detector.pollent = grpc_polling_entity_create_from_pollset(pollset);
+  detector.is_done = 0;
+  detector.success = 0;
+  memset(&detector.response, 0, sizeof(detector.response));
+  memset(&request, 0, sizeof(grpc_httpcli_request));
+  request.host = (char*)GRPC_COMPUTE_ENGINE_DETECTION_HOST;
+  request.http.path = (char*)"/";
+  grpc_httpcli_context_init(&context);
+  grpc_resource_quota* resource_quota =
+      grpc_resource_quota_create("google_default_credentials");
+  grpc_httpcli_get(
+      &context, &detector.pollent, resource_quota, &request,
+      grpc_core::ExecCtx::Get()->Now() + max_detection_delay,
+      GRPC_CLOSURE_CREATE(on_metadata_server_detection_http_response, &detector,
+                          grpc_schedule_on_exec_ctx),
+      &detector.response);
+  grpc_resource_quota_unref_internal(resource_quota);
+  grpc_core::ExecCtx::Get()->Flush();
+  /* Block until we get the response. This is not ideal but this should only be
+    called once for the lifetime of the process by the default credentials. */
+  gpr_mu_lock(g_polling_mu);
+  while (!detector.is_done) {
+    grpc_pollset_worker* worker = nullptr;
+    if (!GRPC_LOG_IF_ERROR(
+            "pollset_work",
+            grpc_pollset_work(grpc_polling_entity_pollset(&detector.pollent),
+                              &worker, GRPC_MILLIS_INF_FUTURE))) {
+      detector.is_done = 1;
+      detector.success = 0;
+    }
+  }
+  gpr_mu_unlock(g_polling_mu);
+  grpc_httpcli_context_destroy(&context);
+  GRPC_CLOSURE_INIT(&destroy_closure, destroy_pollset,
+                    grpc_polling_entity_pollset(&detector.pollent),
+                    grpc_schedule_on_exec_ctx);
+  grpc_pollset_shutdown(grpc_polling_entity_pollset(&detector.pollent),
+                        &destroy_closure);
+  g_polling_mu = nullptr;
+  grpc_core::ExecCtx::Get()->Flush();
+  gpr_free(grpc_polling_entity_pollset(&detector.pollent));
+  grpc_http_response_destroy(&detector.response);
+  return detector.success;
+}
+
+/* Takes ownership of creds_path if not NULL. */
+static grpc_error* create_default_creds_from_path(
+    char* creds_path, grpc_core::RefCountedPtr<grpc_call_credentials>* creds) {
+  grpc_json* json = nullptr;
+  grpc_auth_json_key key;
+  grpc_auth_refresh_token token;
+  grpc_core::RefCountedPtr<grpc_call_credentials> result;
+  grpc_slice creds_data = grpc_empty_slice();
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (creds_path == nullptr) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("creds_path unset");
+    goto end;
+  }
+  error = grpc_load_file(creds_path, 0, &creds_data);
+  if (error != GRPC_ERROR_NONE) {
+    goto end;
+  }
+  json = grpc_json_parse_string_with_len(
+      reinterpret_cast<char*> GRPC_SLICE_START_PTR(creds_data),
+      GRPC_SLICE_LENGTH(creds_data));
+  if (json == nullptr) {
+    error = grpc_error_set_str(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to parse JSON"),
+        GRPC_ERROR_STR_RAW_BYTES, grpc_slice_ref_internal(creds_data));
+    goto end;
+  }
+
+  /* First, try an auth json key. */
+  key = grpc_auth_json_key_create_from_json(json);
+  if (grpc_auth_json_key_is_valid(&key)) {
+    result =
+        grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
+            key, grpc_max_auth_token_lifetime());
+    if (result == nullptr) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "grpc_service_account_jwt_access_credentials_create_from_auth_json_"
+          "key failed");
+    }
+    goto end;
+  }
+
+  /* Then try a refresh token if the auth json key was invalid. */
+  token = grpc_auth_refresh_token_create_from_json(json);
+  if (grpc_auth_refresh_token_is_valid(&token)) {
+    result =
+        grpc_refresh_token_credentials_create_from_auth_refresh_token(token);
+    if (result == nullptr) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "grpc_refresh_token_credentials_create_from_auth_refresh_token "
+          "failed");
+    }
+    goto end;
+  }
+
+end:
+  GPR_ASSERT((result == nullptr) + (error == GRPC_ERROR_NONE) == 1);
+  if (creds_path != nullptr) gpr_free(creds_path);
+  grpc_slice_unref_internal(creds_data);
+  if (json != nullptr) grpc_json_destroy(json);
+  *creds = result;
+  return error;
+}
+
+grpc_channel_credentials* grpc_google_default_credentials_create() {
+  grpc_channel_credentials* result = nullptr;
+  grpc_core::RefCountedPtr<grpc_call_credentials> call_creds;
+  grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+      "Failed to create Google credentials");
+  grpc_error* err;
+  grpc_core::ExecCtx exec_ctx;
+
+  GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
+
+  gpr_once_init(&g_once, init_default_credentials);
+
+  /* First, try the environment variable. */
+  err = create_default_creds_from_path(
+      gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds);
+  if (err == GRPC_ERROR_NONE) goto end;
+  error = grpc_error_add_child(error, err);
+
+  /* Then the well-known file. */
+  err = create_default_creds_from_path(
+      grpc_get_well_known_google_credentials_file_path(), &call_creds);
+  if (err == GRPC_ERROR_NONE) goto end;
+  error = grpc_error_add_child(error, err);
+
+  gpr_mu_lock(&g_state_mu);
+
+  /* Try a platform-provided hint for GCE. */
+  if (!g_metadata_server_available) {
+    g_is_on_gce = g_gce_tenancy_checker();
+    g_metadata_server_available = g_is_on_gce;
+  }
+  /* TODO: Add a platform-provided hint for GAE. */
+
+  /* Do a network test for metadata server. */
+  if (!g_metadata_server_available) {
+    g_metadata_server_available = is_metadata_server_reachable();
+  }
+  gpr_mu_unlock(&g_state_mu);
+
+  if (g_metadata_server_available) {
+    call_creds = grpc_core::RefCountedPtr<grpc_call_credentials>(
+        grpc_google_compute_engine_credentials_create(nullptr));
+    if (call_creds == nullptr) {
+      error = grpc_error_add_child(
+          error, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                     "Failed to get credentials from network"));
+    }
+  }
+
+end:
+  if (call_creds != nullptr) {
+    /* Create google default credentials. */
+    grpc_channel_credentials* ssl_creds =
+        grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr);
+    GPR_ASSERT(ssl_creds != nullptr);
+    grpc_alts_credentials_options* options =
+        grpc_alts_credentials_client_options_create();
+    grpc_channel_credentials* alts_creds =
+        grpc_alts_credentials_create(options);
+    grpc_alts_credentials_options_destroy(options);
+    auto creds =
+        grpc_core::MakeRefCounted<grpc_google_default_channel_credentials>(
+            alts_creds != nullptr ? alts_creds->Ref() : nullptr,
+            ssl_creds != nullptr ? ssl_creds->Ref() : nullptr);
+    if (ssl_creds) ssl_creds->Unref();
+    if (alts_creds) alts_creds->Unref();
+    result = grpc_composite_channel_credentials_create(
+        creds.get(), call_creds.get(), nullptr);
+    GPR_ASSERT(result != nullptr);
+  } else {
+    gpr_log(GPR_ERROR, "Could not create google default credentials: %s",
+            grpc_error_string(error));
+  }
+  GRPC_ERROR_UNREF(error);
+  return result;
+}
+
+namespace grpc_core {
+namespace internal {
+
+void set_gce_tenancy_checker_for_testing(grpc_gce_tenancy_checker checker) {
+  g_gce_tenancy_checker = checker;
+}
+
+void grpc_flush_cached_google_default_credentials(void) {
+  grpc_core::ExecCtx exec_ctx;
+  gpr_once_init(&g_once, init_default_credentials);
+  gpr_mu_lock(&g_state_mu);
+  g_metadata_server_available = 0;
+  gpr_mu_unlock(&g_state_mu);
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+/* -- Well known credentials path. -- */
+
+static grpc_well_known_credentials_path_getter creds_path_getter = nullptr;
+
+char* grpc_get_well_known_google_credentials_file_path(void) {
+  if (creds_path_getter != nullptr) return creds_path_getter();
+  return grpc_get_well_known_google_credentials_file_path_impl();
+}
+
+void grpc_override_well_known_credentials_path_getter(
+    grpc_well_known_credentials_path_getter getter) {
+  creds_path_getter = getter;
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.h b/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.h
new file mode 100644
index 0000000..bf00f72
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/credentials/credentials.h"
+
+#define GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY "gcloud"
+#define GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE \
+  "application_default_credentials.json"
+
+#ifdef GPR_WINDOWS
+#define GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR "APPDATA"
+#define GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX \
+  GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY    \
+  "/" GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE
+#else
+#define GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR "HOME"
+#define GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX         \
+  ".config/" GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY \
+  "/" GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE
+#endif
+
+class grpc_google_default_channel_credentials
+    : public grpc_channel_credentials {
+ public:
+  grpc_google_default_channel_credentials(
+      grpc_core::RefCountedPtr<grpc_channel_credentials> alts_creds,
+      grpc_core::RefCountedPtr<grpc_channel_credentials> ssl_creds)
+      : grpc_channel_credentials(GRPC_CHANNEL_CREDENTIALS_TYPE_GOOGLE_DEFAULT),
+        alts_creds_(std::move(alts_creds)),
+        ssl_creds_(std::move(ssl_creds)) {}
+
+  ~grpc_google_default_channel_credentials() override = default;
+
+  grpc_core::RefCountedPtr<grpc_channel_security_connector>
+  create_security_connector(
+      grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+      const char* target, const grpc_channel_args* args,
+      grpc_channel_args** new_args) override;
+
+  const grpc_channel_credentials* alts_creds() const {
+    return alts_creds_.get();
+  }
+  const grpc_channel_credentials* ssl_creds() const { return ssl_creds_.get(); }
+
+ private:
+  grpc_core::RefCountedPtr<grpc_channel_credentials> alts_creds_;
+  grpc_core::RefCountedPtr<grpc_channel_credentials> ssl_creds_;
+};
+
+namespace grpc_core {
+namespace internal {
+
+typedef bool (*grpc_gce_tenancy_checker)(void);
+
+void set_gce_tenancy_checker_for_testing(grpc_gce_tenancy_checker checker);
+
+// TEST-ONLY. Reset the internal global state.
+void grpc_flush_cached_google_default_credentials(void);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.cc
new file mode 100644
index 0000000..5cd561f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.cc
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/iam/iam_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+grpc_google_iam_credentials::~grpc_google_iam_credentials() {
+  grpc_credentials_mdelem_array_destroy(&md_array_);
+}
+
+bool grpc_google_iam_credentials::get_request_metadata(
+    grpc_polling_entity* pollent, grpc_auth_metadata_context context,
+    grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
+    grpc_error** error) {
+  grpc_credentials_mdelem_array_append(md_array, &md_array_);
+  return true;
+}
+
+void grpc_google_iam_credentials::cancel_get_request_metadata(
+    grpc_credentials_mdelem_array* md_array, grpc_error* error) {
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_google_iam_credentials::grpc_google_iam_credentials(
+    const char* token, const char* authority_selector)
+    : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_IAM) {
+  grpc_mdelem md = grpc_mdelem_from_slices(
+      grpc_slice_from_static_string(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY),
+      grpc_slice_from_copied_string(token));
+  grpc_credentials_mdelem_array_add(&md_array_, md);
+  GRPC_MDELEM_UNREF(md);
+  md = grpc_mdelem_from_slices(
+      grpc_slice_from_static_string(GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY),
+      grpc_slice_from_copied_string(authority_selector));
+  grpc_credentials_mdelem_array_add(&md_array_, md);
+  GRPC_MDELEM_UNREF(md);
+}
+
+grpc_call_credentials* grpc_google_iam_credentials_create(
+    const char* token, const char* authority_selector, void* reserved) {
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE(
+      "grpc_iam_credentials_create(token=%s, authority_selector=%s, "
+      "reserved=%p)",
+      3, (token, authority_selector, reserved));
+  GPR_ASSERT(reserved == nullptr);
+  GPR_ASSERT(token != nullptr);
+  GPR_ASSERT(authority_selector != nullptr);
+  return grpc_core::MakeRefCounted<grpc_google_iam_credentials>(
+             token, authority_selector)
+      .release();
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.h b/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.h
new file mode 100644
index 0000000..36f5ee8
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+class grpc_google_iam_credentials : public grpc_call_credentials {
+ public:
+  grpc_google_iam_credentials(const char* token,
+                              const char* authority_selector);
+  ~grpc_google_iam_credentials() override;
+
+  bool get_request_metadata(grpc_polling_entity* pollent,
+                            grpc_auth_metadata_context context,
+                            grpc_credentials_mdelem_array* md_array,
+                            grpc_closure* on_request_metadata,
+                            grpc_error** error) override;
+
+  void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array,
+                                   grpc_error* error) override;
+
+ private:
+  grpc_credentials_mdelem_array md_array_;
+};
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.cc b/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.cc
new file mode 100644
index 0000000..1c4827d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.cc
@@ -0,0 +1,314 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/jwt/json_token.h"
+
+#include <string.h>
+
+#include <grpc/grpc_security.h>
+#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/gpr/string.h"
+#include "src/core/lib/security/util/json_util.h"
+#include "src/core/lib/slice/b64.h"
+
+extern "C" {
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+}
+
+/* --- Constants. --- */
+
+/* 1 hour max. */
+gpr_timespec grpc_max_auth_token_lifetime() {
+  gpr_timespec out;
+  out.tv_sec = 3600;
+  out.tv_nsec = 0;
+  out.clock_type = GPR_TIMESPAN;
+  return out;
+}
+
+#define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256"
+#define GRPC_JWT_TYPE "JWT"
+
+/* --- Override for testing. --- */
+
+static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override =
+    nullptr;
+
+/* --- grpc_auth_json_key. --- */
+
+int grpc_auth_json_key_is_valid(const grpc_auth_json_key* json_key) {
+  return (json_key != nullptr) &&
+         strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID);
+}
+
+grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json* json) {
+  grpc_auth_json_key result;
+  BIO* bio = nullptr;
+  const char* prop_value;
+  int success = 0;
+
+  memset(&result, 0, sizeof(grpc_auth_json_key));
+  result.type = GRPC_AUTH_JSON_TYPE_INVALID;
+  if (json == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid json.");
+    goto end;
+  }
+
+  prop_value = grpc_json_get_string_property(json, "type");
+  if (prop_value == nullptr ||
+      strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT)) {
+    goto end;
+  }
+  result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT;
+
+  if (!grpc_copy_json_string_property(json, "private_key_id",
+                                      &result.private_key_id) ||
+      !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
+      !grpc_copy_json_string_property(json, "client_email",
+                                      &result.client_email)) {
+    goto end;
+  }
+
+  prop_value = grpc_json_get_string_property(json, "private_key");
+  if (prop_value == nullptr) {
+    goto end;
+  }
+  bio = BIO_new(BIO_s_mem());
+  success = BIO_puts(bio, prop_value);
+  if ((success < 0) || (static_cast<size_t>(success) != strlen(prop_value))) {
+    gpr_log(GPR_ERROR, "Could not write into openssl BIO.");
+    goto end;
+  }
+  result.private_key =
+      PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, (void*)"");
+  if (result.private_key == nullptr) {
+    gpr_log(GPR_ERROR, "Could not deserialize private key.");
+    goto end;
+  }
+  success = 1;
+
+end:
+  if (bio != nullptr) BIO_free(bio);
+  if (!success) grpc_auth_json_key_destruct(&result);
+  return result;
+}
+
+grpc_auth_json_key grpc_auth_json_key_create_from_string(
+    const char* json_string) {
+  char* scratchpad = gpr_strdup(json_string);
+  grpc_json* json = grpc_json_parse_string(scratchpad);
+  grpc_auth_json_key result = grpc_auth_json_key_create_from_json(json);
+  if (json != nullptr) grpc_json_destroy(json);
+  gpr_free(scratchpad);
+  return result;
+}
+
+void grpc_auth_json_key_destruct(grpc_auth_json_key* json_key) {
+  if (json_key == nullptr) return;
+  json_key->type = GRPC_AUTH_JSON_TYPE_INVALID;
+  if (json_key->client_id != nullptr) {
+    gpr_free(json_key->client_id);
+    json_key->client_id = nullptr;
+  }
+  if (json_key->private_key_id != nullptr) {
+    gpr_free(json_key->private_key_id);
+    json_key->private_key_id = nullptr;
+  }
+  if (json_key->client_email != nullptr) {
+    gpr_free(json_key->client_email);
+    json_key->client_email = nullptr;
+  }
+  if (json_key->private_key != nullptr) {
+    RSA_free(json_key->private_key);
+    json_key->private_key = nullptr;
+  }
+}
+
+/* --- jwt encoding and signature. --- */
+
+static grpc_json* create_child(grpc_json* brother, grpc_json* parent,
+                               const char* key, const char* value,
+                               grpc_json_type type) {
+  grpc_json* child = grpc_json_create(type);
+  if (brother) brother->next = child;
+  if (!parent->child) parent->child = child;
+  child->parent = parent;
+  child->value = value;
+  child->key = key;
+  return child;
+}
+
+static char* encoded_jwt_header(const char* key_id, const char* algorithm) {
+  grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* child = nullptr;
+  char* json_str = nullptr;
+  char* result = nullptr;
+
+  child = create_child(nullptr, json, "alg", algorithm, GRPC_JSON_STRING);
+  child = create_child(child, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING);
+  create_child(child, json, "kid", key_id, GRPC_JSON_STRING);
+
+  json_str = grpc_json_dump_to_string(json, 0);
+  result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
+  gpr_free(json_str);
+  grpc_json_destroy(json);
+  return result;
+}
+
+static char* encoded_jwt_claim(const grpc_auth_json_key* json_key,
+                               const char* audience,
+                               gpr_timespec token_lifetime, const char* scope) {
+  grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* child = nullptr;
+  char* json_str = nullptr;
+  char* result = nullptr;
+  gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
+  gpr_timespec expiration = gpr_time_add(now, token_lifetime);
+  char now_str[GPR_LTOA_MIN_BUFSIZE];
+  char expiration_str[GPR_LTOA_MIN_BUFSIZE];
+  if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()) > 0) {
+    gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value.");
+    expiration = gpr_time_add(now, grpc_max_auth_token_lifetime());
+  }
+  int64_ttoa(now.tv_sec, now_str);
+  int64_ttoa(expiration.tv_sec, expiration_str);
+
+  child = create_child(nullptr, json, "iss", json_key->client_email,
+                       GRPC_JSON_STRING);
+  if (scope != nullptr) {
+    child = create_child(child, json, "scope", scope, GRPC_JSON_STRING);
+  } else {
+    /* Unscoped JWTs need a sub field. */
+    child = create_child(child, json, "sub", json_key->client_email,
+                         GRPC_JSON_STRING);
+  }
+
+  child = create_child(child, json, "aud", audience, GRPC_JSON_STRING);
+  child = create_child(child, json, "iat", now_str, GRPC_JSON_NUMBER);
+  create_child(child, json, "exp", expiration_str, GRPC_JSON_NUMBER);
+
+  json_str = grpc_json_dump_to_string(json, 0);
+  result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
+  gpr_free(json_str);
+  grpc_json_destroy(json);
+  return result;
+}
+
+static char* dot_concat_and_free_strings(char* str1, char* str2) {
+  size_t str1_len = strlen(str1);
+  size_t str2_len = strlen(str2);
+  size_t result_len = str1_len + 1 /* dot */ + str2_len;
+  char* result =
+      static_cast<char*>(gpr_malloc(result_len + 1 /* NULL terminated */));
+  char* current = result;
+  memcpy(current, str1, str1_len);
+  current += str1_len;
+  *(current++) = '.';
+  memcpy(current, str2, str2_len);
+  current += str2_len;
+  GPR_ASSERT(current >= result);
+  GPR_ASSERT((uintptr_t)(current - result) == result_len);
+  *current = '\0';
+  gpr_free(str1);
+  gpr_free(str2);
+  return result;
+}
+
+const EVP_MD* openssl_digest_from_algorithm(const char* algorithm) {
+  if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) {
+    return EVP_sha256();
+  } else {
+    gpr_log(GPR_ERROR, "Unknown algorithm %s.", algorithm);
+    return nullptr;
+  }
+}
+
+char* compute_and_encode_signature(const grpc_auth_json_key* json_key,
+                                   const char* signature_algorithm,
+                                   const char* to_sign) {
+  const EVP_MD* md = openssl_digest_from_algorithm(signature_algorithm);
+  EVP_MD_CTX* md_ctx = nullptr;
+  EVP_PKEY* key = EVP_PKEY_new();
+  size_t sig_len = 0;
+  unsigned char* sig = nullptr;
+  char* result = nullptr;
+  if (md == nullptr) return nullptr;
+  md_ctx = EVP_MD_CTX_create();
+  if (md_ctx == nullptr) {
+    gpr_log(GPR_ERROR, "Could not create MD_CTX");
+    goto end;
+  }
+  EVP_PKEY_set1_RSA(key, json_key->private_key);
+  if (EVP_DigestSignInit(md_ctx, nullptr, md, nullptr, key) != 1) {
+    gpr_log(GPR_ERROR, "DigestInit failed.");
+    goto end;
+  }
+  if (EVP_DigestSignUpdate(md_ctx, to_sign, strlen(to_sign)) != 1) {
+    gpr_log(GPR_ERROR, "DigestUpdate failed.");
+    goto end;
+  }
+  if (EVP_DigestSignFinal(md_ctx, nullptr, &sig_len) != 1) {
+    gpr_log(GPR_ERROR, "DigestFinal (get signature length) failed.");
+    goto end;
+  }
+  sig = static_cast<unsigned char*>(gpr_malloc(sig_len));
+  if (EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) {
+    gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed.");
+    goto end;
+  }
+  result = grpc_base64_encode(sig, sig_len, 1, 0);
+
+end:
+  if (key != nullptr) EVP_PKEY_free(key);
+  if (md_ctx != nullptr) EVP_MD_CTX_destroy(md_ctx);
+  if (sig != nullptr) gpr_free(sig);
+  return result;
+}
+
+char* grpc_jwt_encode_and_sign(const grpc_auth_json_key* json_key,
+                               const char* audience,
+                               gpr_timespec token_lifetime, const char* scope) {
+  if (g_jwt_encode_and_sign_override != nullptr) {
+    return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime,
+                                          scope);
+  } else {
+    const char* sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM;
+    char* to_sign = dot_concat_and_free_strings(
+        encoded_jwt_header(json_key->private_key_id, sig_algo),
+        encoded_jwt_claim(json_key, audience, token_lifetime, scope));
+    char* sig = compute_and_encode_signature(json_key, sig_algo, to_sign);
+    if (sig == nullptr) {
+      gpr_free(to_sign);
+      return nullptr;
+    }
+    return dot_concat_and_free_strings(to_sign, sig);
+  }
+}
+
+void grpc_jwt_encode_and_sign_set_override(
+    grpc_jwt_encode_and_sign_override func) {
+  g_jwt_encode_and_sign_override = func;
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.h b/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.h
new file mode 100644
index 0000000..3ed9901
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.h
@@ -0,0 +1,77 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/grpc_shadow_boringssl.h"
+
+#include <grpc/slice.h>
+#include <openssl/rsa.h>
+
+#include "src/core/lib/json/json.h"
+
+/* --- Constants. --- */
+
+#define GRPC_JWT_OAUTH2_AUDIENCE "https://www.googleapis.com/oauth2/v3/token"
+
+/* --- auth_json_key parsing. --- */
+
+typedef struct {
+  const char* type;
+  char* private_key_id;
+  char* client_id;
+  char* client_email;
+  RSA* private_key;
+} grpc_auth_json_key;
+
+/* Returns 1 if the object is valid, 0 otherwise. */
+int grpc_auth_json_key_is_valid(const grpc_auth_json_key* json_key);
+
+/* Creates a json_key object from string. Returns an invalid object if a parsing
+   error has been encountered. */
+grpc_auth_json_key grpc_auth_json_key_create_from_string(
+    const char* json_string);
+
+/* Creates a json_key object from parsed json. Returns an invalid object if a
+   parsing error has been encountered. */
+grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json* json);
+
+/* Destructs the object. */
+void grpc_auth_json_key_destruct(grpc_auth_json_key* json_key);
+
+/* --- json token encoding and signing. --- */
+
+/* Caller is responsible for calling gpr_free on the returned value. May return
+   NULL on invalid input. The scope parameter may be NULL. */
+char* grpc_jwt_encode_and_sign(const grpc_auth_json_key* json_key,
+                               const char* audience,
+                               gpr_timespec token_lifetime, const char* scope);
+
+/* Override encode_and_sign function for testing. */
+typedef char* (*grpc_jwt_encode_and_sign_override)(
+    const grpc_auth_json_key* json_key, const char* audience,
+    gpr_timespec token_lifetime, const char* scope);
+
+/* Set a custom encode_and_sign override for testing. */
+void grpc_jwt_encode_and_sign_set_override(
+    grpc_jwt_encode_and_sign_override func);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.cc
new file mode 100644
index 0000000..f2591a1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.cc
@@ -0,0 +1,181 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
+
+#include <inttypes.h>
+#include <string.h>
+
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+void grpc_service_account_jwt_access_credentials::reset_cache() {
+  GRPC_MDELEM_UNREF(cached_.jwt_md);
+  cached_.jwt_md = GRPC_MDNULL;
+  if (cached_.service_url != nullptr) {
+    gpr_free(cached_.service_url);
+    cached_.service_url = nullptr;
+  }
+  cached_.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
+}
+
+grpc_service_account_jwt_access_credentials::
+    ~grpc_service_account_jwt_access_credentials() {
+  grpc_auth_json_key_destruct(&key_);
+  reset_cache();
+  gpr_mu_destroy(&cache_mu_);
+}
+
+bool grpc_service_account_jwt_access_credentials::get_request_metadata(
+    grpc_polling_entity* pollent, grpc_auth_metadata_context context,
+    grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
+    grpc_error** error) {
+  gpr_timespec refresh_threshold = gpr_time_from_seconds(
+      GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
+
+  /* See if we can return a cached jwt. */
+  grpc_mdelem jwt_md = GRPC_MDNULL;
+  {
+    gpr_mu_lock(&cache_mu_);
+    if (cached_.service_url != nullptr &&
+        strcmp(cached_.service_url, context.service_url) == 0 &&
+        !GRPC_MDISNULL(cached_.jwt_md) &&
+        (gpr_time_cmp(
+             gpr_time_sub(cached_.jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)),
+             refresh_threshold) > 0)) {
+      jwt_md = GRPC_MDELEM_REF(cached_.jwt_md);
+    }
+    gpr_mu_unlock(&cache_mu_);
+  }
+
+  if (GRPC_MDISNULL(jwt_md)) {
+    char* jwt = nullptr;
+    /* Generate a new jwt. */
+    gpr_mu_lock(&cache_mu_);
+    reset_cache();
+    jwt = grpc_jwt_encode_and_sign(&key_, context.service_url, jwt_lifetime_,
+                                   nullptr);
+    if (jwt != nullptr) {
+      char* md_value;
+      gpr_asprintf(&md_value, "Bearer %s", jwt);
+      gpr_free(jwt);
+      cached_.jwt_expiration =
+          gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), jwt_lifetime_);
+      cached_.service_url = gpr_strdup(context.service_url);
+      cached_.jwt_md = grpc_mdelem_from_slices(
+          grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
+          grpc_slice_from_copied_string(md_value));
+      gpr_free(md_value);
+      jwt_md = GRPC_MDELEM_REF(cached_.jwt_md);
+    }
+    gpr_mu_unlock(&cache_mu_);
+  }
+
+  if (!GRPC_MDISNULL(jwt_md)) {
+    grpc_credentials_mdelem_array_add(md_array, jwt_md);
+    GRPC_MDELEM_UNREF(jwt_md);
+  } else {
+    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT.");
+  }
+  return true;
+}
+
+void grpc_service_account_jwt_access_credentials::cancel_get_request_metadata(
+    grpc_credentials_mdelem_array* md_array, grpc_error* error) {
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_service_account_jwt_access_credentials::
+    grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,
+                                                gpr_timespec token_lifetime)
+    : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_JWT), key_(key) {
+  gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime();
+  if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) {
+    gpr_log(GPR_INFO,
+            "Cropping token lifetime to maximum allowed value (%d secs).",
+            static_cast<int>(max_token_lifetime.tv_sec));
+    token_lifetime = grpc_max_auth_token_lifetime();
+  }
+  jwt_lifetime_ = token_lifetime;
+  gpr_mu_init(&cache_mu_);
+  reset_cache();
+}
+
+grpc_core::RefCountedPtr<grpc_call_credentials>
+grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
+    grpc_auth_json_key key, gpr_timespec token_lifetime) {
+  if (!grpc_auth_json_key_is_valid(&key)) {
+    gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
+    return nullptr;
+  }
+  return grpc_core::MakeRefCounted<grpc_service_account_jwt_access_credentials>(
+      key, token_lifetime);
+}
+
+static char* redact_private_key(const char* json_key) {
+  char* json_copy = gpr_strdup(json_key);
+  grpc_json* json = grpc_json_parse_string(json_copy);
+  if (!json) {
+    gpr_free(json_copy);
+    return gpr_strdup("<Json failed to parse.>");
+  }
+  const char* redacted = "<redacted>";
+  grpc_json* current = json->child;
+  while (current) {
+    if (current->type == GRPC_JSON_STRING &&
+        strcmp(current->key, "private_key") == 0) {
+      current->value = const_cast<char*>(redacted);
+      break;
+    }
+    current = current->next;
+  }
+  char* clean_json = grpc_json_dump_to_string(json, 2);
+  gpr_free(json_copy);
+  grpc_json_destroy(json);
+  return clean_json;
+}
+
+grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
+    const char* json_key, gpr_timespec token_lifetime, void* reserved) {
+  if (grpc_api_trace.enabled()) {
+    char* clean_json = redact_private_key(json_key);
+    gpr_log(GPR_INFO,
+            "grpc_service_account_jwt_access_credentials_create("
+            "json_key=%s, "
+            "token_lifetime="
+            "gpr_timespec { tv_sec: %" PRId64
+            ", tv_nsec: %d, clock_type: %d }, "
+            "reserved=%p)",
+            clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec,
+            static_cast<int>(token_lifetime.clock_type), reserved);
+    gpr_free(clean_json);
+  }
+  GPR_ASSERT(reserved == nullptr);
+  grpc_core::ExecCtx exec_ctx;
+  return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
+             grpc_auth_json_key_create_from_string(json_key), token_lifetime)
+      .release();
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.h b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.h
new file mode 100644
index 0000000..5af909f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/credentials/jwt/json_token.h"
+
+class grpc_service_account_jwt_access_credentials
+    : public grpc_call_credentials {
+ public:
+  grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,
+                                              gpr_timespec token_lifetime);
+  ~grpc_service_account_jwt_access_credentials() override;
+
+  bool get_request_metadata(grpc_polling_entity* pollent,
+                            grpc_auth_metadata_context context,
+                            grpc_credentials_mdelem_array* md_array,
+                            grpc_closure* on_request_metadata,
+                            grpc_error** error) override;
+
+  void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array,
+                                   grpc_error* error) override;
+
+  const gpr_timespec& jwt_lifetime() const { return jwt_lifetime_; }
+  const grpc_auth_json_key& key() const { return key_; }
+
+ private:
+  void reset_cache();
+
+  // Have a simple cache for now with just 1 entry. We could have a map based on
+  // the service_url for a more sophisticated one.
+  gpr_mu cache_mu_;
+  struct {
+    grpc_mdelem jwt_md = GRPC_MDNULL;
+    char* service_url = nullptr;
+    gpr_timespec jwt_expiration;
+  } cached_;
+
+  grpc_auth_json_key key_;
+  gpr_timespec jwt_lifetime_;
+};
+
+// Private constructor for jwt credentials from an already parsed json key.
+// Takes ownership of the key.
+grpc_core::RefCountedPtr<grpc_call_credentials>
+grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
+    grpc_auth_json_key key, gpr_timespec token_lifetime);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.cc b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.cc
new file mode 100644
index 0000000..cdef0f3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.cc
@@ -0,0 +1,938 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/grpc_shadow_boringssl.h"
+
+#include "src/core/lib/security/credentials/jwt/jwt_verifier.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+extern "C" {
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+}
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/slice/b64.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/ssl_types.h"
+
+/* --- Utils. --- */
+
+const char* grpc_jwt_verifier_status_to_string(
+    grpc_jwt_verifier_status status) {
+  switch (status) {
+    case GRPC_JWT_VERIFIER_OK:
+      return "OK";
+    case GRPC_JWT_VERIFIER_BAD_SIGNATURE:
+      return "BAD_SIGNATURE";
+    case GRPC_JWT_VERIFIER_BAD_FORMAT:
+      return "BAD_FORMAT";
+    case GRPC_JWT_VERIFIER_BAD_AUDIENCE:
+      return "BAD_AUDIENCE";
+    case GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR:
+      return "KEY_RETRIEVAL_ERROR";
+    case GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE:
+      return "TIME_CONSTRAINT_FAILURE";
+    case GRPC_JWT_VERIFIER_GENERIC_ERROR:
+      return "GENERIC_ERROR";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+static const EVP_MD* evp_md_from_alg(const char* alg) {
+  if (strcmp(alg, "RS256") == 0) {
+    return EVP_sha256();
+  } else if (strcmp(alg, "RS384") == 0) {
+    return EVP_sha384();
+  } else if (strcmp(alg, "RS512") == 0) {
+    return EVP_sha512();
+  } else {
+    return nullptr;
+  }
+}
+
+static grpc_json* parse_json_part_from_jwt(const char* str, size_t len,
+                                           grpc_slice* buffer) {
+  grpc_json* json;
+
+  *buffer = grpc_base64_decode_with_len(str, len, 1);
+  if (GRPC_SLICE_IS_EMPTY(*buffer)) {
+    gpr_log(GPR_ERROR, "Invalid base64.");
+    return nullptr;
+  }
+  json = grpc_json_parse_string_with_len(
+      reinterpret_cast<char*> GRPC_SLICE_START_PTR(*buffer),
+      GRPC_SLICE_LENGTH(*buffer));
+  if (json == nullptr) {
+    grpc_slice_unref_internal(*buffer);
+    gpr_log(GPR_ERROR, "JSON parsing error.");
+  }
+  return json;
+}
+
+static const char* validate_string_field(const grpc_json* json,
+                                         const char* key) {
+  if (json->type != GRPC_JSON_STRING) {
+    gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value);
+    return nullptr;
+  }
+  return json->value;
+}
+
+static gpr_timespec validate_time_field(const grpc_json* json,
+                                        const char* key) {
+  gpr_timespec result = gpr_time_0(GPR_CLOCK_REALTIME);
+  if (json->type != GRPC_JSON_NUMBER) {
+    gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value);
+    return result;
+  }
+  result.tv_sec = strtol(json->value, nullptr, 10);
+  return result;
+}
+
+/* --- JOSE header. see http://tools.ietf.org/html/rfc7515#section-4 --- */
+
+typedef struct {
+  const char* alg;
+  const char* kid;
+  const char* typ;
+  /* TODO(jboeuf): Add others as needed (jku, jwk, x5u, x5c and so on...). */
+  grpc_slice buffer;
+} jose_header;
+
+static void jose_header_destroy(jose_header* h) {
+  grpc_slice_unref_internal(h->buffer);
+  gpr_free(h);
+}
+
+/* Takes ownership of json and buffer. */
+static jose_header* jose_header_from_json(grpc_json* json, grpc_slice buffer) {
+  grpc_json* cur;
+  jose_header* h = static_cast<jose_header*>(gpr_zalloc(sizeof(jose_header)));
+  h->buffer = buffer;
+  for (cur = json->child; cur != nullptr; cur = cur->next) {
+    if (strcmp(cur->key, "alg") == 0) {
+      /* We only support RSA-1.5 signatures for now.
+         Beware of this if we add HMAC support:
+         https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
+       */
+      if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) ||
+          evp_md_from_alg(cur->value) == nullptr) {
+        gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value);
+        goto error;
+      }
+      h->alg = cur->value;
+    } else if (strcmp(cur->key, "typ") == 0) {
+      h->typ = validate_string_field(cur, "typ");
+      if (h->typ == nullptr) goto error;
+    } else if (strcmp(cur->key, "kid") == 0) {
+      h->kid = validate_string_field(cur, "kid");
+      if (h->kid == nullptr) goto error;
+    }
+  }
+  if (h->alg == nullptr) {
+    gpr_log(GPR_ERROR, "Missing alg field.");
+    goto error;
+  }
+  grpc_json_destroy(json);
+  h->buffer = buffer;
+  return h;
+
+error:
+  grpc_json_destroy(json);
+  jose_header_destroy(h);
+  return nullptr;
+}
+
+/* --- JWT claims. see http://tools.ietf.org/html/rfc7519#section-4.1 */
+
+struct grpc_jwt_claims {
+  /* Well known properties already parsed. */
+  const char* sub;
+  const char* iss;
+  const char* aud;
+  const char* jti;
+  gpr_timespec iat;
+  gpr_timespec exp;
+  gpr_timespec nbf;
+
+  grpc_json* json;
+  grpc_slice buffer;
+};
+
+void grpc_jwt_claims_destroy(grpc_jwt_claims* claims) {
+  grpc_json_destroy(claims->json);
+  grpc_slice_unref_internal(claims->buffer);
+  gpr_free(claims);
+}
+
+const grpc_json* grpc_jwt_claims_json(const grpc_jwt_claims* claims) {
+  if (claims == nullptr) return nullptr;
+  return claims->json;
+}
+
+const char* grpc_jwt_claims_subject(const grpc_jwt_claims* claims) {
+  if (claims == nullptr) return nullptr;
+  return claims->sub;
+}
+
+const char* grpc_jwt_claims_issuer(const grpc_jwt_claims* claims) {
+  if (claims == nullptr) return nullptr;
+  return claims->iss;
+}
+
+const char* grpc_jwt_claims_id(const grpc_jwt_claims* claims) {
+  if (claims == nullptr) return nullptr;
+  return claims->jti;
+}
+
+const char* grpc_jwt_claims_audience(const grpc_jwt_claims* claims) {
+  if (claims == nullptr) return nullptr;
+  return claims->aud;
+}
+
+gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims* claims) {
+  if (claims == nullptr) return gpr_inf_past(GPR_CLOCK_REALTIME);
+  return claims->iat;
+}
+
+gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims* claims) {
+  if (claims == nullptr) return gpr_inf_future(GPR_CLOCK_REALTIME);
+  return claims->exp;
+}
+
+gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims* claims) {
+  if (claims == nullptr) return gpr_inf_past(GPR_CLOCK_REALTIME);
+  return claims->nbf;
+}
+
+/* Takes ownership of json and buffer even in case of failure. */
+grpc_jwt_claims* grpc_jwt_claims_from_json(grpc_json* json, grpc_slice buffer) {
+  grpc_json* cur;
+  grpc_jwt_claims* claims =
+      static_cast<grpc_jwt_claims*>(gpr_malloc(sizeof(grpc_jwt_claims)));
+  memset(claims, 0, sizeof(grpc_jwt_claims));
+  claims->json = json;
+  claims->buffer = buffer;
+  claims->iat = gpr_inf_past(GPR_CLOCK_REALTIME);
+  claims->nbf = gpr_inf_past(GPR_CLOCK_REALTIME);
+  claims->exp = gpr_inf_future(GPR_CLOCK_REALTIME);
+
+  /* Per the spec, all fields are optional. */
+  for (cur = json->child; cur != nullptr; cur = cur->next) {
+    if (strcmp(cur->key, "sub") == 0) {
+      claims->sub = validate_string_field(cur, "sub");
+      if (claims->sub == nullptr) goto error;
+    } else if (strcmp(cur->key, "iss") == 0) {
+      claims->iss = validate_string_field(cur, "iss");
+      if (claims->iss == nullptr) goto error;
+    } else if (strcmp(cur->key, "aud") == 0) {
+      claims->aud = validate_string_field(cur, "aud");
+      if (claims->aud == nullptr) goto error;
+    } else if (strcmp(cur->key, "jti") == 0) {
+      claims->jti = validate_string_field(cur, "jti");
+      if (claims->jti == nullptr) goto error;
+    } else if (strcmp(cur->key, "iat") == 0) {
+      claims->iat = validate_time_field(cur, "iat");
+      if (gpr_time_cmp(claims->iat, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
+        goto error;
+    } else if (strcmp(cur->key, "exp") == 0) {
+      claims->exp = validate_time_field(cur, "exp");
+      if (gpr_time_cmp(claims->exp, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
+        goto error;
+    } else if (strcmp(cur->key, "nbf") == 0) {
+      claims->nbf = validate_time_field(cur, "nbf");
+      if (gpr_time_cmp(claims->nbf, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
+        goto error;
+    }
+  }
+  return claims;
+
+error:
+  grpc_jwt_claims_destroy(claims);
+  return nullptr;
+}
+
+grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims* claims,
+                                               const char* audience) {
+  gpr_timespec skewed_now;
+  int audience_ok;
+
+  GPR_ASSERT(claims != nullptr);
+
+  skewed_now =
+      gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew);
+  if (gpr_time_cmp(skewed_now, claims->nbf) < 0) {
+    gpr_log(GPR_ERROR, "JWT is not valid yet.");
+    return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
+  }
+  skewed_now =
+      gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew);
+  if (gpr_time_cmp(skewed_now, claims->exp) > 0) {
+    gpr_log(GPR_ERROR, "JWT is expired.");
+    return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
+  }
+
+  /* This should be probably up to the upper layer to decide but let's harcode
+     the 99% use case here for email issuers, where the JWT must be self
+     issued. */
+  if (grpc_jwt_issuer_email_domain(claims->iss) != nullptr &&
+      claims->sub != nullptr && strcmp(claims->iss, claims->sub) != 0) {
+    gpr_log(GPR_ERROR,
+            "Email issuer (%s) cannot assert another subject (%s) than itself.",
+            claims->iss, claims->sub);
+    return GRPC_JWT_VERIFIER_BAD_SUBJECT;
+  }
+
+  if (audience == nullptr) {
+    audience_ok = claims->aud == nullptr;
+  } else {
+    audience_ok = claims->aud != nullptr && strcmp(audience, claims->aud) == 0;
+  }
+  if (!audience_ok) {
+    gpr_log(GPR_ERROR, "Audience mismatch: expected %s and found %s.",
+            audience == nullptr ? "NULL" : audience,
+            claims->aud == nullptr ? "NULL" : claims->aud);
+    return GRPC_JWT_VERIFIER_BAD_AUDIENCE;
+  }
+  return GRPC_JWT_VERIFIER_OK;
+}
+
+/* --- verifier_cb_ctx object. --- */
+
+typedef enum {
+  HTTP_RESPONSE_OPENID = 0,
+  HTTP_RESPONSE_KEYS,
+  HTTP_RESPONSE_COUNT /* must be last */
+} http_response_index;
+
+typedef struct {
+  grpc_jwt_verifier* verifier;
+  grpc_polling_entity pollent;
+  jose_header* header;
+  grpc_jwt_claims* claims;
+  char* audience;
+  grpc_slice signature;
+  grpc_slice signed_data;
+  void* user_data;
+  grpc_jwt_verification_done_cb user_cb;
+  grpc_http_response responses[HTTP_RESPONSE_COUNT];
+} verifier_cb_ctx;
+
+/* Takes ownership of the header, claims and signature. */
+static verifier_cb_ctx* verifier_cb_ctx_create(
+    grpc_jwt_verifier* verifier, grpc_pollset* pollset, jose_header* header,
+    grpc_jwt_claims* claims, const char* audience, grpc_slice signature,
+    const char* signed_jwt, size_t signed_jwt_len, void* user_data,
+    grpc_jwt_verification_done_cb cb) {
+  grpc_core::ExecCtx exec_ctx;
+  verifier_cb_ctx* ctx =
+      static_cast<verifier_cb_ctx*>(gpr_zalloc(sizeof(verifier_cb_ctx)));
+  ctx->verifier = verifier;
+  ctx->pollent = grpc_polling_entity_create_from_pollset(pollset);
+  ctx->header = header;
+  ctx->audience = gpr_strdup(audience);
+  ctx->claims = claims;
+  ctx->signature = signature;
+  ctx->signed_data = grpc_slice_from_copied_buffer(signed_jwt, signed_jwt_len);
+  ctx->user_data = user_data;
+  ctx->user_cb = cb;
+
+  return ctx;
+}
+
+void verifier_cb_ctx_destroy(verifier_cb_ctx* ctx) {
+  if (ctx->audience != nullptr) gpr_free(ctx->audience);
+  if (ctx->claims != nullptr) grpc_jwt_claims_destroy(ctx->claims);
+  grpc_slice_unref_internal(ctx->signature);
+  grpc_slice_unref_internal(ctx->signed_data);
+  jose_header_destroy(ctx->header);
+  for (size_t i = 0; i < HTTP_RESPONSE_COUNT; i++) {
+    grpc_http_response_destroy(&ctx->responses[i]);
+  }
+  /* TODO: see what to do with claims... */
+  gpr_free(ctx);
+}
+
+/* --- grpc_jwt_verifier object. --- */
+
+/* Clock skew defaults to one minute. */
+gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0, GPR_TIMESPAN};
+
+/* Max delay defaults to one minute. */
+grpc_millis grpc_jwt_verifier_max_delay = 60 * GPR_MS_PER_SEC;
+
+typedef struct {
+  char* email_domain;
+  char* key_url_prefix;
+} email_key_mapping;
+
+struct grpc_jwt_verifier {
+  email_key_mapping* mappings;
+  size_t num_mappings; /* Should be very few, linear search ok. */
+  size_t allocated_mappings;
+  grpc_httpcli_context http_ctx;
+};
+
+static grpc_json* json_from_http(const grpc_httpcli_response* response) {
+  grpc_json* json = nullptr;
+
+  if (response == nullptr) {
+    gpr_log(GPR_ERROR, "HTTP response is NULL.");
+    return nullptr;
+  }
+  if (response->status != 200) {
+    gpr_log(GPR_ERROR, "Call to http server failed with error %d.",
+            response->status);
+    return nullptr;
+  }
+
+  json = grpc_json_parse_string_with_len(response->body, response->body_length);
+  if (json == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid JSON found in response.");
+  }
+  return json;
+}
+
+static const grpc_json* find_property_by_name(const grpc_json* json,
+                                              const char* name) {
+  const grpc_json* cur;
+  for (cur = json->child; cur != nullptr; cur = cur->next) {
+    if (strcmp(cur->key, name) == 0) return cur;
+  }
+  return nullptr;
+}
+
+static EVP_PKEY* extract_pkey_from_x509(const char* x509_str) {
+  X509* x509 = nullptr;
+  EVP_PKEY* result = nullptr;
+  BIO* bio = BIO_new(BIO_s_mem());
+  size_t len = strlen(x509_str);
+  GPR_ASSERT(len < INT_MAX);
+  BIO_write(bio, x509_str, static_cast<int>(len));
+  x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
+  if (x509 == nullptr) {
+    gpr_log(GPR_ERROR, "Unable to parse x509 cert.");
+    goto end;
+  }
+  result = X509_get_pubkey(x509);
+  if (result == nullptr) {
+    gpr_log(GPR_ERROR, "Cannot find public key in X509 cert.");
+  }
+
+end:
+  BIO_free(bio);
+  X509_free(x509);
+  return result;
+}
+
+static BIGNUM* bignum_from_base64(const char* b64) {
+  BIGNUM* result = nullptr;
+  grpc_slice bin;
+
+  if (b64 == nullptr) return nullptr;
+  bin = grpc_base64_decode(b64, 1);
+  if (GRPC_SLICE_IS_EMPTY(bin)) {
+    gpr_log(GPR_ERROR, "Invalid base64 for big num.");
+    return nullptr;
+  }
+  result = BN_bin2bn(GRPC_SLICE_START_PTR(bin),
+                     TSI_SIZE_AS_SIZE(GRPC_SLICE_LENGTH(bin)), nullptr);
+  grpc_slice_unref_internal(bin);
+  return result;
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+// Provide compatibility across OpenSSL 1.02 and 1.1.
+static int RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d) {
+  /* If the fields n and e in r are NULL, the corresponding input
+   * parameters MUST be non-NULL for n and e.  d may be
+   * left NULL (in case only the public key is used).
+   */
+  if ((r->n == nullptr && n == nullptr) || (r->e == nullptr && e == nullptr)) {
+    return 0;
+  }
+
+  if (n != nullptr) {
+    BN_free(r->n);
+    r->n = n;
+  }
+  if (e != nullptr) {
+    BN_free(r->e);
+    r->e = e;
+  }
+  if (d != nullptr) {
+    BN_free(r->d);
+    r->d = d;
+  }
+
+  return 1;
+}
+#endif  // OPENSSL_VERSION_NUMBER < 0x10100000L
+
+static EVP_PKEY* pkey_from_jwk(const grpc_json* json, const char* kty) {
+  const grpc_json* key_prop;
+  RSA* rsa = nullptr;
+  EVP_PKEY* result = nullptr;
+  BIGNUM* tmp_n = nullptr;
+  BIGNUM* tmp_e = nullptr;
+
+  GPR_ASSERT(kty != nullptr && json != nullptr);
+  if (strcmp(kty, "RSA") != 0) {
+    gpr_log(GPR_ERROR, "Unsupported key type %s.", kty);
+    goto end;
+  }
+  rsa = RSA_new();
+  if (rsa == nullptr) {
+    gpr_log(GPR_ERROR, "Could not create rsa key.");
+    goto end;
+  }
+  for (key_prop = json->child; key_prop != nullptr; key_prop = key_prop->next) {
+    if (strcmp(key_prop->key, "n") == 0) {
+      tmp_n = bignum_from_base64(validate_string_field(key_prop, "n"));
+      if (tmp_n == nullptr) goto end;
+    } else if (strcmp(key_prop->key, "e") == 0) {
+      tmp_e = bignum_from_base64(validate_string_field(key_prop, "e"));
+      if (tmp_e == nullptr) goto end;
+    }
+  }
+  if (tmp_e == nullptr || tmp_n == nullptr) {
+    gpr_log(GPR_ERROR, "Missing RSA public key field.");
+    goto end;
+  }
+  if (!RSA_set0_key(rsa, tmp_n, tmp_e, nullptr)) {
+    gpr_log(GPR_ERROR, "Cannot set RSA key from inputs.");
+    goto end;
+  }
+  /* RSA_set0_key takes ownership on success. */
+  tmp_n = nullptr;
+  tmp_e = nullptr;
+  result = EVP_PKEY_new();
+  EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */
+
+end:
+  RSA_free(rsa);
+  BN_free(tmp_n);
+  BN_free(tmp_e);
+  return result;
+}
+
+static EVP_PKEY* find_verification_key(const grpc_json* json,
+                                       const char* header_alg,
+                                       const char* header_kid) {
+  const grpc_json* jkey;
+  const grpc_json* jwk_keys;
+  /* Try to parse the json as a JWK set:
+     https://tools.ietf.org/html/rfc7517#section-5. */
+  jwk_keys = find_property_by_name(json, "keys");
+  if (jwk_keys == nullptr) {
+    /* Use the google proprietary format which is:
+       { <kid1>: <x5091>, <kid2>: <x5092>, ... } */
+    const grpc_json* cur = find_property_by_name(json, header_kid);
+    if (cur == nullptr) return nullptr;
+    return extract_pkey_from_x509(cur->value);
+  }
+
+  if (jwk_keys->type != GRPC_JSON_ARRAY) {
+    gpr_log(GPR_ERROR,
+            "Unexpected value type of keys property in jwks key set.");
+    return nullptr;
+  }
+  /* Key format is specified in:
+     https://tools.ietf.org/html/rfc7518#section-6. */
+  for (jkey = jwk_keys->child; jkey != nullptr; jkey = jkey->next) {
+    grpc_json* key_prop;
+    const char* alg = nullptr;
+    const char* kid = nullptr;
+    const char* kty = nullptr;
+
+    if (jkey->type != GRPC_JSON_OBJECT) continue;
+    for (key_prop = jkey->child; key_prop != nullptr;
+         key_prop = key_prop->next) {
+      if (strcmp(key_prop->key, "alg") == 0 &&
+          key_prop->type == GRPC_JSON_STRING) {
+        alg = key_prop->value;
+      } else if (strcmp(key_prop->key, "kid") == 0 &&
+                 key_prop->type == GRPC_JSON_STRING) {
+        kid = key_prop->value;
+      } else if (strcmp(key_prop->key, "kty") == 0 &&
+                 key_prop->type == GRPC_JSON_STRING) {
+        kty = key_prop->value;
+      }
+    }
+    if (alg != nullptr && kid != nullptr && kty != nullptr &&
+        strcmp(kid, header_kid) == 0 && strcmp(alg, header_alg) == 0) {
+      return pkey_from_jwk(jkey, kty);
+    }
+  }
+  gpr_log(GPR_ERROR,
+          "Could not find matching key in key set for kid=%s and alg=%s",
+          header_kid, header_alg);
+  return nullptr;
+}
+
+static int verify_jwt_signature(EVP_PKEY* key, const char* alg,
+                                grpc_slice signature, grpc_slice signed_data) {
+  EVP_MD_CTX* md_ctx = EVP_MD_CTX_create();
+  const EVP_MD* md = evp_md_from_alg(alg);
+  int result = 0;
+
+  GPR_ASSERT(md != nullptr); /* Checked before. */
+  if (md_ctx == nullptr) {
+    gpr_log(GPR_ERROR, "Could not create EVP_MD_CTX.");
+    goto end;
+  }
+  if (EVP_DigestVerifyInit(md_ctx, nullptr, md, nullptr, key) != 1) {
+    gpr_log(GPR_ERROR, "EVP_DigestVerifyInit failed.");
+    goto end;
+  }
+  if (EVP_DigestVerifyUpdate(md_ctx, GRPC_SLICE_START_PTR(signed_data),
+                             GRPC_SLICE_LENGTH(signed_data)) != 1) {
+    gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed.");
+    goto end;
+  }
+  if (EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(signature),
+                            GRPC_SLICE_LENGTH(signature)) != 1) {
+    gpr_log(GPR_ERROR, "JWT signature verification failed.");
+    goto end;
+  }
+  result = 1;
+
+end:
+  EVP_MD_CTX_destroy(md_ctx);
+  return result;
+}
+
+static void on_keys_retrieved(void* user_data, grpc_error* error) {
+  verifier_cb_ctx* ctx = static_cast<verifier_cb_ctx*>(user_data);
+  grpc_json* json = json_from_http(&ctx->responses[HTTP_RESPONSE_KEYS]);
+  EVP_PKEY* verification_key = nullptr;
+  grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR;
+  grpc_jwt_claims* claims = nullptr;
+
+  if (json == nullptr) {
+    status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
+    goto end;
+  }
+  verification_key =
+      find_verification_key(json, ctx->header->alg, ctx->header->kid);
+  if (verification_key == nullptr) {
+    gpr_log(GPR_ERROR, "Could not find verification key with kid %s.",
+            ctx->header->kid);
+    status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
+    goto end;
+  }
+
+  if (!verify_jwt_signature(verification_key, ctx->header->alg, ctx->signature,
+                            ctx->signed_data)) {
+    status = GRPC_JWT_VERIFIER_BAD_SIGNATURE;
+    goto end;
+  }
+
+  status = grpc_jwt_claims_check(ctx->claims, ctx->audience);
+  if (status == GRPC_JWT_VERIFIER_OK) {
+    /* Pass ownership. */
+    claims = ctx->claims;
+    ctx->claims = nullptr;
+  }
+
+end:
+  if (json != nullptr) grpc_json_destroy(json);
+  EVP_PKEY_free(verification_key);
+  ctx->user_cb(ctx->user_data, status, claims);
+  verifier_cb_ctx_destroy(ctx);
+}
+
+static void on_openid_config_retrieved(void* user_data, grpc_error* error) {
+  const grpc_json* cur;
+  verifier_cb_ctx* ctx = static_cast<verifier_cb_ctx*>(user_data);
+  const grpc_http_response* response = &ctx->responses[HTTP_RESPONSE_OPENID];
+  grpc_json* json = json_from_http(response);
+  grpc_httpcli_request req;
+  const char* jwks_uri;
+  grpc_resource_quota* resource_quota = nullptr;
+
+  /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */
+  if (json == nullptr) goto error;
+  cur = find_property_by_name(json, "jwks_uri");
+  if (cur == nullptr) {
+    gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config.");
+    goto error;
+  }
+  jwks_uri = validate_string_field(cur, "jwks_uri");
+  if (jwks_uri == nullptr) goto error;
+  if (strstr(jwks_uri, "https://") != jwks_uri) {
+    gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri);
+    goto error;
+  }
+  jwks_uri += 8;
+  req.handshaker = &grpc_httpcli_ssl;
+  req.host = gpr_strdup(jwks_uri);
+  req.http.path = const_cast<char*>(strchr(jwks_uri, '/'));
+  if (req.http.path == nullptr) {
+    req.http.path = (char*)"";
+  } else {
+    *(req.host + (req.http.path - jwks_uri)) = '\0';
+  }
+
+  /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
+     channel. This would allow us to cancel an authentication query when under
+     extreme memory pressure. */
+  resource_quota = grpc_resource_quota_create("jwt_verifier");
+  grpc_httpcli_get(
+      &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
+      grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay,
+      GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx),
+      &ctx->responses[HTTP_RESPONSE_KEYS]);
+  grpc_resource_quota_unref_internal(resource_quota);
+  grpc_json_destroy(json);
+  gpr_free(req.host);
+  return;
+
+error:
+  if (json != nullptr) grpc_json_destroy(json);
+  ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, nullptr);
+  verifier_cb_ctx_destroy(ctx);
+}
+
+static email_key_mapping* verifier_get_mapping(grpc_jwt_verifier* v,
+                                               const char* email_domain) {
+  size_t i;
+  if (v->mappings == nullptr) return nullptr;
+  for (i = 0; i < v->num_mappings; i++) {
+    if (strcmp(email_domain, v->mappings[i].email_domain) == 0) {
+      return &v->mappings[i];
+    }
+  }
+  return nullptr;
+}
+
+static void verifier_put_mapping(grpc_jwt_verifier* v, const char* email_domain,
+                                 const char* key_url_prefix) {
+  email_key_mapping* mapping = verifier_get_mapping(v, email_domain);
+  GPR_ASSERT(v->num_mappings < v->allocated_mappings);
+  if (mapping != nullptr) {
+    gpr_free(mapping->key_url_prefix);
+    mapping->key_url_prefix = gpr_strdup(key_url_prefix);
+    return;
+  }
+  v->mappings[v->num_mappings].email_domain = gpr_strdup(email_domain);
+  v->mappings[v->num_mappings].key_url_prefix = gpr_strdup(key_url_prefix);
+  v->num_mappings++;
+  GPR_ASSERT(v->num_mappings <= v->allocated_mappings);
+}
+
+/* Very non-sophisticated way to detect an email address. Should be good
+   enough for now... */
+const char* grpc_jwt_issuer_email_domain(const char* issuer) {
+  const char* at_sign = strchr(issuer, '@');
+  if (at_sign == nullptr) return nullptr;
+  const char* email_domain = at_sign + 1;
+  if (*email_domain == '\0') return nullptr;
+  const char* dot = strrchr(email_domain, '.');
+  if (dot == nullptr || dot == email_domain) return email_domain;
+  GPR_ASSERT(dot > email_domain);
+  /* There may be a subdomain, we just want the domain. */
+  dot = static_cast<const char*>(gpr_memrchr(
+      (void*)email_domain, '.', static_cast<size_t>(dot - email_domain)));
+  if (dot == nullptr) return email_domain;
+  return dot + 1;
+}
+
+/* Takes ownership of ctx. */
+static void retrieve_key_and_verify(verifier_cb_ctx* ctx) {
+  const char* email_domain;
+  grpc_closure* http_cb;
+  char* path_prefix = nullptr;
+  const char* iss;
+  grpc_httpcli_request req;
+  grpc_resource_quota* resource_quota = nullptr;
+  memset(&req, 0, sizeof(grpc_httpcli_request));
+  req.handshaker = &grpc_httpcli_ssl;
+  http_response_index rsp_idx;
+
+  GPR_ASSERT(ctx != nullptr && ctx->header != nullptr &&
+             ctx->claims != nullptr);
+  iss = ctx->claims->iss;
+  if (ctx->header->kid == nullptr) {
+    gpr_log(GPR_ERROR, "Missing kid in jose header.");
+    goto error;
+  }
+  if (iss == nullptr) {
+    gpr_log(GPR_ERROR, "Missing iss in claims.");
+    goto error;
+  }
+
+  /* This code relies on:
+     https://openid.net/specs/openid-connect-discovery-1_0.html
+     Nobody seems to implement the account/email/webfinger part 2. of the spec
+     so we will rely instead on email/url mappings if we detect such an issuer.
+     Part 4, on the other hand is implemented by both google and salesforce. */
+  email_domain = grpc_jwt_issuer_email_domain(iss);
+  if (email_domain != nullptr) {
+    email_key_mapping* mapping;
+    GPR_ASSERT(ctx->verifier != nullptr);
+    mapping = verifier_get_mapping(ctx->verifier, email_domain);
+    if (mapping == nullptr) {
+      gpr_log(GPR_ERROR, "Missing mapping for issuer email.");
+      goto error;
+    }
+    req.host = gpr_strdup(mapping->key_url_prefix);
+    path_prefix = strchr(req.host, '/');
+    if (path_prefix == nullptr) {
+      gpr_asprintf(&req.http.path, "/%s", iss);
+    } else {
+      *(path_prefix++) = '\0';
+      gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss);
+    }
+    http_cb =
+        GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx);
+    rsp_idx = HTTP_RESPONSE_KEYS;
+  } else {
+    req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
+    path_prefix = strchr(req.host, '/');
+    if (path_prefix == nullptr) {
+      req.http.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX);
+    } else {
+      *(path_prefix++) = 0;
+      gpr_asprintf(&req.http.path, "/%s%s", path_prefix,
+                   GRPC_OPENID_CONFIG_URL_SUFFIX);
+    }
+    http_cb = GRPC_CLOSURE_CREATE(on_openid_config_retrieved, ctx,
+                                  grpc_schedule_on_exec_ctx);
+    rsp_idx = HTTP_RESPONSE_OPENID;
+  }
+
+  /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
+     channel. This would allow us to cancel an authentication query when under
+     extreme memory pressure. */
+  resource_quota = grpc_resource_quota_create("jwt_verifier");
+  grpc_httpcli_get(
+      &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
+      grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay, http_cb,
+      &ctx->responses[rsp_idx]);
+  grpc_resource_quota_unref_internal(resource_quota);
+  gpr_free(req.host);
+  gpr_free(req.http.path);
+  return;
+
+error:
+  ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, nullptr);
+  verifier_cb_ctx_destroy(ctx);
+}
+
+void grpc_jwt_verifier_verify(grpc_jwt_verifier* verifier,
+                              grpc_pollset* pollset, const char* jwt,
+                              const char* audience,
+                              grpc_jwt_verification_done_cb cb,
+                              void* user_data) {
+  const char* dot = nullptr;
+  grpc_json* json;
+  jose_header* header = nullptr;
+  grpc_jwt_claims* claims = nullptr;
+  grpc_slice header_buffer;
+  grpc_slice claims_buffer;
+  grpc_slice signature;
+  size_t signed_jwt_len;
+  const char* cur = jwt;
+
+  GPR_ASSERT(verifier != nullptr && jwt != nullptr && audience != nullptr &&
+             cb != nullptr);
+  dot = strchr(cur, '.');
+  if (dot == nullptr) goto error;
+  json = parse_json_part_from_jwt(cur, static_cast<size_t>(dot - cur),
+                                  &header_buffer);
+  if (json == nullptr) goto error;
+  header = jose_header_from_json(json, header_buffer);
+  if (header == nullptr) goto error;
+
+  cur = dot + 1;
+  dot = strchr(cur, '.');
+  if (dot == nullptr) goto error;
+  json = parse_json_part_from_jwt(cur, static_cast<size_t>(dot - cur),
+                                  &claims_buffer);
+  if (json == nullptr) goto error;
+  claims = grpc_jwt_claims_from_json(json, claims_buffer);
+  if (claims == nullptr) goto error;
+
+  signed_jwt_len = static_cast<size_t>(dot - jwt);
+  cur = dot + 1;
+  signature = grpc_base64_decode(cur, 1);
+  if (GRPC_SLICE_IS_EMPTY(signature)) goto error;
+  retrieve_key_and_verify(
+      verifier_cb_ctx_create(verifier, pollset, header, claims, audience,
+                             signature, jwt, signed_jwt_len, user_data, cb));
+  return;
+
+error:
+  if (header != nullptr) jose_header_destroy(header);
+  if (claims != nullptr) grpc_jwt_claims_destroy(claims);
+  cb(user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, nullptr);
+}
+
+grpc_jwt_verifier* grpc_jwt_verifier_create(
+    const grpc_jwt_verifier_email_domain_key_url_mapping* mappings,
+    size_t num_mappings) {
+  grpc_jwt_verifier* v =
+      static_cast<grpc_jwt_verifier*>(gpr_zalloc(sizeof(grpc_jwt_verifier)));
+  grpc_httpcli_context_init(&v->http_ctx);
+
+  /* We know at least of one mapping. */
+  v->allocated_mappings = 1 + num_mappings;
+  v->mappings = static_cast<email_key_mapping*>(
+      gpr_malloc(v->allocated_mappings * sizeof(email_key_mapping)));
+  verifier_put_mapping(v, GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN,
+                       GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX);
+  /* User-Provided mappings. */
+  if (mappings != nullptr) {
+    size_t i;
+    for (i = 0; i < num_mappings; i++) {
+      verifier_put_mapping(v, mappings[i].email_domain,
+                           mappings[i].key_url_prefix);
+    }
+  }
+  return v;
+}
+
+void grpc_jwt_verifier_destroy(grpc_jwt_verifier* v) {
+  size_t i;
+  if (v == nullptr) return;
+  grpc_httpcli_context_destroy(&v->http_ctx);
+  if (v->mappings != nullptr) {
+    for (i = 0; i < v->num_mappings; i++) {
+      gpr_free(v->mappings[i].email_domain);
+      gpr_free(v->mappings[i].key_url_prefix);
+    }
+    gpr_free(v->mappings);
+  }
+  gpr_free(v);
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.h b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.h
new file mode 100644
index 0000000..cdb0987
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.h
@@ -0,0 +1,123 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/json/json.h"
+
+#include <grpc/slice.h>
+#include <grpc/support/time.h>
+
+/* --- Constants. --- */
+
+#define GRPC_OPENID_CONFIG_URL_SUFFIX "/.well-known/openid-configuration"
+#define GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN "gserviceaccount.com"
+#define GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX \
+  "www.googleapis.com/robot/v1/metadata/x509"
+
+/* --- grpc_jwt_verifier_status. --- */
+
+typedef enum {
+  GRPC_JWT_VERIFIER_OK = 0,
+  GRPC_JWT_VERIFIER_BAD_SIGNATURE,
+  GRPC_JWT_VERIFIER_BAD_FORMAT,
+  GRPC_JWT_VERIFIER_BAD_AUDIENCE,
+  GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR,
+  GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE,
+  GRPC_JWT_VERIFIER_BAD_SUBJECT,
+  GRPC_JWT_VERIFIER_GENERIC_ERROR
+} grpc_jwt_verifier_status;
+
+const char* grpc_jwt_verifier_status_to_string(grpc_jwt_verifier_status status);
+
+/* --- grpc_jwt_claims. --- */
+
+typedef struct grpc_jwt_claims grpc_jwt_claims;
+
+void grpc_jwt_claims_destroy(grpc_jwt_claims* claims);
+
+/* Returns the whole JSON tree of the claims. */
+const grpc_json* grpc_jwt_claims_json(const grpc_jwt_claims* claims);
+
+/* Access to registered claims in https://tools.ietf.org/html/rfc7519#page-9 */
+const char* grpc_jwt_claims_subject(const grpc_jwt_claims* claims);
+const char* grpc_jwt_claims_issuer(const grpc_jwt_claims* claims);
+const char* grpc_jwt_claims_id(const grpc_jwt_claims* claims);
+const char* grpc_jwt_claims_audience(const grpc_jwt_claims* claims);
+gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims* claims);
+gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims* claims);
+gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims* claims);
+
+/* --- grpc_jwt_verifier. --- */
+
+typedef struct grpc_jwt_verifier grpc_jwt_verifier;
+
+typedef struct {
+  /* The email domain is the part after the @ sign. */
+  const char* email_domain;
+
+  /* The key url prefix will be used to get the public key from the issuer:
+     https://<key_url_prefix>/<issuer_email>
+     Therefore the key_url_prefix must NOT contain https://. */
+  const char* key_url_prefix;
+} grpc_jwt_verifier_email_domain_key_url_mapping;
+
+/* Globals to control the verifier. Not thread-safe. */
+extern gpr_timespec grpc_jwt_verifier_clock_skew;
+extern grpc_millis grpc_jwt_verifier_max_delay;
+
+/* The verifier can be created with some custom mappings to help with key
+   discovery in the case where the issuer is an email address.
+   mappings can be NULL in which case num_mappings MUST be 0.
+   A verifier object has one built-in mapping (unless overridden):
+   GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN ->
+   GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX.*/
+grpc_jwt_verifier* grpc_jwt_verifier_create(
+    const grpc_jwt_verifier_email_domain_key_url_mapping* mappings,
+    size_t num_mappings);
+
+/*The verifier must not be destroyed if there are still outstanding callbacks.*/
+void grpc_jwt_verifier_destroy(grpc_jwt_verifier* verifier);
+
+/* User provided callback that will be called when the verification of the JWT
+   is done (maybe in another thread).
+   It is the responsibility of the callee to call grpc_jwt_claims_destroy on
+   the claims. */
+typedef void (*grpc_jwt_verification_done_cb)(void* user_data,
+                                              grpc_jwt_verifier_status status,
+                                              grpc_jwt_claims* claims);
+
+/* Verifies for the JWT for the given expected audience. */
+void grpc_jwt_verifier_verify(grpc_jwt_verifier* verifier,
+                              grpc_pollset* pollset, const char* jwt,
+                              const char* audience,
+                              grpc_jwt_verification_done_cb cb,
+                              void* user_data);
+
+/* --- TESTING ONLY exposed functions. --- */
+
+grpc_jwt_claims* grpc_jwt_claims_from_json(grpc_json* json, grpc_slice buffer);
+grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims* claims,
+                                               const char* audience);
+const char* grpc_jwt_issuer_email_domain(const char* issuer);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.cc
new file mode 100644
index 0000000..6f6f95a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.cc
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/local/local_credentials.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/security/security_connector/local/local_security_connector.h"
+
+#define GRPC_CREDENTIALS_TYPE_LOCAL "Local"
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_local_credentials::create_security_connector(
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const char* target_name, const grpc_channel_args* args,
+    grpc_channel_args** new_args) {
+  return grpc_local_channel_security_connector_create(
+      this->Ref(), std::move(request_metadata_creds), args, target_name);
+}
+
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_local_server_credentials::create_security_connector() {
+  return grpc_local_server_security_connector_create(this->Ref());
+}
+
+grpc_local_credentials::grpc_local_credentials(
+    grpc_local_connect_type connect_type)
+    : grpc_channel_credentials(GRPC_CREDENTIALS_TYPE_LOCAL),
+      connect_type_(connect_type) {}
+
+grpc_channel_credentials* grpc_local_credentials_create(
+    grpc_local_connect_type connect_type) {
+  return grpc_core::New<grpc_local_credentials>(connect_type);
+}
+
+grpc_local_server_credentials::grpc_local_server_credentials(
+    grpc_local_connect_type connect_type)
+    : grpc_server_credentials(GRPC_CREDENTIALS_TYPE_LOCAL),
+      connect_type_(connect_type) {}
+
+grpc_server_credentials* grpc_local_server_credentials_create(
+    grpc_local_connect_type connect_type) {
+  return grpc_core::New<grpc_local_server_credentials>(connect_type);
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.h b/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.h
new file mode 100644
index 0000000..60a8a4f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_LOCAL_LOCAL_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_LOCAL_LOCAL_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+/* Main class for grpc local channel credential. */
+class grpc_local_credentials final : public grpc_channel_credentials {
+ public:
+  explicit grpc_local_credentials(grpc_local_connect_type connect_type);
+  ~grpc_local_credentials() override = default;
+
+  grpc_core::RefCountedPtr<grpc_channel_security_connector>
+  create_security_connector(
+      grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+      const char* target_name, const grpc_channel_args* args,
+      grpc_channel_args** new_args) override;
+
+  grpc_local_connect_type connect_type() const { return connect_type_; }
+
+ private:
+  grpc_local_connect_type connect_type_;
+};
+
+/* Main class for grpc local server credential. */
+class grpc_local_server_credentials final : public grpc_server_credentials {
+ public:
+  explicit grpc_local_server_credentials(grpc_local_connect_type connect_type);
+  ~grpc_local_server_credentials() override = default;
+
+  grpc_core::RefCountedPtr<grpc_server_security_connector>
+  create_security_connector() override;
+
+  grpc_local_connect_type connect_type() const { return connect_type_; }
+
+ private:
+  grpc_local_connect_type connect_type_;
+};
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_LOCAL_LOCAL_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
new file mode 100644
index 0000000..ad63b01
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
@@ -0,0 +1,517 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/util/json_util.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+//
+// Auth Refresh Token.
+//
+
+int grpc_auth_refresh_token_is_valid(
+    const grpc_auth_refresh_token* refresh_token) {
+  return (refresh_token != nullptr) &&
+         strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID);
+}
+
+grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
+    const grpc_json* json) {
+  grpc_auth_refresh_token result;
+  const char* prop_value;
+  int success = 0;
+
+  memset(&result, 0, sizeof(grpc_auth_refresh_token));
+  result.type = GRPC_AUTH_JSON_TYPE_INVALID;
+  if (json == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid json.");
+    goto end;
+  }
+
+  prop_value = grpc_json_get_string_property(json, "type");
+  if (prop_value == nullptr ||
+      strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) {
+    goto end;
+  }
+  result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
+
+  if (!grpc_copy_json_string_property(json, "client_secret",
+                                      &result.client_secret) ||
+      !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
+      !grpc_copy_json_string_property(json, "refresh_token",
+                                      &result.refresh_token)) {
+    goto end;
+  }
+  success = 1;
+
+end:
+  if (!success) grpc_auth_refresh_token_destruct(&result);
+  return result;
+}
+
+grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
+    const char* json_string) {
+  char* scratchpad = gpr_strdup(json_string);
+  grpc_json* json = grpc_json_parse_string(scratchpad);
+  grpc_auth_refresh_token result =
+      grpc_auth_refresh_token_create_from_json(json);
+  if (json != nullptr) grpc_json_destroy(json);
+  gpr_free(scratchpad);
+  return result;
+}
+
+void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token) {
+  if (refresh_token == nullptr) return;
+  refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
+  if (refresh_token->client_id != nullptr) {
+    gpr_free(refresh_token->client_id);
+    refresh_token->client_id = nullptr;
+  }
+  if (refresh_token->client_secret != nullptr) {
+    gpr_free(refresh_token->client_secret);
+    refresh_token->client_secret = nullptr;
+  }
+  if (refresh_token->refresh_token != nullptr) {
+    gpr_free(refresh_token->refresh_token);
+    refresh_token->refresh_token = nullptr;
+  }
+}
+
+//
+// Oauth2 Token Fetcher credentials.
+//
+
+grpc_oauth2_token_fetcher_credentials::
+    ~grpc_oauth2_token_fetcher_credentials() {
+  GRPC_MDELEM_UNREF(access_token_md_);
+  gpr_mu_destroy(&mu_);
+  grpc_pollset_set_destroy(grpc_polling_entity_pollset_set(&pollent_));
+  grpc_httpcli_context_destroy(&httpcli_context_);
+}
+
+grpc_credentials_status
+grpc_oauth2_token_fetcher_credentials_parse_server_response(
+    const grpc_http_response* response, grpc_mdelem* token_md,
+    grpc_millis* token_lifetime) {
+  char* null_terminated_body = nullptr;
+  char* new_access_token = nullptr;
+  grpc_credentials_status status = GRPC_CREDENTIALS_OK;
+  grpc_json* json = nullptr;
+
+  if (response == nullptr) {
+    gpr_log(GPR_ERROR, "Received NULL response.");
+    status = GRPC_CREDENTIALS_ERROR;
+    goto end;
+  }
+
+  if (response->body_length > 0) {
+    null_terminated_body =
+        static_cast<char*>(gpr_malloc(response->body_length + 1));
+    null_terminated_body[response->body_length] = '\0';
+    memcpy(null_terminated_body, response->body, response->body_length);
+  }
+
+  if (response->status != 200) {
+    gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
+            response->status,
+            null_terminated_body != nullptr ? null_terminated_body : "");
+    status = GRPC_CREDENTIALS_ERROR;
+    goto end;
+  } else {
+    grpc_json* access_token = nullptr;
+    grpc_json* token_type = nullptr;
+    grpc_json* expires_in = nullptr;
+    grpc_json* ptr;
+    json = grpc_json_parse_string(null_terminated_body);
+    if (json == nullptr) {
+      gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    if (json->type != GRPC_JSON_OBJECT) {
+      gpr_log(GPR_ERROR, "Response should be a JSON object");
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    for (ptr = json->child; ptr; ptr = ptr->next) {
+      if (strcmp(ptr->key, "access_token") == 0) {
+        access_token = ptr;
+      } else if (strcmp(ptr->key, "token_type") == 0) {
+        token_type = ptr;
+      } else if (strcmp(ptr->key, "expires_in") == 0) {
+        expires_in = ptr;
+      }
+    }
+    if (access_token == nullptr || access_token->type != GRPC_JSON_STRING) {
+      gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    if (token_type == nullptr || token_type->type != GRPC_JSON_STRING) {
+      gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    if (expires_in == nullptr || expires_in->type != GRPC_JSON_NUMBER) {
+      gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    gpr_asprintf(&new_access_token, "%s %s", token_type->value,
+                 access_token->value);
+    *token_lifetime = strtol(expires_in->value, nullptr, 10) * GPR_MS_PER_SEC;
+    if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(*token_md);
+    *token_md = grpc_mdelem_from_slices(
+        grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
+        grpc_slice_from_copied_string(new_access_token));
+    status = GRPC_CREDENTIALS_OK;
+  }
+
+end:
+  if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) {
+    GRPC_MDELEM_UNREF(*token_md);
+    *token_md = GRPC_MDNULL;
+  }
+  if (null_terminated_body != nullptr) gpr_free(null_terminated_body);
+  if (new_access_token != nullptr) gpr_free(new_access_token);
+  if (json != nullptr) grpc_json_destroy(json);
+  return status;
+}
+
+static void on_oauth2_token_fetcher_http_response(void* user_data,
+                                                  grpc_error* error) {
+  GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error));
+  grpc_credentials_metadata_request* r =
+      static_cast<grpc_credentials_metadata_request*>(user_data);
+  grpc_oauth2_token_fetcher_credentials* c =
+      reinterpret_cast<grpc_oauth2_token_fetcher_credentials*>(r->creds.get());
+  c->on_http_response(r, error);
+}
+
+void grpc_oauth2_token_fetcher_credentials::on_http_response(
+    grpc_credentials_metadata_request* r, grpc_error* error) {
+  grpc_mdelem access_token_md = GRPC_MDNULL;
+  grpc_millis token_lifetime;
+  grpc_credentials_status status =
+      grpc_oauth2_token_fetcher_credentials_parse_server_response(
+          &r->response, &access_token_md, &token_lifetime);
+  // Update cache and grab list of pending requests.
+  gpr_mu_lock(&mu_);
+  token_fetch_pending_ = false;
+  access_token_md_ = GRPC_MDELEM_REF(access_token_md);
+  token_expiration_ =
+      status == GRPC_CREDENTIALS_OK
+          ? gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                         gpr_time_from_millis(token_lifetime, GPR_TIMESPAN))
+          : gpr_inf_past(GPR_CLOCK_MONOTONIC);
+  grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_;
+  pending_requests_ = nullptr;
+  gpr_mu_unlock(&mu_);
+  // Invoke callbacks for all pending requests.
+  while (pending_request != nullptr) {
+    if (status == GRPC_CREDENTIALS_OK) {
+      grpc_credentials_mdelem_array_add(pending_request->md_array,
+                                        access_token_md);
+    } else {
+      error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+          "Error occurred when fetching oauth2 token.", &error, 1);
+    }
+    GRPC_CLOSURE_SCHED(pending_request->on_request_metadata, error);
+    grpc_polling_entity_del_from_pollset_set(
+        pending_request->pollent, grpc_polling_entity_pollset_set(&pollent_));
+    grpc_oauth2_pending_get_request_metadata* prev = pending_request;
+    pending_request = pending_request->next;
+    gpr_free(prev);
+  }
+  GRPC_MDELEM_UNREF(access_token_md);
+  Unref();
+  grpc_credentials_metadata_request_destroy(r);
+}
+
+bool grpc_oauth2_token_fetcher_credentials::get_request_metadata(
+    grpc_polling_entity* pollent, grpc_auth_metadata_context context,
+    grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
+    grpc_error** error) {
+  // Check if we can use the cached token.
+  grpc_millis refresh_threshold =
+      GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS * GPR_MS_PER_SEC;
+  grpc_mdelem cached_access_token_md = GRPC_MDNULL;
+  gpr_mu_lock(&mu_);
+  if (!GRPC_MDISNULL(access_token_md_) &&
+      gpr_time_cmp(
+          gpr_time_sub(token_expiration_, gpr_now(GPR_CLOCK_MONOTONIC)),
+          gpr_time_from_seconds(GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS,
+                                GPR_TIMESPAN)) > 0) {
+    cached_access_token_md = GRPC_MDELEM_REF(access_token_md_);
+  }
+  if (!GRPC_MDISNULL(cached_access_token_md)) {
+    gpr_mu_unlock(&mu_);
+    grpc_credentials_mdelem_array_add(md_array, cached_access_token_md);
+    GRPC_MDELEM_UNREF(cached_access_token_md);
+    return true;
+  }
+  // Couldn't get the token from the cache.
+  // Add request to pending_requests_ and start a new fetch if needed.
+  grpc_oauth2_pending_get_request_metadata* pending_request =
+      static_cast<grpc_oauth2_pending_get_request_metadata*>(
+          gpr_malloc(sizeof(*pending_request)));
+  pending_request->md_array = md_array;
+  pending_request->on_request_metadata = on_request_metadata;
+  pending_request->pollent = pollent;
+  grpc_polling_entity_add_to_pollset_set(
+      pollent, grpc_polling_entity_pollset_set(&pollent_));
+  pending_request->next = pending_requests_;
+  pending_requests_ = pending_request;
+  bool start_fetch = false;
+  if (!token_fetch_pending_) {
+    token_fetch_pending_ = true;
+    start_fetch = true;
+  }
+  gpr_mu_unlock(&mu_);
+  if (start_fetch) {
+    Ref().release();
+    fetch_oauth2(grpc_credentials_metadata_request_create(this->Ref()),
+                 &httpcli_context_, &pollent_,
+                 on_oauth2_token_fetcher_http_response,
+                 grpc_core::ExecCtx::Get()->Now() + refresh_threshold);
+  }
+  return false;
+}
+
+void grpc_oauth2_token_fetcher_credentials::cancel_get_request_metadata(
+    grpc_credentials_mdelem_array* md_array, grpc_error* error) {
+  gpr_mu_lock(&mu_);
+  grpc_oauth2_pending_get_request_metadata* prev = nullptr;
+  grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_;
+  while (pending_request != nullptr) {
+    if (pending_request->md_array == md_array) {
+      // Remove matching pending request from the list.
+      if (prev != nullptr) {
+        prev->next = pending_request->next;
+      } else {
+        pending_requests_ = pending_request->next;
+      }
+      // Invoke the callback immediately with an error.
+      GRPC_CLOSURE_SCHED(pending_request->on_request_metadata,
+                         GRPC_ERROR_REF(error));
+      gpr_free(pending_request);
+      break;
+    }
+    prev = pending_request;
+    pending_request = pending_request->next;
+  }
+  gpr_mu_unlock(&mu_);
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_oauth2_token_fetcher_credentials::grpc_oauth2_token_fetcher_credentials()
+    : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2),
+      token_expiration_(gpr_inf_past(GPR_CLOCK_MONOTONIC)),
+      pollent_(grpc_polling_entity_create_from_pollset_set(
+          grpc_pollset_set_create())) {
+  gpr_mu_init(&mu_);
+  grpc_httpcli_context_init(&httpcli_context_);
+}
+
+//
+//  Google Compute Engine credentials.
+//
+
+namespace {
+
+class grpc_compute_engine_token_fetcher_credentials
+    : public grpc_oauth2_token_fetcher_credentials {
+ public:
+  grpc_compute_engine_token_fetcher_credentials() = default;
+  ~grpc_compute_engine_token_fetcher_credentials() override = default;
+
+ protected:
+  void fetch_oauth2(grpc_credentials_metadata_request* metadata_req,
+                    grpc_httpcli_context* http_context,
+                    grpc_polling_entity* pollent,
+                    grpc_iomgr_cb_func response_cb,
+                    grpc_millis deadline) override {
+    grpc_http_header header = {(char*)"Metadata-Flavor", (char*)"Google"};
+    grpc_httpcli_request request;
+    memset(&request, 0, sizeof(grpc_httpcli_request));
+    request.host = (char*)GRPC_COMPUTE_ENGINE_METADATA_HOST;
+    request.http.path = (char*)GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
+    request.http.hdr_count = 1;
+    request.http.hdrs = &header;
+    /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
+       channel. This would allow us to cancel an authentication query when under
+       extreme memory pressure. */
+    grpc_resource_quota* resource_quota =
+        grpc_resource_quota_create("oauth2_credentials");
+    grpc_httpcli_get(http_context, pollent, resource_quota, &request, deadline,
+                     GRPC_CLOSURE_CREATE(response_cb, metadata_req,
+                                         grpc_schedule_on_exec_ctx),
+                     &metadata_req->response);
+    grpc_resource_quota_unref_internal(resource_quota);
+  }
+};
+
+}  // namespace
+
+grpc_call_credentials* grpc_google_compute_engine_credentials_create(
+    void* reserved) {
+  GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1,
+                 (reserved));
+  GPR_ASSERT(reserved == nullptr);
+  return grpc_core::MakeRefCounted<
+             grpc_compute_engine_token_fetcher_credentials>()
+      .release();
+}
+
+//
+// Google Refresh Token credentials.
+//
+
+grpc_google_refresh_token_credentials::
+    ~grpc_google_refresh_token_credentials() {
+  grpc_auth_refresh_token_destruct(&refresh_token_);
+}
+
+void grpc_google_refresh_token_credentials::fetch_oauth2(
+    grpc_credentials_metadata_request* metadata_req,
+    grpc_httpcli_context* httpcli_context, grpc_polling_entity* pollent,
+    grpc_iomgr_cb_func response_cb, grpc_millis deadline) {
+  grpc_http_header header = {(char*)"Content-Type",
+                             (char*)"application/x-www-form-urlencoded"};
+  grpc_httpcli_request request;
+  char* body = nullptr;
+  gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
+               refresh_token_.client_id, refresh_token_.client_secret,
+               refresh_token_.refresh_token);
+  memset(&request, 0, sizeof(grpc_httpcli_request));
+  request.host = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
+  request.http.path = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
+  request.http.hdr_count = 1;
+  request.http.hdrs = &header;
+  request.handshaker = &grpc_httpcli_ssl;
+  /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
+     channel. This would allow us to cancel an authentication query when under
+     extreme memory pressure. */
+  grpc_resource_quota* resource_quota =
+      grpc_resource_quota_create("oauth2_credentials_refresh");
+  grpc_httpcli_post(
+      httpcli_context, pollent, resource_quota, &request, body, strlen(body),
+      deadline,
+      GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx),
+      &metadata_req->response);
+  grpc_resource_quota_unref_internal(resource_quota);
+  gpr_free(body);
+}
+
+grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials(
+    grpc_auth_refresh_token refresh_token)
+    : refresh_token_(refresh_token) {}
+
+grpc_core::RefCountedPtr<grpc_call_credentials>
+grpc_refresh_token_credentials_create_from_auth_refresh_token(
+    grpc_auth_refresh_token refresh_token) {
+  if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
+    gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
+    return nullptr;
+  }
+  return grpc_core::MakeRefCounted<grpc_google_refresh_token_credentials>(
+      refresh_token);
+}
+
+static char* create_loggable_refresh_token(grpc_auth_refresh_token* token) {
+  if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) {
+    return gpr_strdup("<Invalid json token>");
+  }
+  char* loggable_token = nullptr;
+  gpr_asprintf(&loggable_token,
+               "{\n type: %s\n client_id: %s\n client_secret: "
+               "<redacted>\n refresh_token: <redacted>\n}",
+               token->type, token->client_id);
+  return loggable_token;
+}
+
+grpc_call_credentials* grpc_google_refresh_token_credentials_create(
+    const char* json_refresh_token, void* reserved) {
+  grpc_auth_refresh_token token =
+      grpc_auth_refresh_token_create_from_string(json_refresh_token);
+  if (grpc_api_trace.enabled()) {
+    char* loggable_token = create_loggable_refresh_token(&token);
+    gpr_log(GPR_INFO,
+            "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
+            "reserved=%p)",
+            loggable_token, reserved);
+    gpr_free(loggable_token);
+  }
+  GPR_ASSERT(reserved == nullptr);
+  return grpc_refresh_token_credentials_create_from_auth_refresh_token(token)
+      .release();
+}
+
+//
+// Oauth2 Access Token credentials.
+//
+
+grpc_access_token_credentials::~grpc_access_token_credentials() {
+  GRPC_MDELEM_UNREF(access_token_md_);
+}
+
+bool grpc_access_token_credentials::get_request_metadata(
+    grpc_polling_entity* pollent, grpc_auth_metadata_context context,
+    grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
+    grpc_error** error) {
+  grpc_credentials_mdelem_array_add(md_array, access_token_md_);
+  return true;
+}
+
+void grpc_access_token_credentials::cancel_get_request_metadata(
+    grpc_credentials_mdelem_array* md_array, grpc_error* error) {
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_access_token_credentials::grpc_access_token_credentials(
+    const char* access_token)
+    : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2) {
+  char* token_md_value;
+  gpr_asprintf(&token_md_value, "Bearer %s", access_token);
+  grpc_core::ExecCtx exec_ctx;
+  access_token_md_ = grpc_mdelem_from_slices(
+      grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
+      grpc_slice_from_copied_string(token_md_value));
+  gpr_free(token_md_value);
+}
+
+grpc_call_credentials* grpc_access_token_credentials_create(
+    const char* access_token, void* reserved) {
+  GRPC_API_TRACE(
+      "grpc_access_token_credentials_create(access_token=<redacted>, "
+      "reserved=%p)",
+      1, (reserved));
+  GPR_ASSERT(reserved == nullptr);
+  return grpc_core::MakeRefCounted<grpc_access_token_credentials>(access_token)
+      .release();
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
new file mode 100644
index 0000000..510a78b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
@@ -0,0 +1,151 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/security/credentials/credentials.h"
+
+// auth_refresh_token parsing.
+typedef struct {
+  const char* type;
+  char* client_id;
+  char* client_secret;
+  char* refresh_token;
+} grpc_auth_refresh_token;
+
+/// Returns 1 if the object is valid, 0 otherwise.
+int grpc_auth_refresh_token_is_valid(
+    const grpc_auth_refresh_token* refresh_token);
+
+/// Creates a refresh token object from string. Returns an invalid object if a
+/// parsing error has been encountered.
+grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
+    const char* json_string);
+
+/// Creates a refresh token object from parsed json. Returns an invalid object
+/// if a parsing error has been encountered.
+grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
+    const grpc_json* json);
+
+/// Destructs the object.
+void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token);
+
+// -- Oauth2 Token Fetcher credentials --
+//
+//  This object is a base for credentials that need to acquire an oauth2 token
+//  from an http service.
+
+struct grpc_oauth2_pending_get_request_metadata {
+  grpc_credentials_mdelem_array* md_array;
+  grpc_closure* on_request_metadata;
+  grpc_polling_entity* pollent;
+  struct grpc_oauth2_pending_get_request_metadata* next;
+};
+
+class grpc_oauth2_token_fetcher_credentials : public grpc_call_credentials {
+ public:
+  grpc_oauth2_token_fetcher_credentials();
+  ~grpc_oauth2_token_fetcher_credentials() override;
+
+  bool get_request_metadata(grpc_polling_entity* pollent,
+                            grpc_auth_metadata_context context,
+                            grpc_credentials_mdelem_array* md_array,
+                            grpc_closure* on_request_metadata,
+                            grpc_error** error) override;
+
+  void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array,
+                                   grpc_error* error) override;
+
+  void on_http_response(grpc_credentials_metadata_request* r,
+                        grpc_error* error);
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  virtual void fetch_oauth2(grpc_credentials_metadata_request* req,
+                            grpc_httpcli_context* httpcli_context,
+                            grpc_polling_entity* pollent, grpc_iomgr_cb_func cb,
+                            grpc_millis deadline) GRPC_ABSTRACT;
+
+ private:
+  gpr_mu mu_;
+  grpc_mdelem access_token_md_ = GRPC_MDNULL;
+  gpr_timespec token_expiration_;
+  bool token_fetch_pending_ = false;
+  grpc_oauth2_pending_get_request_metadata* pending_requests_ = nullptr;
+  grpc_httpcli_context httpcli_context_;
+  grpc_polling_entity pollent_;
+};
+
+// Google refresh token credentials.
+class grpc_google_refresh_token_credentials final
+    : public grpc_oauth2_token_fetcher_credentials {
+ public:
+  grpc_google_refresh_token_credentials(grpc_auth_refresh_token refresh_token);
+  ~grpc_google_refresh_token_credentials() override;
+
+  const grpc_auth_refresh_token& refresh_token() const {
+    return refresh_token_;
+  }
+
+ protected:
+  void fetch_oauth2(grpc_credentials_metadata_request* req,
+                    grpc_httpcli_context* httpcli_context,
+                    grpc_polling_entity* pollent, grpc_iomgr_cb_func cb,
+                    grpc_millis deadline) override;
+
+ private:
+  grpc_auth_refresh_token refresh_token_;
+};
+
+// Access token credentials.
+class grpc_access_token_credentials final : public grpc_call_credentials {
+ public:
+  grpc_access_token_credentials(const char* access_token);
+  ~grpc_access_token_credentials() override;
+
+  bool get_request_metadata(grpc_polling_entity* pollent,
+                            grpc_auth_metadata_context context,
+                            grpc_credentials_mdelem_array* md_array,
+                            grpc_closure* on_request_metadata,
+                            grpc_error** error) override;
+
+  void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array,
+                                   grpc_error* error) override;
+
+ private:
+  grpc_mdelem access_token_md_;
+};
+
+// Private constructor for refresh token credentials from an already parsed
+// refresh token. Takes ownership of the refresh token.
+grpc_core::RefCountedPtr<grpc_call_credentials>
+grpc_refresh_token_credentials_create_from_auth_refresh_token(
+    grpc_auth_refresh_token token);
+
+// Exposed for testing only.
+grpc_credentials_status
+grpc_oauth2_token_fetcher_credentials_parse_server_response(
+    const struct grpc_http_response* response, grpc_mdelem* token_md,
+    grpc_millis* token_lifetime);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.cc
new file mode 100644
index 0000000..52982fd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.cc
@@ -0,0 +1,252 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/plugin/plugin_credentials.h"
+
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/validate_metadata.h"
+
+grpc_core::TraceFlag grpc_plugin_credentials_trace(false, "plugin_credentials");
+
+grpc_plugin_credentials::~grpc_plugin_credentials() {
+  gpr_mu_destroy(&mu_);
+  if (plugin_.state != nullptr && plugin_.destroy != nullptr) {
+    plugin_.destroy(plugin_.state);
+  }
+}
+
+void grpc_plugin_credentials::pending_request_remove_locked(
+    pending_request* pending_request) {
+  if (pending_request->prev == nullptr) {
+    pending_requests_ = pending_request->next;
+  } else {
+    pending_request->prev->next = pending_request->next;
+  }
+  if (pending_request->next != nullptr) {
+    pending_request->next->prev = pending_request->prev;
+  }
+}
+
+// Checks if the request has been cancelled.
+// If not, removes it from the pending list, so that it cannot be
+// cancelled out from under us.
+// When this returns, r->cancelled indicates whether the request was
+// cancelled before completion.
+void grpc_plugin_credentials::pending_request_complete(pending_request* r) {
+  GPR_DEBUG_ASSERT(r->creds == this);
+  gpr_mu_lock(&mu_);
+  if (!r->cancelled) pending_request_remove_locked(r);
+  gpr_mu_unlock(&mu_);
+  // Ref to credentials not needed anymore.
+  Unref();
+}
+
+static grpc_error* process_plugin_result(
+    grpc_plugin_credentials::pending_request* r, const grpc_metadata* md,
+    size_t num_md, grpc_status_code status, const char* error_details) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (status != GRPC_STATUS_OK) {
+    char* msg;
+    gpr_asprintf(&msg, "Getting metadata from plugin failed with error: %s",
+                 error_details);
+    error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+  } else {
+    bool seen_illegal_header = false;
+    for (size_t i = 0; i < num_md; ++i) {
+      if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin",
+                             grpc_validate_header_key_is_legal(md[i].key))) {
+        seen_illegal_header = true;
+        break;
+      } else if (!grpc_is_binary_header(md[i].key) &&
+                 !GRPC_LOG_IF_ERROR(
+                     "validate_metadata_from_plugin",
+                     grpc_validate_header_nonbin_value_is_legal(md[i].value))) {
+        gpr_log(GPR_ERROR, "Plugin added invalid metadata value.");
+        seen_illegal_header = true;
+        break;
+      }
+    }
+    if (seen_illegal_header) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal metadata");
+    } else {
+      for (size_t i = 0; i < num_md; ++i) {
+        grpc_mdelem mdelem =
+            grpc_mdelem_create(md[i].key, md[i].value, nullptr);
+        grpc_credentials_mdelem_array_add(r->md_array, mdelem);
+        GRPC_MDELEM_UNREF(mdelem);
+      }
+    }
+  }
+  return error;
+}
+
+static void plugin_md_request_metadata_ready(void* request,
+                                             const grpc_metadata* md,
+                                             size_t num_md,
+                                             grpc_status_code status,
+                                             const char* error_details) {
+  /* called from application code */
+  grpc_core::ExecCtx exec_ctx(GRPC_EXEC_CTX_FLAG_IS_FINISHED |
+                              GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP);
+  grpc_plugin_credentials::pending_request* r =
+      static_cast<grpc_plugin_credentials::pending_request*>(request);
+  if (grpc_plugin_credentials_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "plugin_credentials[%p]: request %p: plugin returned "
+            "asynchronously",
+            r->creds, r);
+  }
+  // Remove request from pending list if not previously cancelled.
+  r->creds->pending_request_complete(r);
+  // If it has not been cancelled, process it.
+  if (!r->cancelled) {
+    grpc_error* error =
+        process_plugin_result(r, md, num_md, status, error_details);
+    GRPC_CLOSURE_SCHED(r->on_request_metadata, error);
+  } else if (grpc_plugin_credentials_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "plugin_credentials[%p]: request %p: plugin was previously "
+            "cancelled",
+            r->creds, r);
+  }
+  gpr_free(r);
+}
+
+bool grpc_plugin_credentials::get_request_metadata(
+    grpc_polling_entity* pollent, grpc_auth_metadata_context context,
+    grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata,
+    grpc_error** error) {
+  bool retval = true;  // Synchronous return.
+  if (plugin_.get_metadata != nullptr) {
+    // Create pending_request object.
+    pending_request* request =
+        static_cast<pending_request*>(gpr_zalloc(sizeof(*request)));
+    request->creds = this;
+    request->md_array = md_array;
+    request->on_request_metadata = on_request_metadata;
+    // Add it to the pending list.
+    gpr_mu_lock(&mu_);
+    if (pending_requests_ != nullptr) {
+      pending_requests_->prev = request;
+    }
+    request->next = pending_requests_;
+    pending_requests_ = request;
+    gpr_mu_unlock(&mu_);
+    // Invoke the plugin.  The callback holds a ref to us.
+    if (grpc_plugin_credentials_trace.enabled()) {
+      gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin",
+              this, request);
+    }
+    Ref().release();
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX];
+    size_t num_creds_md = 0;
+    grpc_status_code status = GRPC_STATUS_OK;
+    const char* error_details = nullptr;
+    if (!plugin_.get_metadata(
+            plugin_.state, context, plugin_md_request_metadata_ready, request,
+            creds_md, &num_creds_md, &status, &error_details)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "plugin_credentials[%p]: request %p: plugin will return "
+                "asynchronously",
+                this, request);
+      }
+      return false;  // Asynchronous return.
+    }
+    // Returned synchronously.
+    // Remove request from pending list if not previously cancelled.
+    request->creds->pending_request_complete(request);
+    // If the request was cancelled, the error will have been returned
+    // asynchronously by plugin_cancel_get_request_metadata(), so return
+    // false.  Otherwise, process the result.
+    if (request->cancelled) {
+      if (grpc_plugin_credentials_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "plugin_credentials[%p]: request %p was cancelled, error "
+                "will be returned asynchronously",
+                this, request);
+      }
+      retval = false;
+    } else {
+      if (grpc_plugin_credentials_trace.enabled()) {
+        gpr_log(GPR_INFO,
+                "plugin_credentials[%p]: request %p: plugin returned "
+                "synchronously",
+                this, request);
+      }
+      *error = process_plugin_result(request, creds_md, num_creds_md, status,
+                                     error_details);
+    }
+    // Clean up.
+    for (size_t i = 0; i < num_creds_md; ++i) {
+      grpc_slice_unref_internal(creds_md[i].key);
+      grpc_slice_unref_internal(creds_md[i].value);
+    }
+    gpr_free((void*)error_details);
+    gpr_free(request);
+  }
+  return retval;
+}
+
+void grpc_plugin_credentials::cancel_get_request_metadata(
+    grpc_credentials_mdelem_array* md_array, grpc_error* error) {
+  gpr_mu_lock(&mu_);
+  for (pending_request* pending_request = pending_requests_;
+       pending_request != nullptr; pending_request = pending_request->next) {
+    if (pending_request->md_array == md_array) {
+      if (grpc_plugin_credentials_trace.enabled()) {
+        gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", this,
+                pending_request);
+      }
+      pending_request->cancelled = true;
+      GRPC_CLOSURE_SCHED(pending_request->on_request_metadata,
+                         GRPC_ERROR_REF(error));
+      pending_request_remove_locked(pending_request);
+      break;
+    }
+  }
+  gpr_mu_unlock(&mu_);
+  GRPC_ERROR_UNREF(error);
+}
+
+grpc_plugin_credentials::grpc_plugin_credentials(
+    grpc_metadata_credentials_plugin plugin)
+    : grpc_call_credentials(plugin.type), plugin_(plugin) {
+  gpr_mu_init(&mu_);
+}
+
+grpc_call_credentials* grpc_metadata_credentials_create_from_plugin(
+    grpc_metadata_credentials_plugin plugin, void* reserved) {
+  GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1,
+                 (reserved));
+  GPR_ASSERT(reserved == nullptr);
+  return grpc_core::New<grpc_plugin_credentials>(plugin);
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.h b/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.h
new file mode 100644
index 0000000..77a957e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+extern grpc_core::TraceFlag grpc_plugin_credentials_trace;
+
+// This type is forward declared as a C struct and we cannot define it as a
+// class. Otherwise, compiler will complain about type mismatch due to
+// -Wmismatched-tags.
+struct grpc_plugin_credentials final : public grpc_call_credentials {
+ public:
+  struct pending_request {
+    bool cancelled;
+    struct grpc_plugin_credentials* creds;
+    grpc_credentials_mdelem_array* md_array;
+    grpc_closure* on_request_metadata;
+    struct pending_request* prev;
+    struct pending_request* next;
+  };
+
+  explicit grpc_plugin_credentials(grpc_metadata_credentials_plugin plugin);
+  ~grpc_plugin_credentials() override;
+
+  bool get_request_metadata(grpc_polling_entity* pollent,
+                            grpc_auth_metadata_context context,
+                            grpc_credentials_mdelem_array* md_array,
+                            grpc_closure* on_request_metadata,
+                            grpc_error** error) override;
+
+  void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array,
+                                   grpc_error* error) override;
+
+  // Checks if the request has been cancelled.
+  // If not, removes it from the pending list, so that it cannot be
+  // cancelled out from under us.
+  // When this returns, r->cancelled indicates whether the request was
+  // cancelled before completion.
+  void pending_request_complete(pending_request* r);
+
+ private:
+  void pending_request_remove_locked(pending_request* pending_request);
+
+  grpc_metadata_credentials_plugin plugin_;
+  gpr_mu mu_;
+  pending_request* pending_requests_ = nullptr;
+};
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.cc
new file mode 100644
index 0000000..83db86f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.cc
@@ -0,0 +1,346 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/tsi/ssl_transport_security.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+//
+// SSL Channel Credentials.
+//
+
+void grpc_tsi_ssl_pem_key_cert_pairs_destroy(tsi_ssl_pem_key_cert_pair* kp,
+                                             size_t num_key_cert_pairs) {
+  if (kp == nullptr) return;
+  for (size_t i = 0; i < num_key_cert_pairs; i++) {
+    gpr_free((void*)kp[i].private_key);
+    gpr_free((void*)kp[i].cert_chain);
+  }
+  gpr_free(kp);
+}
+
+grpc_ssl_credentials::grpc_ssl_credentials(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
+    const verify_peer_options* verify_options)
+    : grpc_channel_credentials(GRPC_CHANNEL_CREDENTIALS_TYPE_SSL) {
+  build_config(pem_root_certs, pem_key_cert_pair, verify_options);
+}
+
+grpc_ssl_credentials::~grpc_ssl_credentials() {
+  gpr_free(config_.pem_root_certs);
+  grpc_tsi_ssl_pem_key_cert_pairs_destroy(config_.pem_key_cert_pair, 1);
+  if (config_.verify_options.verify_peer_destruct != nullptr) {
+    config_.verify_options.verify_peer_destruct(
+        config_.verify_options.verify_peer_callback_userdata);
+  }
+}
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_ssl_credentials::create_security_connector(
+    grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+    const char* target, const grpc_channel_args* args,
+    grpc_channel_args** new_args) {
+  const char* overridden_target_name = nullptr;
+  tsi_ssl_session_cache* ssl_session_cache = nullptr;
+  for (size_t i = 0; args && i < args->num_args; i++) {
+    grpc_arg* arg = &args->args[i];
+    if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
+        arg->type == GRPC_ARG_STRING) {
+      overridden_target_name = arg->value.string;
+    }
+    if (strcmp(arg->key, GRPC_SSL_SESSION_CACHE_ARG) == 0 &&
+        arg->type == GRPC_ARG_POINTER) {
+      ssl_session_cache =
+          static_cast<tsi_ssl_session_cache*>(arg->value.pointer.p);
+    }
+  }
+  grpc_core::RefCountedPtr<grpc_channel_security_connector> sc =
+      grpc_ssl_channel_security_connector_create(
+          this->Ref(), std::move(call_creds), &config_, target,
+          overridden_target_name, ssl_session_cache);
+  if (sc == nullptr) {
+    return sc;
+  }
+  grpc_arg new_arg = grpc_channel_arg_string_create(
+      (char*)GRPC_ARG_HTTP2_SCHEME, (char*)"https");
+  *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1);
+  return sc;
+}
+
+void grpc_ssl_credentials::build_config(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
+    const verify_peer_options* verify_options) {
+  config_.pem_root_certs = gpr_strdup(pem_root_certs);
+  if (pem_key_cert_pair != nullptr) {
+    GPR_ASSERT(pem_key_cert_pair->private_key != nullptr);
+    GPR_ASSERT(pem_key_cert_pair->cert_chain != nullptr);
+    config_.pem_key_cert_pair = static_cast<tsi_ssl_pem_key_cert_pair*>(
+        gpr_zalloc(sizeof(tsi_ssl_pem_key_cert_pair)));
+    config_.pem_key_cert_pair->cert_chain =
+        gpr_strdup(pem_key_cert_pair->cert_chain);
+    config_.pem_key_cert_pair->private_key =
+        gpr_strdup(pem_key_cert_pair->private_key);
+  } else {
+    config_.pem_key_cert_pair = nullptr;
+  }
+  if (verify_options != nullptr) {
+    memcpy(&config_.verify_options, verify_options,
+           sizeof(verify_peer_options));
+  } else {
+    // Otherwise set all options to default values
+    memset(&config_.verify_options, 0, sizeof(verify_peer_options));
+  }
+}
+
+grpc_channel_credentials* grpc_ssl_credentials_create(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
+    const verify_peer_options* verify_options, void* reserved) {
+  GRPC_API_TRACE(
+      "grpc_ssl_credentials_create(pem_root_certs=%s, "
+      "pem_key_cert_pair=%p, "
+      "verify_options=%p, "
+      "reserved=%p)",
+      4, (pem_root_certs, pem_key_cert_pair, verify_options, reserved));
+  GPR_ASSERT(reserved == nullptr);
+
+  return grpc_core::New<grpc_ssl_credentials>(pem_root_certs, pem_key_cert_pair,
+                                              verify_options);
+}
+
+//
+// SSL Server Credentials.
+//
+
+struct grpc_ssl_server_credentials_options {
+  grpc_ssl_client_certificate_request_type client_certificate_request;
+  grpc_ssl_server_certificate_config* certificate_config;
+  grpc_ssl_server_certificate_config_fetcher* certificate_config_fetcher;
+};
+
+grpc_ssl_server_credentials::grpc_ssl_server_credentials(
+    const grpc_ssl_server_credentials_options& options)
+    : grpc_server_credentials(GRPC_CHANNEL_CREDENTIALS_TYPE_SSL) {
+  if (options.certificate_config_fetcher != nullptr) {
+    config_.client_certificate_request = options.client_certificate_request;
+    certificate_config_fetcher_ = *options.certificate_config_fetcher;
+  } else {
+    build_config(options.certificate_config->pem_root_certs,
+                 options.certificate_config->pem_key_cert_pairs,
+                 options.certificate_config->num_key_cert_pairs,
+                 options.client_certificate_request);
+  }
+}
+
+grpc_ssl_server_credentials::~grpc_ssl_server_credentials() {
+  grpc_tsi_ssl_pem_key_cert_pairs_destroy(config_.pem_key_cert_pairs,
+                                          config_.num_key_cert_pairs);
+  gpr_free(config_.pem_root_certs);
+}
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_ssl_server_credentials::create_security_connector() {
+  return grpc_ssl_server_security_connector_create(this->Ref());
+}
+
+tsi_ssl_pem_key_cert_pair* grpc_convert_grpc_to_tsi_cert_pairs(
+    const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs) {
+  tsi_ssl_pem_key_cert_pair* tsi_pairs = nullptr;
+  if (num_key_cert_pairs > 0) {
+    GPR_ASSERT(pem_key_cert_pairs != nullptr);
+    tsi_pairs = static_cast<tsi_ssl_pem_key_cert_pair*>(
+        gpr_zalloc(num_key_cert_pairs * sizeof(tsi_ssl_pem_key_cert_pair)));
+  }
+  for (size_t i = 0; i < num_key_cert_pairs; i++) {
+    GPR_ASSERT(pem_key_cert_pairs[i].private_key != nullptr);
+    GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != nullptr);
+    tsi_pairs[i].cert_chain = gpr_strdup(pem_key_cert_pairs[i].cert_chain);
+    tsi_pairs[i].private_key = gpr_strdup(pem_key_cert_pairs[i].private_key);
+  }
+  return tsi_pairs;
+}
+
+void grpc_ssl_server_credentials::build_config(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs,
+    grpc_ssl_client_certificate_request_type client_certificate_request) {
+  config_.client_certificate_request = client_certificate_request;
+  config_.pem_root_certs = gpr_strdup(pem_root_certs);
+  config_.pem_key_cert_pairs = grpc_convert_grpc_to_tsi_cert_pairs(
+      pem_key_cert_pairs, num_key_cert_pairs);
+  config_.num_key_cert_pairs = num_key_cert_pairs;
+}
+
+grpc_ssl_server_certificate_config* grpc_ssl_server_certificate_config_create(
+    const char* pem_root_certs,
+    const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs) {
+  grpc_ssl_server_certificate_config* config =
+      static_cast<grpc_ssl_server_certificate_config*>(
+          gpr_zalloc(sizeof(grpc_ssl_server_certificate_config)));
+  config->pem_root_certs = gpr_strdup(pem_root_certs);
+  if (num_key_cert_pairs > 0) {
+    GPR_ASSERT(pem_key_cert_pairs != nullptr);
+    config->pem_key_cert_pairs = static_cast<grpc_ssl_pem_key_cert_pair*>(
+        gpr_zalloc(num_key_cert_pairs * sizeof(grpc_ssl_pem_key_cert_pair)));
+  }
+  config->num_key_cert_pairs = num_key_cert_pairs;
+  for (size_t i = 0; i < num_key_cert_pairs; i++) {
+    GPR_ASSERT(pem_key_cert_pairs[i].private_key != nullptr);
+    GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != nullptr);
+    config->pem_key_cert_pairs[i].cert_chain =
+        gpr_strdup(pem_key_cert_pairs[i].cert_chain);
+    config->pem_key_cert_pairs[i].private_key =
+        gpr_strdup(pem_key_cert_pairs[i].private_key);
+  }
+  return config;
+}
+
+void grpc_ssl_server_certificate_config_destroy(
+    grpc_ssl_server_certificate_config* config) {
+  if (config == nullptr) return;
+  for (size_t i = 0; i < config->num_key_cert_pairs; i++) {
+    gpr_free((void*)config->pem_key_cert_pairs[i].private_key);
+    gpr_free((void*)config->pem_key_cert_pairs[i].cert_chain);
+  }
+  gpr_free(config->pem_key_cert_pairs);
+  gpr_free(config->pem_root_certs);
+  gpr_free(config);
+}
+
+grpc_ssl_server_credentials_options*
+grpc_ssl_server_credentials_create_options_using_config(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config* config) {
+  grpc_ssl_server_credentials_options* options = nullptr;
+  if (config == nullptr) {
+    gpr_log(GPR_ERROR, "Certificate config must not be NULL.");
+    goto done;
+  }
+  options = static_cast<grpc_ssl_server_credentials_options*>(
+      gpr_zalloc(sizeof(grpc_ssl_server_credentials_options)));
+  options->client_certificate_request = client_certificate_request;
+  options->certificate_config = config;
+done:
+  return options;
+}
+
+grpc_ssl_server_credentials_options*
+grpc_ssl_server_credentials_create_options_using_config_fetcher(
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_certificate_config_callback cb, void* user_data) {
+  if (cb == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid certificate config callback parameter.");
+    return nullptr;
+  }
+
+  grpc_ssl_server_certificate_config_fetcher* fetcher =
+      static_cast<grpc_ssl_server_certificate_config_fetcher*>(
+          gpr_zalloc(sizeof(grpc_ssl_server_certificate_config_fetcher)));
+  fetcher->cb = cb;
+  fetcher->user_data = user_data;
+
+  grpc_ssl_server_credentials_options* options =
+      static_cast<grpc_ssl_server_credentials_options*>(
+          gpr_zalloc(sizeof(grpc_ssl_server_credentials_options)));
+  options->client_certificate_request = client_certificate_request;
+  options->certificate_config_fetcher = fetcher;
+
+  return options;
+}
+
+grpc_server_credentials* grpc_ssl_server_credentials_create(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs, int force_client_auth, void* reserved) {
+  return grpc_ssl_server_credentials_create_ex(
+      pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs,
+      force_client_auth
+          ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+          : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+      reserved);
+}
+
+grpc_server_credentials* grpc_ssl_server_credentials_create_ex(
+    const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs,
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    void* reserved) {
+  GRPC_API_TRACE(
+      "grpc_ssl_server_credentials_create_ex("
+      "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, "
+      "client_certificate_request=%d, reserved=%p)",
+      5,
+      (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs,
+       client_certificate_request, reserved));
+  GPR_ASSERT(reserved == nullptr);
+
+  grpc_ssl_server_certificate_config* cert_config =
+      grpc_ssl_server_certificate_config_create(
+          pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs);
+  grpc_ssl_server_credentials_options* options =
+      grpc_ssl_server_credentials_create_options_using_config(
+          client_certificate_request, cert_config);
+
+  return grpc_ssl_server_credentials_create_with_options(options);
+}
+
+grpc_server_credentials* grpc_ssl_server_credentials_create_with_options(
+    grpc_ssl_server_credentials_options* options) {
+  grpc_server_credentials* retval = nullptr;
+
+  if (options == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid options trying to create SSL server credentials.");
+    goto done;
+  }
+
+  if (options->certificate_config == nullptr &&
+      options->certificate_config_fetcher == nullptr) {
+    gpr_log(GPR_ERROR,
+            "SSL server credentials options must specify either "
+            "certificate config or fetcher.");
+    goto done;
+  } else if (options->certificate_config_fetcher != nullptr &&
+             options->certificate_config_fetcher->cb == nullptr) {
+    gpr_log(GPR_ERROR, "Certificate config fetcher callback must not be NULL.");
+    goto done;
+  }
+
+  retval = grpc_core::New<grpc_ssl_server_credentials>(*options);
+
+done:
+  grpc_ssl_server_credentials_options_destroy(options);
+  return retval;
+}
+
+void grpc_ssl_server_credentials_options_destroy(
+    grpc_ssl_server_credentials_options* o) {
+  if (o == nullptr) return;
+  gpr_free(o->certificate_config_fetcher);
+  grpc_ssl_server_certificate_config_destroy(o->certificate_config);
+  gpr_free(o);
+}
diff --git a/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.h b/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.h
new file mode 100644
index 0000000..e117432
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+#include "src/core/lib/security/security_connector/ssl/ssl_security_connector.h"
+
+class grpc_ssl_credentials : public grpc_channel_credentials {
+ public:
+  grpc_ssl_credentials(const char* pem_root_certs,
+                       grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
+                       const verify_peer_options* verify_options);
+
+  ~grpc_ssl_credentials() override;
+
+  grpc_core::RefCountedPtr<grpc_channel_security_connector>
+  create_security_connector(
+      grpc_core::RefCountedPtr<grpc_call_credentials> call_creds,
+      const char* target, const grpc_channel_args* args,
+      grpc_channel_args** new_args) override;
+
+ private:
+  void build_config(const char* pem_root_certs,
+                    grpc_ssl_pem_key_cert_pair* pem_key_cert_pair,
+                    const verify_peer_options* verify_options);
+
+  grpc_ssl_config config_;
+};
+
+struct grpc_ssl_server_certificate_config {
+  grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs = nullptr;
+  size_t num_key_cert_pairs = 0;
+  char* pem_root_certs = nullptr;
+};
+
+struct grpc_ssl_server_certificate_config_fetcher {
+  grpc_ssl_server_certificate_config_callback cb = nullptr;
+  void* user_data;
+};
+
+class grpc_ssl_server_credentials final : public grpc_server_credentials {
+ public:
+  grpc_ssl_server_credentials(
+      const grpc_ssl_server_credentials_options& options);
+  ~grpc_ssl_server_credentials() override;
+
+  grpc_core::RefCountedPtr<grpc_server_security_connector>
+  create_security_connector() override;
+
+  bool has_cert_config_fetcher() const {
+    return certificate_config_fetcher_.cb != nullptr;
+  }
+
+  grpc_ssl_certificate_config_reload_status FetchCertConfig(
+      grpc_ssl_server_certificate_config** config) {
+    GPR_DEBUG_ASSERT(has_cert_config_fetcher());
+    return certificate_config_fetcher_.cb(certificate_config_fetcher_.user_data,
+                                          config);
+  }
+
+  const grpc_ssl_server_config& config() const { return config_; }
+
+ private:
+  void build_config(
+      const char* pem_root_certs,
+      grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, size_t num_key_cert_pairs,
+      grpc_ssl_client_certificate_request_type client_certificate_request);
+
+  grpc_ssl_server_config config_;
+  grpc_ssl_server_certificate_config_fetcher certificate_config_fetcher_;
+};
+
+tsi_ssl_pem_key_cert_pair* grpc_convert_grpc_to_tsi_cert_pairs(
+    const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs);
+
+void grpc_tsi_ssl_pem_key_cert_pairs_destroy(tsi_ssl_pem_key_cert_pair* kp,
+                                             size_t num_key_cert_pairs);
+
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.cc
new file mode 100644
index 0000000..3ad0cc3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.cc
@@ -0,0 +1,264 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/security_connector/alts/alts_security_connector.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/credentials/alts/alts_credentials.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/transport/transport.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+#include "src/core/tsi/transport_security.h"
+
+namespace {
+
+void alts_set_rpc_protocol_versions(
+    grpc_gcp_rpc_protocol_versions* rpc_versions) {
+  grpc_gcp_rpc_protocol_versions_set_max(rpc_versions,
+                                         GRPC_PROTOCOL_VERSION_MAX_MAJOR,
+                                         GRPC_PROTOCOL_VERSION_MAX_MINOR);
+  grpc_gcp_rpc_protocol_versions_set_min(rpc_versions,
+                                         GRPC_PROTOCOL_VERSION_MIN_MAJOR,
+                                         GRPC_PROTOCOL_VERSION_MIN_MINOR);
+}
+
+void alts_check_peer(tsi_peer peer,
+                     grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                     grpc_closure* on_peer_checked) {
+  *auth_context =
+      grpc_core::internal::grpc_alts_auth_context_from_tsi_peer(&peer);
+  tsi_peer_destruct(&peer);
+  grpc_error* error =
+      *auth_context != nullptr
+          ? GRPC_ERROR_NONE
+          : GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                "Could not get ALTS auth context from TSI peer");
+  GRPC_CLOSURE_SCHED(on_peer_checked, error);
+}
+
+class grpc_alts_channel_security_connector final
+    : public grpc_channel_security_connector {
+ public:
+  grpc_alts_channel_security_connector(
+      grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+      grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+      const char* target_name)
+      : grpc_channel_security_connector(/*url_scheme=*/nullptr,
+                                        std::move(channel_creds),
+                                        std::move(request_metadata_creds)),
+        target_name_(gpr_strdup(target_name)) {
+    grpc_alts_credentials* creds =
+        static_cast<grpc_alts_credentials*>(mutable_channel_creds());
+    alts_set_rpc_protocol_versions(&creds->mutable_options()->rpc_versions);
+  }
+
+  ~grpc_alts_channel_security_connector() override { gpr_free(target_name_); }
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_manager) override {
+    tsi_handshaker* handshaker = nullptr;
+    const grpc_alts_credentials* creds =
+        static_cast<const grpc_alts_credentials*>(channel_creds());
+    GPR_ASSERT(alts_tsi_handshaker_create(creds->options(), target_name_,
+                                          creds->handshaker_service_url(), true,
+                                          interested_parties,
+                                          &handshaker) == TSI_OK);
+    grpc_handshake_manager_add(
+        handshake_manager, grpc_security_handshaker_create(handshaker, this));
+  }
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                  grpc_closure* on_peer_checked) override {
+    alts_check_peer(peer, auth_context, on_peer_checked);
+  }
+
+  int cmp(const grpc_security_connector* other_sc) const override {
+    auto* other =
+        reinterpret_cast<const grpc_alts_channel_security_connector*>(other_sc);
+    int c = channel_security_connector_cmp(other);
+    if (c != 0) return c;
+    return strcmp(target_name_, other->target_name_);
+  }
+
+  bool check_call_host(const char* host, grpc_auth_context* auth_context,
+                       grpc_closure* on_call_host_checked,
+                       grpc_error** error) override {
+    if (host == nullptr || strcmp(host, target_name_) != 0) {
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "ALTS call host does not match target name");
+    }
+    return true;
+  }
+
+  void cancel_check_call_host(grpc_closure* on_call_host_checked,
+                              grpc_error* error) override {
+    GRPC_ERROR_UNREF(error);
+  }
+
+ private:
+  char* target_name_;
+};
+
+class grpc_alts_server_security_connector final
+    : public grpc_server_security_connector {
+ public:
+  grpc_alts_server_security_connector(
+      grpc_core::RefCountedPtr<grpc_server_credentials> server_creds)
+      : grpc_server_security_connector(/*url_scheme=*/nullptr,
+                                       std::move(server_creds)) {
+    grpc_alts_server_credentials* creds =
+        reinterpret_cast<grpc_alts_server_credentials*>(mutable_server_creds());
+    alts_set_rpc_protocol_versions(&creds->mutable_options()->rpc_versions);
+  }
+  ~grpc_alts_server_security_connector() override = default;
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_manager) override {
+    tsi_handshaker* handshaker = nullptr;
+    const grpc_alts_server_credentials* creds =
+        static_cast<const grpc_alts_server_credentials*>(server_creds());
+    GPR_ASSERT(alts_tsi_handshaker_create(
+                   creds->options(), nullptr, creds->handshaker_service_url(),
+                   false, interested_parties, &handshaker) == TSI_OK);
+    grpc_handshake_manager_add(
+        handshake_manager, grpc_security_handshaker_create(handshaker, this));
+  }
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                  grpc_closure* on_peer_checked) override {
+    alts_check_peer(peer, auth_context, on_peer_checked);
+  }
+
+  int cmp(const grpc_security_connector* other) const override {
+    return server_security_connector_cmp(
+        static_cast<const grpc_server_security_connector*>(other));
+  }
+};
+}  // namespace
+
+namespace grpc_core {
+namespace internal {
+grpc_core::RefCountedPtr<grpc_auth_context>
+grpc_alts_auth_context_from_tsi_peer(const tsi_peer* peer) {
+  if (peer == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to grpc_alts_auth_context_from_tsi_peer()");
+    return nullptr;
+  }
+  /* Validate certificate type. */
+  const tsi_peer_property* cert_type_prop =
+      tsi_peer_get_property_by_name(peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY);
+  if (cert_type_prop == nullptr ||
+      strncmp(cert_type_prop->value.data, TSI_ALTS_CERTIFICATE_TYPE,
+              cert_type_prop->value.length) != 0) {
+    gpr_log(GPR_ERROR, "Invalid or missing certificate type property.");
+    return nullptr;
+  }
+  /* Validate RPC protocol versions. */
+  const tsi_peer_property* rpc_versions_prop =
+      tsi_peer_get_property_by_name(peer, TSI_ALTS_RPC_VERSIONS);
+  if (rpc_versions_prop == nullptr) {
+    gpr_log(GPR_ERROR, "Missing rpc protocol versions property.");
+    return nullptr;
+  }
+  grpc_gcp_rpc_protocol_versions local_versions, peer_versions;
+  alts_set_rpc_protocol_versions(&local_versions);
+  grpc_slice slice = grpc_slice_from_copied_buffer(
+      rpc_versions_prop->value.data, rpc_versions_prop->value.length);
+  bool decode_result =
+      grpc_gcp_rpc_protocol_versions_decode(slice, &peer_versions);
+  grpc_slice_unref_internal(slice);
+  if (!decode_result) {
+    gpr_log(GPR_ERROR, "Invalid peer rpc protocol versions.");
+    return nullptr;
+  }
+  /* TODO: Pass highest common rpc protocol version to grpc caller. */
+  bool check_result = grpc_gcp_rpc_protocol_versions_check(
+      &local_versions, &peer_versions, nullptr);
+  if (!check_result) {
+    gpr_log(GPR_ERROR, "Mismatch of local and peer rpc protocol versions.");
+    return nullptr;
+  }
+  /* Create auth context. */
+  auto ctx = grpc_core::MakeRefCounted<grpc_auth_context>(nullptr);
+  grpc_auth_context_add_cstring_property(
+      ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
+      GRPC_ALTS_TRANSPORT_SECURITY_TYPE);
+  size_t i = 0;
+  for (i = 0; i < peer->property_count; i++) {
+    const tsi_peer_property* tsi_prop = &peer->properties[i];
+    /* Add service account to auth context. */
+    if (strcmp(tsi_prop->name, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 0) {
+      grpc_auth_context_add_property(
+          ctx.get(), TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY,
+          tsi_prop->value.data, tsi_prop->value.length);
+      GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name(
+                     ctx.get(), TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 1);
+    }
+  }
+  if (!grpc_auth_context_peer_is_authenticated(ctx.get())) {
+    gpr_log(GPR_ERROR, "Invalid unauthenticated peer.");
+    ctx.reset(DEBUG_LOCATION, "test");
+    return nullptr;
+  }
+  return ctx;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_alts_channel_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const char* target_name) {
+  if (channel_creds == nullptr || target_name == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid arguments to grpc_alts_channel_security_connector_create()");
+    return nullptr;
+  }
+  return grpc_core::MakeRefCounted<grpc_alts_channel_security_connector>(
+      std::move(channel_creds), std::move(request_metadata_creds), target_name);
+}
+
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_alts_server_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_creds) {
+  if (server_creds == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid arguments to grpc_alts_server_security_connector_create()");
+    return nullptr;
+  }
+  return grpc_core::MakeRefCounted<grpc_alts_server_security_connector>(
+      std::move(server_creds));
+}
diff --git a/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.h
new file mode 100644
index 0000000..b96dc36
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_ALTS_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_ALTS_SECURITY_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+
+#define GRPC_ALTS_TRANSPORT_SECURITY_TYPE "alts"
+
+/**
+ * This method creates an ALTS channel security connector.
+ *
+ * - channel_creds: channel credential instance.
+ * - request_metadata_creds: credential object which will be sent with each
+ *   request. This parameter can be nullptr.
+ * - target_name: the name of the endpoint that the channel is connecting to.
+ * - sc: address of ALTS channel security connector instance to be returned from
+ *   the method.
+ *
+ * It returns nullptr on failure.
+ */
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_alts_channel_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const char* target_name);
+
+/**
+ * This method creates an ALTS server security connector.
+ *
+ * - server_creds: server credential instance.
+ * - sc: address of ALTS server security connector instance to be returned from
+ *   the method.
+ *
+ * It returns nullptr on failure.
+ */
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_alts_server_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_creds);
+
+namespace grpc_core {
+namespace internal {
+
+/* Exposed only for testing. */
+grpc_core::RefCountedPtr<grpc_auth_context>
+grpc_alts_auth_context_from_tsi_peer(const tsi_peer* peer);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_ALTS_SECURITY_CONNECTOR_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.cc
new file mode 100644
index 0000000..e3b8aff
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.cc
@@ -0,0 +1,305 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/security_connector/fake/fake_security_connector.h"
+
+#include <stdbool.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/alpn/alpn.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
+#include "src/core/tsi/fake_transport_security.h"
+
+namespace {
+class grpc_fake_channel_security_connector final
+    : public grpc_channel_security_connector {
+ public:
+  grpc_fake_channel_security_connector(
+      grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+      grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+      const char* target, const grpc_channel_args* args)
+      : grpc_channel_security_connector(GRPC_FAKE_SECURITY_URL_SCHEME,
+                                        std::move(channel_creds),
+                                        std::move(request_metadata_creds)),
+        target_(gpr_strdup(target)),
+        expected_targets_(
+            gpr_strdup(grpc_fake_transport_get_expected_targets(args))),
+        is_lb_channel_(grpc_core::FindTargetAuthorityTableInArgs(args) !=
+                       nullptr) {
+    const grpc_arg* target_name_override_arg =
+        grpc_channel_args_find(args, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG);
+    if (target_name_override_arg != nullptr) {
+      target_name_override_ =
+          gpr_strdup(grpc_channel_arg_get_string(target_name_override_arg));
+    } else {
+      target_name_override_ = nullptr;
+    }
+  }
+
+  ~grpc_fake_channel_security_connector() override {
+    gpr_free(target_);
+    gpr_free(expected_targets_);
+    if (target_name_override_ != nullptr) gpr_free(target_name_override_);
+  }
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                  grpc_closure* on_peer_checked) override;
+
+  int cmp(const grpc_security_connector* other_sc) const override {
+    auto* other =
+        reinterpret_cast<const grpc_fake_channel_security_connector*>(other_sc);
+    int c = channel_security_connector_cmp(other);
+    if (c != 0) return c;
+    c = strcmp(target_, other->target_);
+    if (c != 0) return c;
+    if (expected_targets_ == nullptr || other->expected_targets_ == nullptr) {
+      c = GPR_ICMP(expected_targets_, other->expected_targets_);
+    } else {
+      c = strcmp(expected_targets_, other->expected_targets_);
+    }
+    if (c != 0) return c;
+    return GPR_ICMP(is_lb_channel_, other->is_lb_channel_);
+  }
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_mgr) override {
+    grpc_handshake_manager_add(
+        handshake_mgr,
+        grpc_security_handshaker_create(
+            tsi_create_fake_handshaker(/*is_client=*/true), this));
+  }
+
+  bool check_call_host(const char* host, grpc_auth_context* auth_context,
+                       grpc_closure* on_call_host_checked,
+                       grpc_error** error) override {
+    char* authority_hostname = nullptr;
+    char* authority_ignored_port = nullptr;
+    char* target_hostname = nullptr;
+    char* target_ignored_port = nullptr;
+    gpr_split_host_port(host, &authority_hostname, &authority_ignored_port);
+    gpr_split_host_port(target_, &target_hostname, &target_ignored_port);
+    if (target_name_override_ != nullptr) {
+      char* fake_security_target_name_override_hostname = nullptr;
+      char* fake_security_target_name_override_ignored_port = nullptr;
+      gpr_split_host_port(target_name_override_,
+                          &fake_security_target_name_override_hostname,
+                          &fake_security_target_name_override_ignored_port);
+      if (strcmp(authority_hostname,
+                 fake_security_target_name_override_hostname) != 0) {
+        gpr_log(GPR_ERROR,
+                "Authority (host) '%s' != Fake Security Target override '%s'",
+                host, fake_security_target_name_override_hostname);
+        abort();
+      }
+      gpr_free(fake_security_target_name_override_hostname);
+      gpr_free(fake_security_target_name_override_ignored_port);
+    } else if (strcmp(authority_hostname, target_hostname) != 0) {
+      gpr_log(GPR_ERROR, "Authority (host) '%s' != Target '%s'",
+              authority_hostname, target_hostname);
+      abort();
+    }
+    gpr_free(authority_hostname);
+    gpr_free(authority_ignored_port);
+    gpr_free(target_hostname);
+    gpr_free(target_ignored_port);
+    return true;
+  }
+
+  void cancel_check_call_host(grpc_closure* on_call_host_checked,
+                              grpc_error* error) override {
+    GRPC_ERROR_UNREF(error);
+  }
+
+  char* target() const { return target_; }
+  char* expected_targets() const { return expected_targets_; }
+  bool is_lb_channel() const { return is_lb_channel_; }
+  char* target_name_override() const { return target_name_override_; }
+
+ private:
+  bool fake_check_target(const char* target_type, const char* target,
+                         const char* set_str) const {
+    GPR_ASSERT(target_type != nullptr);
+    GPR_ASSERT(target != nullptr);
+    char** set = nullptr;
+    size_t set_size = 0;
+    gpr_string_split(set_str, ",", &set, &set_size);
+    bool found = false;
+    for (size_t i = 0; i < set_size; ++i) {
+      if (set[i] != nullptr && strcmp(target, set[i]) == 0) found = true;
+    }
+    for (size_t i = 0; i < set_size; ++i) {
+      gpr_free(set[i]);
+    }
+    gpr_free(set);
+    return found;
+  }
+
+  void fake_secure_name_check() const {
+    if (expected_targets_ == nullptr) return;
+    char** lbs_and_backends = nullptr;
+    size_t lbs_and_backends_size = 0;
+    bool success = false;
+    gpr_string_split(expected_targets_, ";", &lbs_and_backends,
+                     &lbs_and_backends_size);
+    if (lbs_and_backends_size > 2 || lbs_and_backends_size == 0) {
+      gpr_log(GPR_ERROR, "Invalid expected targets arg value: '%s'",
+              expected_targets_);
+      goto done;
+    }
+    if (is_lb_channel_) {
+      if (lbs_and_backends_size != 2) {
+        gpr_log(GPR_ERROR,
+                "Invalid expected targets arg value: '%s'. Expectations for LB "
+                "channels must be of the form 'be1,be2,be3,...;lb1,lb2,...",
+                expected_targets_);
+        goto done;
+      }
+      if (!fake_check_target("LB", target_, lbs_and_backends[1])) {
+        gpr_log(GPR_ERROR, "LB target '%s' not found in expected set '%s'",
+                target_, lbs_and_backends[1]);
+        goto done;
+      }
+      success = true;
+    } else {
+      if (!fake_check_target("Backend", target_, lbs_and_backends[0])) {
+        gpr_log(GPR_ERROR, "Backend target '%s' not found in expected set '%s'",
+                target_, lbs_and_backends[0]);
+        goto done;
+      }
+      success = true;
+    }
+  done:
+    for (size_t i = 0; i < lbs_and_backends_size; ++i) {
+      gpr_free(lbs_and_backends[i]);
+    }
+    gpr_free(lbs_and_backends);
+    if (!success) abort();
+  }
+
+  char* target_;
+  char* expected_targets_;
+  bool is_lb_channel_;
+  char* target_name_override_;
+};
+
+static void fake_check_peer(
+    grpc_security_connector* sc, tsi_peer peer,
+    grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+    grpc_closure* on_peer_checked) {
+  const char* prop_name;
+  grpc_error* error = GRPC_ERROR_NONE;
+  *auth_context = nullptr;
+  if (peer.property_count != 1) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Fake peers should only have 1 property.");
+    goto end;
+  }
+  prop_name = peer.properties[0].name;
+  if (prop_name == nullptr ||
+      strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) {
+    char* msg;
+    gpr_asprintf(&msg, "Unexpected property in fake peer: %s.",
+                 prop_name == nullptr ? "<EMPTY>" : prop_name);
+    error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    goto end;
+  }
+  if (strncmp(peer.properties[0].value.data, TSI_FAKE_CERTIFICATE_TYPE,
+              peer.properties[0].value.length)) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Invalid value for cert type property.");
+    goto end;
+  }
+  *auth_context = grpc_core::MakeRefCounted<grpc_auth_context>(nullptr);
+  grpc_auth_context_add_cstring_property(
+      auth_context->get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
+      GRPC_FAKE_TRANSPORT_SECURITY_TYPE);
+end:
+  GRPC_CLOSURE_SCHED(on_peer_checked, error);
+  tsi_peer_destruct(&peer);
+}
+
+void grpc_fake_channel_security_connector::check_peer(
+    tsi_peer peer, grpc_endpoint* ep,
+    grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+    grpc_closure* on_peer_checked) {
+  fake_check_peer(this, peer, auth_context, on_peer_checked);
+  fake_secure_name_check();
+}
+
+class grpc_fake_server_security_connector
+    : public grpc_server_security_connector {
+ public:
+  grpc_fake_server_security_connector(
+      grpc_core::RefCountedPtr<grpc_server_credentials> server_creds)
+      : grpc_server_security_connector(GRPC_FAKE_SECURITY_URL_SCHEME,
+                                       std::move(server_creds)) {}
+  ~grpc_fake_server_security_connector() override = default;
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                  grpc_closure* on_peer_checked) override {
+    fake_check_peer(this, peer, auth_context, on_peer_checked);
+  }
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_mgr) override {
+    grpc_handshake_manager_add(
+        handshake_mgr,
+        grpc_security_handshaker_create(
+            tsi_create_fake_handshaker(/*=is_client*/ false), this));
+  }
+
+  int cmp(const grpc_security_connector* other) const override {
+    return server_security_connector_cmp(
+        static_cast<const grpc_server_security_connector*>(other));
+  }
+};
+}  // namespace
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_fake_channel_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const char* target, const grpc_channel_args* args) {
+  return grpc_core::MakeRefCounted<grpc_fake_channel_security_connector>(
+      std::move(channel_creds), std::move(request_metadata_creds), target,
+      args);
+}
+
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_fake_server_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_creds) {
+  return grpc_core::MakeRefCounted<grpc_fake_server_security_connector>(
+      std::move(server_creds));
+}
diff --git a/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.h
new file mode 100644
index 0000000..344a234
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_FAKE_FAKE_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_FAKE_FAKE_SECURITY_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
+
+#define GRPC_FAKE_SECURITY_URL_SCHEME "http+fake_security"
+
+/* Creates a fake connector that emulates real channel security.  */
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_fake_channel_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const char* target, const grpc_channel_args* args);
+
+/* Creates a fake connector that emulates real server security.  */
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_fake_server_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_creds);
+
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_FAKE_FAKE_SECURITY_CONNECTOR_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/load_system_roots.h b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots.h
new file mode 100644
index 0000000..5fdec15
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_H
+
+namespace grpc_core {
+
+// Returns a slice containing roots from the OS trust store
+grpc_slice LoadSystemRootCerts();
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_H */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_fallback.cc b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_fallback.cc
new file mode 100644
index 0000000..73d1245
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_fallback.cc
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+#include "src/core/lib/security/security_connector/load_system_roots.h"
+
+#ifndef GPR_LINUX
+
+namespace grpc_core {
+
+grpc_slice LoadSystemRootCerts() { return grpc_empty_slice(); }
+
+}  // namespace grpc_core
+
+#endif /* GPR_LINUX */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.cc b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.cc
new file mode 100644
index 0000000..924fa8a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.cc
@@ -0,0 +1,165 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+#include "src/core/lib/security/security_connector/load_system_roots_linux.h"
+
+#ifdef GPR_LINUX
+
+#include "src/core/lib/security/security_connector/load_system_roots.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/iomgr/load_file.h"
+
+namespace grpc_core {
+namespace {
+
+const char* kLinuxCertFiles[] = {
+    "/etc/ssl/certs/ca-certificates.crt", "/etc/pki/tls/certs/ca-bundle.crt",
+    "/etc/ssl/ca-bundle.pem", "/etc/pki/tls/cacert.pem",
+    "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"};
+const char* kLinuxCertDirectories[] = {
+    "/etc/ssl/certs", "/system/etc/security/cacerts", "/usr/local/share/certs",
+    "/etc/pki/tls/certs", "/etc/openssl/certs"};
+
+grpc_slice GetSystemRootCerts() {
+  grpc_slice valid_bundle_slice = grpc_empty_slice();
+  size_t num_cert_files_ = GPR_ARRAY_SIZE(kLinuxCertFiles);
+  for (size_t i = 0; i < num_cert_files_; i++) {
+    grpc_error* error =
+        grpc_load_file(kLinuxCertFiles[i], 1, &valid_bundle_slice);
+    if (error == GRPC_ERROR_NONE) {
+      return valid_bundle_slice;
+    }
+  }
+  return grpc_empty_slice();
+}
+
+}  // namespace
+
+void GetAbsoluteFilePath(const char* valid_file_dir,
+                         const char* file_entry_name, char* path_buffer) {
+  if (valid_file_dir != nullptr && file_entry_name != nullptr) {
+    int path_len = snprintf(path_buffer, MAXPATHLEN, "%s/%s", valid_file_dir,
+                            file_entry_name);
+    if (path_len == 0) {
+      gpr_log(GPR_ERROR, "failed to get absolute path for file: %s",
+              file_entry_name);
+    }
+  }
+}
+
+grpc_slice CreateRootCertsBundle(const char* certs_directory) {
+  grpc_slice bundle_slice = grpc_empty_slice();
+  if (certs_directory == nullptr) {
+    return bundle_slice;
+  }
+  DIR* ca_directory = opendir(certs_directory);
+  if (ca_directory == nullptr) {
+    return bundle_slice;
+  }
+  struct FileData {
+    char path[MAXPATHLEN];
+    off_t size;
+  };
+  InlinedVector<FileData, 2> roots_filenames;
+  size_t total_bundle_size = 0;
+  struct dirent* directory_entry;
+  while ((directory_entry = readdir(ca_directory)) != nullptr) {
+    struct stat dir_entry_stat;
+    const char* file_entry_name = directory_entry->d_name;
+    FileData file_data;
+    GetAbsoluteFilePath(certs_directory, file_entry_name, file_data.path);
+    int stat_return = stat(file_data.path, &dir_entry_stat);
+    if (stat_return == -1 || !S_ISREG(dir_entry_stat.st_mode)) {
+      // no subdirectories.
+      if (stat_return == -1) {
+        gpr_log(GPR_ERROR, "failed to get status for file: %s", file_data.path);
+      }
+      continue;
+    }
+    file_data.size = dir_entry_stat.st_size;
+    total_bundle_size += file_data.size;
+    roots_filenames.push_back(file_data);
+  }
+  closedir(ca_directory);
+  char* bundle_string = static_cast<char*>(gpr_zalloc(total_bundle_size + 1));
+  size_t bytes_read = 0;
+  for (size_t i = 0; i < roots_filenames.size(); i++) {
+    int file_descriptor = open(roots_filenames[i].path, O_RDONLY);
+    if (file_descriptor != -1) {
+      // Read file into bundle.
+      size_t cert_file_size = roots_filenames[i].size;
+      int read_ret =
+          read(file_descriptor, bundle_string + bytes_read, cert_file_size);
+      if (read_ret != -1) {
+        bytes_read += read_ret;
+      } else {
+        gpr_log(GPR_ERROR, "failed to read file: %s", roots_filenames[i].path);
+      }
+    }
+  }
+  bundle_slice = grpc_slice_new(bundle_string, bytes_read, gpr_free);
+  return bundle_slice;
+}
+
+grpc_slice LoadSystemRootCerts() {
+  grpc_slice result = grpc_empty_slice();
+  // Prioritize user-specified custom directory if flag is set.
+  char* custom_dir = gpr_getenv("GRPC_SYSTEM_SSL_ROOTS_DIR");
+  if (custom_dir != nullptr) {
+    result = CreateRootCertsBundle(custom_dir);
+    gpr_free(custom_dir);
+  }
+  // If the custom directory is empty/invalid/not specified, fallback to
+  // distribution-specific directory.
+  if (GRPC_SLICE_IS_EMPTY(result)) {
+    result = GetSystemRootCerts();
+  }
+  if (GRPC_SLICE_IS_EMPTY(result)) {
+    for (size_t i = 0; i < GPR_ARRAY_SIZE(kLinuxCertDirectories); i++) {
+      result = CreateRootCertsBundle(kLinuxCertDirectories[i]);
+      if (!GRPC_SLICE_IS_EMPTY(result)) {
+        break;
+      }
+    }
+  }
+  return result;
+}
+
+}  // namespace grpc_core
+
+#endif /* GPR_LINUX */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.h b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.h
new file mode 100644
index 0000000..12617df
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_LINUX_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_LINUX_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX
+
+namespace grpc_core {
+
+// Creates a bundle slice containing the contents of all certificate files in
+// a directory.
+// Returns such slice.
+// Exposed for testing purposes only.
+grpc_slice CreateRootCertsBundle(const char* certs_directory);
+
+// Gets the absolute file path needed to load a certificate file.
+// Populates path_buffer, which must be of size MAXPATHLEN.
+// Exposed for testing purposes only.
+void GetAbsoluteFilePath(const char* valid_file_dir,
+                         const char* file_entry_name, char* path_buffer);
+
+}  // namespace grpc_core
+
+#endif /* GPR_LINUX */
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_LINUX_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.cc
new file mode 100644
index 0000000..7cc482c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.cc
@@ -0,0 +1,253 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/security_connector/local/local_security_connector.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/filters/client_channel/client_channel.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_utils.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/security/credentials/local/local_credentials.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/tsi/local_transport_security.h"
+
+#define GRPC_UDS_URI_PATTERN "unix:"
+#define GRPC_LOCAL_TRANSPORT_SECURITY_TYPE "local"
+
+namespace {
+
+grpc_core::RefCountedPtr<grpc_auth_context> local_auth_context_create() {
+  /* Create auth context. */
+  grpc_core::RefCountedPtr<grpc_auth_context> ctx =
+      grpc_core::MakeRefCounted<grpc_auth_context>(nullptr);
+  grpc_auth_context_add_cstring_property(
+      ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
+      GRPC_LOCAL_TRANSPORT_SECURITY_TYPE);
+  GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name(
+                 ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME) == 1);
+  return ctx;
+}
+
+void local_check_peer(grpc_security_connector* sc, tsi_peer peer,
+                      grpc_endpoint* ep,
+                      grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                      grpc_closure* on_peer_checked,
+                      grpc_local_connect_type type) {
+  int fd = grpc_endpoint_get_fd(ep);
+  grpc_resolved_address resolved_addr;
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  resolved_addr.len = GRPC_MAX_SOCKADDR_SIZE;
+  bool is_endpoint_local = false;
+  if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(resolved_addr.addr),
+                  &resolved_addr.len) == 0) {
+    grpc_resolved_address addr_normalized;
+    grpc_resolved_address* addr =
+        grpc_sockaddr_is_v4mapped(&resolved_addr, &addr_normalized)
+            ? &addr_normalized
+            : &resolved_addr;
+    grpc_sockaddr* sock_addr = reinterpret_cast<grpc_sockaddr*>(&addr->addr);
+    // UDS
+    if (type == UDS && grpc_is_unix_socket(addr)) {
+      is_endpoint_local = true;
+      // IPV4
+    } else if (type == LOCAL_TCP && sock_addr->sa_family == GRPC_AF_INET) {
+      const grpc_sockaddr_in* addr4 =
+          reinterpret_cast<const grpc_sockaddr_in*>(sock_addr);
+      if (grpc_htonl(addr4->sin_addr.s_addr) == INADDR_LOOPBACK) {
+        is_endpoint_local = true;
+      }
+      // IPv6
+    } else if (type == LOCAL_TCP && sock_addr->sa_family == GRPC_AF_INET6) {
+      const grpc_sockaddr_in6* addr6 =
+          reinterpret_cast<const grpc_sockaddr_in6*>(addr);
+      if (memcmp(&addr6->sin6_addr, &in6addr_loopback,
+                 sizeof(in6addr_loopback)) == 0) {
+        is_endpoint_local = true;
+      }
+    }
+  }
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (!is_endpoint_local) {
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Endpoint is neither UDS or TCP loopback address.");
+    GRPC_CLOSURE_SCHED(on_peer_checked, error);
+    return;
+  }
+  /* Create an auth context which is necessary to pass the santiy check in
+   * {client, server}_auth_filter that verifies if the peer's auth context is
+   * obtained during handshakes. The auth context is only checked for its
+   * existence and not actually used.
+   */
+  *auth_context = local_auth_context_create();
+  error = *auth_context != nullptr ? GRPC_ERROR_NONE
+                                   : GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                         "Could not create local auth context");
+  GRPC_CLOSURE_SCHED(on_peer_checked, error);
+}
+
+class grpc_local_channel_security_connector final
+    : public grpc_channel_security_connector {
+ public:
+  grpc_local_channel_security_connector(
+      grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+      grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+      const char* target_name)
+      : grpc_channel_security_connector(nullptr, std::move(channel_creds),
+                                        std::move(request_metadata_creds)),
+        target_name_(gpr_strdup(target_name)) {}
+
+  ~grpc_local_channel_security_connector() override { gpr_free(target_name_); }
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_manager) override {
+    tsi_handshaker* handshaker = nullptr;
+    GPR_ASSERT(local_tsi_handshaker_create(true /* is_client */, &handshaker) ==
+               TSI_OK);
+    grpc_handshake_manager_add(
+        handshake_manager, grpc_security_handshaker_create(handshaker, this));
+  }
+
+  int cmp(const grpc_security_connector* other_sc) const override {
+    auto* other =
+        reinterpret_cast<const grpc_local_channel_security_connector*>(
+            other_sc);
+    int c = channel_security_connector_cmp(other);
+    if (c != 0) return c;
+    return strcmp(target_name_, other->target_name_);
+  }
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                  grpc_closure* on_peer_checked) override {
+    grpc_local_credentials* creds =
+        reinterpret_cast<grpc_local_credentials*>(mutable_channel_creds());
+    local_check_peer(this, peer, ep, auth_context, on_peer_checked,
+                     creds->connect_type());
+  }
+
+  bool check_call_host(const char* host, grpc_auth_context* auth_context,
+                       grpc_closure* on_call_host_checked,
+                       grpc_error** error) override {
+    if (host == nullptr || strcmp(host, target_name_) != 0) {
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "local call host does not match target name");
+    }
+    return true;
+  }
+
+  void cancel_check_call_host(grpc_closure* on_call_host_checked,
+                              grpc_error* error) override {
+    GRPC_ERROR_UNREF(error);
+  }
+
+  const char* target_name() const { return target_name_; }
+
+ private:
+  char* target_name_;
+};
+
+class grpc_local_server_security_connector final
+    : public grpc_server_security_connector {
+ public:
+  grpc_local_server_security_connector(
+      grpc_core::RefCountedPtr<grpc_server_credentials> server_creds)
+      : grpc_server_security_connector(nullptr, std::move(server_creds)) {}
+  ~grpc_local_server_security_connector() override = default;
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_manager) override {
+    tsi_handshaker* handshaker = nullptr;
+    GPR_ASSERT(local_tsi_handshaker_create(false /* is_client */,
+                                           &handshaker) == TSI_OK);
+    grpc_handshake_manager_add(
+        handshake_manager, grpc_security_handshaker_create(handshaker, this));
+  }
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                  grpc_closure* on_peer_checked) override {
+    grpc_local_server_credentials* creds =
+        static_cast<grpc_local_server_credentials*>(mutable_server_creds());
+    local_check_peer(this, peer, ep, auth_context, on_peer_checked,
+                     creds->connect_type());
+  }
+
+  int cmp(const grpc_security_connector* other) const override {
+    return server_security_connector_cmp(
+        static_cast<const grpc_server_security_connector*>(other));
+  }
+};
+}  // namespace
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_local_channel_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const grpc_channel_args* args, const char* target_name) {
+  if (channel_creds == nullptr || target_name == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid arguments to grpc_local_channel_security_connector_create()");
+    return nullptr;
+  }
+  // Perform sanity check on UDS address. For TCP local connection, the check
+  // will be done during check_peer procedure.
+  grpc_local_credentials* creds =
+      static_cast<grpc_local_credentials*>(channel_creds.get());
+  const grpc_arg* server_uri_arg =
+      grpc_channel_args_find(args, GRPC_ARG_SERVER_URI);
+  const char* server_uri_str = grpc_channel_arg_get_string(server_uri_arg);
+  if (creds->connect_type() == UDS &&
+      strncmp(GRPC_UDS_URI_PATTERN, server_uri_str,
+              strlen(GRPC_UDS_URI_PATTERN)) != 0) {
+    gpr_log(GPR_ERROR,
+            "Invalid UDS target name to "
+            "grpc_local_channel_security_connector_create()");
+    return nullptr;
+  }
+  return grpc_core::MakeRefCounted<grpc_local_channel_security_connector>(
+      channel_creds, request_metadata_creds, target_name);
+}
+
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_local_server_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_creds) {
+  if (server_creds == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid arguments to grpc_local_server_security_connector_create()");
+    return nullptr;
+  }
+  return grpc_core::MakeRefCounted<grpc_local_server_security_connector>(
+      std::move(server_creds));
+}
diff --git a/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.h
new file mode 100644
index 0000000..6eee0ca
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOCAL_LOCAL_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOCAL_LOCAL_SECURITY_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/context/security_context.h"
+
+/**
+ * This method creates a local channel security connector.
+ *
+ * - channel_creds: channel credential instance.
+ * - request_metadata_creds: credential object which will be sent with each
+ *   request. This parameter can be nullptr.
+ * - target_name: the name of the endpoint that the channel is connecting to.
+ * - args: channel args passed from the caller.
+ * - sc: address of local channel security connector instance to be returned
+ *   from the method.
+ *
+ * It returns nullptr on failure.
+ */
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_local_channel_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const grpc_channel_args* args, const char* target_name);
+
+/**
+ * This method creates a local server security connector.
+ *
+ * - server_creds: server credential instance.
+ * - sc: address of local server security connector instance to be returned from
+ *   the method.
+ *
+ * It returns nullptr on failure.
+ */
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_local_server_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_creds);
+
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOCAL_LOCAL_SECURITY_CONNECTOR_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/security_connector.cc
new file mode 100644
index 0000000..96a1960
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/security_connector.cc
@@ -0,0 +1,129 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/security_connector/security_connector.h"
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/alpn/alpn.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/load_file.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/security_connector/load_system_roots.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_security_connector_refcount(
+    false, "security_connector_refcount");
+
+grpc_server_security_connector::grpc_server_security_connector(
+    const char* url_scheme,
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_creds)
+    : grpc_security_connector(url_scheme),
+      server_creds_(std::move(server_creds)) {}
+
+grpc_channel_security_connector::grpc_channel_security_connector(
+    const char* url_scheme,
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds)
+    : grpc_security_connector(url_scheme),
+      channel_creds_(std::move(channel_creds)),
+      request_metadata_creds_(std::move(request_metadata_creds)) {}
+grpc_channel_security_connector::~grpc_channel_security_connector() {}
+
+int grpc_security_connector_cmp(const grpc_security_connector* sc,
+                                const grpc_security_connector* other) {
+  if (sc == nullptr || other == nullptr) return GPR_ICMP(sc, other);
+  return sc->cmp(other);
+}
+
+int grpc_channel_security_connector::channel_security_connector_cmp(
+    const grpc_channel_security_connector* other) const {
+  const grpc_channel_security_connector* other_sc =
+      static_cast<const grpc_channel_security_connector*>(other);
+  GPR_ASSERT(channel_creds() != nullptr);
+  GPR_ASSERT(other_sc->channel_creds() != nullptr);
+  int c = GPR_ICMP(channel_creds(), other_sc->channel_creds());
+  if (c != 0) return c;
+  return GPR_ICMP(request_metadata_creds(), other_sc->request_metadata_creds());
+}
+
+int grpc_server_security_connector::server_security_connector_cmp(
+    const grpc_server_security_connector* other) const {
+  const grpc_server_security_connector* other_sc =
+      static_cast<const grpc_server_security_connector*>(other);
+  GPR_ASSERT(server_creds() != nullptr);
+  GPR_ASSERT(other_sc->server_creds() != nullptr);
+  return GPR_ICMP(server_creds(), other_sc->server_creds());
+}
+
+static void connector_arg_destroy(void* p) {
+  static_cast<grpc_security_connector*>(p)->Unref(DEBUG_LOCATION,
+                                                  "connector_arg_destroy");
+}
+
+static void* connector_arg_copy(void* p) {
+  return static_cast<grpc_security_connector*>(p)
+      ->Ref(DEBUG_LOCATION, "connector_arg_copy")
+      .release();
+}
+
+static int connector_cmp(void* a, void* b) {
+  return static_cast<grpc_security_connector*>(a)->cmp(
+      static_cast<grpc_security_connector*>(b));
+}
+
+static const grpc_arg_pointer_vtable connector_arg_vtable = {
+    connector_arg_copy, connector_arg_destroy, connector_cmp};
+
+grpc_arg grpc_security_connector_to_arg(grpc_security_connector* sc) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_ARG_SECURITY_CONNECTOR, sc,
+                                         &connector_arg_vtable);
+}
+
+grpc_security_connector* grpc_security_connector_from_arg(const grpc_arg* arg) {
+  if (strcmp(arg->key, GRPC_ARG_SECURITY_CONNECTOR)) return nullptr;
+  if (arg->type != GRPC_ARG_POINTER) {
+    gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type,
+            GRPC_ARG_SECURITY_CONNECTOR);
+    return nullptr;
+  }
+  return static_cast<grpc_security_connector*>(arg->value.pointer.p);
+}
+
+grpc_security_connector* grpc_security_connector_find_in_args(
+    const grpc_channel_args* args) {
+  size_t i;
+  if (args == nullptr) return nullptr;
+  for (i = 0; i < args->num_args; i++) {
+    grpc_security_connector* sc =
+        grpc_security_connector_from_arg(&args->args[i]);
+    if (sc != nullptr) return sc;
+  }
+  return nullptr;
+}
diff --git a/third_party/grpc/src/core/lib/security/security_connector/security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/security_connector.h
new file mode 100644
index 0000000..74b0ef2
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/security_connector.h
@@ -0,0 +1,174 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include <grpc/grpc_security.h>
+
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/tsi/ssl_transport_security.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_security_connector_refcount;
+
+typedef enum { GRPC_SECURITY_OK = 0, GRPC_SECURITY_ERROR } grpc_security_status;
+
+/* --- security_connector object. ---
+
+    A security connector object represents away to configure the underlying
+    transport security mechanism and check the resulting trusted peer.  */
+
+#define GRPC_ARG_SECURITY_CONNECTOR "grpc.security_connector"
+
+class grpc_security_connector
+    : public grpc_core::RefCounted<grpc_security_connector> {
+ public:
+  explicit grpc_security_connector(const char* url_scheme)
+      : grpc_core::RefCounted<grpc_security_connector>(
+            &grpc_trace_security_connector_refcount),
+        url_scheme_(url_scheme) {}
+  virtual ~grpc_security_connector() = default;
+
+  /* Check the peer. Callee takes ownership of the peer object.
+     When done, sets *auth_context and invokes on_peer_checked. */
+  virtual void check_peer(
+      tsi_peer peer, grpc_endpoint* ep,
+      grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+      grpc_closure* on_peer_checked) GRPC_ABSTRACT;
+
+  /* Compares two security connectors. */
+  virtual int cmp(const grpc_security_connector* other) const GRPC_ABSTRACT;
+
+  const char* url_scheme() const { return url_scheme_; }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ private:
+  const char* url_scheme_;
+};
+
+/* Util to encapsulate the connector in a channel arg. */
+grpc_arg grpc_security_connector_to_arg(grpc_security_connector* sc);
+
+/* Util to get the connector from a channel arg. */
+grpc_security_connector* grpc_security_connector_from_arg(const grpc_arg* arg);
+
+/* Util to find the connector from channel args. */
+grpc_security_connector* grpc_security_connector_find_in_args(
+    const grpc_channel_args* args);
+
+/* --- channel_security_connector object. ---
+
+    A channel security connector object represents a way to configure the
+    underlying transport security mechanism on the client side.  */
+
+class grpc_channel_security_connector : public grpc_security_connector {
+ public:
+  grpc_channel_security_connector(
+      const char* url_scheme,
+      grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+      grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds);
+  ~grpc_channel_security_connector() override;
+
+  /// Checks that the host that will be set for a call is acceptable.
+  /// Returns true if completed synchronously, in which case \a error will
+  /// be set to indicate the result.  Otherwise, \a on_call_host_checked
+  /// will be invoked when complete.
+  virtual bool check_call_host(const char* host,
+                               grpc_auth_context* auth_context,
+                               grpc_closure* on_call_host_checked,
+                               grpc_error** error) GRPC_ABSTRACT;
+  /// Cancels a pending asychronous call to
+  /// grpc_channel_security_connector_check_call_host() with
+  /// \a on_call_host_checked as its callback.
+  virtual void cancel_check_call_host(grpc_closure* on_call_host_checked,
+                                      grpc_error* error) GRPC_ABSTRACT;
+  /// Registers handshakers with \a handshake_mgr.
+  virtual void add_handshakers(grpc_pollset_set* interested_parties,
+                               grpc_handshake_manager* handshake_mgr)
+      GRPC_ABSTRACT;
+
+  const grpc_channel_credentials* channel_creds() const {
+    return channel_creds_.get();
+  }
+  grpc_channel_credentials* mutable_channel_creds() {
+    return channel_creds_.get();
+  }
+  const grpc_call_credentials* request_metadata_creds() const {
+    return request_metadata_creds_.get();
+  }
+  grpc_call_credentials* mutable_request_metadata_creds() {
+    return request_metadata_creds_.get();
+  }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  // Helper methods to be used in subclasses.
+  int channel_security_connector_cmp(
+      const grpc_channel_security_connector* other) const;
+
+ private:
+  grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds_;
+  grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds_;
+};
+
+/* --- server_security_connector object. ---
+
+    A server security connector object represents a way to configure the
+    underlying transport security mechanism on the server side.  */
+
+class grpc_server_security_connector : public grpc_security_connector {
+ public:
+  grpc_server_security_connector(
+      const char* url_scheme,
+      grpc_core::RefCountedPtr<grpc_server_credentials> server_creds);
+  ~grpc_server_security_connector() override = default;
+
+  virtual void add_handshakers(grpc_pollset_set* interested_parties,
+                               grpc_handshake_manager* handshake_mgr)
+      GRPC_ABSTRACT;
+
+  const grpc_server_credentials* server_creds() const {
+    return server_creds_.get();
+  }
+  grpc_server_credentials* mutable_server_creds() {
+    return server_creds_.get();
+  }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  // Helper methods to be used in subclasses.
+  int server_security_connector_cmp(
+      const grpc_server_security_connector* other) const;
+
+ private:
+  grpc_core::RefCountedPtr<grpc_server_credentials> server_creds_;
+};
+
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc
new file mode 100644
index 0000000..7414ab1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc
@@ -0,0 +1,452 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/security_connector/ssl/ssl_security_connector.h"
+
+#include <stdbool.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/alpn/alpn.h"
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
+#include "src/core/lib/security/security_connector/load_system_roots.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/tsi/ssl_transport_security.h"
+#include "src/core/tsi/transport_security.h"
+
+namespace {
+grpc_error* ssl_check_peer(
+    const char* peer_name, const tsi_peer* peer,
+    grpc_core::RefCountedPtr<grpc_auth_context>* auth_context) {
+#if TSI_OPENSSL_ALPN_SUPPORT
+  /* Check the ALPN if ALPN is supported. */
+  const tsi_peer_property* p =
+      tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL);
+  if (p == nullptr) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Cannot check peer: missing selected ALPN property.");
+  }
+  if (!grpc_chttp2_is_alpn_version_supported(p->value.data, p->value.length)) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Cannot check peer: invalid ALPN value.");
+  }
+#endif /* TSI_OPENSSL_ALPN_SUPPORT */
+  /* Check the peer name if specified. */
+  if (peer_name != nullptr && !grpc_ssl_host_matches_name(peer, peer_name)) {
+    char* msg;
+    gpr_asprintf(&msg, "Peer name %s is not in peer certificate", peer_name);
+    grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+    return error;
+  }
+  *auth_context = grpc_ssl_peer_to_auth_context(peer);
+  return GRPC_ERROR_NONE;
+}
+
+class grpc_ssl_channel_security_connector final
+    : public grpc_channel_security_connector {
+ public:
+  grpc_ssl_channel_security_connector(
+      grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+      grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+      const grpc_ssl_config* config, const char* target_name,
+      const char* overridden_target_name)
+      : grpc_channel_security_connector(GRPC_SSL_URL_SCHEME,
+                                        std::move(channel_creds),
+                                        std::move(request_metadata_creds)),
+        overridden_target_name_(overridden_target_name == nullptr
+                                    ? nullptr
+                                    : gpr_strdup(overridden_target_name)),
+        verify_options_(&config->verify_options) {
+    char* port;
+    gpr_split_host_port(target_name, &target_name_, &port);
+    gpr_free(port);
+  }
+
+  ~grpc_ssl_channel_security_connector() override {
+    tsi_ssl_client_handshaker_factory_unref(client_handshaker_factory_);
+    if (target_name_ != nullptr) gpr_free(target_name_);
+    if (overridden_target_name_ != nullptr) gpr_free(overridden_target_name_);
+  }
+
+  grpc_security_status InitializeHandshakerFactory(
+      const grpc_ssl_config* config, const char* pem_root_certs,
+      const tsi_ssl_root_certs_store* root_store,
+      tsi_ssl_session_cache* ssl_session_cache) {
+    bool has_key_cert_pair =
+        config->pem_key_cert_pair != nullptr &&
+        config->pem_key_cert_pair->private_key != nullptr &&
+        config->pem_key_cert_pair->cert_chain != nullptr;
+    tsi_ssl_client_handshaker_options options;
+    memset(&options, 0, sizeof(options));
+    GPR_DEBUG_ASSERT(pem_root_certs != nullptr);
+    options.pem_root_certs = pem_root_certs;
+    options.root_store = root_store;
+    options.alpn_protocols =
+        grpc_fill_alpn_protocol_strings(&options.num_alpn_protocols);
+    if (has_key_cert_pair) {
+      options.pem_key_cert_pair = config->pem_key_cert_pair;
+    }
+    options.cipher_suites = grpc_get_ssl_cipher_suites();
+    options.session_cache = ssl_session_cache;
+    const tsi_result result =
+        tsi_create_ssl_client_handshaker_factory_with_options(
+            &options, &client_handshaker_factory_);
+    gpr_free((void*)options.alpn_protocols);
+    if (result != TSI_OK) {
+      gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
+              tsi_result_to_string(result));
+      return GRPC_SECURITY_ERROR;
+    }
+    return GRPC_SECURITY_OK;
+  }
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_mgr) override {
+    // Instantiate TSI handshaker.
+    tsi_handshaker* tsi_hs = nullptr;
+    tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker(
+        client_handshaker_factory_,
+        overridden_target_name_ != nullptr ? overridden_target_name_
+                                           : target_name_,
+        &tsi_hs);
+    if (result != TSI_OK) {
+      gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
+              tsi_result_to_string(result));
+      return;
+    }
+    // Create handshakers.
+    grpc_handshake_manager_add(handshake_mgr,
+                               grpc_security_handshaker_create(tsi_hs, this));
+  }
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                  grpc_closure* on_peer_checked) override {
+    const char* target_name = overridden_target_name_ != nullptr
+                                  ? overridden_target_name_
+                                  : target_name_;
+    grpc_error* error = ssl_check_peer(target_name, &peer, auth_context);
+    if (error == GRPC_ERROR_NONE &&
+        verify_options_->verify_peer_callback != nullptr) {
+      const tsi_peer_property* p =
+          tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY);
+      if (p == nullptr) {
+        error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "Cannot check peer: missing pem cert property.");
+      } else {
+        char* peer_pem = static_cast<char*>(gpr_malloc(p->value.length + 1));
+        memcpy(peer_pem, p->value.data, p->value.length);
+        peer_pem[p->value.length] = '\0';
+        int callback_status = verify_options_->verify_peer_callback(
+            target_name, peer_pem,
+            verify_options_->verify_peer_callback_userdata);
+        gpr_free(peer_pem);
+        if (callback_status) {
+          char* msg;
+          gpr_asprintf(&msg, "Verify peer callback returned a failure (%d)",
+                       callback_status);
+          error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+          gpr_free(msg);
+        }
+      }
+    }
+    GRPC_CLOSURE_SCHED(on_peer_checked, error);
+    tsi_peer_destruct(&peer);
+  }
+
+  int cmp(const grpc_security_connector* other_sc) const override {
+    auto* other =
+        reinterpret_cast<const grpc_ssl_channel_security_connector*>(other_sc);
+    int c = channel_security_connector_cmp(other);
+    if (c != 0) return c;
+    c = strcmp(target_name_, other->target_name_);
+    if (c != 0) return c;
+    return (overridden_target_name_ == nullptr ||
+            other->overridden_target_name_ == nullptr)
+               ? GPR_ICMP(overridden_target_name_,
+                          other->overridden_target_name_)
+               : strcmp(overridden_target_name_,
+                        other->overridden_target_name_);
+  }
+
+  bool check_call_host(const char* host, grpc_auth_context* auth_context,
+                       grpc_closure* on_call_host_checked,
+                       grpc_error** error) override {
+    grpc_security_status status = GRPC_SECURITY_ERROR;
+    tsi_peer peer = grpc_shallow_peer_from_ssl_auth_context(auth_context);
+    if (grpc_ssl_host_matches_name(&peer, host)) status = GRPC_SECURITY_OK;
+    /* If the target name was overridden, then the original target_name was
+       'checked' transitively during the previous peer check at the end of the
+       handshake. */
+    if (overridden_target_name_ != nullptr && strcmp(host, target_name_) == 0) {
+      status = GRPC_SECURITY_OK;
+    }
+    if (status != GRPC_SECURITY_OK) {
+      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+          "call host does not match SSL server name");
+    }
+    grpc_shallow_peer_destruct(&peer);
+    return true;
+  }
+
+  void cancel_check_call_host(grpc_closure* on_call_host_checked,
+                              grpc_error* error) override {
+    GRPC_ERROR_UNREF(error);
+  }
+
+ private:
+  tsi_ssl_client_handshaker_factory* client_handshaker_factory_;
+  char* target_name_;
+  char* overridden_target_name_;
+  const verify_peer_options* verify_options_;
+};
+
+class grpc_ssl_server_security_connector
+    : public grpc_server_security_connector {
+ public:
+  grpc_ssl_server_security_connector(
+      grpc_core::RefCountedPtr<grpc_server_credentials> server_creds)
+      : grpc_server_security_connector(GRPC_SSL_URL_SCHEME,
+                                       std::move(server_creds)) {}
+
+  ~grpc_ssl_server_security_connector() override {
+    tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_);
+  }
+
+  bool has_cert_config_fetcher() const {
+    return static_cast<const grpc_ssl_server_credentials*>(server_creds())
+        ->has_cert_config_fetcher();
+  }
+
+  const tsi_ssl_server_handshaker_factory* server_handshaker_factory() const {
+    return server_handshaker_factory_;
+  }
+
+  grpc_security_status InitializeHandshakerFactory() {
+    if (has_cert_config_fetcher()) {
+      // Load initial credentials from certificate_config_fetcher:
+      if (!try_fetch_ssl_server_credentials()) {
+        gpr_log(GPR_ERROR,
+                "Failed loading SSL server credentials from fetcher.");
+        return GRPC_SECURITY_ERROR;
+      }
+    } else {
+      auto* server_credentials =
+          static_cast<const grpc_ssl_server_credentials*>(server_creds());
+      size_t num_alpn_protocols = 0;
+      const char** alpn_protocol_strings =
+          grpc_fill_alpn_protocol_strings(&num_alpn_protocols);
+      const tsi_result result = tsi_create_ssl_server_handshaker_factory_ex(
+          server_credentials->config().pem_key_cert_pairs,
+          server_credentials->config().num_key_cert_pairs,
+          server_credentials->config().pem_root_certs,
+          grpc_get_tsi_client_certificate_request_type(
+              server_credentials->config().client_certificate_request),
+          grpc_get_ssl_cipher_suites(), alpn_protocol_strings,
+          static_cast<uint16_t>(num_alpn_protocols),
+          &server_handshaker_factory_);
+      gpr_free((void*)alpn_protocol_strings);
+      if (result != TSI_OK) {
+        gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
+                tsi_result_to_string(result));
+        return GRPC_SECURITY_ERROR;
+      }
+    }
+    return GRPC_SECURITY_OK;
+  }
+
+  void add_handshakers(grpc_pollset_set* interested_parties,
+                       grpc_handshake_manager* handshake_mgr) override {
+    // Instantiate TSI handshaker.
+    try_fetch_ssl_server_credentials();
+    tsi_handshaker* tsi_hs = nullptr;
+    tsi_result result = tsi_ssl_server_handshaker_factory_create_handshaker(
+        server_handshaker_factory_, &tsi_hs);
+    if (result != TSI_OK) {
+      gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
+              tsi_result_to_string(result));
+      return;
+    }
+    // Create handshakers.
+    grpc_handshake_manager_add(handshake_mgr,
+                               grpc_security_handshaker_create(tsi_hs, this));
+  }
+
+  void check_peer(tsi_peer peer, grpc_endpoint* ep,
+                  grpc_core::RefCountedPtr<grpc_auth_context>* auth_context,
+                  grpc_closure* on_peer_checked) override {
+    grpc_error* error = ssl_check_peer(nullptr, &peer, auth_context);
+    tsi_peer_destruct(&peer);
+    GRPC_CLOSURE_SCHED(on_peer_checked, error);
+  }
+
+  int cmp(const grpc_security_connector* other) const override {
+    return server_security_connector_cmp(
+        static_cast<const grpc_server_security_connector*>(other));
+  }
+
+ private:
+  /* Attempts to fetch the server certificate config if a callback is available.
+   * Current certificate config will continue to be used if the callback returns
+   * an error. Returns true if new credentials were sucessfully loaded. */
+  bool try_fetch_ssl_server_credentials() {
+    grpc_ssl_server_certificate_config* certificate_config = nullptr;
+    bool status;
+
+    if (!has_cert_config_fetcher()) return false;
+
+    grpc_ssl_server_credentials* server_creds =
+        static_cast<grpc_ssl_server_credentials*>(this->mutable_server_creds());
+    grpc_ssl_certificate_config_reload_status cb_result =
+        server_creds->FetchCertConfig(&certificate_config);
+    if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED) {
+      gpr_log(GPR_DEBUG, "No change in SSL server credentials.");
+      status = false;
+    } else if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW) {
+      status = try_replace_server_handshaker_factory(certificate_config);
+    } else {
+      // Log error, continue using previously-loaded credentials.
+      gpr_log(GPR_ERROR,
+              "Failed fetching new server credentials, continuing to "
+              "use previously-loaded credentials.");
+      status = false;
+    }
+
+    if (certificate_config != nullptr) {
+      grpc_ssl_server_certificate_config_destroy(certificate_config);
+    }
+    return status;
+  }
+
+  /* Attempts to replace the server_handshaker_factory with a new factory using
+   * the provided grpc_ssl_server_certificate_config. Should new factory
+   * creation fail, the existing factory will not be replaced. Returns true on
+   * success (new factory created). */
+  bool try_replace_server_handshaker_factory(
+      const grpc_ssl_server_certificate_config* config) {
+    if (config == nullptr) {
+      gpr_log(GPR_ERROR,
+              "Server certificate config callback returned invalid (NULL) "
+              "config.");
+      return false;
+    }
+    gpr_log(GPR_DEBUG, "Using new server certificate config (%p).", config);
+
+    size_t num_alpn_protocols = 0;
+    const char** alpn_protocol_strings =
+        grpc_fill_alpn_protocol_strings(&num_alpn_protocols);
+    tsi_ssl_pem_key_cert_pair* cert_pairs = grpc_convert_grpc_to_tsi_cert_pairs(
+        config->pem_key_cert_pairs, config->num_key_cert_pairs);
+    tsi_ssl_server_handshaker_factory* new_handshaker_factory = nullptr;
+    const grpc_ssl_server_credentials* server_creds =
+        static_cast<const grpc_ssl_server_credentials*>(this->server_creds());
+    GPR_DEBUG_ASSERT(config->pem_root_certs != nullptr);
+    tsi_result result = tsi_create_ssl_server_handshaker_factory_ex(
+        cert_pairs, config->num_key_cert_pairs, config->pem_root_certs,
+        grpc_get_tsi_client_certificate_request_type(
+            server_creds->config().client_certificate_request),
+        grpc_get_ssl_cipher_suites(), alpn_protocol_strings,
+        static_cast<uint16_t>(num_alpn_protocols), &new_handshaker_factory);
+    gpr_free(cert_pairs);
+    gpr_free((void*)alpn_protocol_strings);
+
+    if (result != TSI_OK) {
+      gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
+              tsi_result_to_string(result));
+      return false;
+    }
+    set_server_handshaker_factory(new_handshaker_factory);
+    return true;
+  }
+
+  void set_server_handshaker_factory(
+      tsi_ssl_server_handshaker_factory* new_factory) {
+    if (server_handshaker_factory_) {
+      tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_);
+    }
+    server_handshaker_factory_ = new_factory;
+  }
+
+  tsi_ssl_server_handshaker_factory* server_handshaker_factory_ = nullptr;
+};
+}  // namespace
+
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_ssl_channel_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const grpc_ssl_config* config, const char* target_name,
+    const char* overridden_target_name,
+    tsi_ssl_session_cache* ssl_session_cache) {
+  if (config == nullptr || target_name == nullptr) {
+    gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name.");
+    return nullptr;
+  }
+
+  const char* pem_root_certs;
+  const tsi_ssl_root_certs_store* root_store;
+  if (config->pem_root_certs == nullptr) {
+    // Use default root certificates.
+    pem_root_certs = grpc_core::DefaultSslRootStore::GetPemRootCerts();
+    if (pem_root_certs == nullptr) {
+      gpr_log(GPR_ERROR, "Could not get default pem root certs.");
+      return nullptr;
+    }
+    root_store = grpc_core::DefaultSslRootStore::GetRootStore();
+  } else {
+    pem_root_certs = config->pem_root_certs;
+    root_store = nullptr;
+  }
+
+  grpc_core::RefCountedPtr<grpc_ssl_channel_security_connector> c =
+      grpc_core::MakeRefCounted<grpc_ssl_channel_security_connector>(
+          std::move(channel_creds), std::move(request_metadata_creds), config,
+          target_name, overridden_target_name);
+  const grpc_security_status result = c->InitializeHandshakerFactory(
+      config, pem_root_certs, root_store, ssl_session_cache);
+  if (result != GRPC_SECURITY_OK) {
+    return nullptr;
+  }
+  return c;
+}
+
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_ssl_server_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_credentials) {
+  GPR_ASSERT(server_credentials != nullptr);
+  grpc_core::RefCountedPtr<grpc_ssl_server_security_connector> c =
+      grpc_core::MakeRefCounted<grpc_ssl_server_security_connector>(
+          std::move(server_credentials));
+  const grpc_security_status retval = c->InitializeHandshakerFactory();
+  if (retval != GRPC_SECURITY_OK) {
+    return nullptr;
+  }
+  return c;
+}
diff --git a/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.h
new file mode 100644
index 0000000..70e26e3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.h
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_SSL_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_SSL_SECURITY_CONNECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+
+#include "src/core/lib/security/security_connector/security_connector.h"
+
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/tsi/ssl_transport_security.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+typedef struct {
+  tsi_ssl_pem_key_cert_pair* pem_key_cert_pair;
+  char* pem_root_certs;
+  verify_peer_options verify_options;
+} grpc_ssl_config;
+
+/* Creates an SSL channel_security_connector.
+   - request_metadata_creds is the credentials object which metadata
+     will be sent with each request. This parameter can be NULL.
+   - config is the SSL config to be used for the SSL channel establishment.
+   - is_client should be 0 for a server or a non-0 value for a client.
+   - secure_peer_name is the secure peer name that should be checked in
+     grpc_channel_security_connector_check_peer. This parameter may be NULL in
+     which case the peer name will not be checked. Note that if this parameter
+     is not NULL, then, pem_root_certs should not be NULL either.
+   - sc is a pointer on the connector to be created.
+  This function returns GRPC_SECURITY_OK in case of success or a
+  specific error code otherwise.
+*/
+grpc_core::RefCountedPtr<grpc_channel_security_connector>
+grpc_ssl_channel_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds,
+    grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds,
+    const grpc_ssl_config* config, const char* target_name,
+    const char* overridden_target_name,
+    tsi_ssl_session_cache* ssl_session_cache);
+
+/* Config for ssl servers. */
+typedef struct {
+  tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs = nullptr;
+  size_t num_key_cert_pairs = 0;
+  char* pem_root_certs = nullptr;
+  grpc_ssl_client_certificate_request_type client_certificate_request =
+      GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
+} grpc_ssl_server_config;
+
+/* Creates an SSL server_security_connector.
+   - config is the SSL config to be used for the SSL channel establishment.
+   - sc is a pointer on the connector to be created.
+  This function returns GRPC_SECURITY_OK in case of success or a
+  specific error code otherwise.
+*/
+grpc_core::RefCountedPtr<grpc_server_security_connector>
+grpc_ssl_server_security_connector_create(
+    grpc_core::RefCountedPtr<grpc_server_credentials> server_credentials);
+
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_SSL_SECURITY_CONNECTOR_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.cc b/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.cc
new file mode 100644
index 0000000..29030f0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.cc
@@ -0,0 +1,349 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/security_connector/ssl_utils.h"
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/ext/transport/chttp2/alpn/alpn.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/load_file.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/security_connector/load_system_roots.h"
+#include "src/core/tsi/ssl_transport_security.h"
+
+/* -- Constants. -- */
+
+#ifndef INSTALL_PREFIX
+static const char* installed_roots_path = "/usr/share/grpc/roots.pem";
+#else
+static const char* installed_roots_path =
+    INSTALL_PREFIX "/share/grpc/roots.pem";
+#endif
+
+/** Environment variable used as a flag to enable/disable loading system root
+    certificates from the OS trust store. */
+#ifndef GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR
+#define GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR "GRPC_NOT_USE_SYSTEM_SSL_ROOTS"
+#endif
+
+#ifndef TSI_OPENSSL_ALPN_SUPPORT
+#define TSI_OPENSSL_ALPN_SUPPORT 1
+#endif
+
+/* -- Overridden default roots. -- */
+
+static grpc_ssl_roots_override_callback ssl_roots_override_cb = nullptr;
+
+void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb) {
+  ssl_roots_override_cb = cb;
+}
+
+/* -- Cipher suites. -- */
+
+/* Defines the cipher suites that we accept by default. All these cipher suites
+   are compliant with HTTP2. */
+#define GRPC_SSL_CIPHER_SUITES     \
+  "ECDHE-ECDSA-AES128-GCM-SHA256:" \
+  "ECDHE-ECDSA-AES256-GCM-SHA384:" \
+  "ECDHE-RSA-AES128-GCM-SHA256:"   \
+  "ECDHE-RSA-AES256-GCM-SHA384"
+
+static gpr_once cipher_suites_once = GPR_ONCE_INIT;
+static const char* cipher_suites = nullptr;
+
+static void init_cipher_suites(void) {
+  char* overridden = gpr_getenv("GRPC_SSL_CIPHER_SUITES");
+  cipher_suites = overridden != nullptr ? overridden : GRPC_SSL_CIPHER_SUITES;
+}
+
+/* --- Util --- */
+
+const char* grpc_get_ssl_cipher_suites(void) {
+  gpr_once_init(&cipher_suites_once, init_cipher_suites);
+  return cipher_suites;
+}
+
+tsi_client_certificate_request_type
+grpc_get_tsi_client_certificate_request_type(
+    grpc_ssl_client_certificate_request_type grpc_request_type) {
+  switch (grpc_request_type) {
+    case GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE:
+      return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
+
+    case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+      return TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
+
+    case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
+      return TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY;
+
+    case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+      return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
+
+    case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
+      return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY;
+
+    default:
+      return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
+  }
+}
+
+const char** grpc_fill_alpn_protocol_strings(size_t* num_alpn_protocols) {
+  GPR_ASSERT(num_alpn_protocols != nullptr);
+  *num_alpn_protocols = grpc_chttp2_num_alpn_versions();
+  const char** alpn_protocol_strings = static_cast<const char**>(
+      gpr_malloc(sizeof(const char*) * (*num_alpn_protocols)));
+  for (size_t i = 0; i < *num_alpn_protocols; i++) {
+    alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i);
+  }
+  return alpn_protocol_strings;
+}
+
+int grpc_ssl_host_matches_name(const tsi_peer* peer, const char* peer_name) {
+  char* allocated_name = nullptr;
+  int r;
+
+  char* ignored_port;
+  gpr_split_host_port(peer_name, &allocated_name, &ignored_port);
+  gpr_free(ignored_port);
+  peer_name = allocated_name;
+  if (!peer_name) return 0;
+
+  // IPv6 zone-id should not be included in comparisons.
+  char* const zone_id = strchr(allocated_name, '%');
+  if (zone_id != nullptr) *zone_id = '\0';
+
+  r = tsi_ssl_peer_matches_name(peer, peer_name);
+  gpr_free(allocated_name);
+  return r;
+}
+
+grpc_core::RefCountedPtr<grpc_auth_context> grpc_ssl_peer_to_auth_context(
+    const tsi_peer* peer) {
+  size_t i;
+  const char* peer_identity_property_name = nullptr;
+
+  /* The caller has checked the certificate type property. */
+  GPR_ASSERT(peer->property_count >= 1);
+  grpc_core::RefCountedPtr<grpc_auth_context> ctx =
+      grpc_core::MakeRefCounted<grpc_auth_context>(nullptr);
+  grpc_auth_context_add_cstring_property(
+      ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
+      GRPC_SSL_TRANSPORT_SECURITY_TYPE);
+  for (i = 0; i < peer->property_count; i++) {
+    const tsi_peer_property* prop = &peer->properties[i];
+    if (prop->name == nullptr) continue;
+    if (strcmp(prop->name, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) {
+      /* If there is no subject alt name, have the CN as the identity. */
+      if (peer_identity_property_name == nullptr) {
+        peer_identity_property_name = GRPC_X509_CN_PROPERTY_NAME;
+      }
+      grpc_auth_context_add_property(ctx.get(), GRPC_X509_CN_PROPERTY_NAME,
+                                     prop->value.data, prop->value.length);
+    } else if (strcmp(prop->name,
+                      TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) {
+      peer_identity_property_name = GRPC_X509_SAN_PROPERTY_NAME;
+      grpc_auth_context_add_property(ctx.get(), GRPC_X509_SAN_PROPERTY_NAME,
+                                     prop->value.data, prop->value.length);
+    } else if (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0) {
+      grpc_auth_context_add_property(ctx.get(),
+                                     GRPC_X509_PEM_CERT_PROPERTY_NAME,
+                                     prop->value.data, prop->value.length);
+    } else if (strcmp(prop->name, TSI_SSL_SESSION_REUSED_PEER_PROPERTY) == 0) {
+      grpc_auth_context_add_property(ctx.get(),
+                                     GRPC_SSL_SESSION_REUSED_PROPERTY,
+                                     prop->value.data, prop->value.length);
+    }
+  }
+  if (peer_identity_property_name != nullptr) {
+    GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name(
+                   ctx.get(), peer_identity_property_name) == 1);
+  }
+  return ctx;
+}
+
+static void add_shallow_auth_property_to_peer(tsi_peer* peer,
+                                              const grpc_auth_property* prop,
+                                              const char* tsi_prop_name) {
+  tsi_peer_property* tsi_prop = &peer->properties[peer->property_count++];
+  tsi_prop->name = const_cast<char*>(tsi_prop_name);
+  tsi_prop->value.data = prop->value;
+  tsi_prop->value.length = prop->value_length;
+}
+
+tsi_peer grpc_shallow_peer_from_ssl_auth_context(
+    const grpc_auth_context* auth_context) {
+  size_t max_num_props = 0;
+  grpc_auth_property_iterator it;
+  const grpc_auth_property* prop;
+  tsi_peer peer;
+  memset(&peer, 0, sizeof(peer));
+
+  it = grpc_auth_context_property_iterator(auth_context);
+  while (grpc_auth_property_iterator_next(&it) != nullptr) max_num_props++;
+
+  if (max_num_props > 0) {
+    peer.properties = static_cast<tsi_peer_property*>(
+        gpr_malloc(max_num_props * sizeof(tsi_peer_property)));
+    it = grpc_auth_context_property_iterator(auth_context);
+    while ((prop = grpc_auth_property_iterator_next(&it)) != nullptr) {
+      if (strcmp(prop->name, GRPC_X509_SAN_PROPERTY_NAME) == 0) {
+        add_shallow_auth_property_to_peer(
+            &peer, prop, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY);
+      } else if (strcmp(prop->name, GRPC_X509_CN_PROPERTY_NAME) == 0) {
+        add_shallow_auth_property_to_peer(
+            &peer, prop, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY);
+      } else if (strcmp(prop->name, GRPC_X509_PEM_CERT_PROPERTY_NAME) == 0) {
+        add_shallow_auth_property_to_peer(&peer, prop,
+                                          TSI_X509_PEM_CERT_PROPERTY);
+      }
+    }
+  }
+  return peer;
+}
+
+void grpc_shallow_peer_destruct(tsi_peer* peer) {
+  if (peer->properties != nullptr) gpr_free(peer->properties);
+}
+
+/* --- Ssl cache implementation. --- */
+
+grpc_ssl_session_cache* grpc_ssl_session_cache_create_lru(size_t capacity) {
+  tsi_ssl_session_cache* cache = tsi_ssl_session_cache_create_lru(capacity);
+  return reinterpret_cast<grpc_ssl_session_cache*>(cache);
+}
+
+void grpc_ssl_session_cache_destroy(grpc_ssl_session_cache* cache) {
+  tsi_ssl_session_cache* tsi_cache =
+      reinterpret_cast<tsi_ssl_session_cache*>(cache);
+  tsi_ssl_session_cache_unref(tsi_cache);
+}
+
+static void* grpc_ssl_session_cache_arg_copy(void* p) {
+  tsi_ssl_session_cache* tsi_cache =
+      reinterpret_cast<tsi_ssl_session_cache*>(p);
+  // destroy call below will unref the pointer.
+  tsi_ssl_session_cache_ref(tsi_cache);
+  return p;
+}
+
+static void grpc_ssl_session_cache_arg_destroy(void* p) {
+  tsi_ssl_session_cache* tsi_cache =
+      reinterpret_cast<tsi_ssl_session_cache*>(p);
+  tsi_ssl_session_cache_unref(tsi_cache);
+}
+
+static int grpc_ssl_session_cache_arg_cmp(void* p, void* q) {
+  return GPR_ICMP(p, q);
+}
+
+grpc_arg grpc_ssl_session_cache_create_channel_arg(
+    grpc_ssl_session_cache* cache) {
+  static const grpc_arg_pointer_vtable vtable = {
+      grpc_ssl_session_cache_arg_copy,
+      grpc_ssl_session_cache_arg_destroy,
+      grpc_ssl_session_cache_arg_cmp,
+  };
+  return grpc_channel_arg_pointer_create(
+      const_cast<char*>(GRPC_SSL_SESSION_CACHE_ARG), cache, &vtable);
+}
+
+/* --- Default SSL root store implementation. --- */
+
+namespace grpc_core {
+
+tsi_ssl_root_certs_store* DefaultSslRootStore::default_root_store_;
+grpc_slice DefaultSslRootStore::default_pem_root_certs_;
+
+const tsi_ssl_root_certs_store* DefaultSslRootStore::GetRootStore() {
+  InitRootStore();
+  return default_root_store_;
+}
+
+const char* DefaultSslRootStore::GetPemRootCerts() {
+  InitRootStore();
+  return GRPC_SLICE_IS_EMPTY(default_pem_root_certs_)
+             ? nullptr
+             : reinterpret_cast<const char*>
+                   GRPC_SLICE_START_PTR(default_pem_root_certs_);
+}
+
+grpc_slice DefaultSslRootStore::ComputePemRootCerts() {
+  grpc_slice result = grpc_empty_slice();
+  char* not_use_system_roots_env_value =
+      gpr_getenv(GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR);
+  const bool not_use_system_roots = gpr_is_true(not_use_system_roots_env_value);
+  gpr_free(not_use_system_roots_env_value);
+  // First try to load the roots from the environment.
+  char* default_root_certs_path =
+      gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
+  if (default_root_certs_path != nullptr) {
+    GRPC_LOG_IF_ERROR("load_file",
+                      grpc_load_file(default_root_certs_path, 1, &result));
+    gpr_free(default_root_certs_path);
+  }
+  // Try overridden roots if needed.
+  grpc_ssl_roots_override_result ovrd_res = GRPC_SSL_ROOTS_OVERRIDE_FAIL;
+  if (GRPC_SLICE_IS_EMPTY(result) && ssl_roots_override_cb != nullptr) {
+    char* pem_root_certs = nullptr;
+    ovrd_res = ssl_roots_override_cb(&pem_root_certs);
+    if (ovrd_res == GRPC_SSL_ROOTS_OVERRIDE_OK) {
+      GPR_ASSERT(pem_root_certs != nullptr);
+      result = grpc_slice_from_copied_buffer(
+          pem_root_certs,
+          strlen(pem_root_certs) + 1);  // nullptr terminator.
+    }
+    gpr_free(pem_root_certs);
+  }
+  // Try loading roots from OS trust store if flag is enabled.
+  if (GRPC_SLICE_IS_EMPTY(result) && !not_use_system_roots) {
+    result = LoadSystemRootCerts();
+  }
+  // Fallback to roots manually shipped with gRPC.
+  if (GRPC_SLICE_IS_EMPTY(result) &&
+      ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) {
+    GRPC_LOG_IF_ERROR("load_file",
+                      grpc_load_file(installed_roots_path, 1, &result));
+  }
+  return result;
+}
+
+void DefaultSslRootStore::InitRootStore() {
+  static gpr_once once = GPR_ONCE_INIT;
+  gpr_once_init(&once, DefaultSslRootStore::InitRootStoreOnce);
+}
+
+void DefaultSslRootStore::InitRootStoreOnce() {
+  default_pem_root_certs_ = ComputePemRootCerts();
+  if (!GRPC_SLICE_IS_EMPTY(default_pem_root_certs_)) {
+    default_root_store_ =
+        tsi_ssl_root_certs_store_create(reinterpret_cast<const char*>(
+            GRPC_SLICE_START_PTR(default_pem_root_certs_)));
+  }
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.h b/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.h
new file mode 100644
index 0000000..c9cd1a1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_UTILS_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_UTILS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include <grpc/grpc_security.h>
+#include <grpc/slice_buffer.h>
+
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/tsi/ssl_transport_security.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+/* --- Util. --- */
+
+/* --- URL schemes. --- */
+#define GRPC_SSL_URL_SCHEME "https"
+
+/* Return HTTP2-compliant cipher suites that gRPC accepts by default. */
+const char* grpc_get_ssl_cipher_suites(void);
+
+/* Map from grpc_ssl_client_certificate_request_type to
+ * tsi_client_certificate_request_type. */
+tsi_client_certificate_request_type
+grpc_get_tsi_client_certificate_request_type(
+    grpc_ssl_client_certificate_request_type grpc_request_type);
+
+/* Return an array of strings containing alpn protocols. */
+const char** grpc_fill_alpn_protocol_strings(size_t* num_alpn_protocols);
+
+/* Exposed for testing only. */
+grpc_core::RefCountedPtr<grpc_auth_context> grpc_ssl_peer_to_auth_context(
+    const tsi_peer* peer);
+tsi_peer grpc_shallow_peer_from_ssl_auth_context(
+    const grpc_auth_context* auth_context);
+void grpc_shallow_peer_destruct(tsi_peer* peer);
+int grpc_ssl_host_matches_name(const tsi_peer* peer, const char* peer_name);
+
+/* --- Default SSL Root Store. --- */
+namespace grpc_core {
+
+// The class implements default SSL root store.
+class DefaultSslRootStore {
+ public:
+  // Gets the default SSL root store. Returns nullptr if not found.
+  static const tsi_ssl_root_certs_store* GetRootStore();
+
+  // Gets the default PEM root certificate.
+  static const char* GetPemRootCerts();
+
+ protected:
+  // Returns default PEM root certificates in nullptr terminated grpc_slice.
+  // This function is protected instead of private, so that it can be tested.
+  static grpc_slice ComputePemRootCerts();
+
+ private:
+  // Construct me not!
+  DefaultSslRootStore();
+
+  // Initialization of default SSL root store.
+  static void InitRootStore();
+
+  // One-time initialization of default SSL root store.
+  static void InitRootStoreOnce();
+
+  // SSL root store in tsi_ssl_root_certs_store object.
+  static tsi_ssl_root_certs_store* default_root_store_;
+
+  // Default PEM root certificates.
+  static grpc_slice default_pem_root_certs_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_UTILS_H \
+        */
diff --git a/third_party/grpc/src/core/lib/security/transport/auth_filters.h b/third_party/grpc/src/core/lib/security/transport/auth_filters.h
new file mode 100644
index 0000000..af2104c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/auth_filters.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc_security.h>
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_client_auth_filter;
+extern const grpc_channel_filter grpc_server_auth_filter;
+
+void grpc_auth_metadata_context_build(
+    const char* url_scheme, grpc_slice call_host, grpc_slice call_method,
+    grpc_auth_context* auth_context,
+    grpc_auth_metadata_context* auth_md_context);
+
+void grpc_auth_metadata_context_reset(grpc_auth_metadata_context* context);
+
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H */
diff --git a/third_party/grpc/src/core/lib/security/transport/client_auth_filter.cc b/third_party/grpc/src/core/lib/security/transport/client_auth_filter.cc
new file mode 100644
index 0000000..66f86b8
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/client_auth_filter.cc
@@ -0,0 +1,429 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/transport/auth_filters.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_stack.h"
+#include "src/core/lib/gpr/string.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/security_connector/security_connector.h"
+#include "src/core/lib/security/security_connector/ssl_utils.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#define MAX_CREDENTIALS_METADATA_COUNT 4
+
+namespace {
+/* We can have a per-call credentials. */
+struct call_data {
+  call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+      : arena(args.arena),
+        owning_call(args.call_stack),
+        call_combiner(args.call_combiner) {}
+
+  // This method is technically the dtor of this class. However, since
+  // `get_request_metadata_cancel_closure` can run in parallel to
+  // `destroy_call_elem`, we cannot call the dtor in them. Otherwise,
+  // fields will be accessed after calling dtor, and msan correctly complains
+  // that the memory is not initialized.
+  void destroy() {
+    grpc_credentials_mdelem_array_destroy(&md_array);
+    creds.reset();
+    grpc_slice_unref_internal(host);
+    grpc_slice_unref_internal(method);
+    grpc_auth_metadata_context_reset(&auth_md_context);
+  }
+
+  gpr_arena* arena;
+  grpc_call_stack* owning_call;
+  grpc_call_combiner* call_combiner;
+  grpc_core::RefCountedPtr<grpc_call_credentials> creds;
+  grpc_slice host = grpc_empty_slice();
+  grpc_slice method = grpc_empty_slice();
+  /* pollset{_set} bound to this call; if we need to make external
+     network requests, they should be done under a pollset added to this
+     pollset_set so that work can progress when this call wants work to progress
+  */
+  grpc_polling_entity* pollent = nullptr;
+  grpc_credentials_mdelem_array md_array;
+  grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT] = {};
+  grpc_auth_metadata_context auth_md_context =
+      grpc_auth_metadata_context();  // Zero-initialize the C struct.
+  grpc_closure async_result_closure;
+  grpc_closure check_call_host_cancel_closure;
+  grpc_closure get_request_metadata_cancel_closure;
+};
+
+/* We can have a per-channel credentials. */
+struct channel_data {
+  channel_data(grpc_channel_security_connector* security_connector,
+               grpc_auth_context* auth_context)
+      : security_connector(
+            security_connector->Ref(DEBUG_LOCATION, "client_auth_filter")),
+        auth_context(auth_context->Ref(DEBUG_LOCATION, "client_auth_filter")) {}
+  ~channel_data() {
+    security_connector.reset(DEBUG_LOCATION, "client_auth_filter");
+    auth_context.reset(DEBUG_LOCATION, "client_auth_filter");
+  }
+
+  grpc_core::RefCountedPtr<grpc_channel_security_connector> security_connector;
+  grpc_core::RefCountedPtr<grpc_auth_context> auth_context;
+};
+}  // namespace
+
+void grpc_auth_metadata_context_reset(
+    grpc_auth_metadata_context* auth_md_context) {
+  if (auth_md_context->service_url != nullptr) {
+    gpr_free(const_cast<char*>(auth_md_context->service_url));
+    auth_md_context->service_url = nullptr;
+  }
+  if (auth_md_context->method_name != nullptr) {
+    gpr_free(const_cast<char*>(auth_md_context->method_name));
+    auth_md_context->method_name = nullptr;
+  }
+  if (auth_md_context->channel_auth_context != nullptr) {
+    const_cast<grpc_auth_context*>(auth_md_context->channel_auth_context)
+        ->Unref(DEBUG_LOCATION, "grpc_auth_metadata_context");
+    auth_md_context->channel_auth_context = nullptr;
+  }
+}
+
+static void add_error(grpc_error** combined, grpc_error* error) {
+  if (error == GRPC_ERROR_NONE) return;
+  if (*combined == GRPC_ERROR_NONE) {
+    *combined = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Client auth metadata plugin error");
+  }
+  *combined = grpc_error_add_child(*combined, error);
+}
+
+static void on_credentials_metadata(void* arg, grpc_error* input_error) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  grpc_call_element* elem =
+      static_cast<grpc_call_element*>(batch->handler_private.extra_arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_auth_metadata_context_reset(&calld->auth_md_context);
+  grpc_error* error = GRPC_ERROR_REF(input_error);
+  if (error == GRPC_ERROR_NONE) {
+    GPR_ASSERT(calld->md_array.size <= MAX_CREDENTIALS_METADATA_COUNT);
+    GPR_ASSERT(batch->send_initial_metadata);
+    grpc_metadata_batch* mdb =
+        batch->payload->send_initial_metadata.send_initial_metadata;
+    for (size_t i = 0; i < calld->md_array.size; ++i) {
+      add_error(&error, grpc_metadata_batch_add_tail(
+                            mdb, &calld->md_links[i],
+                            GRPC_MDELEM_REF(calld->md_array.md[i])));
+    }
+  }
+  if (error == GRPC_ERROR_NONE) {
+    grpc_call_next_op(elem, batch);
+  } else {
+    error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS,
+                               GRPC_STATUS_UNAVAILABLE);
+    grpc_transport_stream_op_batch_finish_with_failure(batch, error,
+                                                       calld->call_combiner);
+  }
+  GRPC_CALL_STACK_UNREF(calld->owning_call, "get_request_metadata");
+}
+
+void grpc_auth_metadata_context_build(
+    const char* url_scheme, grpc_slice call_host, grpc_slice call_method,
+    grpc_auth_context* auth_context,
+    grpc_auth_metadata_context* auth_md_context) {
+  char* service = grpc_slice_to_c_string(call_method);
+  char* last_slash = strrchr(service, '/');
+  char* method_name = nullptr;
+  char* service_url = nullptr;
+  grpc_auth_metadata_context_reset(auth_md_context);
+  if (last_slash == nullptr) {
+    gpr_log(GPR_ERROR, "No '/' found in fully qualified method name");
+    service[0] = '\0';
+    method_name = gpr_strdup("");
+  } else if (last_slash == service) {
+    method_name = gpr_strdup("");
+  } else {
+    *last_slash = '\0';
+    method_name = gpr_strdup(last_slash + 1);
+  }
+  char* host_and_port = grpc_slice_to_c_string(call_host);
+  if (url_scheme != nullptr && strcmp(url_scheme, GRPC_SSL_URL_SCHEME) == 0) {
+    /* Remove the port if it is 443. */
+    char* port_delimiter = strrchr(host_and_port, ':');
+    if (port_delimiter != nullptr && strcmp(port_delimiter + 1, "443") == 0) {
+      *port_delimiter = '\0';
+    }
+  }
+  gpr_asprintf(&service_url, "%s://%s%s",
+               url_scheme == nullptr ? "" : url_scheme, host_and_port, service);
+  auth_md_context->service_url = service_url;
+  auth_md_context->method_name = method_name;
+  auth_md_context->channel_auth_context =
+      auth_context == nullptr
+          ? nullptr
+          : auth_context->Ref(DEBUG_LOCATION, "grpc_auth_metadata_context")
+                .release();
+  gpr_free(service);
+  gpr_free(host_and_port);
+}
+
+static void cancel_get_request_metadata(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (error != GRPC_ERROR_NONE) {
+    calld->creds->cancel_get_request_metadata(&calld->md_array,
+                                              GRPC_ERROR_REF(error));
+  }
+}
+
+static void send_security_metadata(grpc_call_element* elem,
+                                   grpc_transport_stream_op_batch* batch) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  grpc_client_security_context* ctx =
+      static_cast<grpc_client_security_context*>(
+          batch->payload->context[GRPC_CONTEXT_SECURITY].value);
+  grpc_call_credentials* channel_call_creds =
+      chand->security_connector->mutable_request_metadata_creds();
+  int call_creds_has_md = (ctx != nullptr) && (ctx->creds != nullptr);
+
+  if (channel_call_creds == nullptr && !call_creds_has_md) {
+    /* Skip sending metadata altogether. */
+    grpc_call_next_op(elem, batch);
+    return;
+  }
+
+  if (channel_call_creds != nullptr && call_creds_has_md) {
+    calld->creds = grpc_core::RefCountedPtr<grpc_call_credentials>(
+        grpc_composite_call_credentials_create(channel_call_creds,
+                                               ctx->creds.get(), nullptr));
+    if (calld->creds == nullptr) {
+      grpc_transport_stream_op_batch_finish_with_failure(
+          batch,
+          grpc_error_set_int(
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                  "Incompatible credentials set on channel and call."),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED),
+          calld->call_combiner);
+      return;
+    }
+  } else {
+    calld->creds =
+        call_creds_has_md ? ctx->creds->Ref() : channel_call_creds->Ref();
+  }
+
+  grpc_auth_metadata_context_build(
+      chand->security_connector->url_scheme(), calld->host, calld->method,
+      chand->auth_context.get(), &calld->auth_md_context);
+
+  GPR_ASSERT(calld->pollent != nullptr);
+  GRPC_CALL_STACK_REF(calld->owning_call, "get_request_metadata");
+  GRPC_CLOSURE_INIT(&calld->async_result_closure, on_credentials_metadata,
+                    batch, grpc_schedule_on_exec_ctx);
+  grpc_error* error = GRPC_ERROR_NONE;
+  if (calld->creds->get_request_metadata(
+          calld->pollent, calld->auth_md_context, &calld->md_array,
+          &calld->async_result_closure, &error)) {
+    // Synchronous return; invoke on_credentials_metadata() directly.
+    on_credentials_metadata(batch, error);
+    GRPC_ERROR_UNREF(error);
+  } else {
+    // Async return; register cancellation closure with call combiner.
+    grpc_call_combiner_set_notify_on_cancel(
+        calld->call_combiner,
+        GRPC_CLOSURE_INIT(&calld->get_request_metadata_cancel_closure,
+                          cancel_get_request_metadata, elem,
+                          grpc_schedule_on_exec_ctx));
+  }
+}
+
+static void on_host_checked(void* arg, grpc_error* error) {
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  grpc_call_element* elem =
+      static_cast<grpc_call_element*>(batch->handler_private.extra_arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (error == GRPC_ERROR_NONE) {
+    send_security_metadata(elem, batch);
+  } else {
+    char* error_msg;
+    char* host = grpc_slice_to_c_string(calld->host);
+    gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
+                 host);
+    gpr_free(host);
+    grpc_transport_stream_op_batch_finish_with_failure(
+        batch,
+        grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg),
+                           GRPC_ERROR_INT_GRPC_STATUS,
+                           GRPC_STATUS_UNAUTHENTICATED),
+        calld->call_combiner);
+    gpr_free(error_msg);
+  }
+  GRPC_CALL_STACK_UNREF(calld->owning_call, "check_call_host");
+}
+
+static void cancel_check_call_host(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  if (error != GRPC_ERROR_NONE) {
+    chand->security_connector->cancel_check_call_host(
+        &calld->async_result_closure, GRPC_ERROR_REF(error));
+  }
+}
+
+static void auth_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  GPR_TIMER_SCOPE("auth_start_transport_stream_op_batch", 0);
+
+  /* grab pointers to our data from the call element */
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+
+  if (!batch->cancel_stream) {
+    // TODO(hcaseyal): move this to init_call_elem once issue #15927 is
+    // resolved.
+    GPR_ASSERT(batch->payload->context != nullptr);
+    if (batch->payload->context[GRPC_CONTEXT_SECURITY].value == nullptr) {
+      batch->payload->context[GRPC_CONTEXT_SECURITY].value =
+          grpc_client_security_context_create(calld->arena, /*creds=*/nullptr);
+      batch->payload->context[GRPC_CONTEXT_SECURITY].destroy =
+          grpc_client_security_context_destroy;
+    }
+    grpc_client_security_context* sec_ctx =
+        static_cast<grpc_client_security_context*>(
+            batch->payload->context[GRPC_CONTEXT_SECURITY].value);
+    sec_ctx->auth_context.reset(DEBUG_LOCATION, "client_auth_filter");
+    sec_ctx->auth_context =
+        chand->auth_context->Ref(DEBUG_LOCATION, "client_auth_filter");
+  }
+
+  if (batch->send_initial_metadata) {
+    grpc_metadata_batch* metadata =
+        batch->payload->send_initial_metadata.send_initial_metadata;
+    if (metadata->idx.named.path != nullptr) {
+      calld->method =
+          grpc_slice_ref_internal(GRPC_MDVALUE(metadata->idx.named.path->md));
+    }
+    if (metadata->idx.named.authority != nullptr) {
+      calld->host = grpc_slice_ref_internal(
+          GRPC_MDVALUE(metadata->idx.named.authority->md));
+      batch->handler_private.extra_arg = elem;
+      GRPC_CALL_STACK_REF(calld->owning_call, "check_call_host");
+      GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch,
+                        grpc_schedule_on_exec_ctx);
+      char* call_host = grpc_slice_to_c_string(calld->host);
+      grpc_error* error = GRPC_ERROR_NONE;
+      if (chand->security_connector->check_call_host(
+              call_host, chand->auth_context.get(),
+              &calld->async_result_closure, &error)) {
+        // Synchronous return; invoke on_host_checked() directly.
+        on_host_checked(batch, error);
+        GRPC_ERROR_UNREF(error);
+      } else {
+        // Async return; register cancellation closure with call combiner.
+        grpc_call_combiner_set_notify_on_cancel(
+            calld->call_combiner,
+            GRPC_CLOSURE_INIT(&calld->check_call_host_cancel_closure,
+                              cancel_check_call_host, elem,
+                              grpc_schedule_on_exec_ctx));
+      }
+      gpr_free(call_host);
+      return; /* early exit */
+    }
+  }
+
+  /* pass control down the stack */
+  grpc_call_next_op(elem, batch);
+}
+
+/* Constructor for call_data */
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  new (elem->call_data) call_data(elem, *args);
+  return GRPC_ERROR_NONE;
+}
+
+static void set_pollset_or_pollset_set(grpc_call_element* elem,
+                                       grpc_polling_entity* pollent) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->pollent = pollent;
+}
+
+/* Destructor for call_data */
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->destroy();
+}
+
+/* Constructor for channel_data */
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  /* The first and the last filters tend to be implemented differently to
+     handle the case that there's no 'next' filter to call on the up or down
+     path */
+  GPR_ASSERT(!args->is_last);
+  grpc_security_connector* sc =
+      grpc_security_connector_find_in_args(args->channel_args);
+  if (sc == nullptr) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Security connector missing from client auth filter args");
+  }
+  grpc_auth_context* auth_context =
+      grpc_find_auth_context_in_args(args->channel_args);
+  if (auth_context == nullptr) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Auth context missing from client auth filter args");
+  }
+  new (elem->channel_data) channel_data(
+      static_cast<grpc_channel_security_connector*>(sc), auth_context);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for channel data */
+static void destroy_channel_elem(grpc_channel_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  chand->~channel_data();
+}
+
+const grpc_channel_filter grpc_client_auth_filter = {
+    auth_start_transport_stream_op_batch,
+    grpc_channel_next_op,
+    sizeof(call_data),
+    init_call_elem,
+    set_pollset_or_pollset_set,
+    destroy_call_elem,
+    sizeof(channel_data),
+    init_channel_elem,
+    destroy_channel_elem,
+    grpc_channel_next_get_info,
+    "client-auth"};
diff --git a/third_party/grpc/src/core/lib/security/transport/secure_endpoint.cc b/third_party/grpc/src/core/lib/security/transport/secure_endpoint.cc
new file mode 100644
index 0000000..14fb558
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/secure_endpoint.cc
@@ -0,0 +1,445 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when
+   using that endpoint. Because of various transitive includes in uv.h,
+   including windows.h on Windows, uv.h must be included before other system
+   headers. Therefore, sockaddr.h must always be included first */
+#include <grpc/support/port_platform.h>
+
+#include <new>
+
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/security/transport/tsi_error.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/tsi/transport_security_grpc.h"
+
+#define STAGING_BUFFER_SIZE 8192
+
+static void on_read(void* user_data, grpc_error* error);
+
+namespace {
+struct secure_endpoint {
+  secure_endpoint(const grpc_endpoint_vtable* vtable,
+                  tsi_frame_protector* protector,
+                  tsi_zero_copy_grpc_protector* zero_copy_protector,
+                  grpc_endpoint* transport, grpc_slice* leftover_slices,
+                  size_t leftover_nslices)
+      : wrapped_ep(transport),
+        protector(protector),
+        zero_copy_protector(zero_copy_protector) {
+    base.vtable = vtable;
+    gpr_mu_init(&protector_mu);
+    GRPC_CLOSURE_INIT(&on_read, ::on_read, this, grpc_schedule_on_exec_ctx);
+    grpc_slice_buffer_init(&source_buffer);
+    grpc_slice_buffer_init(&leftover_bytes);
+    for (size_t i = 0; i < leftover_nslices; i++) {
+      grpc_slice_buffer_add(&leftover_bytes,
+                            grpc_slice_ref_internal(leftover_slices[i]));
+    }
+    grpc_slice_buffer_init(&output_buffer);
+    gpr_ref_init(&ref, 1);
+  }
+
+  ~secure_endpoint() {
+    grpc_endpoint_destroy(wrapped_ep);
+    tsi_frame_protector_destroy(protector);
+    tsi_zero_copy_grpc_protector_destroy(zero_copy_protector);
+    grpc_slice_buffer_destroy_internal(&source_buffer);
+    grpc_slice_buffer_destroy_internal(&leftover_bytes);
+    grpc_slice_unref_internal(read_staging_buffer);
+    grpc_slice_unref_internal(write_staging_buffer);
+    grpc_slice_buffer_destroy_internal(&output_buffer);
+    gpr_mu_destroy(&protector_mu);
+  }
+
+  grpc_endpoint base;
+  grpc_endpoint* wrapped_ep;
+  struct tsi_frame_protector* protector;
+  struct tsi_zero_copy_grpc_protector* zero_copy_protector;
+  gpr_mu protector_mu;
+  /* saved upper level callbacks and user_data. */
+  grpc_closure* read_cb = nullptr;
+  grpc_closure* write_cb = nullptr;
+  grpc_closure on_read;
+  grpc_slice_buffer* read_buffer = nullptr;
+  grpc_slice_buffer source_buffer;
+  /* saved handshaker leftover data to unprotect. */
+  grpc_slice_buffer leftover_bytes;
+  /* buffers for read and write */
+  grpc_slice read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE);
+  grpc_slice write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE);
+  grpc_slice_buffer output_buffer;
+
+  gpr_refcount ref;
+};
+}  // namespace
+
+grpc_core::TraceFlag grpc_trace_secure_endpoint(false, "secure_endpoint");
+
+static void destroy(secure_endpoint* ep) { grpc_core::Delete(ep); }
+
+#ifndef NDEBUG
+#define SECURE_ENDPOINT_UNREF(ep, reason) \
+  secure_endpoint_unref((ep), (reason), __FILE__, __LINE__)
+#define SECURE_ENDPOINT_REF(ep, reason) \
+  secure_endpoint_ref((ep), (reason), __FILE__, __LINE__)
+static void secure_endpoint_unref(secure_endpoint* ep, const char* reason,
+                                  const char* file, int line) {
+  if (grpc_trace_secure_endpoint.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "SECENDP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val,
+            val - 1);
+  }
+  if (gpr_unref(&ep->ref)) {
+    destroy(ep);
+  }
+}
+
+static void secure_endpoint_ref(secure_endpoint* ep, const char* reason,
+                                const char* file, int line) {
+  if (grpc_trace_secure_endpoint.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "SECENDP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val,
+            val + 1);
+  }
+  gpr_ref(&ep->ref);
+}
+#else
+#define SECURE_ENDPOINT_UNREF(ep, reason) secure_endpoint_unref((ep))
+#define SECURE_ENDPOINT_REF(ep, reason) secure_endpoint_ref((ep))
+static void secure_endpoint_unref(secure_endpoint* ep) {
+  if (gpr_unref(&ep->ref)) {
+    destroy(ep);
+  }
+}
+
+static void secure_endpoint_ref(secure_endpoint* ep) { gpr_ref(&ep->ref); }
+#endif
+
+static void flush_read_staging_buffer(secure_endpoint* ep, uint8_t** cur,
+                                      uint8_t** end) {
+  grpc_slice_buffer_add(ep->read_buffer, ep->read_staging_buffer);
+  ep->read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE);
+  *cur = GRPC_SLICE_START_PTR(ep->read_staging_buffer);
+  *end = GRPC_SLICE_END_PTR(ep->read_staging_buffer);
+}
+
+static void call_read_cb(secure_endpoint* ep, grpc_error* error) {
+  if (grpc_trace_secure_endpoint.enabled()) {
+    size_t i;
+    for (i = 0; i < ep->read_buffer->count; i++) {
+      char* data = grpc_dump_slice(ep->read_buffer->slices[i],
+                                   GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_INFO, "READ %p: %s", ep, data);
+      gpr_free(data);
+    }
+  }
+  ep->read_buffer = nullptr;
+  GRPC_CLOSURE_SCHED(ep->read_cb, error);
+  SECURE_ENDPOINT_UNREF(ep, "read");
+}
+
+static void on_read(void* user_data, grpc_error* error) {
+  unsigned i;
+  uint8_t keep_looping = 0;
+  tsi_result result = TSI_OK;
+  secure_endpoint* ep = static_cast<secure_endpoint*>(user_data);
+  uint8_t* cur = GRPC_SLICE_START_PTR(ep->read_staging_buffer);
+  uint8_t* end = GRPC_SLICE_END_PTR(ep->read_staging_buffer);
+
+  if (error != GRPC_ERROR_NONE) {
+    grpc_slice_buffer_reset_and_unref_internal(ep->read_buffer);
+    call_read_cb(ep, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+                         "Secure read failed", &error, 1));
+    return;
+  }
+
+  if (ep->zero_copy_protector != nullptr) {
+    // Use zero-copy grpc protector to unprotect.
+    result = tsi_zero_copy_grpc_protector_unprotect(
+        ep->zero_copy_protector, &ep->source_buffer, ep->read_buffer);
+  } else {
+    // Use frame protector to unprotect.
+    /* TODO(yangg) check error, maybe bail out early */
+    for (i = 0; i < ep->source_buffer.count; i++) {
+      grpc_slice encrypted = ep->source_buffer.slices[i];
+      uint8_t* message_bytes = GRPC_SLICE_START_PTR(encrypted);
+      size_t message_size = GRPC_SLICE_LENGTH(encrypted);
+
+      while (message_size > 0 || keep_looping) {
+        size_t unprotected_buffer_size_written = static_cast<size_t>(end - cur);
+        size_t processed_message_size = message_size;
+        gpr_mu_lock(&ep->protector_mu);
+        result = tsi_frame_protector_unprotect(
+            ep->protector, message_bytes, &processed_message_size, cur,
+            &unprotected_buffer_size_written);
+        gpr_mu_unlock(&ep->protector_mu);
+        if (result != TSI_OK) {
+          gpr_log(GPR_ERROR, "Decryption error: %s",
+                  tsi_result_to_string(result));
+          break;
+        }
+        message_bytes += processed_message_size;
+        message_size -= processed_message_size;
+        cur += unprotected_buffer_size_written;
+
+        if (cur == end) {
+          flush_read_staging_buffer(ep, &cur, &end);
+          /* Force to enter the loop again to extract buffered bytes in
+             protector. The bytes could be buffered because of running out of
+             staging_buffer. If this happens at the end of all slices, doing
+             another unprotect avoids leaving data in the protector. */
+          keep_looping = 1;
+        } else if (unprotected_buffer_size_written > 0) {
+          keep_looping = 1;
+        } else {
+          keep_looping = 0;
+        }
+      }
+      if (result != TSI_OK) break;
+    }
+
+    if (cur != GRPC_SLICE_START_PTR(ep->read_staging_buffer)) {
+      grpc_slice_buffer_add(
+          ep->read_buffer,
+          grpc_slice_split_head(
+              &ep->read_staging_buffer,
+              static_cast<size_t>(
+                  cur - GRPC_SLICE_START_PTR(ep->read_staging_buffer))));
+    }
+  }
+
+  /* TODO(yangg) experiment with moving this block after read_cb to see if it
+     helps latency */
+  grpc_slice_buffer_reset_and_unref_internal(&ep->source_buffer);
+
+  if (result != TSI_OK) {
+    grpc_slice_buffer_reset_and_unref_internal(ep->read_buffer);
+    call_read_cb(
+        ep, grpc_set_tsi_error_result(
+                GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unwrap failed"), result));
+    return;
+  }
+
+  call_read_cb(ep, GRPC_ERROR_NONE);
+}
+
+static void endpoint_read(grpc_endpoint* secure_ep, grpc_slice_buffer* slices,
+                          grpc_closure* cb) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  ep->read_cb = cb;
+  ep->read_buffer = slices;
+  grpc_slice_buffer_reset_and_unref_internal(ep->read_buffer);
+
+  SECURE_ENDPOINT_REF(ep, "read");
+  if (ep->leftover_bytes.count) {
+    grpc_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer);
+    GPR_ASSERT(ep->leftover_bytes.count == 0);
+    on_read(ep, GRPC_ERROR_NONE);
+    return;
+  }
+
+  grpc_endpoint_read(ep->wrapped_ep, &ep->source_buffer, &ep->on_read);
+}
+
+static void flush_write_staging_buffer(secure_endpoint* ep, uint8_t** cur,
+                                       uint8_t** end) {
+  grpc_slice_buffer_add(&ep->output_buffer, ep->write_staging_buffer);
+  ep->write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE);
+  *cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer);
+  *end = GRPC_SLICE_END_PTR(ep->write_staging_buffer);
+}
+
+static void endpoint_write(grpc_endpoint* secure_ep, grpc_slice_buffer* slices,
+                           grpc_closure* cb, void* arg) {
+  GPR_TIMER_SCOPE("secure_endpoint.endpoint_write", 0);
+
+  unsigned i;
+  tsi_result result = TSI_OK;
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  uint8_t* cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer);
+  uint8_t* end = GRPC_SLICE_END_PTR(ep->write_staging_buffer);
+
+  grpc_slice_buffer_reset_and_unref_internal(&ep->output_buffer);
+
+  if (grpc_trace_secure_endpoint.enabled()) {
+    for (i = 0; i < slices->count; i++) {
+      char* data =
+          grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      gpr_log(GPR_INFO, "WRITE %p: %s", ep, data);
+      gpr_free(data);
+    }
+  }
+
+  if (ep->zero_copy_protector != nullptr) {
+    // Use zero-copy grpc protector to protect.
+    result = tsi_zero_copy_grpc_protector_protect(ep->zero_copy_protector,
+                                                  slices, &ep->output_buffer);
+  } else {
+    // Use frame protector to protect.
+    for (i = 0; i < slices->count; i++) {
+      grpc_slice plain = slices->slices[i];
+      uint8_t* message_bytes = GRPC_SLICE_START_PTR(plain);
+      size_t message_size = GRPC_SLICE_LENGTH(plain);
+      while (message_size > 0) {
+        size_t protected_buffer_size_to_send = static_cast<size_t>(end - cur);
+        size_t processed_message_size = message_size;
+        gpr_mu_lock(&ep->protector_mu);
+        result = tsi_frame_protector_protect(ep->protector, message_bytes,
+                                             &processed_message_size, cur,
+                                             &protected_buffer_size_to_send);
+        gpr_mu_unlock(&ep->protector_mu);
+        if (result != TSI_OK) {
+          gpr_log(GPR_ERROR, "Encryption error: %s",
+                  tsi_result_to_string(result));
+          break;
+        }
+        message_bytes += processed_message_size;
+        message_size -= processed_message_size;
+        cur += protected_buffer_size_to_send;
+
+        if (cur == end) {
+          flush_write_staging_buffer(ep, &cur, &end);
+        }
+      }
+      if (result != TSI_OK) break;
+    }
+    if (result == TSI_OK) {
+      size_t still_pending_size;
+      do {
+        size_t protected_buffer_size_to_send = static_cast<size_t>(end - cur);
+        gpr_mu_lock(&ep->protector_mu);
+        result = tsi_frame_protector_protect_flush(
+            ep->protector, cur, &protected_buffer_size_to_send,
+            &still_pending_size);
+        gpr_mu_unlock(&ep->protector_mu);
+        if (result != TSI_OK) break;
+        cur += protected_buffer_size_to_send;
+        if (cur == end) {
+          flush_write_staging_buffer(ep, &cur, &end);
+        }
+      } while (still_pending_size > 0);
+      if (cur != GRPC_SLICE_START_PTR(ep->write_staging_buffer)) {
+        grpc_slice_buffer_add(
+            &ep->output_buffer,
+            grpc_slice_split_head(
+                &ep->write_staging_buffer,
+                static_cast<size_t>(
+                    cur - GRPC_SLICE_START_PTR(ep->write_staging_buffer))));
+      }
+    }
+  }
+
+  if (result != TSI_OK) {
+    /* TODO(yangg) do different things according to the error type? */
+    grpc_slice_buffer_reset_and_unref_internal(&ep->output_buffer);
+    GRPC_CLOSURE_SCHED(
+        cb, grpc_set_tsi_error_result(
+                GRPC_ERROR_CREATE_FROM_STATIC_STRING("Wrap failed"), result));
+    return;
+  }
+
+  grpc_endpoint_write(ep->wrapped_ep, &ep->output_buffer, cb, arg);
+}
+
+static void endpoint_shutdown(grpc_endpoint* secure_ep, grpc_error* why) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  grpc_endpoint_shutdown(ep->wrapped_ep, why);
+}
+
+static void endpoint_destroy(grpc_endpoint* secure_ep) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  SECURE_ENDPOINT_UNREF(ep, "destroy");
+}
+
+static void endpoint_add_to_pollset(grpc_endpoint* secure_ep,
+                                    grpc_pollset* pollset) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  grpc_endpoint_add_to_pollset(ep->wrapped_ep, pollset);
+}
+
+static void endpoint_add_to_pollset_set(grpc_endpoint* secure_ep,
+                                        grpc_pollset_set* pollset_set) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  grpc_endpoint_add_to_pollset_set(ep->wrapped_ep, pollset_set);
+}
+
+static void endpoint_delete_from_pollset_set(grpc_endpoint* secure_ep,
+                                             grpc_pollset_set* pollset_set) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  grpc_endpoint_delete_from_pollset_set(ep->wrapped_ep, pollset_set);
+}
+
+static char* endpoint_get_peer(grpc_endpoint* secure_ep) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  return grpc_endpoint_get_peer(ep->wrapped_ep);
+}
+
+static int endpoint_get_fd(grpc_endpoint* secure_ep) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  return grpc_endpoint_get_fd(ep->wrapped_ep);
+}
+
+static grpc_resource_user* endpoint_get_resource_user(
+    grpc_endpoint* secure_ep) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  return grpc_endpoint_get_resource_user(ep->wrapped_ep);
+}
+
+static bool endpoint_can_track_err(grpc_endpoint* secure_ep) {
+  secure_endpoint* ep = reinterpret_cast<secure_endpoint*>(secure_ep);
+  return grpc_endpoint_can_track_err(ep->wrapped_ep);
+}
+
+static const grpc_endpoint_vtable vtable = {endpoint_read,
+                                            endpoint_write,
+                                            endpoint_add_to_pollset,
+                                            endpoint_add_to_pollset_set,
+                                            endpoint_delete_from_pollset_set,
+                                            endpoint_shutdown,
+                                            endpoint_destroy,
+                                            endpoint_get_resource_user,
+                                            endpoint_get_peer,
+                                            endpoint_get_fd,
+                                            endpoint_can_track_err};
+
+grpc_endpoint* grpc_secure_endpoint_create(
+    struct tsi_frame_protector* protector,
+    struct tsi_zero_copy_grpc_protector* zero_copy_protector,
+    grpc_endpoint* transport, grpc_slice* leftover_slices,
+    size_t leftover_nslices) {
+  secure_endpoint* ep = grpc_core::New<secure_endpoint>(
+      &vtable, protector, zero_copy_protector, transport, leftover_slices,
+      leftover_nslices);
+  return &ep->base;
+}
diff --git a/third_party/grpc/src/core/lib/security/transport/secure_endpoint.h b/third_party/grpc/src/core/lib/security/transport/secure_endpoint.h
new file mode 100644
index 0000000..e7e3351
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/secure_endpoint.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/lib/iomgr/endpoint.h"
+
+struct tsi_frame_protector;
+struct tsi_zero_copy_grpc_protector;
+
+extern grpc_core::TraceFlag grpc_trace_secure_endpoint;
+
+/* Takes ownership of protector, zero_copy_protector, and to_wrap, and refs
+ * leftover_slices. If zero_copy_protector is not NULL, protector will never be
+ * used. */
+grpc_endpoint* grpc_secure_endpoint_create(
+    struct tsi_frame_protector* protector,
+    struct tsi_zero_copy_grpc_protector* zero_copy_protector,
+    grpc_endpoint* to_wrap, grpc_slice* leftover_slices,
+    size_t leftover_nslices);
+
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H */
diff --git a/third_party/grpc/src/core/lib/security/transport/security_handshaker.cc b/third_party/grpc/src/core/lib/security/transport/security_handshaker.cc
new file mode 100644
index 0000000..01831da
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/security_handshaker.cc
@@ -0,0 +1,546 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/transport/security_handshaker.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/slice_buffer.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"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/security/transport/tsi_error.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/transport_security_grpc.h"
+
+#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256
+
+namespace {
+struct security_handshaker {
+  security_handshaker(tsi_handshaker* handshaker,
+                      grpc_security_connector* connector);
+  ~security_handshaker() {
+    gpr_mu_destroy(&mu);
+    tsi_handshaker_destroy(handshaker);
+    tsi_handshaker_result_destroy(handshaker_result);
+    if (endpoint_to_destroy != nullptr) {
+      grpc_endpoint_destroy(endpoint_to_destroy);
+    }
+    if (read_buffer_to_destroy != nullptr) {
+      grpc_slice_buffer_destroy_internal(read_buffer_to_destroy);
+      gpr_free(read_buffer_to_destroy);
+    }
+    gpr_free(handshake_buffer);
+    grpc_slice_buffer_destroy_internal(&outgoing);
+    auth_context.reset(DEBUG_LOCATION, "handshake");
+    connector.reset(DEBUG_LOCATION, "handshake");
+  }
+
+  void Ref() { refs.Ref(); }
+  void Unref() {
+    if (refs.Unref()) {
+      grpc_core::Delete(this);
+    }
+  }
+
+  grpc_handshaker base;
+
+  // State set at creation time.
+  tsi_handshaker* handshaker;
+  grpc_core::RefCountedPtr<grpc_security_connector> connector;
+
+  gpr_mu mu;
+  grpc_core::RefCount refs;
+
+  bool shutdown = false;
+  // Endpoint and read buffer to destroy after a shutdown.
+  grpc_endpoint* endpoint_to_destroy = nullptr;
+  grpc_slice_buffer* read_buffer_to_destroy = nullptr;
+
+  // State saved while performing the handshake.
+  grpc_handshaker_args* args = nullptr;
+  grpc_closure* on_handshake_done = nullptr;
+
+  size_t handshake_buffer_size;
+  unsigned char* handshake_buffer;
+  grpc_slice_buffer outgoing;
+  grpc_closure on_handshake_data_sent_to_peer;
+  grpc_closure on_handshake_data_received_from_peer;
+  grpc_closure on_peer_checked;
+  grpc_core::RefCountedPtr<grpc_auth_context> auth_context;
+  tsi_handshaker_result* handshaker_result = nullptr;
+};
+}  // namespace
+
+static size_t move_read_buffer_into_handshake_buffer(security_handshaker* h) {
+  size_t bytes_in_read_buffer = h->args->read_buffer->length;
+  if (h->handshake_buffer_size < bytes_in_read_buffer) {
+    h->handshake_buffer = static_cast<uint8_t*>(
+        gpr_realloc(h->handshake_buffer, bytes_in_read_buffer));
+    h->handshake_buffer_size = bytes_in_read_buffer;
+  }
+  size_t offset = 0;
+  while (h->args->read_buffer->count > 0) {
+    grpc_slice next_slice = grpc_slice_buffer_take_first(h->args->read_buffer);
+    memcpy(h->handshake_buffer + offset, GRPC_SLICE_START_PTR(next_slice),
+           GRPC_SLICE_LENGTH(next_slice));
+    offset += GRPC_SLICE_LENGTH(next_slice);
+    grpc_slice_unref_internal(next_slice);
+  }
+  return bytes_in_read_buffer;
+}
+
+// Set args fields to NULL, saving the endpoint and read buffer for
+// later destruction.
+static void cleanup_args_for_failure_locked(security_handshaker* h) {
+  h->endpoint_to_destroy = h->args->endpoint;
+  h->args->endpoint = nullptr;
+  h->read_buffer_to_destroy = h->args->read_buffer;
+  h->args->read_buffer = nullptr;
+  grpc_channel_args_destroy(h->args->args);
+  h->args->args = nullptr;
+}
+
+// If the handshake failed or we're shutting down, clean up and invoke the
+// callback with the error.
+static void security_handshake_failed_locked(security_handshaker* h,
+                                             grpc_error* error) {
+  if (error == GRPC_ERROR_NONE) {
+    // If we were shut down after the handshake succeeded but before an
+    // endpoint callback was invoked, we need to generate our own error.
+    error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown");
+  }
+  const char* msg = grpc_error_string(error);
+  gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg);
+
+  if (!h->shutdown) {
+    // TODO(ctiller): It is currently necessary to shutdown endpoints
+    // before destroying them, even if we know that there are no
+    // pending read/write callbacks.  This should be fixed, at which
+    // point this can be removed.
+    grpc_endpoint_shutdown(h->args->endpoint, GRPC_ERROR_REF(error));
+    // Not shutting down, so the write failed.  Clean up before
+    // invoking the callback.
+    cleanup_args_for_failure_locked(h);
+    // Set shutdown to true so that subsequent calls to
+    // security_handshaker_shutdown() do nothing.
+    h->shutdown = true;
+  }
+  // Invoke callback.
+  GRPC_CLOSURE_SCHED(h->on_handshake_done, error);
+}
+
+static void on_peer_checked_inner(security_handshaker* h, grpc_error* error) {
+  if (error != GRPC_ERROR_NONE || h->shutdown) {
+    security_handshake_failed_locked(h, GRPC_ERROR_REF(error));
+    return;
+  }
+  // Create zero-copy frame protector, if implemented.
+  tsi_zero_copy_grpc_protector* zero_copy_protector = nullptr;
+  tsi_result result = tsi_handshaker_result_create_zero_copy_grpc_protector(
+      h->handshaker_result, nullptr, &zero_copy_protector);
+  if (result != TSI_OK && result != TSI_UNIMPLEMENTED) {
+    error = grpc_set_tsi_error_result(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+            "Zero-copy frame protector creation failed"),
+        result);
+    security_handshake_failed_locked(h, error);
+    return;
+  }
+  // Create frame protector if zero-copy frame protector is NULL.
+  tsi_frame_protector* protector = nullptr;
+  if (zero_copy_protector == nullptr) {
+    result = tsi_handshaker_result_create_frame_protector(h->handshaker_result,
+                                                          nullptr, &protector);
+    if (result != TSI_OK) {
+      error = grpc_set_tsi_error_result(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                            "Frame protector creation failed"),
+                                        result);
+      security_handshake_failed_locked(h, error);
+      return;
+    }
+  }
+  // Get unused bytes.
+  const unsigned char* unused_bytes = nullptr;
+  size_t unused_bytes_size = 0;
+  result = tsi_handshaker_result_get_unused_bytes(
+      h->handshaker_result, &unused_bytes, &unused_bytes_size);
+  // Create secure endpoint.
+  if (unused_bytes_size > 0) {
+    grpc_slice slice =
+        grpc_slice_from_copied_buffer((char*)unused_bytes, unused_bytes_size);
+    h->args->endpoint = grpc_secure_endpoint_create(
+        protector, zero_copy_protector, h->args->endpoint, &slice, 1);
+    grpc_slice_unref_internal(slice);
+  } else {
+    h->args->endpoint = grpc_secure_endpoint_create(
+        protector, zero_copy_protector, h->args->endpoint, nullptr, 0);
+  }
+  tsi_handshaker_result_destroy(h->handshaker_result);
+  h->handshaker_result = nullptr;
+  // Add auth context to channel args.
+  grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context.get());
+  grpc_channel_args* tmp_args = h->args->args;
+  h->args->args =
+      grpc_channel_args_copy_and_add(tmp_args, &auth_context_arg, 1);
+  grpc_channel_args_destroy(tmp_args);
+  // Invoke callback.
+  GRPC_CLOSURE_SCHED(h->on_handshake_done, GRPC_ERROR_NONE);
+  // Set shutdown to true so that subsequent calls to
+  // security_handshaker_shutdown() do nothing.
+  h->shutdown = true;
+}
+
+static void on_peer_checked(void* arg, grpc_error* error) {
+  security_handshaker* h = static_cast<security_handshaker*>(arg);
+  gpr_mu_lock(&h->mu);
+  on_peer_checked_inner(h, error);
+  gpr_mu_unlock(&h->mu);
+  h->Unref();
+}
+
+static grpc_error* check_peer_locked(security_handshaker* h) {
+  tsi_peer peer;
+  tsi_result result =
+      tsi_handshaker_result_extract_peer(h->handshaker_result, &peer);
+  if (result != TSI_OK) {
+    return grpc_set_tsi_error_result(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Peer extraction failed"), result);
+  }
+  h->connector->check_peer(peer, h->args->endpoint, &h->auth_context,
+                           &h->on_peer_checked);
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* on_handshake_next_done_locked(
+    security_handshaker* h, tsi_result result,
+    const unsigned char* bytes_to_send, size_t bytes_to_send_size,
+    tsi_handshaker_result* handshaker_result) {
+  grpc_error* error = GRPC_ERROR_NONE;
+  // Handshaker was shutdown.
+  if (h->shutdown) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown");
+  }
+  // Read more if we need to.
+  if (result == TSI_INCOMPLETE_DATA) {
+    GPR_ASSERT(bytes_to_send_size == 0);
+    grpc_endpoint_read(h->args->endpoint, h->args->read_buffer,
+                       &h->on_handshake_data_received_from_peer);
+    return error;
+  }
+  if (result != TSI_OK) {
+    return grpc_set_tsi_error_result(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake failed"), result);
+  }
+  // Update handshaker result.
+  if (handshaker_result != nullptr) {
+    GPR_ASSERT(h->handshaker_result == nullptr);
+    h->handshaker_result = handshaker_result;
+  }
+  if (bytes_to_send_size > 0) {
+    // Send data to peer, if needed.
+    grpc_slice to_send = grpc_slice_from_copied_buffer(
+        reinterpret_cast<const char*>(bytes_to_send), bytes_to_send_size);
+    grpc_slice_buffer_reset_and_unref_internal(&h->outgoing);
+    grpc_slice_buffer_add(&h->outgoing, to_send);
+    grpc_endpoint_write(h->args->endpoint, &h->outgoing,
+                        &h->on_handshake_data_sent_to_peer, nullptr);
+  } else if (handshaker_result == nullptr) {
+    // There is nothing to send, but need to read from peer.
+    grpc_endpoint_read(h->args->endpoint, h->args->read_buffer,
+                       &h->on_handshake_data_received_from_peer);
+  } else {
+    // Handshake has finished, check peer and so on.
+    error = check_peer_locked(h);
+  }
+  return error;
+}
+
+static void on_handshake_next_done_grpc_wrapper(
+    tsi_result result, void* user_data, const unsigned char* bytes_to_send,
+    size_t bytes_to_send_size, tsi_handshaker_result* handshaker_result) {
+  security_handshaker* h = static_cast<security_handshaker*>(user_data);
+  gpr_mu_lock(&h->mu);
+  grpc_error* error = on_handshake_next_done_locked(
+      h, result, bytes_to_send, bytes_to_send_size, handshaker_result);
+  if (error != GRPC_ERROR_NONE) {
+    security_handshake_failed_locked(h, error);
+    gpr_mu_unlock(&h->mu);
+    h->Unref();
+  } else {
+    gpr_mu_unlock(&h->mu);
+  }
+}
+
+static grpc_error* do_handshaker_next_locked(
+    security_handshaker* h, const unsigned char* bytes_received,
+    size_t bytes_received_size) {
+  // Invoke TSI handshaker.
+  const unsigned char* bytes_to_send = nullptr;
+  size_t bytes_to_send_size = 0;
+  tsi_handshaker_result* handshaker_result = nullptr;
+  tsi_result result = tsi_handshaker_next(
+      h->handshaker, bytes_received, bytes_received_size, &bytes_to_send,
+      &bytes_to_send_size, &handshaker_result,
+      &on_handshake_next_done_grpc_wrapper, h);
+  if (result == TSI_ASYNC) {
+    // Handshaker operating asynchronously. Nothing else to do here;
+    // callback will be invoked in a TSI thread.
+    return GRPC_ERROR_NONE;
+  }
+  // Handshaker returned synchronously. Invoke callback directly in
+  // this thread with our existing exec_ctx.
+  return on_handshake_next_done_locked(h, result, bytes_to_send,
+                                       bytes_to_send_size, handshaker_result);
+}
+
+static void on_handshake_data_received_from_peer(void* arg, grpc_error* error) {
+  security_handshaker* h = static_cast<security_handshaker*>(arg);
+  gpr_mu_lock(&h->mu);
+  if (error != GRPC_ERROR_NONE || h->shutdown) {
+    security_handshake_failed_locked(
+        h, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+               "Handshake read failed", &error, 1));
+    gpr_mu_unlock(&h->mu);
+    h->Unref();
+    return;
+  }
+  // Copy all slices received.
+  size_t bytes_received_size = move_read_buffer_into_handshake_buffer(h);
+  // Call TSI handshaker.
+  error =
+      do_handshaker_next_locked(h, h->handshake_buffer, bytes_received_size);
+
+  if (error != GRPC_ERROR_NONE) {
+    security_handshake_failed_locked(h, error);
+    gpr_mu_unlock(&h->mu);
+    h->Unref();
+  } else {
+    gpr_mu_unlock(&h->mu);
+  }
+}
+
+static void on_handshake_data_sent_to_peer(void* arg, grpc_error* error) {
+  security_handshaker* h = static_cast<security_handshaker*>(arg);
+  gpr_mu_lock(&h->mu);
+  if (error != GRPC_ERROR_NONE || h->shutdown) {
+    security_handshake_failed_locked(
+        h, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+               "Handshake write failed", &error, 1));
+    gpr_mu_unlock(&h->mu);
+    h->Unref();
+    return;
+  }
+  // We may be done.
+  if (h->handshaker_result == nullptr) {
+    grpc_endpoint_read(h->args->endpoint, h->args->read_buffer,
+                       &h->on_handshake_data_received_from_peer);
+  } else {
+    error = check_peer_locked(h);
+    if (error != GRPC_ERROR_NONE) {
+      security_handshake_failed_locked(h, error);
+      gpr_mu_unlock(&h->mu);
+      h->Unref();
+      return;
+    }
+  }
+  gpr_mu_unlock(&h->mu);
+}
+
+//
+// public handshaker API
+//
+
+static void security_handshaker_destroy(grpc_handshaker* handshaker) {
+  security_handshaker* h = reinterpret_cast<security_handshaker*>(handshaker);
+  h->Unref();
+}
+
+static void security_handshaker_shutdown(grpc_handshaker* handshaker,
+                                         grpc_error* why) {
+  security_handshaker* h = reinterpret_cast<security_handshaker*>(handshaker);
+  gpr_mu_lock(&h->mu);
+  if (!h->shutdown) {
+    h->shutdown = true;
+    tsi_handshaker_shutdown(h->handshaker);
+    grpc_endpoint_shutdown(h->args->endpoint, GRPC_ERROR_REF(why));
+    cleanup_args_for_failure_locked(h);
+  }
+  gpr_mu_unlock(&h->mu);
+  GRPC_ERROR_UNREF(why);
+}
+
+static void security_handshaker_do_handshake(grpc_handshaker* handshaker,
+                                             grpc_tcp_server_acceptor* acceptor,
+                                             grpc_closure* on_handshake_done,
+                                             grpc_handshaker_args* args) {
+  security_handshaker* h = reinterpret_cast<security_handshaker*>(handshaker);
+  gpr_mu_lock(&h->mu);
+  h->args = args;
+  h->on_handshake_done = on_handshake_done;
+  h->Ref();
+  size_t bytes_received_size = move_read_buffer_into_handshake_buffer(h);
+  grpc_error* error =
+      do_handshaker_next_locked(h, h->handshake_buffer, bytes_received_size);
+  if (error != GRPC_ERROR_NONE) {
+    security_handshake_failed_locked(h, error);
+    gpr_mu_unlock(&h->mu);
+    h->Unref();
+    return;
+  }
+  gpr_mu_unlock(&h->mu);
+}
+
+static const grpc_handshaker_vtable security_handshaker_vtable = {
+    security_handshaker_destroy, security_handshaker_shutdown,
+    security_handshaker_do_handshake, "security"};
+
+namespace {
+security_handshaker::security_handshaker(tsi_handshaker* handshaker,
+                                         grpc_security_connector* connector)
+    : handshaker(handshaker),
+      connector(connector->Ref(DEBUG_LOCATION, "handshake")),
+      handshake_buffer_size(GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE),
+      handshake_buffer(
+          static_cast<uint8_t*>(gpr_malloc(handshake_buffer_size))) {
+  grpc_handshaker_init(&security_handshaker_vtable, &base);
+  gpr_mu_init(&mu);
+  grpc_slice_buffer_init(&outgoing);
+  GRPC_CLOSURE_INIT(&on_handshake_data_sent_to_peer,
+                    ::on_handshake_data_sent_to_peer, this,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&on_handshake_data_received_from_peer,
+                    ::on_handshake_data_received_from_peer, this,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&on_peer_checked, ::on_peer_checked, this,
+                    grpc_schedule_on_exec_ctx);
+}
+}  // namespace
+
+static grpc_handshaker* security_handshaker_create(
+    tsi_handshaker* handshaker, grpc_security_connector* connector) {
+  security_handshaker* h =
+      grpc_core::New<security_handshaker>(handshaker, connector);
+  return &h->base;
+}
+
+//
+// fail_handshaker
+//
+
+static void fail_handshaker_destroy(grpc_handshaker* handshaker) {
+  gpr_free(handshaker);
+}
+
+static void fail_handshaker_shutdown(grpc_handshaker* handshaker,
+                                     grpc_error* why) {
+  GRPC_ERROR_UNREF(why);
+}
+
+static void fail_handshaker_do_handshake(grpc_handshaker* handshaker,
+                                         grpc_tcp_server_acceptor* acceptor,
+                                         grpc_closure* on_handshake_done,
+                                         grpc_handshaker_args* args) {
+  GRPC_CLOSURE_SCHED(on_handshake_done,
+                     GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                         "Failed to create security handshaker"));
+}
+
+static const grpc_handshaker_vtable fail_handshaker_vtable = {
+    fail_handshaker_destroy, fail_handshaker_shutdown,
+    fail_handshaker_do_handshake, "security_fail"};
+
+static grpc_handshaker* fail_handshaker_create() {
+  grpc_handshaker* h = static_cast<grpc_handshaker*>(gpr_malloc(sizeof(*h)));
+  grpc_handshaker_init(&fail_handshaker_vtable, h);
+  return h;
+}
+
+//
+// handshaker factories
+//
+
+static void client_handshaker_factory_add_handshakers(
+    grpc_handshaker_factory* handshaker_factory, const grpc_channel_args* args,
+    grpc_pollset_set* interested_parties,
+    grpc_handshake_manager* handshake_mgr) {
+  grpc_channel_security_connector* security_connector =
+      reinterpret_cast<grpc_channel_security_connector*>(
+          grpc_security_connector_find_in_args(args));
+  if (security_connector) {
+    security_connector->add_handshakers(interested_parties, handshake_mgr);
+  }
+}
+
+static void server_handshaker_factory_add_handshakers(
+    grpc_handshaker_factory* hf, const grpc_channel_args* args,
+    grpc_pollset_set* interested_parties,
+    grpc_handshake_manager* handshake_mgr) {
+  grpc_server_security_connector* security_connector =
+      reinterpret_cast<grpc_server_security_connector*>(
+          grpc_security_connector_find_in_args(args));
+  if (security_connector) {
+    security_connector->add_handshakers(interested_parties, handshake_mgr);
+  }
+}
+
+static void handshaker_factory_destroy(
+    grpc_handshaker_factory* handshaker_factory) {}
+
+static const grpc_handshaker_factory_vtable client_handshaker_factory_vtable = {
+    client_handshaker_factory_add_handshakers, handshaker_factory_destroy};
+
+static grpc_handshaker_factory client_handshaker_factory = {
+    &client_handshaker_factory_vtable};
+
+static const grpc_handshaker_factory_vtable server_handshaker_factory_vtable = {
+    server_handshaker_factory_add_handshakers, handshaker_factory_destroy};
+
+static grpc_handshaker_factory server_handshaker_factory = {
+    &server_handshaker_factory_vtable};
+
+//
+// exported functions
+//
+
+grpc_handshaker* grpc_security_handshaker_create(
+    tsi_handshaker* handshaker, grpc_security_connector* connector) {
+  // If no TSI handshaker was created, return a handshaker that always fails.
+  // Otherwise, return a real security handshaker.
+  if (handshaker == nullptr) {
+    return fail_handshaker_create();
+  } else {
+    return security_handshaker_create(handshaker, connector);
+  }
+}
+
+void grpc_security_register_handshaker_factories() {
+  grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_CLIENT,
+                                   &client_handshaker_factory);
+  grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_SERVER,
+                                   &server_handshaker_factory);
+}
diff --git a/third_party/grpc/src/core/lib/security/transport/security_handshaker.h b/third_party/grpc/src/core/lib/security/transport/security_handshaker.h
new file mode 100644
index 0000000..88483b0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/security_handshaker.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/handshaker.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
+
+/// Creates a security handshaker using \a handshaker.
+grpc_handshaker* grpc_security_handshaker_create(
+    tsi_handshaker* handshaker, grpc_security_connector* connector);
+
+/// Registers security handshaker factories.
+void grpc_security_register_handshaker_factories();
+
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H */
diff --git a/third_party/grpc/src/core/lib/security/transport/server_auth_filter.cc b/third_party/grpc/src/core/lib/security/transport/server_auth_filter.cc
new file mode 100644
index 0000000..f93eb42
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/server_auth_filter.cc
@@ -0,0 +1,325 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/auth_filters.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+static void recv_initial_metadata_ready(void* arg, grpc_error* error);
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
+
+namespace {
+enum async_state {
+  STATE_INIT = 0,
+  STATE_DONE,
+  STATE_CANCELLED,
+};
+
+struct channel_data {
+  channel_data(grpc_auth_context* auth_context, grpc_server_credentials* creds)
+      : auth_context(auth_context->Ref()), creds(creds->Ref()) {}
+  ~channel_data() { auth_context.reset(DEBUG_LOCATION, "server_auth_filter"); }
+
+  grpc_core::RefCountedPtr<grpc_auth_context> auth_context;
+  grpc_core::RefCountedPtr<grpc_server_credentials> creds;
+};
+
+struct call_data {
+  call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+      : call_combiner(args.call_combiner), owning_call(args.call_stack) {
+    GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
+                      ::recv_initial_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+                      ::recv_trailing_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    // Create server security context.  Set its auth context from channel
+    // data and save it in the call context.
+    grpc_server_security_context* server_ctx =
+        grpc_server_security_context_create(args.arena);
+    channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+    server_ctx->auth_context =
+        chand->auth_context->Ref(DEBUG_LOCATION, "server_auth_filter");
+    if (args.context[GRPC_CONTEXT_SECURITY].value != nullptr) {
+      args.context[GRPC_CONTEXT_SECURITY].destroy(
+          args.context[GRPC_CONTEXT_SECURITY].value);
+    }
+    args.context[GRPC_CONTEXT_SECURITY].value = server_ctx;
+    args.context[GRPC_CONTEXT_SECURITY].destroy =
+        grpc_server_security_context_destroy;
+  }
+
+  ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); }
+
+  grpc_call_combiner* call_combiner;
+  grpc_call_stack* owning_call;
+  grpc_transport_stream_op_batch* recv_initial_metadata_batch;
+  grpc_closure* original_recv_initial_metadata_ready;
+  grpc_closure recv_initial_metadata_ready;
+  grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
+  grpc_closure recv_trailing_metadata_ready;
+  grpc_closure* original_recv_trailing_metadata_ready;
+  grpc_error* recv_trailing_metadata_error;
+  bool seen_recv_trailing_metadata_ready = false;
+  grpc_metadata_array md;
+  const grpc_metadata* consumed_md;
+  size_t num_consumed_md;
+  grpc_closure cancel_closure;
+  gpr_atm state = STATE_INIT;  // async_state
+};
+
+}  // namespace
+
+static grpc_metadata_array metadata_batch_to_md_array(
+    const grpc_metadata_batch* batch) {
+  grpc_linked_mdelem* l;
+  grpc_metadata_array result;
+  grpc_metadata_array_init(&result);
+  for (l = batch->list.head; l != nullptr; l = l->next) {
+    grpc_metadata* usr_md = nullptr;
+    grpc_mdelem md = l->md;
+    grpc_slice key = GRPC_MDKEY(md);
+    grpc_slice value = GRPC_MDVALUE(md);
+    if (result.count == result.capacity) {
+      result.capacity = GPR_MAX(result.capacity + 8, result.capacity * 2);
+      result.metadata = static_cast<grpc_metadata*>(gpr_realloc(
+          result.metadata, result.capacity * sizeof(grpc_metadata)));
+    }
+    usr_md = &result.metadata[result.count++];
+    usr_md->key = grpc_slice_ref_internal(key);
+    usr_md->value = grpc_slice_ref_internal(value);
+  }
+  return result;
+}
+
+static grpc_filtered_mdelem remove_consumed_md(void* user_data,
+                                               grpc_mdelem md) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  size_t i;
+  for (i = 0; i < calld->num_consumed_md; i++) {
+    const grpc_metadata* consumed_md = &calld->consumed_md[i];
+    if (grpc_slice_eq(GRPC_MDKEY(md), consumed_md->key) &&
+        grpc_slice_eq(GRPC_MDVALUE(md), consumed_md->value))
+      return GRPC_FILTERED_REMOVE();
+  }
+  return GRPC_FILTERED_MDELEM(md);
+}
+
+static void on_md_processing_done_inner(grpc_call_element* elem,
+                                        const grpc_metadata* consumed_md,
+                                        size_t num_consumed_md,
+                                        const grpc_metadata* response_md,
+                                        size_t num_response_md,
+                                        grpc_error* error) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_transport_stream_op_batch* batch = calld->recv_initial_metadata_batch;
+  /* TODO(jboeuf): Implement support for response_md. */
+  if (response_md != nullptr && num_response_md > 0) {
+    gpr_log(GPR_INFO,
+            "response_md in auth metadata processing not supported for now. "
+            "Ignoring...");
+  }
+  if (error == GRPC_ERROR_NONE) {
+    calld->consumed_md = consumed_md;
+    calld->num_consumed_md = num_consumed_md;
+    error = grpc_metadata_batch_filter(
+        batch->payload->recv_initial_metadata.recv_initial_metadata,
+        remove_consumed_md, elem, "Response metadata filtering error");
+  }
+  calld->recv_initial_metadata_error = GRPC_ERROR_REF(error);
+  grpc_closure* closure = calld->original_recv_initial_metadata_ready;
+  calld->original_recv_initial_metadata_ready = nullptr;
+  if (calld->seen_recv_trailing_metadata_ready) {
+    GRPC_CALL_COMBINER_START(calld->call_combiner,
+                             &calld->recv_trailing_metadata_ready,
+                             calld->recv_trailing_metadata_error,
+                             "continue recv_trailing_metadata_ready");
+  }
+  GRPC_CLOSURE_SCHED(closure, error);
+}
+
+// Called from application code.
+static void on_md_processing_done(
+    void* user_data, const grpc_metadata* consumed_md, size_t num_consumed_md,
+    const grpc_metadata* response_md, size_t num_response_md,
+    grpc_status_code status, const char* error_details) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_core::ExecCtx exec_ctx;
+  // If the call was not cancelled while we were in flight, process the result.
+  if (gpr_atm_full_cas(&calld->state, static_cast<gpr_atm>(STATE_INIT),
+                       static_cast<gpr_atm>(STATE_DONE))) {
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (status != GRPC_STATUS_OK) {
+      if (error_details == nullptr) {
+        error_details = "Authentication metadata processing failed.";
+      }
+      error = grpc_error_set_int(
+          GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details),
+          GRPC_ERROR_INT_GRPC_STATUS, status);
+    }
+    on_md_processing_done_inner(elem, consumed_md, num_consumed_md, response_md,
+                                num_response_md, error);
+  }
+  // Clean up.
+  for (size_t i = 0; i < calld->md.count; i++) {
+    grpc_slice_unref_internal(calld->md.metadata[i].key);
+    grpc_slice_unref_internal(calld->md.metadata[i].value);
+  }
+  grpc_metadata_array_destroy(&calld->md);
+  GRPC_CALL_STACK_UNREF(calld->owning_call, "server_auth_metadata");
+}
+
+static void cancel_call(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  // If the result was not already processed, invoke the callback now.
+  if (error != GRPC_ERROR_NONE &&
+      gpr_atm_full_cas(&calld->state, static_cast<gpr_atm>(STATE_INIT),
+                       static_cast<gpr_atm>(STATE_CANCELLED))) {
+    on_md_processing_done_inner(elem, nullptr, 0, nullptr, 0,
+                                GRPC_ERROR_REF(error));
+  }
+}
+
+static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_transport_stream_op_batch* batch = calld->recv_initial_metadata_batch;
+  if (error == GRPC_ERROR_NONE) {
+    if (chand->creds != nullptr &&
+        chand->creds->auth_metadata_processor().process != nullptr) {
+      // We're calling out to the application, so we need to make sure
+      // to drop the call combiner early if we get cancelled.
+      GRPC_CLOSURE_INIT(&calld->cancel_closure, cancel_call, elem,
+                        grpc_schedule_on_exec_ctx);
+      grpc_call_combiner_set_notify_on_cancel(calld->call_combiner,
+                                              &calld->cancel_closure);
+      GRPC_CALL_STACK_REF(calld->owning_call, "server_auth_metadata");
+      calld->md = metadata_batch_to_md_array(
+          batch->payload->recv_initial_metadata.recv_initial_metadata);
+      chand->creds->auth_metadata_processor().process(
+          chand->creds->auth_metadata_processor().state,
+          chand->auth_context.get(), calld->md.metadata, calld->md.count,
+          on_md_processing_done, elem);
+      return;
+    }
+  }
+  grpc_closure* closure = calld->original_recv_initial_metadata_ready;
+  calld->original_recv_initial_metadata_ready = nullptr;
+  if (calld->seen_recv_trailing_metadata_ready) {
+    GRPC_CALL_COMBINER_START(calld->call_combiner,
+                             &calld->recv_trailing_metadata_ready,
+                             calld->recv_trailing_metadata_error,
+                             "continue recv_trailing_metadata_ready");
+  }
+  GRPC_CLOSURE_RUN(closure, GRPC_ERROR_REF(error));
+}
+
+static void recv_trailing_metadata_ready(void* user_data, grpc_error* err) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->original_recv_initial_metadata_ready != nullptr) {
+    calld->recv_trailing_metadata_error = GRPC_ERROR_REF(err);
+    calld->seen_recv_trailing_metadata_ready = true;
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "deferring recv_trailing_metadata_ready until "
+                            "after recv_initial_metadata_ready");
+    return;
+  }
+  err = grpc_error_add_child(
+      GRPC_ERROR_REF(err), GRPC_ERROR_REF(calld->recv_initial_metadata_error));
+  GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, err);
+}
+
+static void auth_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (batch->recv_initial_metadata) {
+    // Inject our callback.
+    calld->recv_initial_metadata_batch = batch;
+    calld->original_recv_initial_metadata_ready =
+        batch->payload->recv_initial_metadata.recv_initial_metadata_ready;
+    batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
+        &calld->recv_initial_metadata_ready;
+  }
+  if (batch->recv_trailing_metadata) {
+    calld->original_recv_trailing_metadata_ready =
+        batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &calld->recv_trailing_metadata_ready;
+  }
+  grpc_call_next_op(elem, batch);
+}
+
+/* Constructor for call_data */
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  new (elem->call_data) call_data(elem, *args);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for call_data */
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->~call_data();
+}
+
+/* Constructor for channel_data */
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  GPR_ASSERT(!args->is_last);
+  grpc_auth_context* auth_context =
+      grpc_find_auth_context_in_args(args->channel_args);
+  GPR_ASSERT(auth_context != nullptr);
+  grpc_server_credentials* creds =
+      grpc_find_server_credentials_in_args(args->channel_args);
+  new (elem->channel_data) channel_data(auth_context, creds);
+  return GRPC_ERROR_NONE;
+}
+
+/* Destructor for channel data */
+static void destroy_channel_elem(grpc_channel_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  chand->~channel_data();
+}
+
+const grpc_channel_filter grpc_server_auth_filter = {
+    auth_start_transport_stream_op_batch,
+    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_channel_next_get_info,
+    "server-auth"};
diff --git a/third_party/grpc/src/core/lib/security/transport/target_authority_table.cc b/third_party/grpc/src/core/lib/security/transport/target_authority_table.cc
new file mode 100644
index 0000000..1eeb557
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/target_authority_table.cc
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/security/transport/target_authority_table.h"
+
+// Channel arg key for the mapping of target addresses to their authorities.
+#define GRPC_ARG_TARGET_AUTHORITY_TABLE "grpc.target_authority_table"
+
+namespace grpc_core {
+namespace {
+
+void* target_authority_table_copy(void* p) {
+  TargetAuthorityTable* table = static_cast<TargetAuthorityTable*>(p);
+  // TODO(roth): When channel_args are converted to C++, pass the
+  // RefCountedPtr<> directly instead of managing the ref manually.
+  table->Ref().release();
+  return p;
+}
+void target_authority_table_destroy(void* p) {
+  TargetAuthorityTable* table = static_cast<TargetAuthorityTable*>(p);
+  table->Unref();
+}
+int target_authority_table_cmp(void* a, void* b) {
+  return TargetAuthorityTable::Cmp(
+      *static_cast<const TargetAuthorityTable*>(a),
+      *static_cast<const TargetAuthorityTable*>(b));
+}
+const grpc_arg_pointer_vtable target_authority_table_arg_vtable = {
+    target_authority_table_copy, target_authority_table_destroy,
+    target_authority_table_cmp};
+
+}  // namespace
+
+grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table) {
+  return grpc_channel_arg_pointer_create((char*)GRPC_ARG_TARGET_AUTHORITY_TABLE,
+                                         table,
+                                         &target_authority_table_arg_vtable);
+}
+
+TargetAuthorityTable* FindTargetAuthorityTableInArgs(
+    const grpc_channel_args* args) {
+  const grpc_arg* arg =
+      grpc_channel_args_find(args, GRPC_ARG_TARGET_AUTHORITY_TABLE);
+  if (arg != nullptr) {
+    if (arg->type == GRPC_ARG_POINTER) {
+      return static_cast<TargetAuthorityTable*>(arg->value.pointer.p);
+    } else {
+      gpr_log(GPR_ERROR, "value of " GRPC_ARG_TARGET_AUTHORITY_TABLE
+                         " channel arg was not pointer type; ignoring");
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/security/transport/target_authority_table.h b/third_party/grpc/src/core/lib/security/transport/target_authority_table.h
new file mode 100644
index 0000000..a2e7dc6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/target_authority_table.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/slice/slice_hash_table.h"
+
+namespace grpc_core {
+
+/// A hash table mapping target addresses to authorities.
+typedef SliceHashTable<UniquePtr<char>> TargetAuthorityTable;
+
+/// Returns a channel argument containing \a table.
+grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table);
+
+/// Returns the target authority table from \a args or nullptr.
+TargetAuthorityTable* FindTargetAuthorityTableInArgs(
+    const grpc_channel_args* args);
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H */
diff --git a/third_party/grpc/src/core/lib/security/transport/tsi_error.cc b/third_party/grpc/src/core/lib/security/transport/tsi_error.cc
new file mode 100644
index 0000000..f78bb8d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/tsi_error.cc
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/transport/tsi_error.h"
+
+grpc_error* grpc_set_tsi_error_result(grpc_error* error, tsi_result result) {
+  return grpc_error_set_int(
+      grpc_error_set_str(
+          error, GRPC_ERROR_STR_TSI_ERROR,
+          grpc_slice_from_static_string(tsi_result_to_string(result))),
+      GRPC_ERROR_INT_TSI_CODE, result);
+}
diff --git a/third_party/grpc/src/core/lib/security/transport/tsi_error.h b/third_party/grpc/src/core/lib/security/transport/tsi_error.h
new file mode 100644
index 0000000..16e04f7
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/transport/tsi_error.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+grpc_error* grpc_set_tsi_error_result(grpc_error* error, tsi_result result);
+
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H */
diff --git a/third_party/grpc/src/core/lib/security/util/json_util.cc b/third_party/grpc/src/core/lib/security/util/json_util.cc
new file mode 100644
index 0000000..fe9f5fe
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/util/json_util.cc
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/util/json_util.h"
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+const char* grpc_json_get_string_property(const grpc_json* json,
+                                          const char* prop_name) {
+  grpc_json* child;
+  for (child = json->child; child != nullptr; child = child->next) {
+    if (child->key == nullptr) {
+      gpr_log(GPR_ERROR, "Invalid (null) JSON key encountered");
+      return nullptr;
+    }
+    if (strcmp(child->key, prop_name) == 0) break;
+  }
+  if (child == nullptr || child->type != GRPC_JSON_STRING) {
+    gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name);
+    return nullptr;
+  }
+  return child->value;
+}
+
+bool grpc_copy_json_string_property(const grpc_json* json,
+                                    const char* prop_name,
+                                    char** copied_value) {
+  const char* prop_value = grpc_json_get_string_property(json, prop_name);
+  if (prop_value == nullptr) return false;
+  *copied_value = gpr_strdup(prop_value);
+  return true;
+}
diff --git a/third_party/grpc/src/core/lib/security/util/json_util.h b/third_party/grpc/src/core/lib/security/util/json_util.h
new file mode 100644
index 0000000..89deffc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/security/util/json_util.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H
+#define GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/lib/json/json.h"
+
+// Constants.
+#define GRPC_AUTH_JSON_TYPE_INVALID "invalid"
+#define GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT "service_account"
+#define GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER "authorized_user"
+
+// Gets a child property from a json node.
+const char* grpc_json_get_string_property(const grpc_json* json,
+                                          const char* prop_name);
+
+// Copies the value of the json child property specified by prop_name.
+// Returns false if the property was not found.
+bool grpc_copy_json_string_property(const grpc_json* json,
+                                    const char* prop_name, char** copied_value);
+
+#endif /* GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H */
diff --git a/third_party/grpc/src/core/lib/slice/b64.cc b/third_party/grpc/src/core/lib/slice/b64.cc
new file mode 100644
index 0000000..27f2724
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/b64.cc
@@ -0,0 +1,240 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/slice/b64.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+/* --- Constants. --- */
+
+static const int8_t base64_bytes[] = {
+    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+    -1,   -1,   -1,   -1,   -1,   -1,   -1,   0x3E, -1,   -1,   -1,   0x3F,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, -1,   -1,
+    -1,   0x7F, -1,   -1,   -1,   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
+    0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, -1,   -1,   -1,   -1,   -1,
+    -1,   0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+    0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+    0x31, 0x32, 0x33, -1,   -1,   -1,   -1,   -1};
+
+static const char base64_url_unsafe_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char base64_url_safe_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+#define GRPC_BASE64_PAD_CHAR '='
+#define GRPC_BASE64_PAD_BYTE 0x7F
+#define GRPC_BASE64_MULTILINE_LINE_LEN 76
+#define GRPC_BASE64_MULTILINE_NUM_BLOCKS (GRPC_BASE64_MULTILINE_LINE_LEN / 4)
+
+/* --- base64 functions. --- */
+
+char* grpc_base64_encode(const void* vdata, size_t data_size, int url_safe,
+                         int multiline) {
+  size_t result_projected_size =
+      grpc_base64_estimate_encoded_size(data_size, url_safe, multiline);
+  char* result = static_cast<char*>(gpr_malloc(result_projected_size));
+  grpc_base64_encode_core(result, vdata, data_size, url_safe, multiline);
+  return result;
+}
+
+size_t grpc_base64_estimate_encoded_size(size_t data_size, int url_safe,
+                                         int multiline) {
+  size_t result_projected_size =
+      4 * ((data_size + 3) / 3) +
+      2 * (multiline ? (data_size / (3 * GRPC_BASE64_MULTILINE_NUM_BLOCKS))
+                     : 0) +
+      1;
+  return result_projected_size;
+}
+
+void grpc_base64_encode_core(char* result, const void* vdata, size_t data_size,
+                             int url_safe, int multiline) {
+  const unsigned char* data = static_cast<const unsigned char*>(vdata);
+  const char* base64_chars =
+      url_safe ? base64_url_safe_chars : base64_url_unsafe_chars;
+  const size_t result_projected_size =
+      grpc_base64_estimate_encoded_size(data_size, url_safe, multiline);
+
+  char* current = result;
+  size_t num_blocks = 0;
+  size_t i = 0;
+
+  /* Encode each block. */
+  while (data_size >= 3) {
+    *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+    *current++ =
+        base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)];
+    *current++ =
+        base64_chars[((data[i + 1] & 0x0F) << 2) | ((data[i + 2] >> 6) & 0x03)];
+    *current++ = base64_chars[data[i + 2] & 0x3F];
+
+    data_size -= 3;
+    i += 3;
+    if (multiline && (++num_blocks == GRPC_BASE64_MULTILINE_NUM_BLOCKS)) {
+      *current++ = '\r';
+      *current++ = '\n';
+      num_blocks = 0;
+    }
+  }
+
+  /* Take care of the tail. */
+  if (data_size == 2) {
+    *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+    *current++ =
+        base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)];
+    *current++ = base64_chars[(data[i + 1] & 0x0F) << 2];
+    *current++ = GRPC_BASE64_PAD_CHAR;
+  } else if (data_size == 1) {
+    *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+    *current++ = base64_chars[(data[i] & 0x03) << 4];
+    *current++ = GRPC_BASE64_PAD_CHAR;
+    *current++ = GRPC_BASE64_PAD_CHAR;
+  }
+
+  GPR_ASSERT(current >= result);
+  GPR_ASSERT((uintptr_t)(current - result) < result_projected_size);
+  result[current - result] = '\0';
+}
+
+grpc_slice grpc_base64_decode(const char* b64, int url_safe) {
+  return grpc_base64_decode_with_len(b64, strlen(b64), url_safe);
+}
+
+static void decode_one_char(const unsigned char* codes, unsigned char* result,
+                            size_t* result_offset) {
+  uint32_t packed = (static_cast<uint32_t>(codes[0]) << 2) |
+                    (static_cast<uint32_t>(codes[1]) >> 4);
+  result[(*result_offset)++] = static_cast<unsigned char>(packed);
+}
+
+static void decode_two_chars(const unsigned char* codes, unsigned char* result,
+                             size_t* result_offset) {
+  uint32_t packed = (static_cast<uint32_t>(codes[0]) << 10) |
+                    (static_cast<uint32_t>(codes[1]) << 4) |
+                    (static_cast<uint32_t>(codes[2]) >> 2);
+  result[(*result_offset)++] = static_cast<unsigned char>(packed >> 8);
+  result[(*result_offset)++] = static_cast<unsigned char>(packed);
+}
+
+static int decode_group(const unsigned char* codes, size_t num_codes,
+                        unsigned char* result, size_t* result_offset) {
+  GPR_ASSERT(num_codes <= 4);
+
+  /* Short end groups that may not have padding. */
+  if (num_codes == 1) {
+    gpr_log(GPR_ERROR, "Invalid group. Must be at least 2 bytes.");
+    return 0;
+  }
+  if (num_codes == 2) {
+    decode_one_char(codes, result, result_offset);
+    return 1;
+  }
+  if (num_codes == 3) {
+    decode_two_chars(codes, result, result_offset);
+    return 1;
+  }
+
+  /* Regular 4 byte groups with padding or not. */
+  GPR_ASSERT(num_codes == 4);
+  if (codes[0] == GRPC_BASE64_PAD_BYTE || codes[1] == GRPC_BASE64_PAD_BYTE) {
+    gpr_log(GPR_ERROR, "Invalid padding detected.");
+    return 0;
+  }
+  if (codes[2] == GRPC_BASE64_PAD_BYTE) {
+    if (codes[3] == GRPC_BASE64_PAD_BYTE) {
+      decode_one_char(codes, result, result_offset);
+    } else {
+      gpr_log(GPR_ERROR, "Invalid padding detected.");
+      return 0;
+    }
+  } else if (codes[3] == GRPC_BASE64_PAD_BYTE) {
+    decode_two_chars(codes, result, result_offset);
+  } else {
+    /* No padding. */
+    uint32_t packed = (static_cast<uint32_t>(codes[0]) << 18) |
+                      (static_cast<uint32_t>(codes[1]) << 12) |
+                      (static_cast<uint32_t>(codes[2]) << 6) | codes[3];
+    result[(*result_offset)++] = static_cast<unsigned char>(packed >> 16);
+    result[(*result_offset)++] = static_cast<unsigned char>(packed >> 8);
+    result[(*result_offset)++] = static_cast<unsigned char>(packed);
+  }
+  return 1;
+}
+
+grpc_slice grpc_base64_decode_with_len(const char* b64, size_t b64_len,
+                                       int url_safe) {
+  grpc_slice result = GRPC_SLICE_MALLOC(b64_len);
+  unsigned char* current = GRPC_SLICE_START_PTR(result);
+  size_t result_size = 0;
+  unsigned char codes[4];
+  size_t num_codes = 0;
+
+  while (b64_len--) {
+    unsigned char c = static_cast<unsigned char>(*b64++);
+    signed char code;
+    if (c >= GPR_ARRAY_SIZE(base64_bytes)) continue;
+    if (url_safe) {
+      if (c == '+' || c == '/') {
+        gpr_log(GPR_ERROR, "Invalid character for url safe base64 %c", c);
+        goto fail;
+      }
+      if (c == '-') {
+        c = '+';
+      } else if (c == '_') {
+        c = '/';
+      }
+    }
+    code = base64_bytes[c];
+    if (code == -1) {
+      if (c != '\r' && c != '\n') {
+        gpr_log(GPR_ERROR, "Invalid character %c", c);
+        goto fail;
+      }
+    } else {
+      codes[num_codes++] = static_cast<unsigned char>(code);
+      if (num_codes == 4) {
+        if (!decode_group(codes, num_codes, current, &result_size)) goto fail;
+        num_codes = 0;
+      }
+    }
+  }
+
+  if (num_codes != 0 &&
+      !decode_group(codes, num_codes, current, &result_size)) {
+    goto fail;
+  }
+  GRPC_SLICE_SET_LENGTH(result, result_size);
+  return result;
+
+fail:
+  grpc_slice_unref_internal(result);
+  return grpc_empty_slice();
+}
diff --git a/third_party/grpc/src/core/lib/slice/b64.h b/third_party/grpc/src/core/lib/slice/b64.h
new file mode 100644
index 0000000..4475568
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/b64.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_B64_H
+#define GRPC_CORE_LIB_SLICE_B64_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+
+/* Encodes data using base64. It is the caller's responsability to free
+   the returned char * using gpr_free. Returns NULL on NULL input.
+   TODO(makdharma) : change the flags to bool from int */
+char* grpc_base64_encode(const void* data, size_t data_size, int url_safe,
+                         int multiline);
+
+/* estimate the upper bound on size of base64 encoded data. The actual size
+ * is guaranteed to be less than or equal to the size returned here. */
+size_t grpc_base64_estimate_encoded_size(size_t data_size, int url_safe,
+                                         int multiline);
+
+/* Encodes data using base64 and write it to memory pointed to by result. It is
+ * the caller's responsiblity to allocate enough memory in |result| to fit the
+ * encoded data. */
+void grpc_base64_encode_core(char* result, const void* vdata, size_t data_size,
+                             int url_safe, int multiline);
+
+/* Decodes data according to the base64 specification. Returns an empty
+   slice in case of failure. */
+grpc_slice grpc_base64_decode(const char* b64, int url_safe);
+
+/* Same as above except that the length is provided by the caller. */
+grpc_slice grpc_base64_decode_with_len(const char* b64, size_t b64_len,
+                                       int url_safe);
+
+#endif /* GRPC_CORE_LIB_SLICE_B64_H */
diff --git a/third_party/grpc/src/core/lib/slice/percent_encoding.cc b/third_party/grpc/src/core/lib/slice/percent_encoding.cc
new file mode 100644
index 0000000..45cd2cc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/percent_encoding.cc
@@ -0,0 +1,169 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/slice/percent_encoding.h"
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+
+const uint8_t grpc_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 grpc_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;
+}
+
+grpc_slice grpc_percent_encode_slice(grpc_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 = GRPC_SLICE_START_PTR(slice);
+  const uint8_t* slice_end = GRPC_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 grpc_slice_ref_internal(slice);
+  }
+  // second pass: actually encode
+  grpc_slice out = GRPC_SLICE_MALLOC(output_length);
+  uint8_t* q = GRPC_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 == GRPC_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 static_cast<uint8_t>(c - '0');
+  if (c >= 'A' && c <= 'F') return static_cast<uint8_t>(c - 'A' + 10);
+  if (c >= 'a' && c <= 'f') return static_cast<uint8_t>(c - 'a' + 10);
+  GPR_UNREACHABLE_CODE(return 255);
+}
+
+bool grpc_strict_percent_decode_slice(grpc_slice slice_in,
+                                      const uint8_t* unreserved_bytes,
+                                      grpc_slice* slice_out) {
+  const uint8_t* p = GRPC_SLICE_START_PTR(slice_in);
+  const uint8_t* in_end = GRPC_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 = grpc_slice_ref_internal(slice_in);
+    return true;
+  }
+  p = GRPC_SLICE_START_PTR(slice_in);
+  *slice_out = GRPC_SLICE_MALLOC(out_length);
+  uint8_t* q = GRPC_SLICE_START_PTR(*slice_out);
+  while (p != in_end) {
+    if (*p == '%') {
+      *q++ = static_cast<uint8_t>(dehex(p[1]) << 4) | (dehex(p[2]));
+      p += 3;
+    } else {
+      *q++ = *p++;
+    }
+  }
+  GPR_ASSERT(q == GRPC_SLICE_END_PTR(*slice_out));
+  return true;
+}
+
+grpc_slice grpc_permissive_percent_decode_slice(grpc_slice slice_in) {
+  const uint8_t* p = GRPC_SLICE_START_PTR(slice_in);
+  const uint8_t* in_end = GRPC_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 grpc_slice_ref_internal(slice_in);
+  }
+  p = GRPC_SLICE_START_PTR(slice_in);
+  grpc_slice out = GRPC_SLICE_MALLOC(out_length);
+  uint8_t* q = GRPC_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++ = static_cast<uint8_t>(dehex(p[1]) << 4) | (dehex(p[2]));
+        p += 3;
+      }
+    } else {
+      *q++ = *p++;
+    }
+  }
+  GPR_ASSERT(q == GRPC_SLICE_END_PTR(out));
+  return out;
+}
diff --git a/third_party/grpc/src/core/lib/slice/percent_encoding.h b/third_party/grpc/src/core/lib/slice/percent_encoding.h
new file mode 100644
index 0000000..6b13ffc
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/percent_encoding.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_PERCENT_ENCODING_H
+#define GRPC_CORE_LIB_SLICE_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 <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include <grpc/slice.h>
+
+/* URL percent encoding spec bitfield (usabel as 'unreserved_bytes' in
+   grpc_percent_encode_slice, grpc_strict_percent_decode_slice).
+   Flags [A-Za-z0-9-_.~] as unreserved bytes for the percent encoding routines
+   */
+extern const uint8_t grpc_url_percent_encoding_unreserved_bytes[256 / 8];
+/* URL percent encoding spec bitfield (usabel as 'unreserved_bytes' in
+   grpc_percent_encode_slice, grpc_strict_percent_decode_slice).
+   Flags ascii7 non-control characters excluding '%' as unreserved bytes for the
+   percent encoding routines */
+extern const uint8_t grpc_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 */
+grpc_slice grpc_percent_encode_slice(grpc_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 grpc_strict_percent_decode_slice(grpc_slice slice_in,
+                                      const uint8_t* unreserved_bytes,
+                                      grpc_slice* slice_out);
+/* Percent-decode a slice, permissively.
+   If a % triplet can not be decoded, pass it through verbatim.
+   This cannot fail. */
+grpc_slice grpc_permissive_percent_decode_slice(grpc_slice slice_in);
+
+#endif /* GRPC_CORE_LIB_SLICE_PERCENT_ENCODING_H */
diff --git a/third_party/grpc/src/core/lib/slice/slice.cc b/third_party/grpc/src/core/lib/slice/slice.cc
new file mode 100644
index 0000000..e842d84
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice.cc
@@ -0,0 +1,501 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include <string.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+char* grpc_slice_to_c_string(grpc_slice slice) {
+  char* out = static_cast<char*>(gpr_malloc(GRPC_SLICE_LENGTH(slice) + 1));
+  memcpy(out, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice));
+  out[GRPC_SLICE_LENGTH(slice)] = 0;
+  return out;
+}
+
+grpc_slice grpc_empty_slice(void) {
+  grpc_slice out;
+  out.refcount = nullptr;
+  out.data.inlined.length = 0;
+  return out;
+}
+
+grpc_slice grpc_slice_copy(grpc_slice s) {
+  grpc_slice out = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(s));
+  memcpy(GRPC_SLICE_START_PTR(out), GRPC_SLICE_START_PTR(s),
+         GRPC_SLICE_LENGTH(s));
+  return out;
+}
+
+grpc_slice grpc_slice_ref_internal(grpc_slice slice) {
+  if (slice.refcount) {
+    slice.refcount->vtable->ref(slice.refcount);
+  }
+  return slice;
+}
+
+void grpc_slice_unref_internal(grpc_slice slice) {
+  if (slice.refcount) {
+    slice.refcount->vtable->unref(slice.refcount);
+  }
+}
+
+/* Public API */
+grpc_slice grpc_slice_ref(grpc_slice slice) {
+  return grpc_slice_ref_internal(slice);
+}
+
+/* Public API */
+void grpc_slice_unref(grpc_slice slice) {
+  if (grpc_core::ExecCtx::Get() == nullptr) {
+    grpc_core::ExecCtx exec_ctx;
+    grpc_slice_unref_internal(slice);
+  } else {
+    grpc_slice_unref_internal(slice);
+  }
+}
+
+/* grpc_slice_from_static_string support structure - a refcount that does
+   nothing */
+static void noop_ref(void* unused) {}
+static void noop_unref(void* unused) {}
+
+static const grpc_slice_refcount_vtable noop_refcount_vtable = {
+    noop_ref, noop_unref, grpc_slice_default_eq_impl,
+    grpc_slice_default_hash_impl};
+static grpc_slice_refcount noop_refcount = {&noop_refcount_vtable,
+                                            &noop_refcount};
+
+size_t grpc_slice_memory_usage(grpc_slice s) {
+  if (s.refcount == nullptr || s.refcount == &noop_refcount) {
+    return 0;
+  } else {
+    return s.data.refcounted.length;
+  }
+}
+
+grpc_slice grpc_slice_from_static_buffer(const void* s, size_t len) {
+  grpc_slice slice;
+  slice.refcount = &noop_refcount;
+  slice.data.refcounted.bytes = (uint8_t*)s;
+  slice.data.refcounted.length = len;
+  return slice;
+}
+
+grpc_slice grpc_slice_from_static_string(const char* s) {
+  return grpc_slice_from_static_buffer(s, strlen(s));
+}
+
+/* grpc_slice_new support structures - we create a refcount object extended
+   with the user provided data pointer & destroy function */
+typedef struct new_slice_refcount {
+  grpc_slice_refcount rc;
+  gpr_refcount refs;
+  void (*user_destroy)(void*);
+  void* user_data;
+} new_slice_refcount;
+
+static void new_slice_ref(void* p) {
+  new_slice_refcount* r = static_cast<new_slice_refcount*>(p);
+  gpr_ref(&r->refs);
+}
+
+static void new_slice_unref(void* p) {
+  new_slice_refcount* r = static_cast<new_slice_refcount*>(p);
+  if (gpr_unref(&r->refs)) {
+    r->user_destroy(r->user_data);
+    gpr_free(r);
+  }
+}
+
+static const grpc_slice_refcount_vtable new_slice_vtable = {
+    new_slice_ref, new_slice_unref, grpc_slice_default_eq_impl,
+    grpc_slice_default_hash_impl};
+
+grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
+                                         void (*destroy)(void*),
+                                         void* user_data) {
+  grpc_slice slice;
+  new_slice_refcount* rc =
+      static_cast<new_slice_refcount*>(gpr_malloc(sizeof(new_slice_refcount)));
+  gpr_ref_init(&rc->refs, 1);
+  rc->rc.vtable = &new_slice_vtable;
+  rc->rc.sub_refcount = &rc->rc;
+  rc->user_destroy = destroy;
+  rc->user_data = user_data;
+
+  slice.refcount = &rc->rc;
+  slice.data.refcounted.bytes = static_cast<uint8_t*>(p);
+  slice.data.refcounted.length = len;
+  return slice;
+}
+
+grpc_slice grpc_slice_new(void* p, size_t len, void (*destroy)(void*)) {
+  /* Pass "p" to *destroy when the slice is no longer needed. */
+  return grpc_slice_new_with_user_data(p, len, destroy, p);
+}
+
+/* grpc_slice_new_with_len support structures - we create a refcount object
+   extended with the user provided data pointer & destroy function */
+typedef struct new_with_len_slice_refcount {
+  grpc_slice_refcount rc;
+  gpr_refcount refs;
+  void* user_data;
+  size_t user_length;
+  void (*user_destroy)(void*, size_t);
+} new_with_len_slice_refcount;
+
+static void new_with_len_ref(void* p) {
+  new_with_len_slice_refcount* r = static_cast<new_with_len_slice_refcount*>(p);
+  gpr_ref(&r->refs);
+}
+
+static void new_with_len_unref(void* p) {
+  new_with_len_slice_refcount* r = static_cast<new_with_len_slice_refcount*>(p);
+  if (gpr_unref(&r->refs)) {
+    r->user_destroy(r->user_data, r->user_length);
+    gpr_free(r);
+  }
+}
+
+static const grpc_slice_refcount_vtable new_with_len_vtable = {
+    new_with_len_ref, new_with_len_unref, grpc_slice_default_eq_impl,
+    grpc_slice_default_hash_impl};
+
+grpc_slice grpc_slice_new_with_len(void* p, size_t len,
+                                   void (*destroy)(void*, size_t)) {
+  grpc_slice slice;
+  new_with_len_slice_refcount* rc = static_cast<new_with_len_slice_refcount*>(
+      gpr_malloc(sizeof(new_with_len_slice_refcount)));
+  gpr_ref_init(&rc->refs, 1);
+  rc->rc.vtable = &new_with_len_vtable;
+  rc->rc.sub_refcount = &rc->rc;
+  rc->user_destroy = destroy;
+  rc->user_data = p;
+  rc->user_length = len;
+
+  slice.refcount = &rc->rc;
+  slice.data.refcounted.bytes = static_cast<uint8_t*>(p);
+  slice.data.refcounted.length = len;
+  return slice;
+}
+
+grpc_slice grpc_slice_from_copied_buffer(const char* source, size_t length) {
+  if (length == 0) return grpc_empty_slice();
+  grpc_slice slice = GRPC_SLICE_MALLOC(length);
+  memcpy(GRPC_SLICE_START_PTR(slice), source, length);
+  return slice;
+}
+
+grpc_slice grpc_slice_from_copied_string(const char* source) {
+  return grpc_slice_from_copied_buffer(source, strlen(source));
+}
+
+typedef struct {
+  grpc_slice_refcount base;
+  gpr_refcount refs;
+} malloc_refcount;
+
+static void malloc_ref(void* p) {
+  malloc_refcount* r = static_cast<malloc_refcount*>(p);
+  gpr_ref(&r->refs);
+}
+
+static void malloc_unref(void* p) {
+  malloc_refcount* r = static_cast<malloc_refcount*>(p);
+  if (gpr_unref(&r->refs)) {
+    gpr_free(r);
+  }
+}
+
+static const grpc_slice_refcount_vtable malloc_vtable = {
+    malloc_ref, malloc_unref, grpc_slice_default_eq_impl,
+    grpc_slice_default_hash_impl};
+
+grpc_slice grpc_slice_malloc_large(size_t length) {
+  grpc_slice slice;
+
+  /* Memory layout used by the slice created here:
+
+     +-----------+----------------------------------------------------------+
+     | refcount  | bytes                                                    |
+     +-----------+----------------------------------------------------------+
+
+     refcount is a malloc_refcount
+     bytes is an array of bytes of the requested length
+     Both parts are placed in the same allocation returned from gpr_malloc */
+  malloc_refcount* rc = static_cast<malloc_refcount*>(
+      gpr_malloc(sizeof(malloc_refcount) + length));
+
+  /* Initial refcount on rc is 1 - and it's up to the caller to release
+     this reference. */
+  gpr_ref_init(&rc->refs, 1);
+
+  rc->base.vtable = &malloc_vtable;
+  rc->base.sub_refcount = &rc->base;
+
+  /* Build up the slice to be returned. */
+  /* The slices refcount points back to the allocated block. */
+  slice.refcount = &rc->base;
+  /* The data bytes are placed immediately after the refcount struct */
+  slice.data.refcounted.bytes = reinterpret_cast<uint8_t*>(rc + 1);
+  /* And the length of the block is set to the requested length */
+  slice.data.refcounted.length = length;
+  return slice;
+}
+
+grpc_slice grpc_slice_malloc(size_t length) {
+  grpc_slice slice;
+
+  if (length > sizeof(slice.data.inlined.bytes)) {
+    return grpc_slice_malloc_large(length);
+  } else {
+    /* small slice: just inline the data */
+    slice.refcount = nullptr;
+    slice.data.inlined.length = static_cast<uint8_t>(length);
+  }
+  return slice;
+}
+
+grpc_slice grpc_slice_sub_no_ref(grpc_slice source, size_t begin, size_t end) {
+  grpc_slice subset;
+
+  GPR_ASSERT(end >= begin);
+
+  if (source.refcount) {
+    /* Enforce preconditions */
+    GPR_ASSERT(source.data.refcounted.length >= end);
+
+    /* Build the result */
+    subset.refcount = source.refcount->sub_refcount;
+    /* Point into the source array */
+    subset.data.refcounted.bytes = source.data.refcounted.bytes + begin;
+    subset.data.refcounted.length = end - begin;
+  } else {
+    /* Enforce preconditions */
+    GPR_ASSERT(source.data.inlined.length >= end);
+    subset.refcount = nullptr;
+    subset.data.inlined.length = static_cast<uint8_t>(end - begin);
+    memcpy(subset.data.inlined.bytes, source.data.inlined.bytes + begin,
+           end - begin);
+  }
+  return subset;
+}
+
+grpc_slice grpc_slice_sub(grpc_slice source, size_t begin, size_t end) {
+  grpc_slice subset;
+
+  if (end - begin <= sizeof(subset.data.inlined.bytes)) {
+    subset.refcount = nullptr;
+    subset.data.inlined.length = static_cast<uint8_t>(end - begin);
+    memcpy(subset.data.inlined.bytes, GRPC_SLICE_START_PTR(source) + begin,
+           end - begin);
+  } else {
+    subset = grpc_slice_sub_no_ref(source, begin, end);
+    /* Bump the refcount */
+    subset.refcount->vtable->ref(subset.refcount);
+  }
+  return subset;
+}
+
+grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice* source, size_t split,
+                                           grpc_slice_ref_whom ref_whom) {
+  grpc_slice tail;
+
+  if (source->refcount == nullptr) {
+    /* inlined data, copy it out */
+    GPR_ASSERT(source->data.inlined.length >= split);
+    tail.refcount = nullptr;
+    tail.data.inlined.length =
+        static_cast<uint8_t>(source->data.inlined.length - split);
+    memcpy(tail.data.inlined.bytes, source->data.inlined.bytes + split,
+           tail.data.inlined.length);
+    source->data.inlined.length = static_cast<uint8_t>(split);
+  } else {
+    size_t tail_length = source->data.refcounted.length - split;
+    GPR_ASSERT(source->data.refcounted.length >= split);
+    if (tail_length < sizeof(tail.data.inlined.bytes) &&
+        ref_whom != GRPC_SLICE_REF_TAIL) {
+      /* Copy out the bytes - it'll be cheaper than refcounting */
+      tail.refcount = nullptr;
+      tail.data.inlined.length = static_cast<uint8_t>(tail_length);
+      memcpy(tail.data.inlined.bytes, source->data.refcounted.bytes + split,
+             tail_length);
+      source->refcount = source->refcount->sub_refcount;
+    } else {
+      /* Build the result */
+      switch (ref_whom) {
+        case GRPC_SLICE_REF_TAIL:
+          tail.refcount = source->refcount->sub_refcount;
+          source->refcount = &noop_refcount;
+          break;
+        case GRPC_SLICE_REF_HEAD:
+          tail.refcount = &noop_refcount;
+          source->refcount = source->refcount->sub_refcount;
+          break;
+        case GRPC_SLICE_REF_BOTH:
+          tail.refcount = source->refcount->sub_refcount;
+          source->refcount = source->refcount->sub_refcount;
+          /* Bump the refcount */
+          tail.refcount->vtable->ref(tail.refcount);
+          break;
+      }
+      /* Point into the source array */
+      tail.data.refcounted.bytes = source->data.refcounted.bytes + split;
+      tail.data.refcounted.length = tail_length;
+    }
+    source->data.refcounted.length = split;
+  }
+
+  return tail;
+}
+
+grpc_slice grpc_slice_split_tail(grpc_slice* source, size_t split) {
+  return grpc_slice_split_tail_maybe_ref(source, split, GRPC_SLICE_REF_BOTH);
+}
+
+grpc_slice grpc_slice_split_head(grpc_slice* source, size_t split) {
+  grpc_slice head;
+
+  if (source->refcount == nullptr) {
+    GPR_ASSERT(source->data.inlined.length >= split);
+
+    head.refcount = nullptr;
+    head.data.inlined.length = static_cast<uint8_t>(split);
+    memcpy(head.data.inlined.bytes, source->data.inlined.bytes, split);
+    source->data.inlined.length =
+        static_cast<uint8_t>(source->data.inlined.length - split);
+    memmove(source->data.inlined.bytes, source->data.inlined.bytes + split,
+            source->data.inlined.length);
+  } else if (split < sizeof(head.data.inlined.bytes)) {
+    GPR_ASSERT(source->data.refcounted.length >= split);
+
+    head.refcount = nullptr;
+    head.data.inlined.length = static_cast<uint8_t>(split);
+    memcpy(head.data.inlined.bytes, source->data.refcounted.bytes, split);
+    source->refcount = source->refcount->sub_refcount;
+    source->data.refcounted.bytes += split;
+    source->data.refcounted.length -= split;
+  } else {
+    GPR_ASSERT(source->data.refcounted.length >= split);
+
+    /* Build the result */
+    head.refcount = source->refcount->sub_refcount;
+    /* Bump the refcount */
+    head.refcount->vtable->ref(head.refcount);
+    /* Point into the source array */
+    head.data.refcounted.bytes = source->data.refcounted.bytes;
+    head.data.refcounted.length = split;
+    source->refcount = source->refcount->sub_refcount;
+    source->data.refcounted.bytes += split;
+    source->data.refcounted.length -= split;
+  }
+
+  return head;
+}
+
+int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b) {
+  if (GRPC_SLICE_LENGTH(a) != GRPC_SLICE_LENGTH(b)) return false;
+  if (GRPC_SLICE_LENGTH(a) == 0) return true;
+  return 0 == memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b),
+                     GRPC_SLICE_LENGTH(a));
+}
+
+int grpc_slice_eq(grpc_slice a, grpc_slice b) {
+  if (a.refcount && b.refcount && a.refcount->vtable == b.refcount->vtable) {
+    return a.refcount->vtable->eq(a, b);
+  }
+  return grpc_slice_default_eq_impl(a, b);
+}
+
+int grpc_slice_cmp(grpc_slice a, grpc_slice b) {
+  int d = static_cast<int>(GRPC_SLICE_LENGTH(a) - GRPC_SLICE_LENGTH(b));
+  if (d != 0) return d;
+  return memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b),
+                GRPC_SLICE_LENGTH(a));
+}
+
+int grpc_slice_str_cmp(grpc_slice a, const char* b) {
+  size_t b_length = strlen(b);
+  int d = static_cast<int>(GRPC_SLICE_LENGTH(a) - b_length);
+  if (d != 0) return d;
+  return memcmp(GRPC_SLICE_START_PTR(a), b, b_length);
+}
+
+int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b) {
+  if (a.refcount == nullptr || b.refcount == nullptr) {
+    return grpc_slice_eq(a, b);
+  }
+  return a.data.refcounted.length == b.data.refcounted.length &&
+         a.data.refcounted.bytes == b.data.refcounted.bytes;
+}
+
+int grpc_slice_buf_start_eq(grpc_slice a, const void* b, size_t len) {
+  if (GRPC_SLICE_LENGTH(a) < len) return 0;
+  return 0 == memcmp(GRPC_SLICE_START_PTR(a), b, len);
+}
+
+int grpc_slice_rchr(grpc_slice s, char c) {
+  const char* b = reinterpret_cast<const char*> GRPC_SLICE_START_PTR(s);
+  int i;
+  for (i = static_cast<int> GRPC_SLICE_LENGTH(s) - 1; i != -1 && b[i] != c; i--)
+    ;
+  return i;
+}
+
+int grpc_slice_chr(grpc_slice s, char c) {
+  const char* b = reinterpret_cast<const char*> GRPC_SLICE_START_PTR(s);
+  const char* p = static_cast<const char*>(memchr(b, c, GRPC_SLICE_LENGTH(s)));
+  return p == nullptr ? -1 : static_cast<int>(p - b);
+}
+
+int grpc_slice_slice(grpc_slice haystack, grpc_slice needle) {
+  size_t haystack_len = GRPC_SLICE_LENGTH(haystack);
+  const uint8_t* haystack_bytes = GRPC_SLICE_START_PTR(haystack);
+  size_t needle_len = GRPC_SLICE_LENGTH(needle);
+  const uint8_t* needle_bytes = GRPC_SLICE_START_PTR(needle);
+
+  if (haystack_len == 0 || needle_len == 0) return -1;
+  if (haystack_len < needle_len) return -1;
+  if (haystack_len == needle_len)
+    return grpc_slice_eq(haystack, needle) ? 0 : -1;
+  if (needle_len == 1)
+    return grpc_slice_chr(haystack, static_cast<char>(*needle_bytes));
+
+  const uint8_t* last = haystack_bytes + haystack_len - needle_len;
+  for (const uint8_t* cur = haystack_bytes; cur != last; ++cur) {
+    if (0 == memcmp(cur, needle_bytes, needle_len)) {
+      return static_cast<int>(cur - haystack_bytes);
+    }
+  }
+  return -1;
+}
+
+grpc_slice grpc_slice_dup(grpc_slice a) {
+  grpc_slice copy = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(a));
+  memcpy(GRPC_SLICE_START_PTR(copy), GRPC_SLICE_START_PTR(a),
+         GRPC_SLICE_LENGTH(a));
+  return copy;
+}
diff --git a/third_party/grpc/src/core/lib/slice/slice_buffer.cc b/third_party/grpc/src/core/lib/slice/slice_buffer.cc
new file mode 100644
index 0000000..1f1c08b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice_buffer.cc
@@ -0,0 +1,379 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+/* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */
+#define GROW(x) (3 * (x) / 2)
+
+static void maybe_embiggen(grpc_slice_buffer* sb) {
+  /* How far away from sb->base_slices is sb->slices pointer */
+  size_t slice_offset = static_cast<size_t>(sb->slices - sb->base_slices);
+  size_t slice_count = sb->count + slice_offset;
+
+  if (slice_count == sb->capacity) {
+    if (sb->base_slices != sb->slices) {
+      /* Make room by moving elements if there's still space unused */
+      memmove(sb->base_slices, sb->slices, sb->count * sizeof(grpc_slice));
+      sb->slices = sb->base_slices;
+    } else {
+      /* Allocate more memory if no more space is available */
+      sb->capacity = GROW(sb->capacity);
+      GPR_ASSERT(sb->capacity > slice_count);
+      if (sb->base_slices == sb->inlined) {
+        sb->base_slices = static_cast<grpc_slice*>(
+            gpr_malloc(sb->capacity * sizeof(grpc_slice)));
+        memcpy(sb->base_slices, sb->inlined, slice_count * sizeof(grpc_slice));
+      } else {
+        sb->base_slices = static_cast<grpc_slice*>(
+            gpr_realloc(sb->base_slices, sb->capacity * sizeof(grpc_slice)));
+      }
+
+      sb->slices = sb->base_slices + slice_offset;
+    }
+  }
+}
+
+void grpc_slice_buffer_init(grpc_slice_buffer* sb) {
+  sb->count = 0;
+  sb->length = 0;
+  sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS;
+  sb->base_slices = sb->slices = sb->inlined;
+}
+
+void grpc_slice_buffer_destroy_internal(grpc_slice_buffer* sb) {
+  grpc_slice_buffer_reset_and_unref_internal(sb);
+  if (sb->base_slices != sb->inlined) {
+    gpr_free(sb->base_slices);
+  }
+}
+
+void grpc_slice_buffer_destroy(grpc_slice_buffer* sb) {
+  if (grpc_core::ExecCtx::Get() == nullptr) {
+    grpc_core::ExecCtx exec_ctx;
+    grpc_slice_buffer_destroy_internal(sb);
+  } else {
+    grpc_slice_buffer_destroy_internal(sb);
+  }
+}
+
+uint8_t* grpc_slice_buffer_tiny_add(grpc_slice_buffer* sb, size_t n) {
+  grpc_slice* back;
+  uint8_t* out;
+
+  sb->length += n;
+
+  if (sb->count == 0) goto add_new;
+  back = &sb->slices[sb->count - 1];
+  if (back->refcount) goto add_new;
+  if ((back->data.inlined.length + n) > sizeof(back->data.inlined.bytes))
+    goto add_new;
+  out = back->data.inlined.bytes + back->data.inlined.length;
+  back->data.inlined.length =
+      static_cast<uint8_t>(back->data.inlined.length + n);
+  return out;
+
+add_new:
+  maybe_embiggen(sb);
+  back = &sb->slices[sb->count];
+  sb->count++;
+  back->refcount = nullptr;
+  back->data.inlined.length = static_cast<uint8_t>(n);
+  return back->data.inlined.bytes;
+}
+
+size_t grpc_slice_buffer_add_indexed(grpc_slice_buffer* sb, grpc_slice s) {
+  size_t out = sb->count;
+  maybe_embiggen(sb);
+  sb->slices[out] = s;
+  sb->length += GRPC_SLICE_LENGTH(s);
+  sb->count = out + 1;
+  return out;
+}
+
+void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_slice s) {
+  size_t n = sb->count;
+  /* if both the last slice in the slice buffer and the slice being added
+     are inlined (that is, that they carry their data inside the slice data
+     structure), and the back slice is not full, then concatenate directly
+     into the back slice, preventing many small slices being passed into
+     writes */
+  if (!s.refcount && n) {
+    grpc_slice* back = &sb->slices[n - 1];
+    if (!back->refcount &&
+        back->data.inlined.length < GRPC_SLICE_INLINED_SIZE) {
+      if (s.data.inlined.length + back->data.inlined.length <=
+          GRPC_SLICE_INLINED_SIZE) {
+        memcpy(back->data.inlined.bytes + back->data.inlined.length,
+               s.data.inlined.bytes, s.data.inlined.length);
+        back->data.inlined.length = static_cast<uint8_t>(
+            back->data.inlined.length + s.data.inlined.length);
+      } else {
+        size_t cp1 = GRPC_SLICE_INLINED_SIZE - back->data.inlined.length;
+        memcpy(back->data.inlined.bytes + back->data.inlined.length,
+               s.data.inlined.bytes, cp1);
+        back->data.inlined.length = GRPC_SLICE_INLINED_SIZE;
+        maybe_embiggen(sb);
+        back = &sb->slices[n];
+        sb->count = n + 1;
+        back->refcount = nullptr;
+        back->data.inlined.length =
+            static_cast<uint8_t>(s.data.inlined.length - cp1);
+        memcpy(back->data.inlined.bytes, s.data.inlined.bytes + cp1,
+               s.data.inlined.length - cp1);
+      }
+      sb->length += s.data.inlined.length;
+      return; /* early out */
+    }
+  }
+  grpc_slice_buffer_add_indexed(sb, s);
+}
+
+void grpc_slice_buffer_addn(grpc_slice_buffer* sb, grpc_slice* s, size_t n) {
+  size_t i;
+  for (i = 0; i < n; i++) {
+    grpc_slice_buffer_add(sb, s[i]);
+  }
+}
+
+void grpc_slice_buffer_pop(grpc_slice_buffer* sb) {
+  if (sb->count != 0) {
+    size_t count = --sb->count;
+    sb->length -= GRPC_SLICE_LENGTH(sb->slices[count]);
+  }
+}
+
+void grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer* sb) {
+  size_t i;
+  for (i = 0; i < sb->count; i++) {
+    grpc_slice_unref_internal(sb->slices[i]);
+  }
+
+  sb->count = 0;
+  sb->length = 0;
+}
+
+void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb) {
+  if (grpc_core::ExecCtx::Get() == nullptr) {
+    grpc_core::ExecCtx exec_ctx;
+    grpc_slice_buffer_reset_and_unref_internal(sb);
+  } else {
+    grpc_slice_buffer_reset_and_unref_internal(sb);
+  }
+}
+
+void grpc_slice_buffer_swap(grpc_slice_buffer* a, grpc_slice_buffer* b) {
+  size_t a_offset = static_cast<size_t>(a->slices - a->base_slices);
+  size_t b_offset = static_cast<size_t>(b->slices - b->base_slices);
+
+  size_t a_count = a->count + a_offset;
+  size_t b_count = b->count + b_offset;
+
+  if (a->base_slices == a->inlined) {
+    if (b->base_slices == b->inlined) {
+      /* swap contents of inlined buffer */
+      grpc_slice temp[GRPC_SLICE_BUFFER_INLINE_ELEMENTS];
+      memcpy(temp, a->base_slices, a_count * sizeof(grpc_slice));
+      memcpy(a->base_slices, b->base_slices, b_count * sizeof(grpc_slice));
+      memcpy(b->base_slices, temp, a_count * sizeof(grpc_slice));
+    } else {
+      /* a is inlined, b is not - copy a inlined into b, fix pointers */
+      a->base_slices = b->base_slices;
+      b->base_slices = b->inlined;
+      memcpy(b->base_slices, a->inlined, a_count * sizeof(grpc_slice));
+    }
+  } else if (b->base_slices == b->inlined) {
+    /* b is inlined, a is not - copy b inlined int a, fix pointers */
+    b->base_slices = a->base_slices;
+    a->base_slices = a->inlined;
+    memcpy(a->base_slices, b->inlined, b_count * sizeof(grpc_slice));
+  } else {
+    /* no inlining: easy swap */
+    GPR_SWAP(grpc_slice*, a->base_slices, b->base_slices);
+  }
+
+  /* Update the slices pointers (cannot do a GPR_SWAP on slices fields here).
+   * Also note that since the base_slices pointers are already swapped we need
+   * use 'b_offset' for 'a->base_slices' and vice versa */
+  a->slices = a->base_slices + b_offset;
+  b->slices = b->base_slices + a_offset;
+
+  /* base_slices and slices fields are correctly set. Swap all other fields */
+  GPR_SWAP(size_t, a->count, b->count);
+  GPR_SWAP(size_t, a->capacity, b->capacity);
+  GPR_SWAP(size_t, a->length, b->length);
+}
+
+void grpc_slice_buffer_move_into(grpc_slice_buffer* src,
+                                 grpc_slice_buffer* dst) {
+  /* anything to move? */
+  if (src->count == 0) {
+    return;
+  }
+  /* anything in dst? */
+  if (dst->count == 0) {
+    grpc_slice_buffer_swap(src, dst);
+    return;
+  }
+  /* both buffers have data - copy, and reset src */
+  grpc_slice_buffer_addn(dst, src->slices, src->count);
+  src->count = 0;
+  src->length = 0;
+}
+
+static void slice_buffer_move_first_maybe_ref(grpc_slice_buffer* src, size_t n,
+                                              grpc_slice_buffer* dst,
+                                              bool incref) {
+  GPR_ASSERT(src->length >= n);
+  if (src->length == n) {
+    grpc_slice_buffer_move_into(src, dst);
+    return;
+  }
+
+  size_t output_len = dst->length + n;
+  size_t new_input_len = src->length - n;
+
+  while (src->count > 0) {
+    grpc_slice slice = grpc_slice_buffer_take_first(src);
+    size_t slice_len = GRPC_SLICE_LENGTH(slice);
+    if (n > slice_len) {
+      grpc_slice_buffer_add(dst, slice);
+      n -= slice_len;
+    } else if (n == slice_len) {
+      grpc_slice_buffer_add(dst, slice);
+      break;
+    } else if (incref) { /* n < slice_len */
+      grpc_slice_buffer_undo_take_first(
+          src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_BOTH));
+      GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n);
+      grpc_slice_buffer_add(dst, slice);
+      break;
+    } else { /* n < slice_len */
+      grpc_slice_buffer_undo_take_first(
+          src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_TAIL));
+      GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n);
+      grpc_slice_buffer_add_indexed(dst, slice);
+      break;
+    }
+  }
+  GPR_ASSERT(dst->length == output_len);
+  GPR_ASSERT(src->length == new_input_len);
+  GPR_ASSERT(src->count > 0);
+}
+
+void grpc_slice_buffer_move_first(grpc_slice_buffer* src, size_t n,
+                                  grpc_slice_buffer* dst) {
+  slice_buffer_move_first_maybe_ref(src, n, dst, true);
+}
+
+void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer* src, size_t n,
+                                         grpc_slice_buffer* dst) {
+  slice_buffer_move_first_maybe_ref(src, n, dst, false);
+}
+
+void grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer* src, size_t n,
+                                              void* dst) {
+  char* dstp = static_cast<char*>(dst);
+  GPR_ASSERT(src->length >= n);
+
+  while (n > 0) {
+    grpc_slice slice = grpc_slice_buffer_take_first(src);
+    size_t slice_len = GRPC_SLICE_LENGTH(slice);
+    if (slice_len > n) {
+      memcpy(dstp, GRPC_SLICE_START_PTR(slice), n);
+      grpc_slice_buffer_undo_take_first(
+          src, grpc_slice_sub_no_ref(slice, n, slice_len));
+      n = 0;
+    } else if (slice_len == n) {
+      memcpy(dstp, GRPC_SLICE_START_PTR(slice), n);
+      grpc_slice_unref_internal(slice);
+      n = 0;
+    } else {
+      memcpy(dstp, GRPC_SLICE_START_PTR(slice), slice_len);
+      dstp += slice_len;
+      n -= slice_len;
+      grpc_slice_unref_internal(slice);
+    }
+  }
+}
+
+void grpc_slice_buffer_trim_end(grpc_slice_buffer* sb, size_t n,
+                                grpc_slice_buffer* garbage) {
+  GPR_ASSERT(n <= sb->length);
+  sb->length -= n;
+  for (;;) {
+    size_t idx = sb->count - 1;
+    grpc_slice slice = sb->slices[idx];
+    size_t slice_len = GRPC_SLICE_LENGTH(slice);
+    if (slice_len > n) {
+      sb->slices[idx] = grpc_slice_split_head(&slice, slice_len - n);
+      if (garbage) {
+        grpc_slice_buffer_add_indexed(garbage, slice);
+      } else {
+        grpc_slice_unref_internal(slice);
+      }
+      return;
+    } else if (slice_len == n) {
+      if (garbage) {
+        grpc_slice_buffer_add_indexed(garbage, slice);
+      } else {
+        grpc_slice_unref_internal(slice);
+      }
+      sb->count = idx;
+      return;
+    } else {
+      if (garbage) {
+        grpc_slice_buffer_add_indexed(garbage, slice);
+      } else {
+        grpc_slice_unref_internal(slice);
+      }
+      n -= slice_len;
+      sb->count = idx;
+    }
+  }
+}
+
+grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer* sb) {
+  grpc_slice slice;
+  GPR_ASSERT(sb->count > 0);
+  slice = sb->slices[0];
+  sb->slices++;
+  sb->count--;
+  sb->length -= GRPC_SLICE_LENGTH(slice);
+
+  return slice;
+}
+
+void grpc_slice_buffer_undo_take_first(grpc_slice_buffer* sb,
+                                       grpc_slice slice) {
+  sb->slices--;
+  sb->slices[0] = slice;
+  sb->count++;
+  sb->length += GRPC_SLICE_LENGTH(slice);
+}
diff --git a/third_party/grpc/src/core/lib/slice/slice_hash_table.h b/third_party/grpc/src/core/lib/slice/slice_hash_table.h
new file mode 100644
index 0000000..4bbcf88
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice_hash_table.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H
+#define GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+/// Hash table implementation.
+///
+/// This implementation uses open addressing
+/// (https://en.wikipedia.org/wiki/Open_addressing) with linear
+/// probing (https://en.wikipedia.org/wiki/Linear_probing).
+///
+/// The keys are \a grpc_slice objects.  The values can be any type.
+///
+/// Hash tables are intentionally immutable, to avoid the need for locking.
+
+namespace grpc_core {
+
+template <typename T>
+class SliceHashTable : public RefCounted<SliceHashTable<T>> {
+ public:
+  struct Entry {
+    grpc_slice key;
+    T value;
+    bool is_set;
+  };
+
+  // Function for comparing values.
+  // TODO(roth): Eliminate this and the Cmp() method from this API once
+  // grpc_channel_args is redesigned to require that keys are unique.
+  typedef int (*ValueCmp)(const T&, const T&);
+
+  /// Creates a new hash table containing \a entries, which is an array
+  /// of length \a num_entries.  Takes ownership of all keys and values in \a
+  /// entries.  If not null, \a value_cmp will be used to compare values in
+  /// the context of \a Cmp(). If null, raw pointer (\a GPR_ICMP) comparison
+  /// will be used.
+  static RefCountedPtr<SliceHashTable> Create(size_t num_entries,
+                                              Entry* entries,
+                                              ValueCmp value_cmp);
+
+  /// Returns the value from the table associated with \a key.
+  /// Returns null if \a key is not found.
+  const T* Get(const grpc_slice& key) const;
+
+  /// Compares \a a vs. \a b.
+  /// A table is considered "smaller" (resp. "greater") if:
+  ///  - GPR_ICMP(a->value_cmp, b->value_cmp) < 1 (resp. > 1),
+  ///  - else, it contains fewer (resp. more) entries,
+  ///  - else, if strcmp(a_key, b_key) < 1 (resp. > 1),
+  ///  - else, if value_cmp(a_value, b_value) < 1 (resp. > 1).
+  static int Cmp(const SliceHashTable& a, const SliceHashTable& b);
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T2, typename... Args>
+  friend T2* New(Args&&... args);
+
+  // So Delete() can call our private dtor.
+  template <typename T2>
+  friend void Delete(T2*);
+
+  SliceHashTable(size_t num_entries, Entry* entries, ValueCmp value_cmp);
+  virtual ~SliceHashTable();
+
+  void Add(grpc_slice key, T& value);
+
+  // Default value comparison function, if none specified by caller.
+  static int DefaultValueCmp(const T& a, const T& b) { return GPR_ICMP(a, b); }
+
+  const ValueCmp value_cmp_;
+  const size_t size_;
+  size_t max_num_probes_;
+  Entry* entries_;
+};
+
+//
+// implementation -- no user-serviceable parts below
+//
+
+template <typename T>
+RefCountedPtr<SliceHashTable<T>> SliceHashTable<T>::Create(size_t num_entries,
+                                                           Entry* entries,
+                                                           ValueCmp value_cmp) {
+  return MakeRefCounted<SliceHashTable<T>>(num_entries, entries, value_cmp);
+}
+
+template <typename T>
+SliceHashTable<T>::SliceHashTable(size_t num_entries, Entry* entries,
+                                  ValueCmp value_cmp)
+    : value_cmp_(value_cmp),
+      // Keep load factor low to improve performance of lookups.
+      size_(num_entries * 2),
+      max_num_probes_(0) {
+  entries_ = static_cast<Entry*>(gpr_zalloc(sizeof(Entry) * size_));
+  for (size_t i = 0; i < num_entries; ++i) {
+    Entry* entry = &entries[i];
+    Add(entry->key, entry->value);
+  }
+}
+
+template <typename T>
+SliceHashTable<T>::~SliceHashTable() {
+  for (size_t i = 0; i < size_; ++i) {
+    Entry& entry = entries_[i];
+    if (entry.is_set) {
+      grpc_slice_unref_internal(entry.key);
+      entry.value.~T();
+    }
+  }
+  gpr_free(entries_);
+}
+
+template <typename T>
+void SliceHashTable<T>::Add(grpc_slice key, T& value) {
+  const size_t hash = grpc_slice_hash(key);
+  for (size_t offset = 0; offset < size_; ++offset) {
+    const size_t idx = (hash + offset) % size_;
+    if (!entries_[idx].is_set) {
+      entries_[idx].is_set = true;
+      entries_[idx].key = key;
+      entries_[idx].value = std::move(value);
+      // Keep track of the maximum number of probes needed, since this
+      // provides an upper bound for lookups.
+      if (offset > max_num_probes_) max_num_probes_ = offset;
+      return;
+    }
+  }
+  GPR_ASSERT(false);  // Table should never be full.
+}
+
+template <typename T>
+const T* SliceHashTable<T>::Get(const grpc_slice& key) const {
+  const size_t hash = grpc_slice_hash(key);
+  // We cap the number of probes at the max number recorded when
+  // populating the table.
+  for (size_t offset = 0; offset <= max_num_probes_; ++offset) {
+    const size_t idx = (hash + offset) % size_;
+    if (!entries_[idx].is_set) break;
+    if (grpc_slice_eq(entries_[idx].key, key)) {
+      return &entries_[idx].value;
+    }
+  }
+  return nullptr;  // Not found.
+}
+
+template <typename T>
+int SliceHashTable<T>::Cmp(const SliceHashTable& a, const SliceHashTable& b) {
+  ValueCmp value_cmp_a =
+      a.value_cmp_ != nullptr ? a.value_cmp_ : DefaultValueCmp;
+  ValueCmp value_cmp_b =
+      b.value_cmp_ != nullptr ? b.value_cmp_ : DefaultValueCmp;
+  // Compare value_fns
+  const int value_fns_cmp = GPR_ICMP((void*)value_cmp_a, (void*)value_cmp_b);
+  if (value_fns_cmp != 0) return value_fns_cmp;
+  // Compare sizes
+  if (a.size_ < b.size_) return -1;
+  if (a.size_ > b.size_) return 1;
+  // Compare rows.
+  for (size_t i = 0; i < a.size_; ++i) {
+    if (!a.entries_[i].is_set) {
+      if (b.entries_[i].is_set) {
+        return -1;  // a empty but b non-empty
+      }
+      continue;  // both empty, no need to check key or value
+    } else if (!b.entries_[i].is_set) {
+      return 1;  // a non-empty but b empty
+    }
+    // neither entry is empty
+    const int key_cmp = grpc_slice_cmp(a.entries_[i].key, b.entries_[i].key);
+    if (key_cmp != 0) return key_cmp;
+    const int value_cmp = value_cmp_a(a.entries_[i].value, b.entries_[i].value);
+    if (value_cmp != 0) return value_cmp;
+  }
+  return 0;
+}
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H */
diff --git a/third_party/grpc/src/core/lib/slice/slice_intern.cc b/third_party/grpc/src/core/lib/slice/slice_intern.cc
new file mode 100644
index 0000000..e53c040
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice_intern.cc
@@ -0,0 +1,332 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/murmur_hash.h"
+#include "src/core/lib/iomgr/iomgr_internal.h" /* for iomgr_abort_on_leaks() */
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+#define LOG2_SHARD_COUNT 5
+#define SHARD_COUNT (1 << LOG2_SHARD_COUNT)
+#define INITIAL_SHARD_CAPACITY 8
+
+#define TABLE_IDX(hash, capacity) (((hash) >> LOG2_SHARD_COUNT) % (capacity))
+#define SHARD_IDX(hash) ((hash) & ((1 << LOG2_SHARD_COUNT) - 1))
+
+typedef struct interned_slice_refcount {
+  grpc_slice_refcount base;
+  grpc_slice_refcount sub;
+  size_t length;
+  gpr_atm refcnt;
+  uint32_t hash;
+  struct interned_slice_refcount* bucket_next;
+} interned_slice_refcount;
+
+typedef struct slice_shard {
+  gpr_mu mu;
+  interned_slice_refcount** strs;
+  size_t count;
+  size_t capacity;
+} slice_shard;
+
+/* hash seed: decided at initialization time */
+static uint32_t g_hash_seed;
+static int g_forced_hash_seed = 0;
+
+static slice_shard g_shards[SHARD_COUNT];
+
+typedef struct {
+  uint32_t hash;
+  uint32_t idx;
+} static_metadata_hash_ent;
+
+static static_metadata_hash_ent
+    static_metadata_hash[4 * GRPC_STATIC_MDSTR_COUNT];
+static uint32_t max_static_metadata_hash_probe;
+static uint32_t static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT];
+
+static void interned_slice_ref(void* p) {
+  interned_slice_refcount* s = static_cast<interned_slice_refcount*>(p);
+  GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) > 0);
+}
+
+static void interned_slice_destroy(interned_slice_refcount* s) {
+  slice_shard* shard = &g_shards[SHARD_IDX(s->hash)];
+  gpr_mu_lock(&shard->mu);
+  GPR_ASSERT(0 == gpr_atm_no_barrier_load(&s->refcnt));
+  interned_slice_refcount** prev_next;
+  interned_slice_refcount* cur;
+  for (prev_next = &shard->strs[TABLE_IDX(s->hash, shard->capacity)],
+      cur = *prev_next;
+       cur != s; prev_next = &cur->bucket_next, cur = cur->bucket_next)
+    ;
+  *prev_next = cur->bucket_next;
+  shard->count--;
+  gpr_free(s);
+  gpr_mu_unlock(&shard->mu);
+}
+
+static void interned_slice_unref(void* p) {
+  interned_slice_refcount* s = static_cast<interned_slice_refcount*>(p);
+  if (1 == gpr_atm_full_fetch_add(&s->refcnt, -1)) {
+    interned_slice_destroy(s);
+  }
+}
+
+static void interned_slice_sub_ref(void* p) {
+  interned_slice_ref((static_cast<char*>(p)) -
+                     offsetof(interned_slice_refcount, sub));
+}
+
+static void interned_slice_sub_unref(void* p) {
+  interned_slice_unref((static_cast<char*>(p)) -
+                       offsetof(interned_slice_refcount, sub));
+}
+
+static uint32_t interned_slice_hash(grpc_slice slice) {
+  interned_slice_refcount* s =
+      reinterpret_cast<interned_slice_refcount*>(slice.refcount);
+  return s->hash;
+}
+
+static int interned_slice_eq(grpc_slice a, grpc_slice b) {
+  return a.refcount == b.refcount;
+}
+
+static const grpc_slice_refcount_vtable interned_slice_vtable = {
+    interned_slice_ref, interned_slice_unref, interned_slice_eq,
+    interned_slice_hash};
+static const grpc_slice_refcount_vtable interned_slice_sub_vtable = {
+    interned_slice_sub_ref, interned_slice_sub_unref,
+    grpc_slice_default_eq_impl, grpc_slice_default_hash_impl};
+
+static void grow_shard(slice_shard* shard) {
+  GPR_TIMER_SCOPE("grow_strtab", 0);
+
+  size_t capacity = shard->capacity * 2;
+  size_t i;
+  interned_slice_refcount** strtab;
+  interned_slice_refcount *s, *next;
+
+  strtab = static_cast<interned_slice_refcount**>(
+      gpr_zalloc(sizeof(interned_slice_refcount*) * capacity));
+
+  for (i = 0; i < shard->capacity; i++) {
+    for (s = shard->strs[i]; s; s = next) {
+      size_t idx = TABLE_IDX(s->hash, capacity);
+      next = s->bucket_next;
+      s->bucket_next = strtab[idx];
+      strtab[idx] = s;
+    }
+  }
+  gpr_free(shard->strs);
+  shard->strs = strtab;
+  shard->capacity = capacity;
+}
+
+static grpc_slice materialize(interned_slice_refcount* s) {
+  grpc_slice slice;
+  slice.refcount = &s->base;
+  slice.data.refcounted.bytes = reinterpret_cast<uint8_t*>(s + 1);
+  slice.data.refcounted.length = s->length;
+  return slice;
+}
+
+uint32_t grpc_slice_default_hash_impl(grpc_slice s) {
+  return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s),
+                          g_hash_seed);
+}
+
+uint32_t grpc_static_slice_hash(grpc_slice s) {
+  return static_metadata_hash_values[GRPC_STATIC_METADATA_INDEX(s)];
+}
+
+int grpc_static_slice_eq(grpc_slice a, grpc_slice b) {
+  return GRPC_STATIC_METADATA_INDEX(a) == GRPC_STATIC_METADATA_INDEX(b);
+}
+
+uint32_t grpc_slice_hash(grpc_slice s) {
+  return s.refcount == nullptr ? grpc_slice_default_hash_impl(s)
+                               : s.refcount->vtable->hash(s);
+}
+
+grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice,
+                                          bool* returned_slice_is_different) {
+  if (GRPC_IS_STATIC_METADATA_STRING(slice)) {
+    return slice;
+  }
+
+  uint32_t hash = grpc_slice_hash(slice);
+  for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) {
+    static_metadata_hash_ent ent =
+        static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)];
+    if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT &&
+        grpc_slice_eq(grpc_static_slice_table[ent.idx], slice)) {
+      *returned_slice_is_different = true;
+      return grpc_static_slice_table[ent.idx];
+    }
+  }
+
+  return slice;
+}
+
+bool grpc_slice_is_interned(grpc_slice slice) {
+  return (slice.refcount && slice.refcount->vtable == &interned_slice_vtable) ||
+         GRPC_IS_STATIC_METADATA_STRING(slice);
+}
+
+grpc_slice grpc_slice_intern(grpc_slice slice) {
+  GPR_TIMER_SCOPE("grpc_slice_intern", 0);
+  if (GRPC_IS_STATIC_METADATA_STRING(slice)) {
+    return slice;
+  }
+
+  uint32_t hash = grpc_slice_hash(slice);
+  for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) {
+    static_metadata_hash_ent ent =
+        static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)];
+    if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT &&
+        grpc_slice_eq(grpc_static_slice_table[ent.idx], slice)) {
+      return grpc_static_slice_table[ent.idx];
+    }
+  }
+
+  interned_slice_refcount* s;
+  slice_shard* shard = &g_shards[SHARD_IDX(hash)];
+
+  gpr_mu_lock(&shard->mu);
+
+  /* search for an existing string */
+  size_t idx = TABLE_IDX(hash, shard->capacity);
+  for (s = shard->strs[idx]; s; s = s->bucket_next) {
+    if (s->hash == hash && grpc_slice_eq(slice, materialize(s))) {
+      if (gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) == 0) {
+        /* If we get here, we've added a ref to something that was about to
+         * die - drop it immediately.
+         * The *only* possible path here (given the shard mutex) should be to
+         * drop from one ref back to zero - assert that with a CAS */
+        GPR_ASSERT(gpr_atm_rel_cas(&s->refcnt, 1, 0));
+        /* and treat this as if we were never here... sshhh */
+      } else {
+        gpr_mu_unlock(&shard->mu);
+        return materialize(s);
+      }
+    }
+  }
+
+  /* not found: create a new string */
+  /* string data goes after the internal_string header */
+  s = static_cast<interned_slice_refcount*>(
+      gpr_malloc(sizeof(*s) + GRPC_SLICE_LENGTH(slice)));
+  gpr_atm_rel_store(&s->refcnt, 1);
+  s->length = GRPC_SLICE_LENGTH(slice);
+  s->hash = hash;
+  s->base.vtable = &interned_slice_vtable;
+  s->base.sub_refcount = &s->sub;
+  s->sub.vtable = &interned_slice_sub_vtable;
+  s->sub.sub_refcount = &s->sub;
+  s->bucket_next = shard->strs[idx];
+  shard->strs[idx] = s;
+  memcpy(s + 1, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice));
+
+  shard->count++;
+
+  if (shard->count > shard->capacity * 2) {
+    grow_shard(shard);
+  }
+
+  gpr_mu_unlock(&shard->mu);
+
+  return materialize(s);
+}
+
+void grpc_test_only_set_slice_hash_seed(uint32_t seed) {
+  g_hash_seed = seed;
+  g_forced_hash_seed = 1;
+}
+
+void grpc_slice_intern_init(void) {
+  if (!g_forced_hash_seed) {
+    g_hash_seed = static_cast<uint32_t>(gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
+  }
+  for (size_t i = 0; i < SHARD_COUNT; i++) {
+    slice_shard* shard = &g_shards[i];
+    gpr_mu_init(&shard->mu);
+    shard->count = 0;
+    shard->capacity = INITIAL_SHARD_CAPACITY;
+    shard->strs = static_cast<interned_slice_refcount**>(
+        gpr_zalloc(sizeof(*shard->strs) * shard->capacity));
+  }
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(static_metadata_hash); i++) {
+    static_metadata_hash[i].hash = 0;
+    static_metadata_hash[i].idx = GRPC_STATIC_MDSTR_COUNT;
+  }
+  max_static_metadata_hash_probe = 0;
+  for (size_t i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
+    static_metadata_hash_values[i] =
+        grpc_slice_default_hash_impl(grpc_static_slice_table[i]);
+    for (size_t j = 0; j < GPR_ARRAY_SIZE(static_metadata_hash); j++) {
+      size_t slot = (static_metadata_hash_values[i] + j) %
+                    GPR_ARRAY_SIZE(static_metadata_hash);
+      if (static_metadata_hash[slot].idx == GRPC_STATIC_MDSTR_COUNT) {
+        static_metadata_hash[slot].hash = static_metadata_hash_values[i];
+        static_metadata_hash[slot].idx = static_cast<uint32_t>(i);
+        if (j > max_static_metadata_hash_probe) {
+          max_static_metadata_hash_probe = static_cast<uint32_t>(j);
+        }
+        break;
+      }
+    }
+  }
+}
+
+void grpc_slice_intern_shutdown(void) {
+  for (size_t i = 0; i < SHARD_COUNT; i++) {
+    slice_shard* shard = &g_shards[i];
+    gpr_mu_destroy(&shard->mu);
+    /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
+    if (shard->count != 0) {
+      gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata strings were leaked",
+              shard->count);
+      for (size_t j = 0; j < shard->capacity; j++) {
+        for (interned_slice_refcount* s = shard->strs[j]; s;
+             s = s->bucket_next) {
+          char* text =
+              grpc_dump_slice(materialize(s), GPR_DUMP_HEX | GPR_DUMP_ASCII);
+          gpr_log(GPR_DEBUG, "LEAKED: %s", text);
+          gpr_free(text);
+        }
+      }
+      if (grpc_iomgr_abort_on_leaks()) {
+        abort();
+      }
+    }
+    gpr_free(shard->strs);
+  }
+}
diff --git a/third_party/grpc/src/core/lib/slice/slice_internal.h b/third_party/grpc/src/core/lib/slice/slice_internal.h
new file mode 100644
index 0000000..5b05951
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice_internal.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H
+#define GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+
+grpc_slice grpc_slice_ref_internal(grpc_slice slice);
+void grpc_slice_unref_internal(grpc_slice slice);
+void grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer* sb);
+void grpc_slice_buffer_partial_unref_internal(grpc_slice_buffer* sb,
+                                              size_t idx);
+void grpc_slice_buffer_destroy_internal(grpc_slice_buffer* sb);
+
+/* Check if a slice is interned */
+bool grpc_slice_is_interned(grpc_slice slice);
+
+void grpc_slice_intern_init(void);
+void grpc_slice_intern_shutdown(void);
+void grpc_test_only_set_slice_hash_seed(uint32_t key);
+// if slice matches a static slice, returns the static slice
+// otherwise returns the passed in slice (without reffing it)
+// used for surface boundaries where we might receive an un-interned static
+// string
+grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice,
+                                          bool* returned_slice_is_different);
+uint32_t grpc_static_slice_hash(grpc_slice s);
+int grpc_static_slice_eq(grpc_slice a, grpc_slice b);
+
+// Returns the memory used by this slice, not counting the slice structure
+// itself. This means that inlined and slices from static strings will return
+// 0. All other slices will return the size of the allocated chars.
+size_t grpc_slice_memory_usage(grpc_slice s);
+
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H */
diff --git a/third_party/grpc/src/core/lib/slice/slice_string_helpers.cc b/third_party/grpc/src/core/lib/slice/slice_string_helpers.cc
new file mode 100644
index 0000000..6af9c33
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice_string_helpers.cc
@@ -0,0 +1,118 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+char* grpc_dump_slice(grpc_slice s, uint32_t flags) {
+  return gpr_dump(reinterpret_cast<const char*> GRPC_SLICE_START_PTR(s),
+                  GRPC_SLICE_LENGTH(s), flags);
+}
+
+/** Finds the initial (\a begin) and final (\a end) offsets of the next
+ * substring from \a str + \a read_offset until the next \a sep or the end of \a
+ * str.
+ *
+ * Returns 1 and updates \a begin and \a end. Returns 0 otherwise. */
+static int slice_find_separator_offset(const grpc_slice str, const char* sep,
+                                       const size_t read_offset, size_t* begin,
+                                       size_t* end) {
+  size_t i;
+  const uint8_t* str_ptr = GRPC_SLICE_START_PTR(str) + read_offset;
+  const size_t str_len = GRPC_SLICE_LENGTH(str) - read_offset;
+  const size_t sep_len = strlen(sep);
+  if (str_len < sep_len) {
+    return 0;
+  }
+
+  for (i = 0; i <= str_len - sep_len; i++) {
+    if (memcmp(str_ptr + i, sep, sep_len) == 0) {
+      *begin = read_offset;
+      *end = read_offset + i;
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static void skip_leading_trailing_spaces(const uint8_t* str_buffer,
+                                         size_t* begin, size_t* end) {
+  while (*begin < *end && str_buffer[*begin] == ' ') {
+    (*begin)++;
+  }
+  while (*begin < *end && str_buffer[*end - 1] == ' ') {
+    (*end)--;
+  }
+}
+
+static void grpc_slice_split_inner(grpc_slice str, const char* sep,
+                                   grpc_slice_buffer* dst, bool no_space) {
+  const size_t sep_len = strlen(sep);
+  size_t begin, end;
+  const uint8_t* str_buffer = GRPC_SLICE_START_PTR(str);
+  size_t sep_pos;
+
+  GPR_ASSERT(sep_len > 0);
+
+  if (slice_find_separator_offset(str, sep, 0, &begin, &end) != 0) {
+    do {
+      sep_pos = end;
+      if (no_space) {
+        skip_leading_trailing_spaces(str_buffer, &begin, &end);
+      }
+      grpc_slice_buffer_add_indexed(dst, grpc_slice_sub(str, begin, end));
+    } while (slice_find_separator_offset(str, sep, sep_pos + sep_len, &begin,
+                                         &end) != 0);
+    begin = sep_pos + sep_len;
+    end = GRPC_SLICE_LENGTH(str);
+    if (no_space) {
+      skip_leading_trailing_spaces(str_buffer, &begin, &end);
+    }
+    grpc_slice_buffer_add_indexed(dst, grpc_slice_sub(str, begin, end));
+  } else { /* no sep found, add whole input */
+    begin = 0;
+    end = GRPC_SLICE_LENGTH(str);
+    if (no_space) {
+      skip_leading_trailing_spaces(str_buffer, &begin, &end);
+    }
+    grpc_slice_buffer_add_indexed(dst, grpc_slice_sub(str, begin, end));
+  }
+}
+
+void grpc_slice_split(grpc_slice str, const char* sep, grpc_slice_buffer* dst) {
+  grpc_slice_split_inner(str, sep, dst, false);
+}
+
+void grpc_slice_split_without_space(grpc_slice str, const char* sep,
+                                    grpc_slice_buffer* dst) {
+  grpc_slice_split_inner(str, sep, dst, true);
+}
+
+bool grpc_parse_slice_to_uint32(grpc_slice str, uint32_t* result) {
+  return gpr_parse_bytes_to_uint32(
+             reinterpret_cast<const char*> GRPC_SLICE_START_PTR(str),
+             GRPC_SLICE_LENGTH(str), result) != 0;
+}
diff --git a/third_party/grpc/src/core/lib/slice/slice_string_helpers.h b/third_party/grpc/src/core/lib/slice/slice_string_helpers.h
new file mode 100644
index 0000000..976f724
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice_string_helpers.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H
+#define GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+
+#include "src/core/lib/gpr/string.h"
+
+/* Calls gpr_dump on a slice. */
+char* grpc_dump_slice(grpc_slice slice, uint32_t flags);
+
+/** Split \a str by the separator \a sep. Results are stored in \a dst, which
+ * should be a properly initialized instance. */
+void grpc_slice_split(grpc_slice str, const char* sep, grpc_slice_buffer* dst);
+
+/** Split \a str by the separator \a sep and remove the leading and trailing
+ * spaces of each resulting token. Results are stored in \a dst, which should be
+ * a properly initialized instance. */
+void grpc_slice_split_without_space(grpc_slice str, const char* sep,
+                                    grpc_slice_buffer* dst);
+
+bool grpc_parse_slice_to_uint32(grpc_slice str, uint32_t* result);
+
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H */
diff --git a/third_party/grpc/src/core/lib/slice/slice_traits.h b/third_party/grpc/src/core/lib/slice/slice_traits.h
new file mode 100644
index 0000000..ee01916
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice_traits.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H
+#define GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include <stdbool.h>
+
+bool grpc_slice_is_legal_header(grpc_slice s);
+bool grpc_slice_is_legal_nonbin_header(grpc_slice s);
+bool grpc_slice_is_bin_suffixed(grpc_slice s);
+
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H */
diff --git a/third_party/grpc/src/core/lib/slice/slice_weak_hash_table.h b/third_party/grpc/src/core/lib/slice/slice_weak_hash_table.h
new file mode 100644
index 0000000..dc3ccc5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/slice/slice_weak_hash_table.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H
+#define GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+/// Weak hash table implementation.
+///
+/// This entries in this table are weak: an entry may be removed at any time due
+/// to a number of reasons: memory pressure, hash collisions, etc.
+///
+/// The keys are \a grpc_slice objects. The values are of arbitrary type.
+///
+/// This class is thread unsafe. It's the caller's responsibility to ensure
+/// proper locking when accessing its methods.
+
+namespace grpc_core {
+
+template <typename T, size_t Size>
+class SliceWeakHashTable : public RefCounted<SliceWeakHashTable<T, Size>> {
+ public:
+  /// Creates a new table of at most \a size entries.
+  static RefCountedPtr<SliceWeakHashTable> Create() {
+    return MakeRefCounted<SliceWeakHashTable<T, Size>>();
+  }
+
+  /// Add a mapping from \a key to \a value, taking ownership of \a key. This
+  /// operation will always succeed. It may discard older entries.
+  void Add(grpc_slice key, T value) {
+    const size_t idx = grpc_slice_hash(key) % Size;
+    entries_[idx].Set(key, std::move(value));
+    return;
+  }
+
+  /// Returns the value from the table associated with / \a key or null if not
+  /// found.
+  const T* Get(const grpc_slice key) const {
+    const size_t idx = grpc_slice_hash(key) % Size;
+    const auto& entry = entries_[idx];
+    return grpc_slice_eq(entry.key(), key) ? entry.value() : nullptr;
+  }
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T2, typename... Args>
+  friend T2* New(Args&&... args);
+
+  // So Delete() can call our private dtor.
+  template <typename T2>
+  friend void Delete(T2*);
+
+  SliceWeakHashTable() = default;
+  ~SliceWeakHashTable() = default;
+
+  /// The type of the table "rows".
+  class Entry {
+   public:
+    Entry() = default;
+    ~Entry() {
+      if (is_set_) grpc_slice_unref_internal(key_);
+    }
+    grpc_slice key() const { return key_; }
+
+    /// Return the entry's value, or null if unset.
+    const T* value() const {
+      if (!is_set_) return nullptr;
+      return &value_;
+    }
+
+    /// Set the \a key and \a value (which is moved) for the entry.
+    void Set(grpc_slice key, T&& value) {
+      if (is_set_) grpc_slice_unref_internal(key_);
+      key_ = key;
+      value_ = std::move(value);
+      is_set_ = true;
+    }
+
+   private:
+    grpc_slice key_;
+    T value_;
+    bool is_set_ = false;
+  };
+
+  Entry entries_[Size];
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H */
diff --git a/third_party/grpc/src/core/lib/surface/README.md b/third_party/grpc/src/core/lib/surface/README.md
new file mode 100644
index 0000000..74cbd71
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/README.md
@@ -0,0 +1,4 @@
+# Surface
+
+Surface provides the bulk of the gRPC Core public API, and translates it into
+calls against core components.
diff --git a/third_party/grpc/src/core/lib/surface/api_trace.cc b/third_party/grpc/src/core/lib/surface/api_trace.cc
new file mode 100644
index 0000000..bab5a79
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/api_trace.cc
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/surface/api_trace.h"
+
+grpc_core::TraceFlag grpc_api_trace(false, "api");
diff --git a/third_party/grpc/src/core/lib/surface/api_trace.h b/third_party/grpc/src/core/lib/surface/api_trace.h
new file mode 100644
index 0000000..72ed830
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/api_trace.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_API_TRACE_H
+#define GRPC_CORE_LIB_SURFACE_API_TRACE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include "src/core/lib/debug/trace.h"
+
+extern grpc_core::TraceFlag grpc_api_trace;
+
+/* Provide unwrapping macros because we're in C89 and variadic macros weren't
+   introduced until C99... */
+#define GRPC_API_TRACE_UNWRAP0()
+#define GRPC_API_TRACE_UNWRAP1(a) , a
+#define GRPC_API_TRACE_UNWRAP2(a, b) , a, b
+#define GRPC_API_TRACE_UNWRAP3(a, b, c) , a, b, c
+#define GRPC_API_TRACE_UNWRAP4(a, b, c, d) , a, b, c, d
+#define GRPC_API_TRACE_UNWRAP5(a, b, c, d, e) , a, b, c, d, e
+#define GRPC_API_TRACE_UNWRAP6(a, b, c, d, e, f) , a, b, c, d, e, f
+#define GRPC_API_TRACE_UNWRAP7(a, b, c, d, e, f, g) , a, b, c, d, e, f, g
+#define GRPC_API_TRACE_UNWRAP8(a, b, c, d, e, f, g, h) , a, b, c, d, e, f, g, h
+#define GRPC_API_TRACE_UNWRAP9(a, b, c, d, e, f, g, h, i) \
+  , a, b, c, d, e, f, g, h, i
+#define GRPC_API_TRACE_UNWRAP10(a, b, c, d, e, f, g, h, i, j) \
+  , a, b, c, d, e, f, g, h, i, j
+
+/* Due to the limitations of C89's preprocessor, the arity of the var-arg list
+   'nargs' must be specified. */
+#define GRPC_API_TRACE(fmt, nargs, args)                      \
+  if (grpc_api_trace.enabled()) {                             \
+    gpr_log(GPR_INFO, fmt GRPC_API_TRACE_UNWRAP##nargs args); \
+  }
+
+#endif /* GRPC_CORE_LIB_SURFACE_API_TRACE_H */
diff --git a/third_party/grpc/src/core/lib/surface/byte_buffer.cc b/third_party/grpc/src/core/lib/surface/byte_buffer.cc
new file mode 100644
index 0000000..6246796
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/byte_buffer.cc
@@ -0,0 +1,92 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slices,
+                                              size_t nslices) {
+  return grpc_raw_compressed_byte_buffer_create(slices, nslices,
+                                                GRPC_COMPRESS_NONE);
+}
+
+grpc_byte_buffer* grpc_raw_compressed_byte_buffer_create(
+    grpc_slice* slices, size_t nslices,
+    grpc_compression_algorithm compression) {
+  size_t i;
+  grpc_byte_buffer* bb =
+      static_cast<grpc_byte_buffer*>(gpr_malloc(sizeof(grpc_byte_buffer)));
+  bb->type = GRPC_BB_RAW;
+  bb->data.raw.compression = compression;
+  grpc_slice_buffer_init(&bb->data.raw.slice_buffer);
+  for (i = 0; i < nslices; i++) {
+    grpc_slice_ref_internal(slices[i]);
+    grpc_slice_buffer_add(&bb->data.raw.slice_buffer, slices[i]);
+  }
+  return bb;
+}
+
+grpc_byte_buffer* grpc_raw_byte_buffer_from_reader(
+    grpc_byte_buffer_reader* reader) {
+  grpc_byte_buffer* bb =
+      static_cast<grpc_byte_buffer*>(gpr_malloc(sizeof(grpc_byte_buffer)));
+  grpc_slice slice;
+  bb->type = GRPC_BB_RAW;
+  bb->data.raw.compression = GRPC_COMPRESS_NONE;
+  grpc_slice_buffer_init(&bb->data.raw.slice_buffer);
+
+  while (grpc_byte_buffer_reader_next(reader, &slice)) {
+    grpc_slice_buffer_add(&bb->data.raw.slice_buffer, slice);
+  }
+  return bb;
+}
+
+grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) {
+  switch (bb->type) {
+    case GRPC_BB_RAW:
+      return grpc_raw_compressed_byte_buffer_create(
+          bb->data.raw.slice_buffer.slices, bb->data.raw.slice_buffer.count,
+          bb->data.raw.compression);
+  }
+  GPR_UNREACHABLE_CODE(return nullptr);
+}
+
+void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) {
+  if (!bb) return;
+  grpc_core::ExecCtx exec_ctx;
+  switch (bb->type) {
+    case GRPC_BB_RAW:
+      grpc_slice_buffer_destroy_internal(&bb->data.raw.slice_buffer);
+      break;
+  }
+  gpr_free(bb);
+}
+
+size_t grpc_byte_buffer_length(grpc_byte_buffer* bb) {
+  switch (bb->type) {
+    case GRPC_BB_RAW:
+      return bb->data.raw.slice_buffer.length;
+  }
+  GPR_UNREACHABLE_CODE(return 0);
+}
diff --git a/third_party/grpc/src/core/lib/surface/byte_buffer_reader.cc b/third_party/grpc/src/core/lib/surface/byte_buffer_reader.cc
new file mode 100644
index 0000000..1debc98
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/byte_buffer_reader.cc
@@ -0,0 +1,129 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/byte_buffer_reader.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/compression/message_compress.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+static int is_compressed(grpc_byte_buffer* buffer) {
+  switch (buffer->type) {
+    case GRPC_BB_RAW:
+      if (buffer->data.raw.compression == GRPC_COMPRESS_NONE) {
+        return 0 /* GPR_FALSE */;
+      }
+      break;
+  }
+  return 1 /* GPR_TRUE */;
+}
+
+int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
+                                 grpc_byte_buffer* buffer) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_slice_buffer decompressed_slices_buffer;
+  reader->buffer_in = buffer;
+  switch (reader->buffer_in->type) {
+    case GRPC_BB_RAW:
+      grpc_slice_buffer_init(&decompressed_slices_buffer);
+      if (is_compressed(reader->buffer_in)) {
+        if (grpc_msg_decompress(
+
+                grpc_compression_algorithm_to_message_compression_algorithm(
+                    reader->buffer_in->data.raw.compression),
+                &reader->buffer_in->data.raw.slice_buffer,
+                &decompressed_slices_buffer) == 0) {
+          gpr_log(GPR_ERROR,
+                  "Unexpected error decompressing data for algorithm with enum "
+                  "value '%d'.",
+                  reader->buffer_in->data.raw.compression);
+          memset(reader, 0, sizeof(*reader));
+          return 0;
+        } else { /* all fine */
+          reader->buffer_out =
+              grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices,
+                                          decompressed_slices_buffer.count);
+        }
+        grpc_slice_buffer_destroy_internal(&decompressed_slices_buffer);
+      } else { /* not compressed, use the input buffer as output */
+        reader->buffer_out = reader->buffer_in;
+      }
+      reader->current.index = 0;
+      break;
+  }
+
+  return 1;
+}
+
+void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader* reader) {
+  switch (reader->buffer_in->type) {
+    case GRPC_BB_RAW:
+      /* keeping the same if-else structure as in the init function */
+      if (is_compressed(reader->buffer_in)) {
+        grpc_byte_buffer_destroy(reader->buffer_out);
+      }
+      break;
+  }
+}
+
+int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
+                                 grpc_slice* slice) {
+  switch (reader->buffer_in->type) {
+    case GRPC_BB_RAW: {
+      grpc_slice_buffer* slice_buffer;
+      slice_buffer = &reader->buffer_out->data.raw.slice_buffer;
+      if (reader->current.index < slice_buffer->count) {
+        *slice = grpc_slice_ref_internal(
+            slice_buffer->slices[reader->current.index]);
+        reader->current.index += 1;
+        return 1;
+      }
+      break;
+    }
+  }
+  return 0;
+}
+
+grpc_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader* reader) {
+  grpc_slice in_slice;
+  size_t bytes_read = 0;
+  const size_t input_size = grpc_byte_buffer_length(reader->buffer_out);
+  grpc_slice out_slice = GRPC_SLICE_MALLOC(input_size);
+  uint8_t* const outbuf = GRPC_SLICE_START_PTR(out_slice); /* just an alias */
+
+  grpc_core::ExecCtx exec_ctx;
+  while (grpc_byte_buffer_reader_next(reader, &in_slice) != 0) {
+    const size_t slice_length = GRPC_SLICE_LENGTH(in_slice);
+    memcpy(&(outbuf[bytes_read]), GRPC_SLICE_START_PTR(in_slice), slice_length);
+    bytes_read += slice_length;
+    grpc_slice_unref_internal(in_slice);
+    GPR_ASSERT(bytes_read <= input_size);
+  }
+
+  return out_slice;
+}
diff --git a/third_party/grpc/src/core/lib/surface/call.cc b/third_party/grpc/src/core/lib/surface/call.cc
new file mode 100644
index 0000000..89b3f77
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/call.cc
@@ -0,0 +1,1979 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/compression/algorithm_metadata.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/alloc.h"
+#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/call_test_only.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/surface/server.h"
+#include "src/core/lib/surface/validate_metadata.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/status_metadata.h"
+#include "src/core/lib/transport/transport.h"
+
+/** The maximum number of concurrent batches possible.
+    Based upon the maximum number of individually queueable ops in the batch
+    api:
+      - initial metadata send
+      - message send
+      - status/close send (depending on client/server)
+      - initial metadata recv
+      - message recv
+      - status/close recv (depending on client/server) */
+#define MAX_CONCURRENT_BATCHES 6
+
+#define MAX_SEND_EXTRA_METADATA_COUNT 3
+
+// Used to create arena for the first call.
+#define ESTIMATED_MDELEM_COUNT 16
+
+struct batch_control {
+  batch_control() { gpr_ref_init(&steps_to_complete, 0); }
+
+  grpc_call* call = nullptr;
+  grpc_transport_stream_op_batch op;
+  /* Share memory for cq_completion and notify_tag as they are never needed
+     simultaneously. Each byte used in this data structure count as six bytes
+     per call, so any savings we can make are worthwhile,
+
+     We use notify_tag to determine whether or not to send notification to the
+     completion queue. Once we've made that determination, we can reuse the
+     memory for cq_completion. */
+  union {
+    grpc_cq_completion cq_completion;
+    struct {
+      /* Any given op indicates completion by either (a) calling a closure or
+         (b) sending a notification on the call's completion queue.  If
+         \a is_closure is true, \a tag indicates a closure to be invoked;
+         otherwise, \a tag indicates the tag to be used in the notification to
+         be sent to the completion queue. */
+      void* tag;
+      bool is_closure;
+    } notify_tag;
+  } completion_data;
+  grpc_closure start_batch;
+  grpc_closure finish_batch;
+  gpr_refcount steps_to_complete;
+  gpr_atm batch_error = reinterpret_cast<gpr_atm>(GRPC_ERROR_NONE);
+};
+
+struct parent_call {
+  parent_call() { gpr_mu_init(&child_list_mu); }
+  ~parent_call() { gpr_mu_destroy(&child_list_mu); }
+
+  gpr_mu child_list_mu;
+  grpc_call* first_child = nullptr;
+};
+
+struct child_call {
+  child_call(grpc_call* parent) : parent(parent) {}
+  grpc_call* parent;
+  /** siblings: children of the same parent form a list, and this list is
+     protected under
+      parent->mu */
+  grpc_call* sibling_next = nullptr;
+  grpc_call* sibling_prev = nullptr;
+};
+
+#define RECV_NONE ((gpr_atm)0)
+#define RECV_INITIAL_METADATA_FIRST ((gpr_atm)1)
+
+struct grpc_call {
+  grpc_call(gpr_arena* arena, const grpc_call_create_args& args)
+      : arena(arena),
+        cq(args.cq),
+        channel(args.channel),
+        is_client(args.server_transport_data == nullptr),
+        stream_op_payload(context) {
+    gpr_ref_init(&ext_ref, 1);
+    grpc_call_combiner_init(&call_combiner);
+    for (int i = 0; i < 2; i++) {
+      for (int j = 0; j < 2; j++) {
+        metadata_batch[i][j].deadline = GRPC_MILLIS_INF_FUTURE;
+      }
+    }
+  }
+
+  ~grpc_call() {
+    gpr_free(static_cast<void*>(const_cast<char*>(final_info.error_string)));
+    grpc_call_combiner_destroy(&call_combiner);
+  }
+
+  gpr_refcount ext_ref;
+  gpr_arena* arena;
+  grpc_call_combiner call_combiner;
+  grpc_completion_queue* cq;
+  grpc_polling_entity pollent;
+  grpc_channel* channel;
+  gpr_timespec start_time = gpr_now(GPR_CLOCK_MONOTONIC);
+  /* parent_call* */ gpr_atm parent_call_atm = 0;
+  child_call* child = nullptr;
+
+  /* client or server call */
+  bool is_client;
+  /** has grpc_call_unref been called */
+  bool destroy_called = false;
+  /** flag indicating that cancellation is inherited */
+  bool cancellation_is_inherited = false;
+  /** which ops are in-flight */
+  bool sent_initial_metadata = false;
+  bool sending_message = false;
+  bool sent_final_op = false;
+  bool received_initial_metadata = false;
+  bool receiving_message = false;
+  bool requested_final_op = false;
+  gpr_atm any_ops_sent_atm = 0;
+  gpr_atm received_final_op_atm = 0;
+
+  batch_control* active_batches[MAX_CONCURRENT_BATCHES] = {};
+  grpc_transport_stream_op_batch_payload stream_op_payload;
+
+  /* first idx: is_receiving, second idx: is_trailing */
+  grpc_metadata_batch metadata_batch[2][2] = {};
+
+  /* Buffered read metadata waiting to be returned to the application.
+     Element 0 is initial metadata, element 1 is trailing metadata. */
+  grpc_metadata_array* buffered_metadata[2] = {};
+
+  grpc_metadata compression_md;
+
+  // A char* indicating the peer name.
+  gpr_atm peer_string = 0;
+
+  /* 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_message_compression_algorithm incoming_message_compression_algorithm =
+      GRPC_MESSAGE_COMPRESS_NONE;
+  /* Stream compression algorithm for *incoming* data */
+  grpc_stream_compression_algorithm incoming_stream_compression_algorithm =
+      GRPC_STREAM_COMPRESS_NONE;
+  /* Supported encodings (compression algorithms), a bitset.
+   * Always support no compression. */
+  uint32_t encodings_accepted_by_peer = 1 << GRPC_MESSAGE_COMPRESS_NONE;
+  /* Supported stream encodings (stream compression algorithms), a bitset */
+  uint32_t stream_encodings_accepted_by_peer = 0;
+
+  /* Contexts for various subsystems (security, tracing, ...). */
+  grpc_call_context_element context[GRPC_CONTEXT_COUNT] = {};
+
+  /* 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];
+  int send_extra_metadata_count;
+  grpc_millis send_deadline;
+
+  grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> sending_stream;
+
+  grpc_core::OrphanablePtr<grpc_core::ByteStream> receiving_stream;
+  grpc_byte_buffer** receiving_buffer = nullptr;
+  grpc_slice receiving_slice = grpc_empty_slice();
+  grpc_closure receiving_slice_ready;
+  grpc_closure receiving_stream_ready;
+  grpc_closure receiving_initial_metadata_ready;
+  grpc_closure receiving_trailing_metadata_ready;
+  uint32_t test_only_last_message_flags = 0;
+  gpr_atm cancelled = 0;
+
+  grpc_closure release_call;
+
+  union {
+    struct {
+      grpc_status_code* status;
+      grpc_slice* status_details;
+      const char** error_string;
+    } client;
+    struct {
+      int* cancelled;
+      // backpointer to owning server if this is a server side call.
+      grpc_server* server;
+    } server;
+  } final_op;
+  gpr_atm status_error = 0;
+
+  /* recv_state can contain one of the following values:
+     RECV_NONE :                 :  no initial metadata and messages received
+     RECV_INITIAL_METADATA_FIRST :  received initial metadata first
+     a batch_control*            :  received messages first
+
+                 +------1------RECV_NONE------3-----+
+                 |                                  |
+                 |                                  |
+                 v                                  v
+     RECV_INITIAL_METADATA_FIRST        receiving_stream_ready_bctlp
+           |           ^                      |           ^
+           |           |                      |           |
+           +-----2-----+                      +-----4-----+
+
+    For 1, 4: See receiving_initial_metadata_ready() function
+    For 2, 3: See receiving_stream_ready() function */
+  gpr_atm recv_state = 0;
+};
+
+grpc_core::TraceFlag grpc_call_error_trace(false, "call_error");
+grpc_core::TraceFlag grpc_compression_trace(false, "compression");
+
+#define CALL_STACK_FROM_CALL(call)   \
+  (grpc_call_stack*)((char*)(call) + \
+                     GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)))
+#define CALL_FROM_CALL_STACK(call_stack) \
+  (grpc_call*)(((char*)(call_stack)) -   \
+               GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)))
+
+#define CALL_ELEM_FROM_CALL(call, idx) \
+  grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx)
+#define CALL_FROM_TOP_ELEM(top_elem) \
+  CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem))
+
+static void execute_batch(grpc_call* call, grpc_transport_stream_op_batch* op,
+                          grpc_closure* start_batch_closure);
+
+static void cancel_with_status(grpc_call* c, grpc_status_code status,
+                               const char* description);
+static void cancel_with_error(grpc_call* c, grpc_error* error);
+static void destroy_call(void* call_stack, grpc_error* error);
+static void receiving_slice_ready(void* bctlp, grpc_error* error);
+static void set_final_status(grpc_call* call, grpc_error* error);
+static void process_data_after_md(batch_control* bctl);
+static void post_batch_completion(batch_control* bctl);
+
+static void add_init_error(grpc_error** composite, grpc_error* new_err) {
+  if (new_err == GRPC_ERROR_NONE) return;
+  if (*composite == GRPC_ERROR_NONE)
+    *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Call creation failed");
+  *composite = grpc_error_add_child(*composite, new_err);
+}
+
+void* grpc_call_arena_alloc(grpc_call* call, size_t size) {
+  return gpr_arena_alloc(call->arena, size);
+}
+
+static parent_call* get_or_create_parent_call(grpc_call* call) {
+  parent_call* p = (parent_call*)gpr_atm_acq_load(&call->parent_call_atm);
+  if (p == nullptr) {
+    p = new (gpr_arena_alloc(call->arena, sizeof(*p))) parent_call();
+    if (!gpr_atm_rel_cas(&call->parent_call_atm, (gpr_atm) nullptr,
+                         (gpr_atm)p)) {
+      p->~parent_call();
+      p = (parent_call*)gpr_atm_acq_load(&call->parent_call_atm);
+    }
+  }
+  return p;
+}
+
+static parent_call* get_parent_call(grpc_call* call) {
+  return (parent_call*)gpr_atm_acq_load(&call->parent_call_atm);
+}
+
+size_t grpc_call_get_initial_size_estimate() {
+  return sizeof(grpc_call) + sizeof(batch_control) * MAX_CONCURRENT_BATCHES +
+         sizeof(grpc_linked_mdelem) * ESTIMATED_MDELEM_COUNT;
+}
+
+grpc_error* grpc_call_create(const grpc_call_create_args* args,
+                             grpc_call** out_call) {
+  GPR_TIMER_SCOPE("grpc_call_create", 0);
+
+  GRPC_CHANNEL_INTERNAL_REF(args->channel, "call");
+
+  grpc_error* error = GRPC_ERROR_NONE;
+  grpc_channel_stack* channel_stack =
+      grpc_channel_get_channel_stack(args->channel);
+  grpc_call* call;
+  size_t initial_size = grpc_channel_get_call_size_estimate(args->channel);
+  GRPC_STATS_INC_CALL_INITIAL_SIZE(initial_size);
+  gpr_arena* arena = gpr_arena_create(initial_size);
+  call = new (gpr_arena_alloc(
+      arena, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) +
+                 channel_stack->call_stack_size)) grpc_call(arena, *args);
+  *out_call = call;
+  grpc_slice path = grpc_empty_slice();
+  if (call->is_client) {
+    call->final_op.client.status_details = nullptr;
+    call->final_op.client.status = nullptr;
+    call->final_op.client.error_string = nullptr;
+    GRPC_STATS_INC_CLIENT_CALLS_CREATED();
+    GPR_ASSERT(args->add_initial_metadata_count <
+               MAX_SEND_EXTRA_METADATA_COUNT);
+    for (size_t i = 0; i < args->add_initial_metadata_count; i++) {
+      call->send_extra_metadata[i].md = args->add_initial_metadata[i];
+      if (grpc_slice_eq(GRPC_MDKEY(args->add_initial_metadata[i]),
+                        GRPC_MDSTR_PATH)) {
+        path = grpc_slice_ref_internal(
+            GRPC_MDVALUE(args->add_initial_metadata[i]));
+      }
+    }
+    call->send_extra_metadata_count =
+        static_cast<int>(args->add_initial_metadata_count);
+  } else {
+    GRPC_STATS_INC_SERVER_CALLS_CREATED();
+    call->final_op.server.cancelled = nullptr;
+    call->final_op.server.server = args->server;
+    GPR_ASSERT(args->add_initial_metadata_count == 0);
+    call->send_extra_metadata_count = 0;
+  }
+
+  grpc_millis send_deadline = args->send_deadline;
+  bool immediately_cancel = false;
+
+  if (args->parent != nullptr) {
+    call->child = new (gpr_arena_alloc(arena, sizeof(child_call)))
+        child_call(args->parent);
+
+    GRPC_CALL_INTERNAL_REF(args->parent, "child");
+    GPR_ASSERT(call->is_client);
+    GPR_ASSERT(!args->parent->is_client);
+
+    if (args->propagation_mask & GRPC_PROPAGATE_DEADLINE) {
+      send_deadline = GPR_MIN(send_deadline, args->parent->send_deadline);
+    }
+    /* for now GRPC_PROPAGATE_TRACING_CONTEXT *MUST* be passed with
+     * GRPC_PROPAGATE_STATS_CONTEXT */
+    /* TODO(ctiller): This should change to use the appropriate census start_op
+     * call. */
+    if (args->propagation_mask & GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT) {
+      if (0 == (args->propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)) {
+        add_init_error(&error, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                   "Census tracing propagation requested "
+                                   "without Census context propagation"));
+      }
+      grpc_call_context_set(call, GRPC_CONTEXT_TRACING,
+                            args->parent->context[GRPC_CONTEXT_TRACING].value,
+                            nullptr);
+    } else if (args->propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT) {
+      add_init_error(&error, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                 "Census context propagation requested "
+                                 "without Census tracing propagation"));
+    }
+    if (args->propagation_mask & GRPC_PROPAGATE_CANCELLATION) {
+      call->cancellation_is_inherited = 1;
+      if (gpr_atm_acq_load(&args->parent->received_final_op_atm)) {
+        immediately_cancel = true;
+      }
+    }
+  }
+  call->send_deadline = send_deadline;
+  /* initial refcount dropped by grpc_call_unref */
+  grpc_call_element_args call_args = {CALL_STACK_FROM_CALL(call),
+                                      args->server_transport_data,
+                                      call->context,
+                                      path,
+                                      call->start_time,
+                                      send_deadline,
+                                      call->arena,
+                                      &call->call_combiner};
+  add_init_error(&error, grpc_call_stack_init(channel_stack, 1, destroy_call,
+                                              call, &call_args));
+  // Publish this call to parent only after the call stack has been initialized.
+  if (args->parent != nullptr) {
+    child_call* cc = call->child;
+    parent_call* pc = get_or_create_parent_call(args->parent);
+    gpr_mu_lock(&pc->child_list_mu);
+    if (pc->first_child == nullptr) {
+      pc->first_child = call;
+      cc->sibling_next = cc->sibling_prev = call;
+    } else {
+      cc->sibling_next = pc->first_child;
+      cc->sibling_prev = pc->first_child->child->sibling_prev;
+      cc->sibling_next->child->sibling_prev =
+          cc->sibling_prev->child->sibling_next = call;
+    }
+    gpr_mu_unlock(&pc->child_list_mu);
+  }
+
+  if (error != GRPC_ERROR_NONE) {
+    cancel_with_error(call, GRPC_ERROR_REF(error));
+  }
+  if (immediately_cancel) {
+    cancel_with_error(call, GRPC_ERROR_CANCELLED);
+  }
+  if (args->cq != nullptr) {
+    GPR_ASSERT(args->pollset_set_alternative == nullptr &&
+               "Only one of 'cq' and 'pollset_set_alternative' should be "
+               "non-nullptr.");
+    GRPC_CQ_INTERNAL_REF(args->cq, "bind");
+    call->pollent =
+        grpc_polling_entity_create_from_pollset(grpc_cq_pollset(args->cq));
+  }
+  if (args->pollset_set_alternative != nullptr) {
+    call->pollent = grpc_polling_entity_create_from_pollset_set(
+        args->pollset_set_alternative);
+  }
+  if (!grpc_polling_entity_is_empty(&call->pollent)) {
+    grpc_call_stack_set_pollset_or_pollset_set(CALL_STACK_FROM_CALL(call),
+                                               &call->pollent);
+  }
+
+  if (call->is_client) {
+    grpc_core::channelz::ChannelNode* channelz_channel =
+        grpc_channel_get_channelz_node(call->channel);
+    if (channelz_channel != nullptr) {
+      channelz_channel->RecordCallStarted();
+    }
+  } else {
+    grpc_core::channelz::ServerNode* channelz_server =
+        grpc_server_get_channelz_node(call->final_op.server.server);
+    if (channelz_server != nullptr) {
+      channelz_server->RecordCallStarted();
+    }
+  }
+
+  grpc_slice_unref_internal(path);
+
+  return error;
+}
+
+void grpc_call_set_completion_queue(grpc_call* call,
+                                    grpc_completion_queue* cq) {
+  GPR_ASSERT(cq);
+
+  if (grpc_polling_entity_pollset_set(&call->pollent) != nullptr) {
+    gpr_log(GPR_ERROR, "A pollset_set is already registered for this call.");
+    abort();
+  }
+  call->cq = cq;
+  GRPC_CQ_INTERNAL_REF(cq, "bind");
+  call->pollent = grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq));
+  grpc_call_stack_set_pollset_or_pollset_set(CALL_STACK_FROM_CALL(call),
+                                             &call->pollent);
+}
+
+#ifndef NDEBUG
+#define REF_REASON reason
+#define REF_ARG , const char* reason
+#else
+#define REF_REASON ""
+#define REF_ARG
+#endif
+void grpc_call_internal_ref(grpc_call* c REF_ARG) {
+  GRPC_CALL_STACK_REF(CALL_STACK_FROM_CALL(c), REF_REASON);
+}
+void grpc_call_internal_unref(grpc_call* c REF_ARG) {
+  GRPC_CALL_STACK_UNREF(CALL_STACK_FROM_CALL(c), REF_REASON);
+}
+
+static void release_call(void* call, grpc_error* error) {
+  grpc_call* c = static_cast<grpc_call*>(call);
+  grpc_channel* channel = c->channel;
+  gpr_arena* arena = c->arena;
+  c->~grpc_call();
+  grpc_channel_update_call_size_estimate(channel, gpr_arena_destroy(arena));
+  GRPC_CHANNEL_INTERNAL_UNREF(channel, "call");
+}
+
+static void destroy_call(void* call, grpc_error* error) {
+  GPR_TIMER_SCOPE("destroy_call", 0);
+  size_t i;
+  int ii;
+  grpc_call* c = static_cast<grpc_call*>(call);
+  for (i = 0; i < 2; i++) {
+    grpc_metadata_batch_destroy(
+        &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]);
+  }
+  c->receiving_stream.reset();
+  parent_call* pc = get_parent_call(c);
+  if (pc != nullptr) {
+    pc->~parent_call();
+  }
+  for (ii = 0; ii < c->send_extra_metadata_count; ii++) {
+    GRPC_MDELEM_UNREF(c->send_extra_metadata[ii].md);
+  }
+  for (i = 0; i < GRPC_CONTEXT_COUNT; i++) {
+    if (c->context[i].destroy) {
+      c->context[i].destroy(c->context[i].value);
+    }
+  }
+  if (c->cq) {
+    GRPC_CQ_INTERNAL_UNREF(c->cq, "bind");
+  }
+
+  grpc_error* status_error =
+      reinterpret_cast<grpc_error*>(gpr_atm_acq_load(&c->status_error));
+  grpc_error_get_status(status_error, c->send_deadline,
+                        &c->final_info.final_status, nullptr, nullptr,
+                        &(c->final_info.error_string));
+  GRPC_ERROR_UNREF(status_error);
+  c->final_info.stats.latency =
+      gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time);
+
+  grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c), &c->final_info,
+                          GRPC_CLOSURE_INIT(&c->release_call, release_call, c,
+                                            grpc_schedule_on_exec_ctx));
+}
+
+void grpc_call_ref(grpc_call* c) { gpr_ref(&c->ext_ref); }
+
+void grpc_call_unref(grpc_call* c) {
+  if (!gpr_unref(&c->ext_ref)) return;
+
+  GPR_TIMER_SCOPE("grpc_call_unref", 0);
+
+  child_call* cc = c->child;
+  grpc_core::ExecCtx exec_ctx;
+
+  GRPC_API_TRACE("grpc_call_unref(c=%p)", 1, (c));
+
+  if (cc) {
+    parent_call* pc = get_parent_call(cc->parent);
+    gpr_mu_lock(&pc->child_list_mu);
+    if (c == pc->first_child) {
+      pc->first_child = cc->sibling_next;
+      if (c == pc->first_child) {
+        pc->first_child = nullptr;
+      }
+    }
+    cc->sibling_prev->child->sibling_next = cc->sibling_next;
+    cc->sibling_next->child->sibling_prev = cc->sibling_prev;
+    gpr_mu_unlock(&pc->child_list_mu);
+    GRPC_CALL_INTERNAL_UNREF(cc->parent, "child");
+  }
+
+  GPR_ASSERT(!c->destroy_called);
+  c->destroy_called = 1;
+  bool cancel = gpr_atm_acq_load(&c->any_ops_sent_atm) != 0 &&
+                gpr_atm_acq_load(&c->received_final_op_atm) == 0;
+  if (cancel) {
+    cancel_with_error(c, GRPC_ERROR_CANCELLED);
+  } else {
+    // Unset the call combiner cancellation closure.  This has the
+    // effect of scheduling the previously set cancellation closure, if
+    // any, so that it can release any internal references it may be
+    // holding to the call stack. Also flush the closures on exec_ctx so that
+    // filters that schedule cancel notification closures on exec_ctx do not
+    // need to take a ref of the call stack to guarantee closure liveness.
+    grpc_call_combiner_set_notify_on_cancel(&c->call_combiner, nullptr);
+    grpc_core::ExecCtx::Get()->Flush();
+  }
+  GRPC_CALL_INTERNAL_UNREF(c, "destroy");
+}
+
+grpc_call_error grpc_call_cancel(grpc_call* call, void* reserved) {
+  GRPC_API_TRACE("grpc_call_cancel(call=%p, reserved=%p)", 2, (call, reserved));
+  GPR_ASSERT(!reserved);
+  grpc_core::ExecCtx exec_ctx;
+  cancel_with_error(call, GRPC_ERROR_CANCELLED);
+  return GRPC_CALL_OK;
+}
+
+// This is called via the call combiner to start sending a batch down
+// the filter stack.
+static void execute_batch_in_call_combiner(void* arg, grpc_error* ignored) {
+  GPR_TIMER_SCOPE("execute_batch_in_call_combiner", 0);
+  grpc_transport_stream_op_batch* batch =
+      static_cast<grpc_transport_stream_op_batch*>(arg);
+  grpc_call* call = static_cast<grpc_call*>(batch->handler_private.extra_arg);
+  grpc_call_element* elem = CALL_ELEM_FROM_CALL(call, 0);
+  GRPC_CALL_LOG_OP(GPR_INFO, elem, batch);
+  elem->filter->start_transport_stream_op_batch(elem, batch);
+}
+
+// start_batch_closure points to a caller-allocated closure to be used
+// for entering the call combiner.
+static void execute_batch(grpc_call* call,
+                          grpc_transport_stream_op_batch* batch,
+                          grpc_closure* start_batch_closure) {
+  batch->handler_private.extra_arg = call;
+  GRPC_CLOSURE_INIT(start_batch_closure, execute_batch_in_call_combiner, batch,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CALL_COMBINER_START(&call->call_combiner, start_batch_closure,
+                           GRPC_ERROR_NONE, "executing batch");
+}
+
+char* grpc_call_get_peer(grpc_call* call) {
+  char* peer_string = (char*)gpr_atm_acq_load(&call->peer_string);
+  if (peer_string != nullptr) return gpr_strdup(peer_string);
+  peer_string = grpc_channel_get_target(call->channel);
+  if (peer_string != nullptr) return peer_string;
+  return gpr_strdup("unknown");
+}
+
+grpc_call* grpc_call_from_top_element(grpc_call_element* elem) {
+  return CALL_FROM_TOP_ELEM(elem);
+}
+
+/*******************************************************************************
+ * CANCELLATION
+ */
+
+grpc_call_error grpc_call_cancel_with_status(grpc_call* c,
+                                             grpc_status_code status,
+                                             const char* description,
+                                             void* reserved) {
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE(
+      "grpc_call_cancel_with_status("
+      "c=%p, status=%d, description=%s, reserved=%p)",
+      4, (c, (int)status, description, reserved));
+  GPR_ASSERT(reserved == nullptr);
+  cancel_with_status(c, status, description);
+  return GRPC_CALL_OK;
+}
+
+typedef struct {
+  grpc_call* call;
+  grpc_closure start_batch;
+  grpc_closure finish_batch;
+} cancel_state;
+
+// The on_complete callback used when sending a cancel_stream batch down
+// the filter stack.  Yields the call combiner when the batch is done.
+static void done_termination(void* arg, grpc_error* error) {
+  cancel_state* state = static_cast<cancel_state*>(arg);
+  GRPC_CALL_COMBINER_STOP(&state->call->call_combiner,
+                          "on_complete for cancel_stream op");
+  GRPC_CALL_INTERNAL_UNREF(state->call, "termination");
+  gpr_free(state);
+}
+
+static void cancel_with_error(grpc_call* c, grpc_error* error) {
+  if (!gpr_atm_rel_cas(&c->cancelled, 0, 1)) {
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  GRPC_CALL_INTERNAL_REF(c, "termination");
+  // Inform the call combiner of the cancellation, so that it can cancel
+  // any in-flight asynchronous actions that may be holding the call
+  // combiner.  This ensures that the cancel_stream batch can be sent
+  // down the filter stack in a timely manner.
+  grpc_call_combiner_cancel(&c->call_combiner, GRPC_ERROR_REF(error));
+  cancel_state* state = static_cast<cancel_state*>(gpr_malloc(sizeof(*state)));
+  state->call = c;
+  GRPC_CLOSURE_INIT(&state->finish_batch, done_termination, state,
+                    grpc_schedule_on_exec_ctx);
+  grpc_transport_stream_op_batch* op =
+      grpc_make_transport_stream_op(&state->finish_batch);
+  op->cancel_stream = true;
+  op->payload->cancel_stream.cancel_error = error;
+  execute_batch(c, op, &state->start_batch);
+}
+
+void grpc_call_cancel_internal(grpc_call* call) {
+  cancel_with_error(call, GRPC_ERROR_CANCELLED);
+}
+
+static grpc_error* error_from_status(grpc_status_code status,
+                                     const char* description) {
+  // copying 'description' is needed to ensure the grpc_call_cancel_with_status
+  // guarantee that can be short-lived.
+  return grpc_error_set_int(
+      grpc_error_set_str(GRPC_ERROR_CREATE_FROM_COPIED_STRING(description),
+                         GRPC_ERROR_STR_GRPC_MESSAGE,
+                         grpc_slice_from_copied_string(description)),
+      GRPC_ERROR_INT_GRPC_STATUS, status);
+}
+
+static void cancel_with_status(grpc_call* c, grpc_status_code status,
+                               const char* description) {
+  cancel_with_error(c, error_from_status(status, description));
+}
+
+static void set_final_status(grpc_call* call, grpc_error* error) {
+  if (grpc_call_error_trace.enabled()) {
+    gpr_log(GPR_DEBUG, "set_final_status %s", call->is_client ? "CLI" : "SVR");
+    gpr_log(GPR_DEBUG, "%s", grpc_error_string(error));
+  }
+  if (call->is_client) {
+    grpc_error_get_status(error, call->send_deadline,
+                          call->final_op.client.status,
+                          call->final_op.client.status_details, nullptr,
+                          call->final_op.client.error_string);
+    // explicitly take a ref
+    grpc_slice_ref_internal(*call->final_op.client.status_details);
+    gpr_atm_rel_store(&call->status_error, reinterpret_cast<gpr_atm>(error));
+    grpc_core::channelz::ChannelNode* channelz_channel =
+        grpc_channel_get_channelz_node(call->channel);
+    if (channelz_channel != nullptr) {
+      if (*call->final_op.client.status != GRPC_STATUS_OK) {
+        channelz_channel->RecordCallFailed();
+      } else {
+        channelz_channel->RecordCallSucceeded();
+      }
+    }
+  } else {
+    *call->final_op.server.cancelled =
+        error != GRPC_ERROR_NONE ||
+        reinterpret_cast<grpc_error*>(gpr_atm_acq_load(&call->status_error)) !=
+            GRPC_ERROR_NONE;
+    grpc_core::channelz::ServerNode* channelz_server =
+        grpc_server_get_channelz_node(call->final_op.server.server);
+    if (channelz_server != nullptr) {
+      if (*call->final_op.server.cancelled) {
+        channelz_server->RecordCallFailed();
+      } else {
+        channelz_server->RecordCallSucceeded();
+      }
+    }
+    GRPC_ERROR_UNREF(error);
+  }
+}
+
+/*******************************************************************************
+ * COMPRESSION
+ */
+
+static void set_incoming_message_compression_algorithm(
+    grpc_call* call, grpc_message_compression_algorithm algo) {
+  GPR_ASSERT(algo < GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT);
+  call->incoming_message_compression_algorithm = algo;
+}
+
+static void set_incoming_stream_compression_algorithm(
+    grpc_call* call, grpc_stream_compression_algorithm algo) {
+  GPR_ASSERT(algo < GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT);
+  call->incoming_stream_compression_algorithm = algo;
+}
+
+grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm(
+    grpc_call* call) {
+  grpc_compression_algorithm algorithm = GRPC_COMPRESS_NONE;
+  grpc_compression_algorithm_from_message_stream_compression_algorithm(
+      &algorithm, call->incoming_message_compression_algorithm,
+      call->incoming_stream_compression_algorithm);
+  return algorithm;
+}
+
+static grpc_compression_algorithm compression_algorithm_for_level_locked(
+    grpc_call* call, grpc_compression_level level) {
+  return grpc_compression_algorithm_for_level(level,
+                                              call->encodings_accepted_by_peer);
+}
+
+uint32_t grpc_call_test_only_get_message_flags(grpc_call* call) {
+  uint32_t flags;
+  flags = call->test_only_last_message_flags;
+  return flags;
+}
+
+static void destroy_encodings_accepted_by_peer(void* p) { return; }
+
+static void set_encodings_accepted_by_peer(grpc_call* call, grpc_mdelem mdel,
+                                           uint32_t* encodings_accepted_by_peer,
+                                           bool stream_encoding) {
+  size_t i;
+  uint32_t algorithm;
+  grpc_slice_buffer accept_encoding_parts;
+  grpc_slice accept_encoding_slice;
+  void* accepted_user_data;
+
+  accepted_user_data =
+      grpc_mdelem_get_user_data(mdel, destroy_encodings_accepted_by_peer);
+  if (accepted_user_data != nullptr) {
+    *encodings_accepted_by_peer =
+        static_cast<uint32_t>(((uintptr_t)accepted_user_data) - 1);
+    return;
+  }
+
+  *encodings_accepted_by_peer = 0;
+
+  accept_encoding_slice = GRPC_MDVALUE(mdel);
+  grpc_slice_buffer_init(&accept_encoding_parts);
+  grpc_slice_split_without_space(accept_encoding_slice, ",",
+                                 &accept_encoding_parts);
+
+  GPR_BITSET(encodings_accepted_by_peer, GRPC_COMPRESS_NONE);
+  for (i = 0; i < accept_encoding_parts.count; i++) {
+    int r;
+    grpc_slice accept_encoding_entry_slice = accept_encoding_parts.slices[i];
+    if (!stream_encoding) {
+      r = grpc_message_compression_algorithm_parse(
+          accept_encoding_entry_slice,
+          reinterpret_cast<grpc_message_compression_algorithm*>(&algorithm));
+    } else {
+      r = grpc_stream_compression_algorithm_parse(
+          accept_encoding_entry_slice,
+          reinterpret_cast<grpc_stream_compression_algorithm*>(&algorithm));
+    }
+    if (r) {
+      GPR_BITSET(encodings_accepted_by_peer, algorithm);
+    } else {
+      char* accept_encoding_entry_str =
+          grpc_slice_to_c_string(accept_encoding_entry_slice);
+      gpr_log(GPR_DEBUG,
+              "Unknown entry in accept encoding metadata: '%s'. Ignoring.",
+              accept_encoding_entry_str);
+      gpr_free(accept_encoding_entry_str);
+    }
+  }
+
+  grpc_slice_buffer_destroy_internal(&accept_encoding_parts);
+
+  grpc_mdelem_set_user_data(
+      mdel, destroy_encodings_accepted_by_peer,
+      (void*)((static_cast<uintptr_t>(*encodings_accepted_by_peer)) + 1));
+}
+
+uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call* call) {
+  uint32_t encodings_accepted_by_peer;
+  encodings_accepted_by_peer = call->encodings_accepted_by_peer;
+  return encodings_accepted_by_peer;
+}
+
+grpc_stream_compression_algorithm
+grpc_call_test_only_get_incoming_stream_encodings(grpc_call* call) {
+  return call->incoming_stream_compression_algorithm;
+}
+
+static grpc_linked_mdelem* linked_from_md(const grpc_metadata* md) {
+  return (grpc_linked_mdelem*)&md->internal_data;
+}
+
+static grpc_metadata* get_md_elem(grpc_metadata* metadata,
+                                  grpc_metadata* additional_metadata, int i,
+                                  int count) {
+  grpc_metadata* res =
+      i < count ? &metadata[i] : &additional_metadata[i - count];
+  GPR_ASSERT(res);
+  return res;
+}
+
+static int prepare_application_metadata(grpc_call* call, int count,
+                                        grpc_metadata* metadata,
+                                        int is_trailing,
+                                        int prepend_extra_metadata,
+                                        grpc_metadata* additional_metadata,
+                                        int additional_metadata_count) {
+  int total_count = count + additional_metadata_count;
+  int i;
+  grpc_metadata_batch* batch =
+      &call->metadata_batch[0 /* is_receiving */][is_trailing];
+  for (i = 0; i < total_count; i++) {
+    const grpc_metadata* md =
+        get_md_elem(metadata, additional_metadata, i, count);
+    grpc_linked_mdelem* l = linked_from_md(md);
+    GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data));
+    if (!GRPC_LOG_IF_ERROR("validate_metadata",
+                           grpc_validate_header_key_is_legal(md->key))) {
+      break;
+    } else if (!grpc_is_binary_header(md->key) &&
+               !GRPC_LOG_IF_ERROR(
+                   "validate_metadata",
+                   grpc_validate_header_nonbin_value_is_legal(md->value))) {
+      break;
+    }
+    l->md = grpc_mdelem_from_grpc_metadata(const_cast<grpc_metadata*>(md));
+  }
+  if (i != total_count) {
+    for (int j = 0; j < i; j++) {
+      const grpc_metadata* md =
+          get_md_elem(metadata, additional_metadata, j, count);
+      grpc_linked_mdelem* l = linked_from_md(md);
+      GRPC_MDELEM_UNREF(l->md);
+    }
+    return 0;
+  }
+  if (prepend_extra_metadata) {
+    if (call->send_extra_metadata_count == 0) {
+      prepend_extra_metadata = 0;
+    } else {
+      for (i = 0; i < call->send_extra_metadata_count; i++) {
+        GRPC_LOG_IF_ERROR("prepare_application_metadata",
+                          grpc_metadata_batch_link_tail(
+                              batch, &call->send_extra_metadata[i]));
+      }
+    }
+  }
+  for (i = 0; i < total_count; i++) {
+    grpc_metadata* md = get_md_elem(metadata, additional_metadata, i, count);
+    grpc_linked_mdelem* l = linked_from_md(md);
+    grpc_error* error = grpc_metadata_batch_link_tail(batch, l);
+    if (error != GRPC_ERROR_NONE) {
+      GRPC_MDELEM_UNREF(l->md);
+    }
+    GRPC_LOG_IF_ERROR("prepare_application_metadata", error);
+  }
+  call->send_extra_metadata_count = 0;
+
+  return 1;
+}
+
+static grpc_message_compression_algorithm decode_message_compression(
+    grpc_mdelem md) {
+  grpc_message_compression_algorithm algorithm =
+      grpc_message_compression_algorithm_from_slice(GRPC_MDVALUE(md));
+  if (algorithm == GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT) {
+    char* md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+    gpr_log(GPR_ERROR,
+            "Invalid incoming message compression algorithm: '%s'. "
+            "Interpreting incoming data as uncompressed.",
+            md_c_str);
+    gpr_free(md_c_str);
+    return GRPC_MESSAGE_COMPRESS_NONE;
+  }
+  return algorithm;
+}
+
+static grpc_stream_compression_algorithm decode_stream_compression(
+    grpc_mdelem md) {
+  grpc_stream_compression_algorithm algorithm =
+      grpc_stream_compression_algorithm_from_slice(GRPC_MDVALUE(md));
+  if (algorithm == GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) {
+    char* md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+    gpr_log(GPR_ERROR,
+            "Invalid incoming stream compression algorithm: '%s'. Interpreting "
+            "incoming data as uncompressed.",
+            md_c_str);
+    gpr_free(md_c_str);
+    return GRPC_STREAM_COMPRESS_NONE;
+  }
+  return algorithm;
+}
+
+static void publish_app_metadata(grpc_call* call, grpc_metadata_batch* b,
+                                 int is_trailing) {
+  if (b->list.count == 0) return;
+  if (!call->is_client && is_trailing) return;
+  if (is_trailing && call->buffered_metadata[1] == nullptr) return;
+  GPR_TIMER_SCOPE("publish_app_metadata", 0);
+  grpc_metadata_array* dest;
+  grpc_metadata* mdusr;
+  dest = call->buffered_metadata[is_trailing];
+  if (dest->count + b->list.count > dest->capacity) {
+    dest->capacity =
+        GPR_MAX(dest->capacity + b->list.count, dest->capacity * 3 / 2);
+    dest->metadata = static_cast<grpc_metadata*>(
+        gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity));
+  }
+  for (grpc_linked_mdelem* l = b->list.head; l != nullptr; l = l->next) {
+    mdusr = &dest->metadata[dest->count++];
+    /* we pass back borrowed slices that are valid whilst the call is valid */
+    mdusr->key = GRPC_MDKEY(l->md);
+    mdusr->value = GRPC_MDVALUE(l->md);
+  }
+}
+
+static void recv_initial_filter(grpc_call* call, grpc_metadata_batch* b) {
+  if (b->idx.named.content_encoding != nullptr) {
+    GPR_TIMER_SCOPE("incoming_stream_compression_algorithm", 0);
+    set_incoming_stream_compression_algorithm(
+        call, decode_stream_compression(b->idx.named.content_encoding->md));
+    grpc_metadata_batch_remove(b, b->idx.named.content_encoding);
+  }
+  if (b->idx.named.grpc_encoding != nullptr) {
+    GPR_TIMER_SCOPE("incoming_message_compression_algorithm", 0);
+    set_incoming_message_compression_algorithm(
+        call, decode_message_compression(b->idx.named.grpc_encoding->md));
+    grpc_metadata_batch_remove(b, b->idx.named.grpc_encoding);
+  }
+  uint32_t message_encodings_accepted_by_peer = 1u;
+  uint32_t stream_encodings_accepted_by_peer = 1u;
+  if (b->idx.named.grpc_accept_encoding != nullptr) {
+    GPR_TIMER_SCOPE("encodings_accepted_by_peer", 0);
+    set_encodings_accepted_by_peer(call, b->idx.named.grpc_accept_encoding->md,
+                                   &message_encodings_accepted_by_peer, false);
+    grpc_metadata_batch_remove(b, b->idx.named.grpc_accept_encoding);
+  }
+  if (b->idx.named.accept_encoding != nullptr) {
+    GPR_TIMER_SCOPE("stream_encodings_accepted_by_peer", 0);
+    set_encodings_accepted_by_peer(call, b->idx.named.accept_encoding->md,
+                                   &stream_encodings_accepted_by_peer, true);
+    grpc_metadata_batch_remove(b, b->idx.named.accept_encoding);
+  }
+  call->encodings_accepted_by_peer =
+      grpc_compression_bitset_from_message_stream_compression_bitset(
+          message_encodings_accepted_by_peer,
+          stream_encodings_accepted_by_peer);
+  publish_app_metadata(call, b, false);
+}
+
+static void recv_trailing_filter(void* args, grpc_metadata_batch* b,
+                                 grpc_error* batch_error) {
+  grpc_call* call = static_cast<grpc_call*>(args);
+  if (batch_error != GRPC_ERROR_NONE) {
+    set_final_status(call, batch_error);
+  } else if (b->idx.named.grpc_status != nullptr) {
+    grpc_status_code status_code =
+        grpc_get_status_code_from_metadata(b->idx.named.grpc_status->md);
+    grpc_error* error = GRPC_ERROR_NONE;
+    if (status_code != GRPC_STATUS_OK) {
+      error = grpc_error_set_int(
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error received from peer"),
+          GRPC_ERROR_INT_GRPC_STATUS, static_cast<intptr_t>(status_code));
+    }
+    if (b->idx.named.grpc_message != nullptr) {
+      error = grpc_error_set_str(
+          error, GRPC_ERROR_STR_GRPC_MESSAGE,
+          grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_message->md)));
+      grpc_metadata_batch_remove(b, b->idx.named.grpc_message);
+    } else if (error != GRPC_ERROR_NONE) {
+      error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE,
+                                 grpc_empty_slice());
+    }
+    set_final_status(call, GRPC_ERROR_REF(error));
+    grpc_metadata_batch_remove(b, b->idx.named.grpc_status);
+    GRPC_ERROR_UNREF(error);
+  } else if (!call->is_client) {
+    set_final_status(call, GRPC_ERROR_NONE);
+  } else {
+    gpr_log(GPR_DEBUG,
+            "Received trailing metadata with no error and no status");
+    set_final_status(
+        call, grpc_error_set_int(
+                  GRPC_ERROR_CREATE_FROM_STATIC_STRING("No status received"),
+                  GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNKNOWN));
+  }
+  publish_app_metadata(call, b, true);
+}
+
+gpr_arena* grpc_call_get_arena(grpc_call* call) { return call->arena; }
+
+grpc_call_stack* grpc_call_get_call_stack(grpc_call* call) {
+  return CALL_STACK_FROM_CALL(call);
+}
+
+/*******************************************************************************
+ * BATCH API IMPLEMENTATION
+ */
+
+static bool are_write_flags_valid(uint32_t flags) {
+  /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */
+  const uint32_t allowed_write_positions =
+      (GRPC_WRITE_USED_MASK | GRPC_WRITE_INTERNAL_USED_MASK);
+  const uint32_t invalid_positions = ~allowed_write_positions;
+  return !(flags & invalid_positions);
+}
+
+static bool are_initial_metadata_flags_valid(uint32_t flags, bool is_client) {
+  /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */
+  uint32_t invalid_positions = ~GRPC_INITIAL_METADATA_USED_MASK;
+  if (!is_client) {
+    invalid_positions |= GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
+  }
+  return !(flags & invalid_positions);
+}
+
+static size_t batch_slot_for_op(grpc_op_type type) {
+  switch (type) {
+    case GRPC_OP_SEND_INITIAL_METADATA:
+      return 0;
+    case GRPC_OP_SEND_MESSAGE:
+      return 1;
+    case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
+    case GRPC_OP_SEND_STATUS_FROM_SERVER:
+      return 2;
+    case GRPC_OP_RECV_INITIAL_METADATA:
+      return 3;
+    case GRPC_OP_RECV_MESSAGE:
+      return 4;
+    case GRPC_OP_RECV_CLOSE_ON_SERVER:
+    case GRPC_OP_RECV_STATUS_ON_CLIENT:
+      return 5;
+  }
+  GPR_UNREACHABLE_CODE(return 123456789);
+}
+
+static batch_control* reuse_or_allocate_batch_control(grpc_call* call,
+                                                      const grpc_op* ops,
+                                                      size_t num_ops) {
+  size_t slot_idx = batch_slot_for_op(ops[0].op);
+  batch_control** pslot = &call->active_batches[slot_idx];
+  batch_control* bctl;
+  if (*pslot != nullptr) {
+    bctl = *pslot;
+    if (bctl->call != nullptr) {
+      return nullptr;
+    }
+    bctl->~batch_control();
+    bctl->op = {};
+  } else {
+    bctl = new (gpr_arena_alloc(call->arena, sizeof(batch_control)))
+        batch_control();
+    *pslot = bctl;
+  }
+  bctl->call = call;
+  bctl->op.payload = &call->stream_op_payload;
+  return bctl;
+}
+
+static void finish_batch_completion(void* user_data,
+                                    grpc_cq_completion* storage) {
+  batch_control* bctl = static_cast<batch_control*>(user_data);
+  grpc_call* call = bctl->call;
+  bctl->call = nullptr;
+  GRPC_CALL_INTERNAL_UNREF(call, "completion");
+}
+
+static void reset_batch_errors(batch_control* bctl) {
+  GRPC_ERROR_UNREF(
+      reinterpret_cast<grpc_error*>(gpr_atm_acq_load(&bctl->batch_error)));
+  gpr_atm_rel_store(&bctl->batch_error,
+                    reinterpret_cast<gpr_atm>(GRPC_ERROR_NONE));
+}
+
+static void post_batch_completion(batch_control* bctl) {
+  grpc_call* next_child_call;
+  grpc_call* call = bctl->call;
+  grpc_error* error = GRPC_ERROR_REF(
+      reinterpret_cast<grpc_error*>(gpr_atm_acq_load(&bctl->batch_error)));
+
+  if (bctl->op.send_initial_metadata) {
+    grpc_metadata_batch_destroy(
+        &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
+  }
+  if (bctl->op.send_message) {
+    call->sending_message = false;
+  }
+  if (bctl->op.send_trailing_metadata) {
+    grpc_metadata_batch_destroy(
+        &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]);
+  }
+  if (bctl->op.recv_trailing_metadata) {
+    /* propagate cancellation to any interested children */
+    gpr_atm_rel_store(&call->received_final_op_atm, 1);
+    parent_call* pc = get_parent_call(call);
+    if (pc != nullptr) {
+      grpc_call* child;
+      gpr_mu_lock(&pc->child_list_mu);
+      child = pc->first_child;
+      if (child != nullptr) {
+        do {
+          next_child_call = child->child->sibling_next;
+          if (child->cancellation_is_inherited) {
+            GRPC_CALL_INTERNAL_REF(child, "propagate_cancel");
+            cancel_with_error(child, GRPC_ERROR_CANCELLED);
+            GRPC_CALL_INTERNAL_UNREF(child, "propagate_cancel");
+          }
+          child = next_child_call;
+        } while (child != pc->first_child);
+      }
+      gpr_mu_unlock(&pc->child_list_mu);
+    }
+    GRPC_ERROR_UNREF(error);
+    error = GRPC_ERROR_NONE;
+  }
+  if (error != GRPC_ERROR_NONE && bctl->op.recv_message &&
+      *call->receiving_buffer != nullptr) {
+    grpc_byte_buffer_destroy(*call->receiving_buffer);
+    *call->receiving_buffer = nullptr;
+  }
+  reset_batch_errors(bctl);
+
+  if (bctl->completion_data.notify_tag.is_closure) {
+    /* unrefs error */
+    bctl->call = nullptr;
+    /* This closure may be meant to be run within some combiner. Since we aren't
+     * running in any combiner here, we need to use GRPC_CLOSURE_SCHED instead
+     * of GRPC_CLOSURE_RUN.
+     */
+    GRPC_CLOSURE_SCHED((grpc_closure*)bctl->completion_data.notify_tag.tag,
+                       error);
+    GRPC_CALL_INTERNAL_UNREF(call, "completion");
+  } else {
+    /* unrefs error */
+    grpc_cq_end_op(bctl->call->cq, bctl->completion_data.notify_tag.tag, error,
+                   finish_batch_completion, bctl,
+                   &bctl->completion_data.cq_completion);
+  }
+}
+
+static void finish_batch_step(batch_control* bctl) {
+  if (gpr_unref(&bctl->steps_to_complete)) {
+    post_batch_completion(bctl);
+  }
+}
+
+static void continue_receiving_slices(batch_control* bctl) {
+  grpc_error* error;
+  grpc_call* call = bctl->call;
+  for (;;) {
+    size_t remaining = call->receiving_stream->length() -
+                       (*call->receiving_buffer)->data.raw.slice_buffer.length;
+    if (remaining == 0) {
+      call->receiving_message = 0;
+      call->receiving_stream.reset();
+      finish_batch_step(bctl);
+      return;
+    }
+    if (call->receiving_stream->Next(remaining, &call->receiving_slice_ready)) {
+      error = call->receiving_stream->Pull(&call->receiving_slice);
+      if (error == GRPC_ERROR_NONE) {
+        grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer,
+                              call->receiving_slice);
+      } else {
+        call->receiving_stream.reset();
+        grpc_byte_buffer_destroy(*call->receiving_buffer);
+        *call->receiving_buffer = nullptr;
+        call->receiving_message = 0;
+        finish_batch_step(bctl);
+        return;
+      }
+    } else {
+      return;
+    }
+  }
+}
+
+static void receiving_slice_ready(void* bctlp, grpc_error* error) {
+  batch_control* bctl = static_cast<batch_control*>(bctlp);
+  grpc_call* call = bctl->call;
+  bool release_error = false;
+
+  if (error == GRPC_ERROR_NONE) {
+    grpc_slice slice;
+    error = call->receiving_stream->Pull(&slice);
+    if (error == GRPC_ERROR_NONE) {
+      grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer,
+                            slice);
+      continue_receiving_slices(bctl);
+    } else {
+      /* Error returned by ByteStream::Pull() needs to be released manually */
+      release_error = true;
+    }
+  }
+
+  if (error != GRPC_ERROR_NONE) {
+    if (grpc_trace_operation_failures.enabled()) {
+      GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error));
+    }
+    call->receiving_stream.reset();
+    grpc_byte_buffer_destroy(*call->receiving_buffer);
+    *call->receiving_buffer = nullptr;
+    call->receiving_message = 0;
+    finish_batch_step(bctl);
+    if (release_error) {
+      GRPC_ERROR_UNREF(error);
+    }
+  }
+}
+
+static void process_data_after_md(batch_control* bctl) {
+  grpc_call* call = bctl->call;
+  if (call->receiving_stream == nullptr) {
+    *call->receiving_buffer = nullptr;
+    call->receiving_message = 0;
+    finish_batch_step(bctl);
+  } else {
+    call->test_only_last_message_flags = call->receiving_stream->flags();
+    if ((call->receiving_stream->flags() & GRPC_WRITE_INTERNAL_COMPRESS) &&
+        (call->incoming_message_compression_algorithm >
+         GRPC_MESSAGE_COMPRESS_NONE)) {
+      grpc_compression_algorithm algo;
+      GPR_ASSERT(
+          grpc_compression_algorithm_from_message_stream_compression_algorithm(
+              &algo, call->incoming_message_compression_algorithm,
+              (grpc_stream_compression_algorithm)0));
+      *call->receiving_buffer =
+          grpc_raw_compressed_byte_buffer_create(nullptr, 0, algo);
+    } else {
+      *call->receiving_buffer = grpc_raw_byte_buffer_create(nullptr, 0);
+    }
+    GRPC_CLOSURE_INIT(&call->receiving_slice_ready, receiving_slice_ready, bctl,
+                      grpc_schedule_on_exec_ctx);
+    continue_receiving_slices(bctl);
+  }
+}
+
+static void receiving_stream_ready(void* bctlp, grpc_error* error) {
+  batch_control* bctl = static_cast<batch_control*>(bctlp);
+  grpc_call* call = bctl->call;
+  if (error != GRPC_ERROR_NONE) {
+    call->receiving_stream.reset();
+    if (reinterpret_cast<grpc_error*>(gpr_atm_acq_load(&bctl->batch_error)) ==
+        GRPC_ERROR_NONE) {
+      gpr_atm_rel_store(&bctl->batch_error,
+                        reinterpret_cast<gpr_atm>(GRPC_ERROR_REF(error)));
+    }
+    cancel_with_error(call, GRPC_ERROR_REF(error));
+  }
+  /* If recv_state is RECV_NONE, we will save the batch_control
+   * object with rel_cas, and will not use it after the cas. Its corresponding
+   * acq_load is in receiving_initial_metadata_ready() */
+  if (error != GRPC_ERROR_NONE || call->receiving_stream == nullptr ||
+      !gpr_atm_rel_cas(&call->recv_state, RECV_NONE, (gpr_atm)bctlp)) {
+    process_data_after_md(bctl);
+  }
+}
+
+// The recv_message_ready callback used when sending a batch containing
+// a recv_message op down the filter stack.  Yields the call combiner
+// before processing the received message.
+static void receiving_stream_ready_in_call_combiner(void* bctlp,
+                                                    grpc_error* error) {
+  batch_control* bctl = static_cast<batch_control*>(bctlp);
+  grpc_call* call = bctl->call;
+  GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_message_ready");
+  receiving_stream_ready(bctlp, error);
+}
+
+static void validate_filtered_metadata(batch_control* bctl) {
+  grpc_compression_algorithm compression_algorithm;
+  grpc_call* call = bctl->call;
+  if (call->incoming_stream_compression_algorithm !=
+          GRPC_STREAM_COMPRESS_NONE &&
+      call->incoming_message_compression_algorithm !=
+          GRPC_MESSAGE_COMPRESS_NONE) {
+    char* error_msg = nullptr;
+    gpr_asprintf(&error_msg,
+                 "Incoming stream has both stream compression (%d) and message "
+                 "compression (%d).",
+                 call->incoming_stream_compression_algorithm,
+                 call->incoming_message_compression_algorithm);
+    gpr_log(GPR_ERROR, "%s", error_msg);
+    cancel_with_status(call, GRPC_STATUS_INTERNAL, error_msg);
+    gpr_free(error_msg);
+  } else if (
+      grpc_compression_algorithm_from_message_stream_compression_algorithm(
+          &compression_algorithm, call->incoming_message_compression_algorithm,
+          call->incoming_stream_compression_algorithm) == 0) {
+    char* error_msg = nullptr;
+    gpr_asprintf(&error_msg,
+                 "Error in incoming message compression (%d) or stream "
+                 "compression (%d).",
+                 call->incoming_stream_compression_algorithm,
+                 call->incoming_message_compression_algorithm);
+    cancel_with_status(call, GRPC_STATUS_INTERNAL, error_msg);
+    gpr_free(error_msg);
+  } else {
+    char* error_msg = nullptr;
+    const grpc_compression_options compression_options =
+        grpc_channel_compression_options(call->channel);
+    if (compression_algorithm >= GRPC_COMPRESS_ALGORITHMS_COUNT) {
+      gpr_asprintf(&error_msg, "Invalid compression algorithm value '%d'.",
+                   compression_algorithm);
+      gpr_log(GPR_ERROR, "%s", error_msg);
+      cancel_with_status(call, GRPC_STATUS_UNIMPLEMENTED, error_msg);
+    } else if (grpc_compression_options_is_algorithm_enabled(
+                   &compression_options, compression_algorithm) == 0) {
+      /* check if algorithm is supported by current channel config */
+      const char* algo_name = nullptr;
+      grpc_compression_algorithm_name(compression_algorithm, &algo_name);
+      gpr_asprintf(&error_msg, "Compression algorithm '%s' is disabled.",
+                   algo_name);
+      gpr_log(GPR_ERROR, "%s", error_msg);
+      cancel_with_status(call, GRPC_STATUS_UNIMPLEMENTED, error_msg);
+    }
+    gpr_free(error_msg);
+
+    GPR_ASSERT(call->encodings_accepted_by_peer != 0);
+    if (!GPR_BITGET(call->encodings_accepted_by_peer, compression_algorithm)) {
+      if (grpc_compression_trace.enabled()) {
+        const char* algo_name = nullptr;
+        grpc_compression_algorithm_name(compression_algorithm, &algo_name);
+        gpr_log(GPR_ERROR,
+                "Compression algorithm ('%s') not present in the bitset of "
+                "accepted encodings ('0x%x')",
+                algo_name, call->encodings_accepted_by_peer);
+      }
+    }
+  }
+}
+
+static void receiving_initial_metadata_ready(void* bctlp, grpc_error* error) {
+  batch_control* bctl = static_cast<batch_control*>(bctlp);
+  grpc_call* call = bctl->call;
+
+  GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_initial_metadata_ready");
+
+  if (error == GRPC_ERROR_NONE) {
+    grpc_metadata_batch* md =
+        &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
+    recv_initial_filter(call, md);
+
+    /* TODO(ctiller): this could be moved into recv_initial_filter now */
+    GPR_TIMER_SCOPE("validate_filtered_metadata", 0);
+    validate_filtered_metadata(bctl);
+
+    if (md->deadline != GRPC_MILLIS_INF_FUTURE && !call->is_client) {
+      call->send_deadline = md->deadline;
+    }
+  } else {
+    if (reinterpret_cast<grpc_error*>(gpr_atm_acq_load(&bctl->batch_error)) ==
+        GRPC_ERROR_NONE) {
+      gpr_atm_rel_store(&bctl->batch_error,
+                        reinterpret_cast<gpr_atm>(GRPC_ERROR_REF(error)));
+    }
+    cancel_with_error(call, GRPC_ERROR_REF(error));
+  }
+
+  grpc_closure* saved_rsr_closure = nullptr;
+  while (true) {
+    gpr_atm rsr_bctlp = gpr_atm_acq_load(&call->recv_state);
+    /* Should only receive initial metadata once */
+    GPR_ASSERT(rsr_bctlp != 1);
+    if (rsr_bctlp == 0) {
+      /* We haven't seen initial metadata and messages before, thus initial
+       * metadata is received first.
+       * no_barrier_cas is used, as this function won't access the batch_control
+       * object saved by receiving_stream_ready() if the initial metadata is
+       * received first. */
+      if (gpr_atm_no_barrier_cas(&call->recv_state, RECV_NONE,
+                                 RECV_INITIAL_METADATA_FIRST)) {
+        break;
+      }
+    } else {
+      /* Already received messages */
+      saved_rsr_closure =
+          GRPC_CLOSURE_CREATE(receiving_stream_ready, (batch_control*)rsr_bctlp,
+                              grpc_schedule_on_exec_ctx);
+      /* No need to modify recv_state */
+      break;
+    }
+  }
+  if (saved_rsr_closure != nullptr) {
+    GRPC_CLOSURE_RUN(saved_rsr_closure, GRPC_ERROR_REF(error));
+  }
+
+  finish_batch_step(bctl);
+}
+
+static void receiving_trailing_metadata_ready(void* bctlp, grpc_error* error) {
+  batch_control* bctl = static_cast<batch_control*>(bctlp);
+  grpc_call* call = bctl->call;
+  GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_trailing_metadata_ready");
+  grpc_metadata_batch* md =
+      &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
+  recv_trailing_filter(call, md, GRPC_ERROR_REF(error));
+  finish_batch_step(bctl);
+}
+
+static void finish_batch(void* bctlp, grpc_error* error) {
+  batch_control* bctl = static_cast<batch_control*>(bctlp);
+  grpc_call* call = bctl->call;
+  GRPC_CALL_COMBINER_STOP(&call->call_combiner, "on_complete");
+  if (reinterpret_cast<grpc_error*>(gpr_atm_acq_load(&bctl->batch_error)) ==
+      GRPC_ERROR_NONE) {
+    gpr_atm_rel_store(&bctl->batch_error,
+                      reinterpret_cast<gpr_atm>(GRPC_ERROR_REF(error)));
+  }
+  if (error != GRPC_ERROR_NONE) {
+    cancel_with_error(call, GRPC_ERROR_REF(error));
+  }
+  finish_batch_step(bctl);
+}
+
+static void free_no_op_completion(void* p, grpc_cq_completion* completion) {
+  gpr_free(completion);
+}
+
+static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops,
+                                        size_t nops, void* notify_tag,
+                                        int is_notify_tag_closure) {
+  GPR_TIMER_SCOPE("call_start_batch", 0);
+
+  size_t i;
+  const grpc_op* op;
+  batch_control* bctl;
+  bool has_send_ops = false;
+  int num_recv_ops = 0;
+  grpc_call_error error = GRPC_CALL_OK;
+  grpc_transport_stream_op_batch* stream_op;
+  grpc_transport_stream_op_batch_payload* stream_op_payload;
+
+  GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, notify_tag);
+
+  if (nops == 0) {
+    if (!is_notify_tag_closure) {
+      GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag));
+      grpc_cq_end_op(call->cq, notify_tag, GRPC_ERROR_NONE,
+                     free_no_op_completion, nullptr,
+                     static_cast<grpc_cq_completion*>(
+                         gpr_malloc(sizeof(grpc_cq_completion))));
+    } else {
+      GRPC_CLOSURE_SCHED((grpc_closure*)notify_tag, GRPC_ERROR_NONE);
+    }
+    error = GRPC_CALL_OK;
+    goto done;
+  }
+
+  bctl = reuse_or_allocate_batch_control(call, ops, nops);
+  if (bctl == nullptr) {
+    return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+  }
+  bctl->completion_data.notify_tag.tag = notify_tag;
+  bctl->completion_data.notify_tag.is_closure =
+      static_cast<uint8_t>(is_notify_tag_closure != 0);
+
+  stream_op = &bctl->op;
+  stream_op_payload = &call->stream_op_payload;
+
+  /* rewrite batch ops into a transport op */
+  for (i = 0; i < nops; i++) {
+    op = &ops[i];
+    if (op->reserved != nullptr) {
+      error = GRPC_CALL_ERROR;
+      goto done_with_error;
+    }
+    switch (op->op) {
+      case GRPC_OP_SEND_INITIAL_METADATA: {
+        /* Flag validation: currently allow no flags */
+        if (!are_initial_metadata_flags_valid(op->flags, call->is_client)) {
+          error = GRPC_CALL_ERROR_INVALID_FLAGS;
+          goto done_with_error;
+        }
+        if (call->sent_initial_metadata) {
+          error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+          goto done_with_error;
+        }
+        /* process compression level */
+        memset(&call->compression_md, 0, sizeof(call->compression_md));
+        size_t additional_metadata_count = 0;
+        grpc_compression_level effective_compression_level =
+            GRPC_COMPRESS_LEVEL_NONE;
+        bool level_set = false;
+        if (op->data.send_initial_metadata.maybe_compression_level.is_set) {
+          effective_compression_level =
+              op->data.send_initial_metadata.maybe_compression_level.level;
+          level_set = true;
+        } else {
+          const grpc_compression_options copts =
+              grpc_channel_compression_options(call->channel);
+          if (copts.default_level.is_set) {
+            level_set = true;
+            effective_compression_level = copts.default_level.level;
+          }
+        }
+        if (level_set && !call->is_client) {
+          const grpc_compression_algorithm calgo =
+              compression_algorithm_for_level_locked(
+                  call, effective_compression_level);
+          /* the following will be picked up by the compress filter and used
+           * as the call's compression algorithm. */
+          call->compression_md.key = GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST;
+          call->compression_md.value = grpc_compression_algorithm_slice(calgo);
+          additional_metadata_count++;
+        }
+
+        if (op->data.send_initial_metadata.count + additional_metadata_count >
+            INT_MAX) {
+          error = GRPC_CALL_ERROR_INVALID_METADATA;
+          goto done_with_error;
+        }
+        stream_op->send_initial_metadata = true;
+        call->sent_initial_metadata = true;
+        if (!prepare_application_metadata(
+                call, static_cast<int>(op->data.send_initial_metadata.count),
+                op->data.send_initial_metadata.metadata, 0, call->is_client,
+                &call->compression_md,
+                static_cast<int>(additional_metadata_count))) {
+          error = GRPC_CALL_ERROR_INVALID_METADATA;
+          goto done_with_error;
+        }
+        /* TODO(ctiller): just make these the same variable? */
+        if (call->is_client) {
+          call->metadata_batch[0][0].deadline = call->send_deadline;
+        }
+        stream_op_payload->send_initial_metadata.send_initial_metadata =
+            &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */];
+        stream_op_payload->send_initial_metadata.send_initial_metadata_flags =
+            op->flags;
+        if (call->is_client) {
+          stream_op_payload->send_initial_metadata.peer_string =
+              &call->peer_string;
+        }
+        has_send_ops = true;
+        break;
+      }
+      case GRPC_OP_SEND_MESSAGE: {
+        if (!are_write_flags_valid(op->flags)) {
+          error = GRPC_CALL_ERROR_INVALID_FLAGS;
+          goto done_with_error;
+        }
+        if (op->data.send_message.send_message == nullptr) {
+          error = GRPC_CALL_ERROR_INVALID_MESSAGE;
+          goto done_with_error;
+        }
+        if (call->sending_message) {
+          error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+          goto done_with_error;
+        }
+        uint32_t flags = op->flags;
+        /* If the outgoing buffer is already compressed, mark it as so in the
+           flags. These will be picked up by the compression filter and further
+           (wasteful) attempts at compression skipped. */
+        if (op->data.send_message.send_message->data.raw.compression >
+            GRPC_COMPRESS_NONE) {
+          flags |= GRPC_WRITE_INTERNAL_COMPRESS;
+        }
+        stream_op->send_message = true;
+        call->sending_message = true;
+        call->sending_stream.Init(
+            &op->data.send_message.send_message->data.raw.slice_buffer, flags);
+        stream_op_payload->send_message.send_message.reset(
+            call->sending_stream.get());
+        has_send_ops = true;
+        break;
+      }
+      case GRPC_OP_SEND_CLOSE_FROM_CLIENT: {
+        /* Flag validation: currently allow no flags */
+        if (op->flags != 0) {
+          error = GRPC_CALL_ERROR_INVALID_FLAGS;
+          goto done_with_error;
+        }
+        if (!call->is_client) {
+          error = GRPC_CALL_ERROR_NOT_ON_SERVER;
+          goto done_with_error;
+        }
+        if (call->sent_final_op) {
+          error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+          goto done_with_error;
+        }
+        stream_op->send_trailing_metadata = true;
+        call->sent_final_op = true;
+        stream_op_payload->send_trailing_metadata.send_trailing_metadata =
+            &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
+        has_send_ops = true;
+        break;
+      }
+      case GRPC_OP_SEND_STATUS_FROM_SERVER: {
+        /* Flag validation: currently allow no flags */
+        if (op->flags != 0) {
+          error = GRPC_CALL_ERROR_INVALID_FLAGS;
+          goto done_with_error;
+        }
+        if (call->is_client) {
+          error = GRPC_CALL_ERROR_NOT_ON_CLIENT;
+          goto done_with_error;
+        }
+        if (call->sent_final_op) {
+          error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+          goto done_with_error;
+        }
+        if (op->data.send_status_from_server.trailing_metadata_count >
+            INT_MAX) {
+          error = GRPC_CALL_ERROR_INVALID_METADATA;
+          goto done_with_error;
+        }
+        stream_op->send_trailing_metadata = true;
+        call->sent_final_op = true;
+        GPR_ASSERT(call->send_extra_metadata_count == 0);
+        call->send_extra_metadata_count = 1;
+        call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem(
+            call->channel, op->data.send_status_from_server.status);
+        grpc_error* status_error =
+            op->data.send_status_from_server.status == GRPC_STATUS_OK
+                ? GRPC_ERROR_NONE
+                : grpc_error_set_int(
+                      GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                          "Server returned error"),
+                      GRPC_ERROR_INT_GRPC_STATUS,
+                      static_cast<intptr_t>(
+                          op->data.send_status_from_server.status));
+        if (op->data.send_status_from_server.status_details != nullptr) {
+          call->send_extra_metadata[1].md = grpc_mdelem_from_slices(
+              GRPC_MDSTR_GRPC_MESSAGE,
+              grpc_slice_ref_internal(
+                  *op->data.send_status_from_server.status_details));
+          call->send_extra_metadata_count++;
+          if (status_error != GRPC_ERROR_NONE) {
+            char* msg = grpc_slice_to_c_string(
+                GRPC_MDVALUE(call->send_extra_metadata[1].md));
+            status_error =
+                grpc_error_set_str(status_error, GRPC_ERROR_STR_GRPC_MESSAGE,
+                                   grpc_slice_from_copied_string(msg));
+            gpr_free(msg);
+          }
+        }
+
+        gpr_atm_rel_store(&call->status_error,
+                          reinterpret_cast<gpr_atm>(status_error));
+        if (!prepare_application_metadata(
+                call,
+                static_cast<int>(
+                    op->data.send_status_from_server.trailing_metadata_count),
+                op->data.send_status_from_server.trailing_metadata, 1, 1,
+                nullptr, 0)) {
+          for (int n = 0; n < call->send_extra_metadata_count; n++) {
+            GRPC_MDELEM_UNREF(call->send_extra_metadata[n].md);
+          }
+          call->send_extra_metadata_count = 0;
+          error = GRPC_CALL_ERROR_INVALID_METADATA;
+          goto done_with_error;
+        }
+        stream_op_payload->send_trailing_metadata.send_trailing_metadata =
+            &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
+        has_send_ops = true;
+        break;
+      }
+      case GRPC_OP_RECV_INITIAL_METADATA: {
+        /* Flag validation: currently allow no flags */
+        if (op->flags != 0) {
+          error = GRPC_CALL_ERROR_INVALID_FLAGS;
+          goto done_with_error;
+        }
+        if (call->received_initial_metadata) {
+          error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+          goto done_with_error;
+        }
+        call->received_initial_metadata = true;
+        call->buffered_metadata[0] =
+            op->data.recv_initial_metadata.recv_initial_metadata;
+        GRPC_CLOSURE_INIT(&call->receiving_initial_metadata_ready,
+                          receiving_initial_metadata_ready, bctl,
+                          grpc_schedule_on_exec_ctx);
+        stream_op->recv_initial_metadata = true;
+        stream_op_payload->recv_initial_metadata.recv_initial_metadata =
+            &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
+        stream_op_payload->recv_initial_metadata.recv_initial_metadata_ready =
+            &call->receiving_initial_metadata_ready;
+        if (!call->is_client) {
+          stream_op_payload->recv_initial_metadata.peer_string =
+              &call->peer_string;
+        }
+        ++num_recv_ops;
+        break;
+      }
+      case GRPC_OP_RECV_MESSAGE: {
+        /* Flag validation: currently allow no flags */
+        if (op->flags != 0) {
+          error = GRPC_CALL_ERROR_INVALID_FLAGS;
+          goto done_with_error;
+        }
+        if (call->receiving_message) {
+          error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+          goto done_with_error;
+        }
+        call->receiving_message = true;
+        stream_op->recv_message = true;
+        call->receiving_buffer = op->data.recv_message.recv_message;
+        stream_op_payload->recv_message.recv_message = &call->receiving_stream;
+        GRPC_CLOSURE_INIT(&call->receiving_stream_ready,
+                          receiving_stream_ready_in_call_combiner, bctl,
+                          grpc_schedule_on_exec_ctx);
+        stream_op_payload->recv_message.recv_message_ready =
+            &call->receiving_stream_ready;
+        ++num_recv_ops;
+        break;
+      }
+      case GRPC_OP_RECV_STATUS_ON_CLIENT: {
+        /* Flag validation: currently allow no flags */
+        if (op->flags != 0) {
+          error = GRPC_CALL_ERROR_INVALID_FLAGS;
+          goto done_with_error;
+        }
+        if (!call->is_client) {
+          error = GRPC_CALL_ERROR_NOT_ON_SERVER;
+          goto done_with_error;
+        }
+        if (call->requested_final_op) {
+          error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+          goto done_with_error;
+        }
+        call->requested_final_op = true;
+        call->buffered_metadata[1] =
+            op->data.recv_status_on_client.trailing_metadata;
+        call->final_op.client.status = op->data.recv_status_on_client.status;
+        call->final_op.client.status_details =
+            op->data.recv_status_on_client.status_details;
+        call->final_op.client.error_string =
+            op->data.recv_status_on_client.error_string;
+        stream_op->recv_trailing_metadata = true;
+        stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
+            &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
+        stream_op_payload->recv_trailing_metadata.collect_stats =
+            &call->final_info.stats.transport_stream_stats;
+        GRPC_CLOSURE_INIT(&call->receiving_trailing_metadata_ready,
+                          receiving_trailing_metadata_ready, bctl,
+                          grpc_schedule_on_exec_ctx);
+        stream_op_payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+            &call->receiving_trailing_metadata_ready;
+        ++num_recv_ops;
+        break;
+      }
+      case GRPC_OP_RECV_CLOSE_ON_SERVER: {
+        /* Flag validation: currently allow no flags */
+        if (op->flags != 0) {
+          error = GRPC_CALL_ERROR_INVALID_FLAGS;
+          goto done_with_error;
+        }
+        if (call->is_client) {
+          error = GRPC_CALL_ERROR_NOT_ON_CLIENT;
+          goto done_with_error;
+        }
+        if (call->requested_final_op) {
+          error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
+          goto done_with_error;
+        }
+        call->requested_final_op = true;
+        call->final_op.server.cancelled =
+            op->data.recv_close_on_server.cancelled;
+        stream_op->recv_trailing_metadata = true;
+        stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
+            &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
+        stream_op_payload->recv_trailing_metadata.collect_stats =
+            &call->final_info.stats.transport_stream_stats;
+        GRPC_CLOSURE_INIT(&call->receiving_trailing_metadata_ready,
+                          receiving_trailing_metadata_ready, bctl,
+                          grpc_schedule_on_exec_ctx);
+        stream_op_payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+            &call->receiving_trailing_metadata_ready;
+        ++num_recv_ops;
+        break;
+      }
+    }
+  }
+
+  GRPC_CALL_INTERNAL_REF(call, "completion");
+  if (!is_notify_tag_closure) {
+    GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag));
+  }
+  gpr_ref_init(&bctl->steps_to_complete, (has_send_ops ? 1 : 0) + num_recv_ops);
+
+  if (has_send_ops) {
+    GRPC_CLOSURE_INIT(&bctl->finish_batch, finish_batch, bctl,
+                      grpc_schedule_on_exec_ctx);
+    stream_op->on_complete = &bctl->finish_batch;
+  }
+
+  gpr_atm_rel_store(&call->any_ops_sent_atm, 1);
+  execute_batch(call, stream_op, &bctl->start_batch);
+
+done:
+  return error;
+
+done_with_error:
+  /* reverse any mutations that occurred */
+  if (stream_op->send_initial_metadata) {
+    call->sent_initial_metadata = false;
+    grpc_metadata_batch_clear(&call->metadata_batch[0][0]);
+  }
+  if (stream_op->send_message) {
+    call->sending_message = false;
+    call->sending_stream->Orphan();
+  }
+  if (stream_op->send_trailing_metadata) {
+    call->sent_final_op = false;
+    grpc_metadata_batch_clear(&call->metadata_batch[0][1]);
+  }
+  if (stream_op->recv_initial_metadata) {
+    call->received_initial_metadata = false;
+  }
+  if (stream_op->recv_message) {
+    call->receiving_message = false;
+  }
+  if (stream_op->recv_trailing_metadata) {
+    call->requested_final_op = false;
+  }
+  goto done;
+}
+
+grpc_call_error grpc_call_start_batch(grpc_call* call, const grpc_op* ops,
+                                      size_t nops, void* tag, void* reserved) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_call_error err;
+
+  GRPC_API_TRACE(
+      "grpc_call_start_batch(call=%p, ops=%p, nops=%lu, tag=%p, "
+      "reserved=%p)",
+      5, (call, ops, (unsigned long)nops, tag, reserved));
+
+  if (reserved != nullptr) {
+    err = GRPC_CALL_ERROR;
+  } else {
+    err = call_start_batch(call, ops, nops, tag, 0);
+  }
+
+  return err;
+}
+
+grpc_call_error grpc_call_start_batch_and_execute(grpc_call* call,
+                                                  const grpc_op* ops,
+                                                  size_t nops,
+                                                  grpc_closure* closure) {
+  return call_start_batch(call, ops, nops, closure, 1);
+}
+
+void grpc_call_context_set(grpc_call* call, grpc_context_index elem,
+                           void* value, void (*destroy)(void* value)) {
+  if (call->context[elem].destroy) {
+    call->context[elem].destroy(call->context[elem].value);
+  }
+  call->context[elem].value = value;
+  call->context[elem].destroy = destroy;
+}
+
+void* grpc_call_context_get(grpc_call* call, grpc_context_index elem) {
+  return call->context[elem].value;
+}
+
+uint8_t grpc_call_is_client(grpc_call* call) { return call->is_client; }
+
+grpc_compression_algorithm grpc_call_compression_for_level(
+    grpc_call* call, grpc_compression_level level) {
+  grpc_compression_algorithm algo =
+      compression_algorithm_for_level_locked(call, level);
+  return algo;
+}
+
+const char* grpc_call_error_to_string(grpc_call_error error) {
+  switch (error) {
+    case GRPC_CALL_ERROR:
+      return "GRPC_CALL_ERROR";
+    case GRPC_CALL_ERROR_ALREADY_ACCEPTED:
+      return "GRPC_CALL_ERROR_ALREADY_ACCEPTED";
+    case GRPC_CALL_ERROR_ALREADY_FINISHED:
+      return "GRPC_CALL_ERROR_ALREADY_FINISHED";
+    case GRPC_CALL_ERROR_ALREADY_INVOKED:
+      return "GRPC_CALL_ERROR_ALREADY_INVOKED";
+    case GRPC_CALL_ERROR_BATCH_TOO_BIG:
+      return "GRPC_CALL_ERROR_BATCH_TOO_BIG";
+    case GRPC_CALL_ERROR_INVALID_FLAGS:
+      return "GRPC_CALL_ERROR_INVALID_FLAGS";
+    case GRPC_CALL_ERROR_INVALID_MESSAGE:
+      return "GRPC_CALL_ERROR_INVALID_MESSAGE";
+    case GRPC_CALL_ERROR_INVALID_METADATA:
+      return "GRPC_CALL_ERROR_INVALID_METADATA";
+    case GRPC_CALL_ERROR_NOT_INVOKED:
+      return "GRPC_CALL_ERROR_NOT_INVOKED";
+    case GRPC_CALL_ERROR_NOT_ON_CLIENT:
+      return "GRPC_CALL_ERROR_NOT_ON_CLIENT";
+    case GRPC_CALL_ERROR_NOT_ON_SERVER:
+      return "GRPC_CALL_ERROR_NOT_ON_SERVER";
+    case GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE:
+      return "GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE";
+    case GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH:
+      return "GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH";
+    case GRPC_CALL_ERROR_TOO_MANY_OPERATIONS:
+      return "GRPC_CALL_ERROR_TOO_MANY_OPERATIONS";
+    case GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN:
+      return "GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN";
+    case GRPC_CALL_OK:
+      return "GRPC_CALL_OK";
+  }
+  GPR_UNREACHABLE_CODE(return "GRPC_CALL_ERROR_UNKNOW");
+}
diff --git a/third_party/grpc/src/core/lib/surface/call.h b/third_party/grpc/src/core/lib/surface/call.h
new file mode 100644
index 0000000..bd7295f
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/call.h
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_CALL_H
+#define GRPC_CORE_LIB_SURFACE_CALL_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/context.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/grpc.h>
+#include <grpc/impl/codegen/compression_types.h>
+
+typedef void (*grpc_ioreq_completion_func)(grpc_call* call, int success,
+                                           void* user_data);
+
+typedef struct grpc_call_create_args {
+  grpc_channel* channel;
+  grpc_server* server;
+
+  grpc_call* parent;
+  uint32_t propagation_mask;
+
+  grpc_completion_queue* cq;
+  /* if not NULL, it'll be used in lieu of cq */
+  grpc_pollset_set* pollset_set_alternative;
+
+  const void* server_transport_data;
+
+  grpc_mdelem* add_initial_metadata;
+  size_t add_initial_metadata_count;
+
+  grpc_millis send_deadline;
+} grpc_call_create_args;
+
+/* Create a new call based on \a args.
+   Regardless of success or failure, always returns a valid new call into *call
+   */
+grpc_error* grpc_call_create(const grpc_call_create_args* args,
+                             grpc_call** call);
+
+void grpc_call_set_completion_queue(grpc_call* call, grpc_completion_queue* cq);
+
+#ifndef NDEBUG
+void grpc_call_internal_ref(grpc_call* call, const char* reason);
+void grpc_call_internal_unref(grpc_call* call, const char* reason);
+#define GRPC_CALL_INTERNAL_REF(call, reason) \
+  grpc_call_internal_ref(call, reason)
+#define GRPC_CALL_INTERNAL_UNREF(call, reason) \
+  grpc_call_internal_unref(call, reason)
+#else
+void grpc_call_internal_ref(grpc_call* call);
+void grpc_call_internal_unref(grpc_call* call);
+#define GRPC_CALL_INTERNAL_REF(call, reason) grpc_call_internal_ref(call)
+#define GRPC_CALL_INTERNAL_UNREF(call, reason) grpc_call_internal_unref(call)
+#endif
+
+gpr_arena* grpc_call_get_arena(grpc_call* call);
+
+grpc_call_stack* grpc_call_get_call_stack(grpc_call* call);
+
+grpc_call_error grpc_call_start_batch_and_execute(grpc_call* call,
+                                                  const grpc_op* ops,
+                                                  size_t nops,
+                                                  grpc_closure* closure);
+
+/* gRPC core internal version of grpc_call_cancel that does not create
+ * exec_ctx. */
+void grpc_call_cancel_internal(grpc_call* call);
+
+/* Given the top call_element, get the call object. */
+grpc_call* grpc_call_from_top_element(grpc_call_element* surface_element);
+
+void grpc_call_log_batch(const char* file, int line, gpr_log_severity severity,
+                         grpc_call* call, const grpc_op* ops, size_t nops,
+                         void* tag);
+
+/* Set a context pointer.
+   No thread safety guarantees are made wrt this value. */
+/* TODO(#9731): add exec_ctx to destroy */
+void grpc_call_context_set(grpc_call* call, grpc_context_index elem,
+                           void* value, void (*destroy)(void* value));
+/* Get a context pointer. */
+void* grpc_call_context_get(grpc_call* call, grpc_context_index elem);
+
+#define GRPC_CALL_LOG_BATCH(sev, call, ops, nops, tag) \
+  if (grpc_api_trace.enabled()) grpc_call_log_batch(sev, call, ops, nops, tag)
+
+uint8_t grpc_call_is_client(grpc_call* call);
+
+/* Get the estimated memory size for a call BESIDES the call stack. Combined
+ * with the size of the call stack, it helps estimate the arena size for the
+ * initial call. */
+size_t grpc_call_get_initial_size_estimate();
+
+/* Return an appropriate compression algorithm for the requested compression \a
+ * level in the context of \a call. */
+grpc_compression_algorithm grpc_call_compression_for_level(
+    grpc_call* call, grpc_compression_level level);
+
+extern grpc_core::TraceFlag grpc_call_error_trace;
+extern grpc_core::TraceFlag grpc_compression_trace;
+
+#endif /* GRPC_CORE_LIB_SURFACE_CALL_H */
diff --git a/third_party/grpc/src/core/lib/surface/call_details.cc b/third_party/grpc/src/core/lib/surface/call_details.cc
new file mode 100644
index 0000000..7f20b1d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/call_details.cc
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+
+#include <string.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+
+void grpc_call_details_init(grpc_call_details* cd) {
+  GRPC_API_TRACE("grpc_call_details_init(cd=%p)", 1, (cd));
+  memset(cd, 0, sizeof(*cd));
+  cd->method = grpc_empty_slice();
+  cd->host = grpc_empty_slice();
+}
+
+void grpc_call_details_destroy(grpc_call_details* cd) {
+  GRPC_API_TRACE("grpc_call_details_destroy(cd=%p)", 1, (cd));
+  grpc_core::ExecCtx exec_ctx;
+  grpc_slice_unref_internal(cd->method);
+  grpc_slice_unref_internal(cd->host);
+}
diff --git a/third_party/grpc/src/core/lib/surface/call_log_batch.cc b/third_party/grpc/src/core/lib/surface/call_log_batch.cc
new file mode 100644
index 0000000..f0c82c0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/call_log_batch.cc
@@ -0,0 +1,120 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/call.h"
+
+#include <inttypes.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+static void add_metadata(gpr_strvec* b, const grpc_metadata* md, size_t count) {
+  size_t i;
+  if (md == nullptr) {
+    gpr_strvec_add(b, gpr_strdup("(nil)"));
+    return;
+  }
+  for (i = 0; i < count; i++) {
+    gpr_strvec_add(b, gpr_strdup("\nkey="));
+    gpr_strvec_add(b, grpc_slice_to_c_string(md[i].key));
+
+    gpr_strvec_add(b, gpr_strdup(" value="));
+    gpr_strvec_add(b,
+                   grpc_dump_slice(md[i].value, GPR_DUMP_HEX | GPR_DUMP_ASCII));
+  }
+}
+
+char* grpc_op_string(const grpc_op* op) {
+  char* tmp;
+  char* out;
+
+  gpr_strvec b;
+  gpr_strvec_init(&b);
+
+  switch (op->op) {
+    case GRPC_OP_SEND_INITIAL_METADATA:
+      gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA"));
+      add_metadata(&b, op->data.send_initial_metadata.metadata,
+                   op->data.send_initial_metadata.count);
+      break;
+    case GRPC_OP_SEND_MESSAGE:
+      gpr_asprintf(&tmp, "SEND_MESSAGE ptr=%p",
+                   op->data.send_message.send_message);
+      gpr_strvec_add(&b, tmp);
+      break;
+    case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
+      gpr_strvec_add(&b, gpr_strdup("SEND_CLOSE_FROM_CLIENT"));
+      break;
+    case GRPC_OP_SEND_STATUS_FROM_SERVER:
+      gpr_asprintf(&tmp, "SEND_STATUS_FROM_SERVER status=%d details=",
+                   op->data.send_status_from_server.status);
+      gpr_strvec_add(&b, tmp);
+      if (op->data.send_status_from_server.status_details != nullptr) {
+        gpr_strvec_add(&b, grpc_dump_slice(
+                               *op->data.send_status_from_server.status_details,
+                               GPR_DUMP_ASCII));
+      } else {
+        gpr_strvec_add(&b, gpr_strdup("(null)"));
+      }
+      add_metadata(&b, op->data.send_status_from_server.trailing_metadata,
+                   op->data.send_status_from_server.trailing_metadata_count);
+      break;
+    case GRPC_OP_RECV_INITIAL_METADATA:
+      gpr_asprintf(&tmp, "RECV_INITIAL_METADATA ptr=%p",
+                   op->data.recv_initial_metadata.recv_initial_metadata);
+      gpr_strvec_add(&b, tmp);
+      break;
+    case GRPC_OP_RECV_MESSAGE:
+      gpr_asprintf(&tmp, "RECV_MESSAGE ptr=%p",
+                   op->data.recv_message.recv_message);
+      gpr_strvec_add(&b, tmp);
+      break;
+    case GRPC_OP_RECV_STATUS_ON_CLIENT:
+      gpr_asprintf(&tmp,
+                   "RECV_STATUS_ON_CLIENT metadata=%p status=%p details=%p",
+                   op->data.recv_status_on_client.trailing_metadata,
+                   op->data.recv_status_on_client.status,
+                   op->data.recv_status_on_client.status_details);
+      gpr_strvec_add(&b, tmp);
+      break;
+    case GRPC_OP_RECV_CLOSE_ON_SERVER:
+      gpr_asprintf(&tmp, "RECV_CLOSE_ON_SERVER cancelled=%p",
+                   op->data.recv_close_on_server.cancelled);
+      gpr_strvec_add(&b, tmp);
+  }
+  out = gpr_strvec_flatten(&b, nullptr);
+  gpr_strvec_destroy(&b);
+
+  return out;
+}
+
+void grpc_call_log_batch(const char* file, int line, gpr_log_severity severity,
+                         grpc_call* call, const grpc_op* ops, size_t nops,
+                         void* tag) {
+  char* tmp;
+  size_t i;
+  for (i = 0; i < nops; i++) {
+    tmp = grpc_op_string(&ops[i]);
+    gpr_log(file, line, severity, "ops[%" PRIuPTR "]: %s", i, tmp);
+    gpr_free(tmp);
+  }
+}
diff --git a/third_party/grpc/src/core/lib/surface/call_test_only.h b/third_party/grpc/src/core/lib/surface/call_test_only.h
new file mode 100644
index 0000000..dbd1a86
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/call_test_only.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H
+#define GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+/** Return the message compression algorithm from \a call.
+ *
+ * \warning This function should \b only be used in test code. */
+grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm(
+    grpc_call* call);
+
+/** Return the message flags from \a call.
+ *
+ * \warning This function should \b only be used in test code. */
+uint32_t grpc_call_test_only_get_message_flags(grpc_call* call);
+
+/** Returns a bitset for the encodings (compression algorithms) supported by \a
+ * call's peer.
+ *
+ * To be indexed by grpc_compression_algorithm enum values. */
+uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call* call);
+
+#endif /* GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H */
diff --git a/third_party/grpc/src/core/lib/surface/channel.cc b/third_party/grpc/src/core/lib/surface/channel.cc
new file mode 100644
index 0000000..e47cb43
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/channel.cc
@@ -0,0 +1,500 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/channel.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/compression.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"
+#include "src/core/lib/channel/channel_trace.h"
+#include "src/core/lib/channel/channelz.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/manual_constructor.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/resource_quota.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+/** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS.
+ *  Avoids needing to take a metadata context lock for sending status
+ *  if the status code is <= NUM_CACHED_STATUS_ELEMS.
+ *  Sized to allow the most commonly used codes to fit in
+ *  (OK, Cancelled, Unknown). */
+#define NUM_CACHED_STATUS_ELEMS 3
+
+typedef struct registered_call {
+  grpc_mdelem path;
+  grpc_mdelem authority;
+  struct registered_call* next;
+} registered_call;
+
+struct grpc_channel {
+  int is_client;
+  grpc_compression_options compression_options;
+
+  gpr_atm call_size_estimate;
+  grpc_resource_user* resource_user;
+
+  gpr_mu registered_call_mu;
+  registered_call* registered_calls;
+
+  grpc_core::RefCountedPtr<grpc_core::channelz::ChannelNode> channelz_channel;
+
+  char* target;
+};
+
+#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack*)((c) + 1))
+
+static void destroy_channel(void* arg, grpc_error* error);
+
+grpc_channel* grpc_channel_create_with_builder(
+    grpc_channel_stack_builder* builder,
+    grpc_channel_stack_type channel_stack_type) {
+  char* target = gpr_strdup(grpc_channel_stack_builder_get_target(builder));
+  grpc_channel_args* args = grpc_channel_args_copy(
+      grpc_channel_stack_builder_get_channel_arguments(builder));
+  grpc_resource_user* resource_user =
+      grpc_channel_stack_builder_get_resource_user(builder);
+  grpc_channel* channel;
+  if (channel_stack_type == GRPC_SERVER_CHANNEL) {
+    GRPC_STATS_INC_SERVER_CHANNELS_CREATED();
+  } else {
+    GRPC_STATS_INC_CLIENT_CHANNELS_CREATED();
+  }
+  grpc_error* error = grpc_channel_stack_builder_finish(
+      builder, sizeof(grpc_channel), 1, destroy_channel, nullptr,
+      reinterpret_cast<void**>(&channel));
+  if (error != GRPC_ERROR_NONE) {
+    gpr_log(GPR_ERROR, "channel stack builder failed: %s",
+            grpc_error_string(error));
+    GRPC_ERROR_UNREF(error);
+    gpr_free(target);
+    grpc_channel_args_destroy(args);
+    return channel;
+  }
+
+  channel->target = target;
+  channel->resource_user = resource_user;
+  channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type);
+  bool channelz_enabled = GRPC_ENABLE_CHANNELZ_DEFAULT;
+  size_t channel_tracer_max_memory =
+      GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT;
+  bool internal_channel = false;
+  // this creates the default ChannelNode. Different types of channels may
+  // override this to ensure a correct ChannelNode is created.
+  grpc_core::channelz::ChannelNodeCreationFunc channel_node_create_func =
+      grpc_core::channelz::ChannelNode::MakeChannelNode;
+  gpr_mu_init(&channel->registered_call_mu);
+  channel->registered_calls = nullptr;
+
+  gpr_atm_no_barrier_store(
+      &channel->call_size_estimate,
+      (gpr_atm)CHANNEL_STACK_FROM_CHANNEL(channel)->call_stack_size +
+          grpc_call_get_initial_size_estimate());
+
+  grpc_compression_options_init(&channel->compression_options);
+  for (size_t i = 0; i < args->num_args; i++) {
+    if (0 ==
+        strcmp(args->args[i].key, GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) {
+      channel->compression_options.default_level.is_set = true;
+      channel->compression_options.default_level.level =
+          static_cast<grpc_compression_level>(grpc_channel_arg_get_integer(
+              &args->args[i],
+              {GRPC_COMPRESS_LEVEL_NONE, GRPC_COMPRESS_LEVEL_NONE,
+               GRPC_COMPRESS_LEVEL_COUNT - 1}));
+    } else if (0 == strcmp(args->args[i].key,
+                           GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) {
+      channel->compression_options.default_algorithm.is_set = true;
+      channel->compression_options.default_algorithm.algorithm =
+          static_cast<grpc_compression_algorithm>(grpc_channel_arg_get_integer(
+              &args->args[i], {GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE,
+                               GRPC_COMPRESS_ALGORITHMS_COUNT - 1}));
+    } else if (0 ==
+               strcmp(args->args[i].key,
+                      GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) {
+      channel->compression_options.enabled_algorithms_bitset =
+          static_cast<uint32_t>(args->args[i].value.integer) |
+          0x1; /* always support no compression */
+    } else if (0 == strcmp(args->args[i].key,
+                           GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE)) {
+      const grpc_integer_options options = {
+          GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX};
+      channel_tracer_max_memory =
+          (size_t)grpc_channel_arg_get_integer(&args->args[i], options);
+    } else if (0 == strcmp(args->args[i].key, GRPC_ARG_ENABLE_CHANNELZ)) {
+      // channelz will not be enabled by default until all concerns in
+      // https://github.com/grpc/grpc/issues/15986 are addressed.
+      channelz_enabled = grpc_channel_arg_get_bool(
+          &args->args[i], GRPC_ENABLE_CHANNELZ_DEFAULT);
+    } else if (0 == strcmp(args->args[i].key,
+                           GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC)) {
+      GPR_ASSERT(args->args[i].type == GRPC_ARG_POINTER);
+      GPR_ASSERT(args->args[i].value.pointer.p != nullptr);
+      channel_node_create_func =
+          reinterpret_cast<grpc_core::channelz::ChannelNodeCreationFunc>(
+              args->args[i].value.pointer.p);
+    } else if (0 == strcmp(args->args[i].key,
+                           GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL)) {
+      internal_channel = grpc_channel_arg_get_bool(&args->args[i], false);
+    }
+  }
+
+  grpc_channel_args_destroy(args);
+  // we only need to do the channelz bookkeeping for clients here. The channelz
+  // bookkeeping for server channels occurs in src/core/lib/surface/server.cc
+  if (channelz_enabled && channel->is_client) {
+    channel->channelz_channel = channel_node_create_func(
+        channel, channel_tracer_max_memory, !internal_channel);
+    channel->channelz_channel->AddTraceEvent(
+        grpc_core::channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string("Channel created"));
+  }
+  return channel;
+}
+
+static grpc_core::UniquePtr<char> get_default_authority(
+    const grpc_channel_args* input_args) {
+  bool has_default_authority = false;
+  char* ssl_override = nullptr;
+  grpc_core::UniquePtr<char> default_authority;
+  const size_t num_args = input_args != nullptr ? input_args->num_args : 0;
+  for (size_t i = 0; i < num_args; ++i) {
+    if (0 == strcmp(input_args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) {
+      has_default_authority = true;
+    } else if (0 == strcmp(input_args->args[i].key,
+                           GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) {
+      ssl_override = grpc_channel_arg_get_string(&input_args->args[i]);
+    }
+  }
+  if (!has_default_authority && ssl_override != nullptr) {
+    default_authority.reset(gpr_strdup(ssl_override));
+  }
+  return default_authority;
+}
+
+static grpc_channel_args* build_channel_args(
+    const grpc_channel_args* input_args, char* default_authority) {
+  grpc_arg new_args[1];
+  size_t num_new_args = 0;
+  if (default_authority != nullptr) {
+    new_args[num_new_args++] = grpc_channel_arg_string_create(
+        const_cast<char*>(GRPC_ARG_DEFAULT_AUTHORITY), default_authority);
+  }
+  return grpc_channel_args_copy_and_add(input_args, new_args, num_new_args);
+}
+
+grpc_core::channelz::ChannelNode* grpc_channel_get_channelz_node(
+    grpc_channel* channel) {
+  return channel->channelz_channel.get();
+}
+
+grpc_channel* grpc_channel_create(const char* target,
+                                  const grpc_channel_args* input_args,
+                                  grpc_channel_stack_type channel_stack_type,
+                                  grpc_transport* optional_transport,
+                                  grpc_resource_user* resource_user) {
+  grpc_channel_stack_builder* builder = grpc_channel_stack_builder_create();
+  const grpc_core::UniquePtr<char> default_authority =
+      get_default_authority(input_args);
+  grpc_channel_args* args =
+      build_channel_args(input_args, default_authority.get());
+  grpc_channel_stack_builder_set_channel_arguments(builder, args);
+  grpc_channel_args_destroy(args);
+  grpc_channel_stack_builder_set_target(builder, target);
+  grpc_channel_stack_builder_set_transport(builder, optional_transport);
+  grpc_channel_stack_builder_set_resource_user(builder, resource_user);
+  if (!grpc_channel_init_create_stack(builder, channel_stack_type)) {
+    grpc_channel_stack_builder_destroy(builder);
+    if (resource_user != nullptr) {
+      grpc_resource_user_free(resource_user, GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
+    }
+    return nullptr;
+  }
+  grpc_channel* channel =
+      grpc_channel_create_with_builder(builder, channel_stack_type);
+  return channel;
+}
+
+size_t grpc_channel_get_call_size_estimate(grpc_channel* channel) {
+#define ROUND_UP_SIZE 256
+  /* We round up our current estimate to the NEXT value of ROUND_UP_SIZE.
+     This ensures:
+      1. a consistent size allocation when our estimate is drifting slowly
+         (which is common) - which tends to help most allocators reuse memory
+      2. a small amount of allowed growth over the estimate without hitting
+         the arena size doubling case, reducing overall memory usage */
+  return (static_cast<size_t>(
+              gpr_atm_no_barrier_load(&channel->call_size_estimate)) +
+          2 * ROUND_UP_SIZE) &
+         ~static_cast<size_t>(ROUND_UP_SIZE - 1);
+}
+
+void grpc_channel_update_call_size_estimate(grpc_channel* channel,
+                                            size_t size) {
+  size_t cur = static_cast<size_t>(
+      gpr_atm_no_barrier_load(&channel->call_size_estimate));
+  if (cur < size) {
+    /* size grew: update estimate */
+    gpr_atm_no_barrier_cas(&channel->call_size_estimate,
+                           static_cast<gpr_atm>(cur),
+                           static_cast<gpr_atm>(size));
+    /* if we lose: never mind, something else will likely update soon enough */
+  } else if (cur == size) {
+    /* no change: holding pattern */
+  } else if (cur > 0) {
+    /* size shrank: decrease estimate */
+    gpr_atm_no_barrier_cas(
+        &channel->call_size_estimate, static_cast<gpr_atm>(cur),
+        static_cast<gpr_atm>(GPR_MIN(cur - 1, (255 * cur + size) / 256)));
+    /* if we lose: never mind, something else will likely update soon enough */
+  }
+}
+
+char* grpc_channel_get_target(grpc_channel* channel) {
+  GRPC_API_TRACE("grpc_channel_get_target(channel=%p)", 1, (channel));
+  return gpr_strdup(channel->target);
+}
+
+void grpc_channel_get_info(grpc_channel* channel,
+                           const grpc_channel_info* channel_info) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_channel_element* elem =
+      grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0);
+  elem->filter->get_channel_info(elem, channel_info);
+}
+
+void grpc_channel_reset_connect_backoff(grpc_channel* channel) {
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE("grpc_channel_reset_connect_backoff(channel=%p)", 1,
+                 (channel));
+  grpc_transport_op* op = grpc_make_transport_op(nullptr);
+  op->reset_connect_backoff = true;
+  grpc_channel_element* elem =
+      grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0);
+  elem->filter->start_transport_op(elem, op);
+}
+
+static grpc_call* grpc_channel_create_call_internal(
+    grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask,
+    grpc_completion_queue* cq, grpc_pollset_set* pollset_set_alternative,
+    grpc_mdelem path_mdelem, grpc_mdelem authority_mdelem,
+    grpc_millis deadline) {
+  grpc_mdelem send_metadata[2];
+  size_t num_metadata = 0;
+
+  GPR_ASSERT(channel->is_client);
+  GPR_ASSERT(!(cq != nullptr && pollset_set_alternative != nullptr));
+
+  send_metadata[num_metadata++] = path_mdelem;
+  if (!GRPC_MDISNULL(authority_mdelem)) {
+    send_metadata[num_metadata++] = authority_mdelem;
+  }
+
+  grpc_call_create_args args;
+  args.channel = channel;
+  args.server = nullptr;
+  args.parent = parent_call;
+  args.propagation_mask = propagation_mask;
+  args.cq = cq;
+  args.pollset_set_alternative = pollset_set_alternative;
+  args.server_transport_data = nullptr;
+  args.add_initial_metadata = send_metadata;
+  args.add_initial_metadata_count = num_metadata;
+  args.send_deadline = deadline;
+
+  grpc_call* call;
+  GRPC_LOG_IF_ERROR("call_create", grpc_call_create(&args, &call));
+  return call;
+}
+
+grpc_call* grpc_channel_create_call(grpc_channel* channel,
+                                    grpc_call* parent_call,
+                                    uint32_t propagation_mask,
+                                    grpc_completion_queue* cq,
+                                    grpc_slice method, const grpc_slice* host,
+                                    gpr_timespec deadline, void* reserved) {
+  GPR_ASSERT(!reserved);
+  grpc_core::ExecCtx exec_ctx;
+  grpc_call* call = grpc_channel_create_call_internal(
+      channel, parent_call, propagation_mask, cq, nullptr,
+      grpc_mdelem_create(GRPC_MDSTR_PATH, method, nullptr),
+      host != nullptr ? grpc_mdelem_create(GRPC_MDSTR_AUTHORITY, *host, nullptr)
+                      : GRPC_MDNULL,
+      grpc_timespec_to_millis_round_up(deadline));
+
+  return call;
+}
+
+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 grpc_slice& method,
+    const grpc_slice* host, grpc_millis deadline, void* reserved) {
+  GPR_ASSERT(!reserved);
+  return grpc_channel_create_call_internal(
+      channel, parent_call, propagation_mask, nullptr, pollset_set,
+      grpc_mdelem_create(GRPC_MDSTR_PATH, method, nullptr),
+      host != nullptr ? grpc_mdelem_create(GRPC_MDSTR_AUTHORITY, *host, nullptr)
+                      : GRPC_MDNULL,
+      deadline);
+}
+
+void* grpc_channel_register_call(grpc_channel* channel, const char* method,
+                                 const char* host, void* reserved) {
+  registered_call* rc =
+      static_cast<registered_call*>(gpr_malloc(sizeof(registered_call)));
+  GRPC_API_TRACE(
+      "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)",
+      4, (channel, method, host, reserved));
+  GPR_ASSERT(!reserved);
+  grpc_core::ExecCtx exec_ctx;
+
+  rc->path = grpc_mdelem_from_slices(
+      GRPC_MDSTR_PATH,
+      grpc_slice_intern(grpc_slice_from_static_string(method)));
+  rc->authority =
+      host ? grpc_mdelem_from_slices(
+                 GRPC_MDSTR_AUTHORITY,
+                 grpc_slice_intern(grpc_slice_from_static_string(host)))
+           : GRPC_MDNULL;
+  gpr_mu_lock(&channel->registered_call_mu);
+  rc->next = channel->registered_calls;
+  channel->registered_calls = rc;
+  gpr_mu_unlock(&channel->registered_call_mu);
+
+  return rc;
+}
+
+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,
+    gpr_timespec deadline, void* reserved) {
+  registered_call* rc = static_cast<registered_call*>(registered_call_handle);
+  GRPC_API_TRACE(
+      "grpc_channel_create_registered_call("
+      "channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, "
+      "registered_call_handle=%p, "
+      "deadline=gpr_timespec { tv_sec: %" PRId64
+      ", tv_nsec: %d, clock_type: %d }, "
+      "reserved=%p)",
+      9,
+      (channel, parent_call, (unsigned)propagation_mask, completion_queue,
+       registered_call_handle, deadline.tv_sec, deadline.tv_nsec,
+       (int)deadline.clock_type, reserved));
+  GPR_ASSERT(!reserved);
+  grpc_core::ExecCtx exec_ctx;
+  grpc_call* call = grpc_channel_create_call_internal(
+      channel, parent_call, propagation_mask, completion_queue, nullptr,
+      GRPC_MDELEM_REF(rc->path), GRPC_MDELEM_REF(rc->authority),
+      grpc_timespec_to_millis_round_up(deadline));
+
+  return call;
+}
+
+#ifndef NDEBUG
+#define REF_REASON reason
+#define REF_ARG , const char* reason
+#else
+#define REF_REASON ""
+#define REF_ARG
+#endif
+void grpc_channel_internal_ref(grpc_channel* c REF_ARG) {
+  GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON);
+}
+
+void grpc_channel_internal_unref(grpc_channel* c REF_ARG) {
+  GRPC_CHANNEL_STACK_UNREF(CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON);
+}
+
+static void destroy_channel(void* arg, grpc_error* error) {
+  grpc_channel* channel = static_cast<grpc_channel*>(arg);
+  if (channel->channelz_channel != nullptr) {
+    channel->channelz_channel->AddTraceEvent(
+        grpc_core::channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string("Channel destroyed"));
+    channel->channelz_channel->MarkChannelDestroyed();
+    channel->channelz_channel.reset();
+  }
+  grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel));
+  while (channel->registered_calls) {
+    registered_call* rc = channel->registered_calls;
+    channel->registered_calls = rc->next;
+    GRPC_MDELEM_UNREF(rc->path);
+    GRPC_MDELEM_UNREF(rc->authority);
+    gpr_free(rc);
+  }
+  if (channel->resource_user != nullptr) {
+    grpc_resource_user_free(channel->resource_user,
+                            GRPC_RESOURCE_QUOTA_CHANNEL_SIZE);
+  }
+  gpr_mu_destroy(&channel->registered_call_mu);
+  gpr_free(channel->target);
+  gpr_free(channel);
+}
+
+void grpc_channel_destroy(grpc_channel* channel) {
+  grpc_transport_op* op = grpc_make_transport_op(nullptr);
+  grpc_channel_element* elem;
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel));
+  op->disconnect_with_error =
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Destroyed");
+  elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0);
+  elem->filter->start_transport_op(elem, op);
+
+  GRPC_CHANNEL_INTERNAL_UNREF(channel, "channel");
+}
+
+grpc_channel_stack* grpc_channel_get_channel_stack(grpc_channel* channel) {
+  return CHANNEL_STACK_FROM_CHANNEL(channel);
+}
+
+grpc_compression_options grpc_channel_compression_options(
+    const grpc_channel* channel) {
+  return channel->compression_options;
+}
+
+grpc_mdelem grpc_channel_get_reffed_status_elem(grpc_channel* channel, int i) {
+  char tmp[GPR_LTOA_MIN_BUFSIZE];
+  switch (i) {
+    case 0:
+      return GRPC_MDELEM_GRPC_STATUS_0;
+    case 1:
+      return GRPC_MDELEM_GRPC_STATUS_1;
+    case 2:
+      return GRPC_MDELEM_GRPC_STATUS_2;
+  }
+  gpr_ltoa(i, tmp);
+  return grpc_mdelem_from_slices(GRPC_MDSTR_GRPC_STATUS,
+                                 grpc_slice_from_copied_string(tmp));
+}
diff --git a/third_party/grpc/src/core/lib/surface/channel.h b/third_party/grpc/src/core/lib/surface/channel.h
new file mode 100644
index 0000000..ab00b8e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/channel.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_H
+#define GRPC_CORE_LIB_SURFACE_CHANNEL_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/channel/channelz.h"
+#include "src/core/lib/surface/channel_stack_type.h"
+
+grpc_channel* grpc_channel_create(const char* target,
+                                  const grpc_channel_args* args,
+                                  grpc_channel_stack_type channel_stack_type,
+                                  grpc_transport* optional_transport,
+                                  grpc_resource_user* resource_user = nullptr);
+
+grpc_channel* grpc_channel_create_with_builder(
+    grpc_channel_stack_builder* builder,
+    grpc_channel_stack_type channel_stack_type);
+
+/** 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 grpc_slice& method,
+    const grpc_slice* host, grpc_millis deadline, void* reserved);
+
+/** Get a (borrowed) pointer to this channels underlying channel stack */
+grpc_channel_stack* grpc_channel_get_channel_stack(grpc_channel* channel);
+
+grpc_core::channelz::ChannelNode* grpc_channel_get_channelz_node(
+    grpc_channel* channel);
+
+/** Get a grpc_mdelem of grpc-status: X where X is the numeric value of
+    status_code.
+
+    The returned elem is owned by the caller. */
+grpc_mdelem grpc_channel_get_reffed_status_elem(grpc_channel* channel,
+                                                int status_code);
+
+size_t grpc_channel_get_call_size_estimate(grpc_channel* channel);
+void grpc_channel_update_call_size_estimate(grpc_channel* channel, size_t size);
+
+#ifndef NDEBUG
+void grpc_channel_internal_ref(grpc_channel* channel, const char* reason);
+void grpc_channel_internal_unref(grpc_channel* channel, const char* reason);
+#define GRPC_CHANNEL_INTERNAL_REF(channel, reason) \
+  grpc_channel_internal_ref(channel, reason)
+#define GRPC_CHANNEL_INTERNAL_UNREF(channel, reason) \
+  grpc_channel_internal_unref(channel, reason)
+#else
+void grpc_channel_internal_ref(grpc_channel* channel);
+void grpc_channel_internal_unref(grpc_channel* channel);
+#define GRPC_CHANNEL_INTERNAL_REF(channel, reason) \
+  grpc_channel_internal_ref(channel)
+#define GRPC_CHANNEL_INTERNAL_UNREF(channel, reason) \
+  grpc_channel_internal_unref(channel)
+#endif
+
+/** Return the channel's compression options. */
+grpc_compression_options grpc_channel_compression_options(
+    const grpc_channel* channel);
+
+#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_H */
diff --git a/third_party/grpc/src/core/lib/surface/channel_init.cc b/third_party/grpc/src/core/lib/surface/channel_init.cc
new file mode 100644
index 0000000..62eb1c3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/channel_init.cc
@@ -0,0 +1,109 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/channel_init.h"
+
+#include <grpc/support/alloc.h>
+
+typedef struct stage_slot {
+  grpc_channel_init_stage fn;
+  void* arg;
+  int priority;
+  size_t insertion_order;
+} stage_slot;
+
+typedef struct stage_slots {
+  stage_slot* slots;
+  size_t num_slots;
+  size_t cap_slots;
+} stage_slots;
+
+static stage_slots g_slots[GRPC_NUM_CHANNEL_STACK_TYPES];
+static bool g_finalized;
+
+void grpc_channel_init_init(void) {
+  for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) {
+    g_slots[i].slots = nullptr;
+    g_slots[i].num_slots = 0;
+    g_slots[i].cap_slots = 0;
+  }
+  g_finalized = false;
+}
+
+void grpc_channel_init_register_stage(grpc_channel_stack_type type,
+                                      int priority,
+                                      grpc_channel_init_stage stage,
+                                      void* stage_arg) {
+  GPR_ASSERT(!g_finalized);
+  if (g_slots[type].cap_slots == g_slots[type].num_slots) {
+    g_slots[type].cap_slots = GPR_MAX(8, 3 * g_slots[type].cap_slots / 2);
+    g_slots[type].slots = static_cast<stage_slot*>(
+        gpr_realloc(g_slots[type].slots,
+                    g_slots[type].cap_slots * sizeof(*g_slots[type].slots)));
+  }
+  stage_slot* s = &g_slots[type].slots[g_slots[type].num_slots++];
+  s->insertion_order = g_slots[type].num_slots;
+  s->priority = priority;
+  s->fn = stage;
+  s->arg = stage_arg;
+}
+
+static int compare_slots(const void* a, const void* b) {
+  const stage_slot* sa = static_cast<const stage_slot*>(a);
+  const stage_slot* sb = static_cast<const stage_slot*>(b);
+
+  int c = GPR_ICMP(sa->priority, sb->priority);
+  if (c != 0) return c;
+  return GPR_ICMP(sa->insertion_order, sb->insertion_order);
+}
+
+void grpc_channel_init_finalize(void) {
+  GPR_ASSERT(!g_finalized);
+  for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) {
+    qsort(g_slots[i].slots, g_slots[i].num_slots, sizeof(*g_slots[i].slots),
+          compare_slots);
+  }
+  g_finalized = true;
+}
+
+void grpc_channel_init_shutdown(void) {
+  for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) {
+    gpr_free(g_slots[i].slots);
+    g_slots[i].slots =
+        static_cast<stage_slot*>((void*)static_cast<uintptr_t>(0xdeadbeef));
+  }
+}
+
+bool grpc_channel_init_create_stack(grpc_channel_stack_builder* builder,
+                                    grpc_channel_stack_type type) {
+  GPR_ASSERT(g_finalized);
+
+  grpc_channel_stack_builder_set_name(builder,
+                                      grpc_channel_stack_type_string(type));
+
+  for (size_t i = 0; i < g_slots[type].num_slots; i++) {
+    const stage_slot* slot = &g_slots[type].slots[i];
+    if (!slot->fn(builder, slot->arg)) {
+      return false;
+    }
+  }
+
+  return true;
+}
diff --git a/third_party/grpc/src/core/lib/surface/channel_init.h b/third_party/grpc/src/core/lib/surface/channel_init.h
new file mode 100644
index 0000000..f018524
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/channel_init.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H
+#define GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/surface/channel_stack_type.h"
+#include "src/core/lib/transport/transport.h"
+
+#define GRPC_CHANNEL_INIT_BUILTIN_PRIORITY 10000
+
+/// 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
+/// a channel stack for various subsystems.
+
+/// One stage of mutation: call functions against \a builder to influence the
+/// finally constructed channel stack
+typedef bool (*grpc_channel_init_stage)(grpc_channel_stack_builder* builder,
+                                        void* arg);
+
+/// Global initialization of the system
+void grpc_channel_init_init(void);
+
+/// Register one stage of mutators.
+/// Stages are run in priority order (lowest to highest), and then in
+/// registration order (in the case of a tie).
+/// Stages are registered against one of the pre-determined channel stack
+/// types.
+void grpc_channel_init_register_stage(grpc_channel_stack_type type,
+                                      int priority,
+                                      grpc_channel_init_stage stage_fn,
+                                      void* stage_arg);
+
+/// Finalize registration. No more calls to grpc_channel_init_register_stage are
+/// allowed.
+void grpc_channel_init_finalize(void);
+/// Shutdown the channel init system
+void grpc_channel_init_shutdown(void);
+
+/// Construct a channel stack of some sort: see channel_stack.h for details
+/// \a type is the type of channel stack to create
+/// \a prefix_bytes is the number of bytes before the channel stack to allocate
+/// \a args are configuration arguments for the channel stack
+/// \a initial_refs is the initial refcount to give the channel stack
+/// \a destroy and \a destroy_arg specify how to destroy the channel stack
+///    if destroy_arg is NULL, the returned value from this function will be
+///    substituted
+/// \a optional_transport is either NULL or a constructed transport object
+/// Returns a pointer to the base of the memory allocated (the actual channel
+/// stack object will be prefix_bytes past that pointer)
+bool grpc_channel_init_create_stack(grpc_channel_stack_builder* builder,
+                                    grpc_channel_stack_type type);
+
+#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H */
diff --git a/third_party/grpc/src/core/lib/surface/channel_ping.cc b/third_party/grpc/src/core/lib/surface/channel_ping.cc
new file mode 100644
index 0000000..bae9459
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/channel_ping.cc
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/channel.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/completion_queue.h"
+
+typedef struct {
+  grpc_closure closure;
+  void* tag;
+  grpc_completion_queue* cq;
+  grpc_cq_completion completion_storage;
+} ping_result;
+
+static void ping_destroy(void* arg, grpc_cq_completion* storage) {
+  gpr_free(arg);
+}
+
+static void ping_done(void* arg, grpc_error* error) {
+  ping_result* pr = static_cast<ping_result*>(arg);
+  grpc_cq_end_op(pr->cq, pr->tag, GRPC_ERROR_REF(error), ping_destroy, pr,
+                 &pr->completion_storage);
+}
+
+void grpc_channel_ping(grpc_channel* channel, grpc_completion_queue* cq,
+                       void* tag, void* reserved) {
+  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(nullptr);
+  ping_result* pr = static_cast<ping_result*>(gpr_malloc(sizeof(*pr)));
+  grpc_channel_element* top_elem =
+      grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
+  grpc_core::ExecCtx exec_ctx;
+  GPR_ASSERT(reserved == nullptr);
+  pr->tag = tag;
+  pr->cq = cq;
+  GRPC_CLOSURE_INIT(&pr->closure, ping_done, pr, grpc_schedule_on_exec_ctx);
+  op->send_ping.on_ack = &pr->closure;
+  op->bind_pollset = grpc_cq_pollset(cq);
+  GPR_ASSERT(grpc_cq_begin_op(cq, tag));
+  top_elem->filter->start_transport_op(top_elem, op);
+}
diff --git a/third_party/grpc/src/core/lib/surface/channel_stack_type.cc b/third_party/grpc/src/core/lib/surface/channel_stack_type.cc
new file mode 100644
index 0000000..fcf96dd
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/channel_stack_type.cc
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include "src/core/lib/surface/channel_stack_type.h"
+
+bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type) {
+  switch (type) {
+    case GRPC_CLIENT_CHANNEL:
+      return true;
+    case GRPC_CLIENT_SUBCHANNEL:
+      return true;
+    case GRPC_CLIENT_LAME_CHANNEL:
+      return true;
+    case GRPC_CLIENT_DIRECT_CHANNEL:
+      return true;
+    case GRPC_SERVER_CHANNEL:
+      return false;
+    case GRPC_NUM_CHANNEL_STACK_TYPES:
+      break;
+  }
+  GPR_UNREACHABLE_CODE(return true;);
+}
+
+const char* grpc_channel_stack_type_string(grpc_channel_stack_type type) {
+  switch (type) {
+    case GRPC_CLIENT_CHANNEL:
+      return "CLIENT_CHANNEL";
+    case GRPC_CLIENT_SUBCHANNEL:
+      return "CLIENT_SUBCHANNEL";
+    case GRPC_SERVER_CHANNEL:
+      return "SERVER_CHANNEL";
+    case GRPC_CLIENT_LAME_CHANNEL:
+      return "CLIENT_LAME_CHANNEL";
+    case GRPC_CLIENT_DIRECT_CHANNEL:
+      return "CLIENT_DIRECT_CHANNEL";
+    case GRPC_NUM_CHANNEL_STACK_TYPES:
+      break;
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
diff --git a/third_party/grpc/src/core/lib/surface/channel_stack_type.h b/third_party/grpc/src/core/lib/surface/channel_stack_type.h
new file mode 100644
index 0000000..8a3c08e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/channel_stack_type.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H
+#define GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+typedef enum {
+  // normal top-half client channel with load-balancing, connection management
+  GRPC_CLIENT_CHANNEL,
+  // bottom-half of a client channel: everything that happens post-load
+  // balancing (bound to a specific transport)
+  GRPC_CLIENT_SUBCHANNEL,
+  // a permanently broken client channel
+  GRPC_CLIENT_LAME_CHANNEL,
+  // a directly connected client channel (without load-balancing, directly talks
+  // to a transport)
+  GRPC_CLIENT_DIRECT_CHANNEL,
+  // server side channel
+  GRPC_SERVER_CHANNEL,
+  // must be last
+  GRPC_NUM_CHANNEL_STACK_TYPES
+} grpc_channel_stack_type;
+
+bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type);
+
+const char* grpc_channel_stack_type_string(grpc_channel_stack_type type);
+
+#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H */
diff --git a/third_party/grpc/src/core/lib/surface/completion_queue.cc b/third_party/grpc/src/core/lib/surface/completion_queue.cc
new file mode 100644
index 0000000..661022e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/completion_queue.cc
@@ -0,0 +1,1408 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/completion_queue.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/spinlock.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/tls.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/event_string.h"
+
+grpc_core::TraceFlag grpc_trace_operation_failures(false, "op_failure");
+grpc_core::DebugOnlyTraceFlag grpc_trace_pending_tags(false, "pending_tags");
+grpc_core::DebugOnlyTraceFlag grpc_trace_cq_refcount(false, "cq_refcount");
+
+// Specifies a cq thread local cache.
+// The first event that occurs on a thread
+// with a cq cache will go into that cache, and
+// will only be returned on the thread that initialized the cache.
+// NOTE: Only one event will ever be cached.
+GPR_TLS_DECL(g_cached_event);
+GPR_TLS_DECL(g_cached_cq);
+
+typedef struct {
+  grpc_pollset_worker** worker;
+  void* tag;
+} plucker;
+
+typedef struct {
+  bool can_get_pollset;
+  bool can_listen;
+  size_t (*size)(void);
+  void (*init)(grpc_pollset* pollset, gpr_mu** mu);
+  grpc_error* (*kick)(grpc_pollset* pollset,
+                      grpc_pollset_worker* specific_worker);
+  grpc_error* (*work)(grpc_pollset* pollset, grpc_pollset_worker** worker,
+                      grpc_millis deadline);
+  void (*shutdown)(grpc_pollset* pollset, grpc_closure* closure);
+  void (*destroy)(grpc_pollset* pollset);
+} cq_poller_vtable;
+
+typedef struct non_polling_worker {
+  gpr_cv cv;
+  bool kicked;
+  struct non_polling_worker* next;
+  struct non_polling_worker* prev;
+} non_polling_worker;
+
+typedef struct {
+  gpr_mu mu;
+  bool kicked_without_poller;
+  non_polling_worker* root;
+  grpc_closure* shutdown;
+} non_polling_poller;
+
+static size_t non_polling_poller_size(void) {
+  return sizeof(non_polling_poller);
+}
+
+static void non_polling_poller_init(grpc_pollset* pollset, gpr_mu** mu) {
+  non_polling_poller* npp = reinterpret_cast<non_polling_poller*>(pollset);
+  gpr_mu_init(&npp->mu);
+  *mu = &npp->mu;
+}
+
+static void non_polling_poller_destroy(grpc_pollset* pollset) {
+  non_polling_poller* npp = reinterpret_cast<non_polling_poller*>(pollset);
+  gpr_mu_destroy(&npp->mu);
+}
+
+static grpc_error* non_polling_poller_work(grpc_pollset* pollset,
+                                           grpc_pollset_worker** worker,
+                                           grpc_millis deadline) {
+  non_polling_poller* npp = reinterpret_cast<non_polling_poller*>(pollset);
+  if (npp->shutdown) return GRPC_ERROR_NONE;
+  if (npp->kicked_without_poller) {
+    npp->kicked_without_poller = false;
+    return GRPC_ERROR_NONE;
+  }
+  non_polling_worker w;
+  gpr_cv_init(&w.cv);
+  if (worker != nullptr) *worker = reinterpret_cast<grpc_pollset_worker*>(&w);
+  if (npp->root == nullptr) {
+    npp->root = w.next = w.prev = &w;
+  } else {
+    w.next = npp->root;
+    w.prev = w.next->prev;
+    w.next->prev = w.prev->next = &w;
+  }
+  w.kicked = false;
+  gpr_timespec deadline_ts =
+      grpc_millis_to_timespec(deadline, GPR_CLOCK_MONOTONIC);
+  while (!npp->shutdown && !w.kicked &&
+         !gpr_cv_wait(&w.cv, &npp->mu, deadline_ts))
+    ;
+  grpc_core::ExecCtx::Get()->InvalidateNow();
+  if (&w == npp->root) {
+    npp->root = w.next;
+    if (&w == npp->root) {
+      if (npp->shutdown) {
+        GRPC_CLOSURE_SCHED(npp->shutdown, GRPC_ERROR_NONE);
+      }
+      npp->root = nullptr;
+    }
+  }
+  w.next->prev = w.prev;
+  w.prev->next = w.next;
+  gpr_cv_destroy(&w.cv);
+  if (worker != nullptr) *worker = nullptr;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error* non_polling_poller_kick(
+    grpc_pollset* pollset, grpc_pollset_worker* specific_worker) {
+  non_polling_poller* p = reinterpret_cast<non_polling_poller*>(pollset);
+  if (specific_worker == nullptr)
+    specific_worker = reinterpret_cast<grpc_pollset_worker*>(p->root);
+  if (specific_worker != nullptr) {
+    non_polling_worker* w =
+        reinterpret_cast<non_polling_worker*>(specific_worker);
+    if (!w->kicked) {
+      w->kicked = true;
+      gpr_cv_signal(&w->cv);
+    }
+  } else {
+    p->kicked_without_poller = true;
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static void non_polling_poller_shutdown(grpc_pollset* pollset,
+                                        grpc_closure* closure) {
+  non_polling_poller* p = reinterpret_cast<non_polling_poller*>(pollset);
+  GPR_ASSERT(closure != nullptr);
+  p->shutdown = closure;
+  if (p->root == nullptr) {
+    GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE);
+  } else {
+    non_polling_worker* w = p->root;
+    do {
+      gpr_cv_signal(&w->cv);
+      w = w->next;
+    } while (w != p->root);
+  }
+}
+
+static const cq_poller_vtable g_poller_vtable_by_poller_type[] = {
+    /* GRPC_CQ_DEFAULT_POLLING */
+    {true, true, grpc_pollset_size, grpc_pollset_init, grpc_pollset_kick,
+     grpc_pollset_work, grpc_pollset_shutdown, grpc_pollset_destroy},
+    /* GRPC_CQ_NON_LISTENING */
+    {true, false, grpc_pollset_size, grpc_pollset_init, grpc_pollset_kick,
+     grpc_pollset_work, grpc_pollset_shutdown, grpc_pollset_destroy},
+    /* GRPC_CQ_NON_POLLING */
+    {false, false, non_polling_poller_size, non_polling_poller_init,
+     non_polling_poller_kick, non_polling_poller_work,
+     non_polling_poller_shutdown, non_polling_poller_destroy},
+};
+
+typedef struct cq_vtable {
+  grpc_cq_completion_type cq_completion_type;
+  size_t data_size;
+  void (*init)(void* data,
+               grpc_experimental_completion_queue_functor* shutdown_callback);
+  void (*shutdown)(grpc_completion_queue* cq);
+  void (*destroy)(void* data);
+  bool (*begin_op)(grpc_completion_queue* cq, void* tag);
+  void (*end_op)(grpc_completion_queue* cq, void* tag, grpc_error* error,
+                 void (*done)(void* done_arg, grpc_cq_completion* storage),
+                 void* done_arg, grpc_cq_completion* storage);
+  grpc_event (*next)(grpc_completion_queue* cq, gpr_timespec deadline,
+                     void* reserved);
+  grpc_event (*pluck)(grpc_completion_queue* cq, void* tag,
+                      gpr_timespec deadline, void* reserved);
+} cq_vtable;
+
+/* Queue that holds the cq_completion_events. Internally uses gpr_mpscq queue
+ * (a lockfree multiproducer single consumer queue). It uses a queue_lock
+ * to support multiple consumers.
+ * Only used in completion queues whose completion_type is GRPC_CQ_NEXT */
+typedef struct grpc_cq_event_queue {
+  /* Spinlock to serialize consumers i.e pop() operations */
+  gpr_spinlock queue_lock;
+
+  gpr_mpscq queue;
+
+  /* A lazy counter of number of items in the queue. This is NOT atomically
+     incremented/decremented along with push/pop operations and hence is only
+     eventually consistent */
+  gpr_atm num_queue_items;
+} grpc_cq_event_queue;
+
+typedef struct cq_next_data {
+  /** Completed events for completion-queues of type GRPC_CQ_NEXT */
+  grpc_cq_event_queue queue;
+
+  /** Counter of how many things have ever been queued on this completion queue
+      useful for avoiding locks to check the queue */
+  gpr_atm things_queued_ever;
+
+  /* Number of outstanding events (+1 if not shut down) */
+  gpr_atm pending_events;
+
+  /** 0 initially. 1 once we initiated shutdown */
+  bool shutdown_called;
+} cq_next_data;
+
+typedef struct cq_pluck_data {
+  /** Completed events for completion-queues of type GRPC_CQ_PLUCK */
+  grpc_cq_completion completed_head;
+  grpc_cq_completion* completed_tail;
+
+  /** Number of pending events (+1 if we're not shutdown) */
+  gpr_atm pending_events;
+
+  /** Counter of how many things have ever been queued on this completion queue
+      useful for avoiding locks to check the queue */
+  gpr_atm things_queued_ever;
+
+  /** 0 initially. 1 once we completed shutting */
+  /* TODO: (sreek) This is not needed since (shutdown == 1) if and only if
+   * (pending_events == 0). So consider removing this in future and use
+   * pending_events */
+  gpr_atm shutdown;
+
+  /** 0 initially. 1 once we initiated shutdown */
+  bool shutdown_called;
+
+  int num_pluckers;
+  plucker pluckers[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS];
+} cq_pluck_data;
+
+typedef struct cq_callback_data {
+  /** No actual completed events queue, unlike other types */
+
+  /** Number of pending events (+1 if we're not shutdown) */
+  gpr_atm pending_events;
+
+  /** Counter of how many things have ever been queued on this completion queue
+      useful for avoiding locks to check the queue */
+  gpr_atm things_queued_ever;
+
+  /** 0 initially. 1 once we initiated shutdown */
+  bool shutdown_called;
+
+  /** A callback that gets invoked when the CQ completes shutdown */
+  grpc_experimental_completion_queue_functor* shutdown_callback;
+} cq_callback_data;
+
+/* Completion queue structure */
+struct grpc_completion_queue {
+  /** Once owning_refs drops to zero, we will destroy the cq */
+  gpr_refcount owning_refs;
+
+  gpr_mu* mu;
+
+  const cq_vtable* vtable;
+  const cq_poller_vtable* poller_vtable;
+
+#ifndef NDEBUG
+  void** outstanding_tags;
+  size_t outstanding_tag_count;
+  size_t outstanding_tag_capacity;
+#endif
+
+  grpc_closure pollset_shutdown_done;
+  int num_polls;
+};
+
+/* Forward declarations */
+static void cq_finish_shutdown_next(grpc_completion_queue* cq);
+static void cq_finish_shutdown_pluck(grpc_completion_queue* cq);
+static void cq_finish_shutdown_callback(grpc_completion_queue* cq);
+static void cq_shutdown_next(grpc_completion_queue* cq);
+static void cq_shutdown_pluck(grpc_completion_queue* cq);
+static void cq_shutdown_callback(grpc_completion_queue* cq);
+
+static bool cq_begin_op_for_next(grpc_completion_queue* cq, void* tag);
+static bool cq_begin_op_for_pluck(grpc_completion_queue* cq, void* tag);
+static bool cq_begin_op_for_callback(grpc_completion_queue* cq, void* tag);
+
+// A cq_end_op function is called when an operation on a given CQ with
+// a given tag has completed. The storage argument is a reference to the
+// space reserved for this completion as it is placed into the corresponding
+// queue. The done argument is a callback that will be invoked when it is
+// safe to free up that storage. The storage MUST NOT be freed until the
+// done callback is invoked.
+static void cq_end_op_for_next(grpc_completion_queue* cq, void* tag,
+                               grpc_error* error,
+                               void (*done)(void* done_arg,
+                                            grpc_cq_completion* storage),
+                               void* done_arg, grpc_cq_completion* storage);
+
+static void cq_end_op_for_pluck(grpc_completion_queue* cq, void* tag,
+                                grpc_error* error,
+                                void (*done)(void* done_arg,
+                                             grpc_cq_completion* storage),
+                                void* done_arg, grpc_cq_completion* storage);
+
+static void cq_end_op_for_callback(grpc_completion_queue* cq, void* tag,
+                                   grpc_error* error,
+                                   void (*done)(void* done_arg,
+                                                grpc_cq_completion* storage),
+                                   void* done_arg, grpc_cq_completion* storage);
+
+static grpc_event cq_next(grpc_completion_queue* cq, gpr_timespec deadline,
+                          void* reserved);
+
+static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag,
+                           gpr_timespec deadline, void* reserved);
+
+// Note that cq_init_next and cq_init_pluck do not use the shutdown_callback
+static void cq_init_next(
+    void* data, grpc_experimental_completion_queue_functor* shutdown_callback);
+static void cq_init_pluck(
+    void* data, grpc_experimental_completion_queue_functor* shutdown_callback);
+static void cq_init_callback(
+    void* data, grpc_experimental_completion_queue_functor* shutdown_callback);
+static void cq_destroy_next(void* data);
+static void cq_destroy_pluck(void* data);
+static void cq_destroy_callback(void* data);
+
+/* Completion queue vtables based on the completion-type */
+static const cq_vtable g_cq_vtable[] = {
+    /* GRPC_CQ_NEXT */
+    {GRPC_CQ_NEXT, sizeof(cq_next_data), cq_init_next, cq_shutdown_next,
+     cq_destroy_next, cq_begin_op_for_next, cq_end_op_for_next, cq_next,
+     nullptr},
+    /* GRPC_CQ_PLUCK */
+    {GRPC_CQ_PLUCK, sizeof(cq_pluck_data), cq_init_pluck, cq_shutdown_pluck,
+     cq_destroy_pluck, cq_begin_op_for_pluck, cq_end_op_for_pluck, nullptr,
+     cq_pluck},
+    /* GRPC_CQ_CALLBACK */
+    {GRPC_CQ_CALLBACK, sizeof(cq_callback_data), cq_init_callback,
+     cq_shutdown_callback, cq_destroy_callback, cq_begin_op_for_callback,
+     cq_end_op_for_callback, nullptr, nullptr},
+};
+
+#define DATA_FROM_CQ(cq) ((void*)(cq + 1))
+#define POLLSET_FROM_CQ(cq) \
+  ((grpc_pollset*)(cq->vtable->data_size + (char*)DATA_FROM_CQ(cq)))
+
+grpc_core::TraceFlag grpc_cq_pluck_trace(false, "queue_pluck");
+
+#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event)                       \
+  if (grpc_api_trace.enabled() && (grpc_cq_pluck_trace.enabled() ||        \
+                                   (event)->type != GRPC_QUEUE_TIMEOUT)) { \
+    char* _ev = grpc_event_string(event);                                  \
+    gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev);                    \
+    gpr_free(_ev);                                                         \
+  }
+
+static void on_pollset_shutdown_done(void* cq, grpc_error* error);
+
+void grpc_cq_global_init() {
+  gpr_tls_init(&g_cached_event);
+  gpr_tls_init(&g_cached_cq);
+}
+
+void grpc_completion_queue_thread_local_cache_init(grpc_completion_queue* cq) {
+  if ((grpc_completion_queue*)gpr_tls_get(&g_cached_cq) == nullptr) {
+    gpr_tls_set(&g_cached_event, (intptr_t)0);
+    gpr_tls_set(&g_cached_cq, (intptr_t)cq);
+  }
+}
+
+int grpc_completion_queue_thread_local_cache_flush(grpc_completion_queue* cq,
+                                                   void** tag, int* ok) {
+  grpc_cq_completion* storage =
+      (grpc_cq_completion*)gpr_tls_get(&g_cached_event);
+  int ret = 0;
+  if (storage != nullptr &&
+      (grpc_completion_queue*)gpr_tls_get(&g_cached_cq) == cq) {
+    *tag = storage->tag;
+    grpc_core::ExecCtx exec_ctx;
+    *ok = (storage->next & static_cast<uintptr_t>(1)) == 1;
+    storage->done(storage->done_arg, storage);
+    ret = 1;
+    cq_next_data* cqd = static_cast<cq_next_data*> DATA_FROM_CQ(cq);
+    if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+      GRPC_CQ_INTERNAL_REF(cq, "shutting_down");
+      gpr_mu_lock(cq->mu);
+      cq_finish_shutdown_next(cq);
+      gpr_mu_unlock(cq->mu);
+      GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down");
+    }
+  }
+  gpr_tls_set(&g_cached_event, (intptr_t)0);
+  gpr_tls_set(&g_cached_cq, (intptr_t)0);
+
+  return ret;
+}
+
+static void cq_event_queue_init(grpc_cq_event_queue* q) {
+  gpr_mpscq_init(&q->queue);
+  q->queue_lock = GPR_SPINLOCK_INITIALIZER;
+  gpr_atm_no_barrier_store(&q->num_queue_items, 0);
+}
+
+static void cq_event_queue_destroy(grpc_cq_event_queue* q) {
+  gpr_mpscq_destroy(&q->queue);
+}
+
+static bool cq_event_queue_push(grpc_cq_event_queue* q, grpc_cq_completion* c) {
+  gpr_mpscq_push(&q->queue, reinterpret_cast<gpr_mpscq_node*>(c));
+  return gpr_atm_no_barrier_fetch_add(&q->num_queue_items, 1) == 0;
+}
+
+static grpc_cq_completion* cq_event_queue_pop(grpc_cq_event_queue* q) {
+  grpc_cq_completion* c = nullptr;
+
+  if (gpr_spinlock_trylock(&q->queue_lock)) {
+    GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_SUCCESSES();
+
+    bool is_empty = false;
+    c = reinterpret_cast<grpc_cq_completion*>(
+        gpr_mpscq_pop_and_check_end(&q->queue, &is_empty));
+    gpr_spinlock_unlock(&q->queue_lock);
+
+    if (c == nullptr && !is_empty) {
+      GRPC_STATS_INC_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES();
+    }
+  } else {
+    GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_FAILURES();
+  }
+
+  if (c) {
+    gpr_atm_no_barrier_fetch_add(&q->num_queue_items, -1);
+  }
+
+  return c;
+}
+
+/* Note: The counter is not incremented/decremented atomically with push/pop.
+ * The count is only eventually consistent */
+static long cq_event_queue_num_items(grpc_cq_event_queue* q) {
+  return static_cast<long>(gpr_atm_no_barrier_load(&q->num_queue_items));
+}
+
+grpc_completion_queue* grpc_completion_queue_create_internal(
+    grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type,
+    grpc_experimental_completion_queue_functor* shutdown_callback) {
+  GPR_TIMER_SCOPE("grpc_completion_queue_create_internal", 0);
+
+  grpc_completion_queue* cq;
+
+  GRPC_API_TRACE(
+      "grpc_completion_queue_create_internal(completion_type=%d, "
+      "polling_type=%d)",
+      2, (completion_type, polling_type));
+
+  const cq_vtable* vtable = &g_cq_vtable[completion_type];
+  const cq_poller_vtable* poller_vtable =
+      &g_poller_vtable_by_poller_type[polling_type];
+
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_STATS_INC_CQS_CREATED();
+
+  cq = static_cast<grpc_completion_queue*>(
+      gpr_zalloc(sizeof(grpc_completion_queue) + vtable->data_size +
+                 poller_vtable->size()));
+
+  cq->vtable = vtable;
+  cq->poller_vtable = poller_vtable;
+
+  /* One for destroy(), one for pollset_shutdown */
+  gpr_ref_init(&cq->owning_refs, 2);
+
+  poller_vtable->init(POLLSET_FROM_CQ(cq), &cq->mu);
+  vtable->init(DATA_FROM_CQ(cq), shutdown_callback);
+
+  GRPC_CLOSURE_INIT(&cq->pollset_shutdown_done, on_pollset_shutdown_done, cq,
+                    grpc_schedule_on_exec_ctx);
+  return cq;
+}
+
+static void cq_init_next(
+    void* data, grpc_experimental_completion_queue_functor* shutdown_callback) {
+  cq_next_data* cqd = static_cast<cq_next_data*>(data);
+  /* Initial count is dropped by grpc_completion_queue_shutdown */
+  gpr_atm_no_barrier_store(&cqd->pending_events, 1);
+  cqd->shutdown_called = false;
+  gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0);
+  cq_event_queue_init(&cqd->queue);
+}
+
+static void cq_destroy_next(void* data) {
+  cq_next_data* cqd = static_cast<cq_next_data*>(data);
+  GPR_ASSERT(cq_event_queue_num_items(&cqd->queue) == 0);
+  cq_event_queue_destroy(&cqd->queue);
+}
+
+static void cq_init_pluck(
+    void* data, grpc_experimental_completion_queue_functor* shutdown_callback) {
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*>(data);
+  /* Initial count is dropped by grpc_completion_queue_shutdown */
+  gpr_atm_no_barrier_store(&cqd->pending_events, 1);
+  cqd->completed_tail = &cqd->completed_head;
+  cqd->completed_head.next = (uintptr_t)cqd->completed_tail;
+  gpr_atm_no_barrier_store(&cqd->shutdown, 0);
+  cqd->shutdown_called = false;
+  cqd->num_pluckers = 0;
+  gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0);
+}
+
+static void cq_destroy_pluck(void* data) {
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*>(data);
+  GPR_ASSERT(cqd->completed_head.next == (uintptr_t)&cqd->completed_head);
+}
+
+static void cq_init_callback(
+    void* data, grpc_experimental_completion_queue_functor* shutdown_callback) {
+  cq_callback_data* cqd = static_cast<cq_callback_data*>(data);
+  /* Initial count is dropped by grpc_completion_queue_shutdown */
+  gpr_atm_no_barrier_store(&cqd->pending_events, 1);
+  cqd->shutdown_called = false;
+  gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0);
+  cqd->shutdown_callback = shutdown_callback;
+}
+
+static void cq_destroy_callback(void* data) {}
+
+grpc_cq_completion_type grpc_get_cq_completion_type(grpc_completion_queue* cq) {
+  return cq->vtable->cq_completion_type;
+}
+
+int grpc_get_cq_poll_num(grpc_completion_queue* cq) {
+  int cur_num_polls;
+  gpr_mu_lock(cq->mu);
+  cur_num_polls = cq->num_polls;
+  gpr_mu_unlock(cq->mu);
+  return cur_num_polls;
+}
+
+#ifndef NDEBUG
+void grpc_cq_internal_ref(grpc_completion_queue* cq, const char* reason,
+                          const char* file, int line) {
+  if (grpc_trace_cq_refcount.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "CQ:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val + 1,
+            reason);
+  }
+#else
+void grpc_cq_internal_ref(grpc_completion_queue* cq) {
+#endif
+  gpr_ref(&cq->owning_refs);
+}
+
+static void on_pollset_shutdown_done(void* arg, grpc_error* error) {
+  grpc_completion_queue* cq = static_cast<grpc_completion_queue*>(arg);
+  GRPC_CQ_INTERNAL_UNREF(cq, "pollset_destroy");
+}
+
+#ifndef NDEBUG
+void grpc_cq_internal_unref(grpc_completion_queue* cq, const char* reason,
+                            const char* file, int line) {
+  if (grpc_trace_cq_refcount.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "CQ:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val - 1,
+            reason);
+  }
+#else
+void grpc_cq_internal_unref(grpc_completion_queue* cq) {
+#endif
+  if (gpr_unref(&cq->owning_refs)) {
+    cq->vtable->destroy(DATA_FROM_CQ(cq));
+    cq->poller_vtable->destroy(POLLSET_FROM_CQ(cq));
+#ifndef NDEBUG
+    gpr_free(cq->outstanding_tags);
+#endif
+    gpr_free(cq);
+  }
+}
+
+#ifndef NDEBUG
+static void cq_check_tag(grpc_completion_queue* cq, void* tag, bool lock_cq) {
+  int found = 0;
+  if (lock_cq) {
+    gpr_mu_lock(cq->mu);
+  }
+
+  for (int i = 0; i < static_cast<int>(cq->outstanding_tag_count); i++) {
+    if (cq->outstanding_tags[i] == tag) {
+      cq->outstanding_tag_count--;
+      GPR_SWAP(void*, cq->outstanding_tags[i],
+               cq->outstanding_tags[cq->outstanding_tag_count]);
+      found = 1;
+      break;
+    }
+  }
+
+  if (lock_cq) {
+    gpr_mu_unlock(cq->mu);
+  }
+
+  GPR_ASSERT(found);
+}
+#else
+static void cq_check_tag(grpc_completion_queue* cq, void* tag, bool lock_cq) {}
+#endif
+
+/* Atomically increments a counter only if the counter is not zero. Returns
+ * true if the increment was successful; false if the counter is zero */
+static bool atm_inc_if_nonzero(gpr_atm* counter) {
+  while (true) {
+    gpr_atm count = gpr_atm_acq_load(counter);
+    /* If zero, we are done. If not, we must to a CAS (instead of an atomic
+     * increment) to maintain the contract: do not increment the counter if it
+     * is zero. */
+    if (count == 0) {
+      return false;
+    } else if (gpr_atm_full_cas(counter, count, count + 1)) {
+      break;
+    }
+  }
+
+  return true;
+}
+
+static bool cq_begin_op_for_next(grpc_completion_queue* cq, void* tag) {
+  cq_next_data* cqd = static_cast<cq_next_data*> DATA_FROM_CQ(cq);
+  return atm_inc_if_nonzero(&cqd->pending_events);
+}
+
+static bool cq_begin_op_for_pluck(grpc_completion_queue* cq, void* tag) {
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
+  return atm_inc_if_nonzero(&cqd->pending_events);
+}
+
+static bool cq_begin_op_for_callback(grpc_completion_queue* cq, void* tag) {
+  cq_callback_data* cqd = static_cast<cq_callback_data*> DATA_FROM_CQ(cq);
+  return atm_inc_if_nonzero(&cqd->pending_events);
+}
+
+bool grpc_cq_begin_op(grpc_completion_queue* cq, void* tag) {
+#ifndef NDEBUG
+  gpr_mu_lock(cq->mu);
+  if (cq->outstanding_tag_count == cq->outstanding_tag_capacity) {
+    cq->outstanding_tag_capacity = GPR_MAX(4, 2 * cq->outstanding_tag_capacity);
+    cq->outstanding_tags = static_cast<void**>(gpr_realloc(
+        cq->outstanding_tags,
+        sizeof(*cq->outstanding_tags) * cq->outstanding_tag_capacity));
+  }
+  cq->outstanding_tags[cq->outstanding_tag_count++] = tag;
+  gpr_mu_unlock(cq->mu);
+#endif
+  return cq->vtable->begin_op(cq, tag);
+}
+
+/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a
+ * completion
+ * type of GRPC_CQ_NEXT) */
+static void cq_end_op_for_next(grpc_completion_queue* cq, void* tag,
+                               grpc_error* error,
+                               void (*done)(void* done_arg,
+                                            grpc_cq_completion* storage),
+                               void* done_arg, grpc_cq_completion* storage) {
+  GPR_TIMER_SCOPE("cq_end_op_for_next", 0);
+
+  if (grpc_api_trace.enabled() ||
+      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
+    const char* errmsg = grpc_error_string(error);
+    GRPC_API_TRACE(
+        "cq_end_op_for_next(cq=%p, tag=%p, error=%s, "
+        "done=%p, done_arg=%p, storage=%p)",
+        6, (cq, tag, errmsg, done, done_arg, storage));
+    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
+      gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
+    }
+  }
+  cq_next_data* cqd = static_cast<cq_next_data*> DATA_FROM_CQ(cq);
+  int is_success = (error == GRPC_ERROR_NONE);
+
+  storage->tag = tag;
+  storage->done = done;
+  storage->done_arg = done_arg;
+  storage->next = static_cast<uintptr_t>(is_success);
+
+  cq_check_tag(cq, tag, true); /* Used in debug builds only */
+
+  if ((grpc_completion_queue*)gpr_tls_get(&g_cached_cq) == cq &&
+      (grpc_cq_completion*)gpr_tls_get(&g_cached_event) == nullptr) {
+    gpr_tls_set(&g_cached_event, (intptr_t)storage);
+  } else {
+    /* Add the completion to the queue */
+    bool is_first = cq_event_queue_push(&cqd->queue, storage);
+    gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1);
+
+    /* Since we do not hold the cq lock here, it is important to do an 'acquire'
+       load here (instead of a 'no_barrier' load) to match with the release
+       store
+       (done via gpr_atm_full_fetch_add(pending_events, -1)) in cq_shutdown_next
+       */
+    bool will_definitely_shutdown = gpr_atm_acq_load(&cqd->pending_events) == 1;
+
+    if (!will_definitely_shutdown) {
+      /* Only kick if this is the first item queued */
+      if (is_first) {
+        gpr_mu_lock(cq->mu);
+        grpc_error* kick_error =
+            cq->poller_vtable->kick(POLLSET_FROM_CQ(cq), nullptr);
+        gpr_mu_unlock(cq->mu);
+
+        if (kick_error != GRPC_ERROR_NONE) {
+          const char* msg = grpc_error_string(kick_error);
+          gpr_log(GPR_ERROR, "Kick failed: %s", msg);
+          GRPC_ERROR_UNREF(kick_error);
+        }
+      }
+      if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+        GRPC_CQ_INTERNAL_REF(cq, "shutting_down");
+        gpr_mu_lock(cq->mu);
+        cq_finish_shutdown_next(cq);
+        gpr_mu_unlock(cq->mu);
+        GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down");
+      }
+    } else {
+      GRPC_CQ_INTERNAL_REF(cq, "shutting_down");
+      gpr_atm_rel_store(&cqd->pending_events, 0);
+      gpr_mu_lock(cq->mu);
+      cq_finish_shutdown_next(cq);
+      gpr_mu_unlock(cq->mu);
+      GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down");
+    }
+  }
+
+  GRPC_ERROR_UNREF(error);
+}
+
+/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a
+ * completion
+ * type of GRPC_CQ_PLUCK) */
+static void cq_end_op_for_pluck(grpc_completion_queue* cq, void* tag,
+                                grpc_error* error,
+                                void (*done)(void* done_arg,
+                                             grpc_cq_completion* storage),
+                                void* done_arg, grpc_cq_completion* storage) {
+  GPR_TIMER_SCOPE("cq_end_op_for_pluck", 0);
+
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
+  int is_success = (error == GRPC_ERROR_NONE);
+
+  if (grpc_api_trace.enabled() ||
+      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
+    const char* errmsg = grpc_error_string(error);
+    GRPC_API_TRACE(
+        "cq_end_op_for_pluck(cq=%p, tag=%p, error=%s, "
+        "done=%p, done_arg=%p, storage=%p)",
+        6, (cq, tag, errmsg, done, done_arg, storage));
+    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
+      gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
+    }
+  }
+
+  storage->tag = tag;
+  storage->done = done;
+  storage->done_arg = done_arg;
+  storage->next =
+      ((uintptr_t)&cqd->completed_head) | (static_cast<uintptr_t>(is_success));
+
+  gpr_mu_lock(cq->mu);
+  cq_check_tag(cq, tag, false); /* Used in debug builds only */
+
+  /* Add to the list of completions */
+  gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1);
+  cqd->completed_tail->next =
+      ((uintptr_t)storage) | (1u & cqd->completed_tail->next);
+  cqd->completed_tail = storage;
+
+  if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+    cq_finish_shutdown_pluck(cq);
+    gpr_mu_unlock(cq->mu);
+  } else {
+    grpc_pollset_worker* pluck_worker = nullptr;
+    for (int i = 0; i < cqd->num_pluckers; i++) {
+      if (cqd->pluckers[i].tag == tag) {
+        pluck_worker = *cqd->pluckers[i].worker;
+        break;
+      }
+    }
+
+    grpc_error* kick_error =
+        cq->poller_vtable->kick(POLLSET_FROM_CQ(cq), pluck_worker);
+
+    gpr_mu_unlock(cq->mu);
+
+    if (kick_error != GRPC_ERROR_NONE) {
+      const char* msg = grpc_error_string(kick_error);
+      gpr_log(GPR_ERROR, "Kick failed: %s", msg);
+
+      GRPC_ERROR_UNREF(kick_error);
+    }
+  }
+
+  GRPC_ERROR_UNREF(error);
+}
+
+/* Complete an event on a completion queue of type GRPC_CQ_CALLBACK */
+static void cq_end_op_for_callback(
+    grpc_completion_queue* cq, void* tag, grpc_error* error,
+    void (*done)(void* done_arg, grpc_cq_completion* storage), void* done_arg,
+    grpc_cq_completion* storage) {
+  GPR_TIMER_SCOPE("cq_end_op_for_callback", 0);
+
+  cq_callback_data* cqd = static_cast<cq_callback_data*> DATA_FROM_CQ(cq);
+  bool is_success = (error == GRPC_ERROR_NONE);
+
+  if (grpc_api_trace.enabled() ||
+      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
+    const char* errmsg = grpc_error_string(error);
+    GRPC_API_TRACE(
+        "cq_end_op_for_callback(cq=%p, tag=%p, error=%s, "
+        "done=%p, done_arg=%p, storage=%p)",
+        6, (cq, tag, errmsg, done, done_arg, storage));
+    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
+      gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
+    }
+  }
+
+  // The callback-based CQ isn't really a queue at all and thus has no need
+  // for reserved storage. Invoke the done callback right away to release it.
+  done(done_arg, storage);
+
+  gpr_mu_lock(cq->mu);
+  cq_check_tag(cq, tag, false); /* Used in debug builds only */
+
+  gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1);
+  if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+    gpr_mu_unlock(cq->mu);
+    cq_finish_shutdown_callback(cq);
+  } else {
+    gpr_mu_unlock(cq->mu);
+  }
+
+  GRPC_ERROR_UNREF(error);
+
+  auto* functor = static_cast<grpc_experimental_completion_queue_functor*>(tag);
+  (*functor->functor_run)(functor, is_success);
+}
+
+void grpc_cq_end_op(grpc_completion_queue* cq, void* tag, grpc_error* error,
+                    void (*done)(void* done_arg, grpc_cq_completion* storage),
+                    void* done_arg, grpc_cq_completion* storage) {
+  cq->vtable->end_op(cq, tag, error, done, done_arg, storage);
+}
+
+typedef struct {
+  gpr_atm last_seen_things_queued_ever;
+  grpc_completion_queue* cq;
+  grpc_millis deadline;
+  grpc_cq_completion* stolen_completion;
+  void* tag; /* for pluck */
+  bool first_loop;
+} cq_is_finished_arg;
+
+class ExecCtxNext : public grpc_core::ExecCtx {
+ public:
+  ExecCtxNext(void* arg) : ExecCtx(0), check_ready_to_finish_arg_(arg) {}
+
+  bool CheckReadyToFinish() override {
+    cq_is_finished_arg* a =
+        static_cast<cq_is_finished_arg*>(check_ready_to_finish_arg_);
+    grpc_completion_queue* cq = a->cq;
+    cq_next_data* cqd = static_cast<cq_next_data*> DATA_FROM_CQ(cq);
+    GPR_ASSERT(a->stolen_completion == nullptr);
+
+    gpr_atm current_last_seen_things_queued_ever =
+        gpr_atm_no_barrier_load(&cqd->things_queued_ever);
+
+    if (current_last_seen_things_queued_ever !=
+        a->last_seen_things_queued_ever) {
+      a->last_seen_things_queued_ever =
+          gpr_atm_no_barrier_load(&cqd->things_queued_ever);
+
+      /* Pop a cq_completion from the queue. Returns NULL if the queue is empty
+       * might return NULL in some cases even if the queue is not empty; but
+       * that
+       * is ok and doesn't affect correctness. Might effect the tail latencies a
+       * bit) */
+      a->stolen_completion = cq_event_queue_pop(&cqd->queue);
+      if (a->stolen_completion != nullptr) {
+        return true;
+      }
+    }
+    return !a->first_loop && a->deadline < grpc_core::ExecCtx::Get()->Now();
+  }
+
+ private:
+  void* check_ready_to_finish_arg_;
+};
+
+#ifndef NDEBUG
+static void dump_pending_tags(grpc_completion_queue* cq) {
+  if (!grpc_trace_pending_tags.enabled()) return;
+
+  gpr_strvec v;
+  gpr_strvec_init(&v);
+  gpr_strvec_add(&v, gpr_strdup("PENDING TAGS:"));
+  gpr_mu_lock(cq->mu);
+  for (size_t i = 0; i < cq->outstanding_tag_count; i++) {
+    char* s;
+    gpr_asprintf(&s, " %p", cq->outstanding_tags[i]);
+    gpr_strvec_add(&v, s);
+  }
+  gpr_mu_unlock(cq->mu);
+  char* out = gpr_strvec_flatten(&v, nullptr);
+  gpr_strvec_destroy(&v);
+  gpr_log(GPR_DEBUG, "%s", out);
+  gpr_free(out);
+}
+#else
+static void dump_pending_tags(grpc_completion_queue* cq) {}
+#endif
+
+static grpc_event cq_next(grpc_completion_queue* cq, gpr_timespec deadline,
+                          void* reserved) {
+  GPR_TIMER_SCOPE("grpc_completion_queue_next", 0);
+
+  grpc_event ret;
+  cq_next_data* cqd = static_cast<cq_next_data*> DATA_FROM_CQ(cq);
+
+  GRPC_API_TRACE(
+      "grpc_completion_queue_next("
+      "cq=%p, "
+      "deadline=gpr_timespec { tv_sec: %" PRId64
+      ", tv_nsec: %d, clock_type: %d }, "
+      "reserved=%p)",
+      5,
+      (cq, deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type,
+       reserved));
+  GPR_ASSERT(!reserved);
+
+  dump_pending_tags(cq);
+
+  GRPC_CQ_INTERNAL_REF(cq, "next");
+
+  grpc_millis deadline_millis = grpc_timespec_to_millis_round_up(deadline);
+  cq_is_finished_arg is_finished_arg = {
+      gpr_atm_no_barrier_load(&cqd->things_queued_ever),
+      cq,
+      deadline_millis,
+      nullptr,
+      nullptr,
+      true};
+  ExecCtxNext exec_ctx(&is_finished_arg);
+  for (;;) {
+    grpc_millis iteration_deadline = deadline_millis;
+
+    if (is_finished_arg.stolen_completion != nullptr) {
+      grpc_cq_completion* c = is_finished_arg.stolen_completion;
+      is_finished_arg.stolen_completion = nullptr;
+      ret.type = GRPC_OP_COMPLETE;
+      ret.success = c->next & 1u;
+      ret.tag = c->tag;
+      c->done(c->done_arg, c);
+      break;
+    }
+
+    grpc_cq_completion* c = cq_event_queue_pop(&cqd->queue);
+
+    if (c != nullptr) {
+      ret.type = GRPC_OP_COMPLETE;
+      ret.success = c->next & 1u;
+      ret.tag = c->tag;
+      c->done(c->done_arg, c);
+      break;
+    } else {
+      /* If c == NULL it means either the queue is empty OR in an transient
+         inconsistent state. If it is the latter, we shold do a 0-timeout poll
+         so that the thread comes back quickly from poll to make a second
+         attempt at popping. Not doing this can potentially deadlock this
+         thread forever (if the deadline is infinity) */
+      if (cq_event_queue_num_items(&cqd->queue) > 0) {
+        iteration_deadline = 0;
+      }
+    }
+
+    if (gpr_atm_acq_load(&cqd->pending_events) == 0) {
+      /* Before returning, check if the queue has any items left over (since
+         gpr_mpscq_pop() can sometimes return NULL even if the queue is not
+         empty. If so, keep retrying but do not return GRPC_QUEUE_SHUTDOWN */
+      if (cq_event_queue_num_items(&cqd->queue) > 0) {
+        /* Go to the beginning of the loop. No point doing a poll because
+           (cq->shutdown == true) is only possible when there is no pending
+           work (i.e cq->pending_events == 0) and any outstanding completion
+           events should have already been queued on this cq */
+        continue;
+      }
+
+      memset(&ret, 0, sizeof(ret));
+      ret.type = GRPC_QUEUE_SHUTDOWN;
+      break;
+    }
+
+    if (!is_finished_arg.first_loop &&
+        grpc_core::ExecCtx::Get()->Now() >= deadline_millis) {
+      memset(&ret, 0, sizeof(ret));
+      ret.type = GRPC_QUEUE_TIMEOUT;
+      dump_pending_tags(cq);
+      break;
+    }
+
+    /* The main polling work happens in grpc_pollset_work */
+    gpr_mu_lock(cq->mu);
+    cq->num_polls++;
+    grpc_error* err = cq->poller_vtable->work(POLLSET_FROM_CQ(cq), nullptr,
+                                              iteration_deadline);
+    gpr_mu_unlock(cq->mu);
+
+    if (err != GRPC_ERROR_NONE) {
+      const char* msg = grpc_error_string(err);
+      gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg);
+
+      GRPC_ERROR_UNREF(err);
+      memset(&ret, 0, sizeof(ret));
+      ret.type = GRPC_QUEUE_TIMEOUT;
+      dump_pending_tags(cq);
+      break;
+    }
+    is_finished_arg.first_loop = false;
+  }
+
+  if (cq_event_queue_num_items(&cqd->queue) > 0 &&
+      gpr_atm_acq_load(&cqd->pending_events) > 0) {
+    gpr_mu_lock(cq->mu);
+    cq->poller_vtable->kick(POLLSET_FROM_CQ(cq), nullptr);
+    gpr_mu_unlock(cq->mu);
+  }
+
+  GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, &ret);
+  GRPC_CQ_INTERNAL_UNREF(cq, "next");
+
+  GPR_ASSERT(is_finished_arg.stolen_completion == nullptr);
+
+  return ret;
+}
+
+/* Finishes the completion queue shutdown. This means that there are no more
+   completion events / tags expected from the completion queue
+   - Must be called under completion queue lock
+   - Must be called only once in completion queue's lifetime
+   - grpc_completion_queue_shutdown() MUST have been called before calling
+   this function */
+static void cq_finish_shutdown_next(grpc_completion_queue* cq) {
+  cq_next_data* cqd = static_cast<cq_next_data*> DATA_FROM_CQ(cq);
+
+  GPR_ASSERT(cqd->shutdown_called);
+  GPR_ASSERT(gpr_atm_no_barrier_load(&cqd->pending_events) == 0);
+
+  cq->poller_vtable->shutdown(POLLSET_FROM_CQ(cq), &cq->pollset_shutdown_done);
+}
+
+static void cq_shutdown_next(grpc_completion_queue* cq) {
+  cq_next_data* cqd = static_cast<cq_next_data*> DATA_FROM_CQ(cq);
+
+  /* Need an extra ref for cq here because:
+   * We call cq_finish_shutdown_next() below, that would call pollset shutdown.
+   * Pollset shutdown decrements the cq ref count which can potentially destroy
+   * the cq (if that happens to be the last ref).
+   * Creating an extra ref here prevents the cq from getting destroyed while
+   * this function is still active */
+  GRPC_CQ_INTERNAL_REF(cq, "shutting_down");
+  gpr_mu_lock(cq->mu);
+  if (cqd->shutdown_called) {
+    gpr_mu_unlock(cq->mu);
+    GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down");
+    return;
+  }
+  cqd->shutdown_called = true;
+  /* Doing a full_fetch_add (i.e acq/release) here to match with
+   * cq_begin_op_for_next and and cq_end_op_for_next functions which read/write
+   * on this counter without necessarily holding a lock on cq */
+  if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+    cq_finish_shutdown_next(cq);
+  }
+  gpr_mu_unlock(cq->mu);
+  GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down");
+}
+
+grpc_event grpc_completion_queue_next(grpc_completion_queue* cq,
+                                      gpr_timespec deadline, void* reserved) {
+  return cq->vtable->next(cq, deadline, reserved);
+}
+
+static int add_plucker(grpc_completion_queue* cq, void* tag,
+                       grpc_pollset_worker** worker) {
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
+  if (cqd->num_pluckers == GRPC_MAX_COMPLETION_QUEUE_PLUCKERS) {
+    return 0;
+  }
+  cqd->pluckers[cqd->num_pluckers].tag = tag;
+  cqd->pluckers[cqd->num_pluckers].worker = worker;
+  cqd->num_pluckers++;
+  return 1;
+}
+
+static void del_plucker(grpc_completion_queue* cq, void* tag,
+                        grpc_pollset_worker** worker) {
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
+  for (int i = 0; i < cqd->num_pluckers; i++) {
+    if (cqd->pluckers[i].tag == tag && cqd->pluckers[i].worker == worker) {
+      cqd->num_pluckers--;
+      GPR_SWAP(plucker, cqd->pluckers[i], cqd->pluckers[cqd->num_pluckers]);
+      return;
+    }
+  }
+  GPR_UNREACHABLE_CODE(return );
+}
+
+class ExecCtxPluck : public grpc_core::ExecCtx {
+ public:
+  ExecCtxPluck(void* arg) : ExecCtx(0), check_ready_to_finish_arg_(arg) {}
+
+  bool CheckReadyToFinish() override {
+    cq_is_finished_arg* a =
+        static_cast<cq_is_finished_arg*>(check_ready_to_finish_arg_);
+    grpc_completion_queue* cq = a->cq;
+    cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
+
+    GPR_ASSERT(a->stolen_completion == nullptr);
+    gpr_atm current_last_seen_things_queued_ever =
+        gpr_atm_no_barrier_load(&cqd->things_queued_ever);
+    if (current_last_seen_things_queued_ever !=
+        a->last_seen_things_queued_ever) {
+      gpr_mu_lock(cq->mu);
+      a->last_seen_things_queued_ever =
+          gpr_atm_no_barrier_load(&cqd->things_queued_ever);
+      grpc_cq_completion* c;
+      grpc_cq_completion* prev = &cqd->completed_head;
+      while ((c = (grpc_cq_completion*)(prev->next &
+                                        ~static_cast<uintptr_t>(1))) !=
+             &cqd->completed_head) {
+        if (c->tag == a->tag) {
+          prev->next = (prev->next & static_cast<uintptr_t>(1)) |
+                       (c->next & ~static_cast<uintptr_t>(1));
+          if (c == cqd->completed_tail) {
+            cqd->completed_tail = prev;
+          }
+          gpr_mu_unlock(cq->mu);
+          a->stolen_completion = c;
+          return true;
+        }
+        prev = c;
+      }
+      gpr_mu_unlock(cq->mu);
+    }
+    return !a->first_loop && a->deadline < grpc_core::ExecCtx::Get()->Now();
+  }
+
+ private:
+  void* check_ready_to_finish_arg_;
+};
+
+static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag,
+                           gpr_timespec deadline, void* reserved) {
+  GPR_TIMER_SCOPE("grpc_completion_queue_pluck", 0);
+
+  grpc_event ret;
+  grpc_cq_completion* c;
+  grpc_cq_completion* prev;
+  grpc_pollset_worker* worker = nullptr;
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
+
+  if (grpc_cq_pluck_trace.enabled()) {
+    GRPC_API_TRACE(
+        "grpc_completion_queue_pluck("
+        "cq=%p, tag=%p, "
+        "deadline=gpr_timespec { tv_sec: %" PRId64
+        ", tv_nsec: %d, clock_type: %d }, "
+        "reserved=%p)",
+        6,
+        (cq, tag, deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type,
+         reserved));
+  }
+  GPR_ASSERT(!reserved);
+
+  dump_pending_tags(cq);
+
+  GRPC_CQ_INTERNAL_REF(cq, "pluck");
+  gpr_mu_lock(cq->mu);
+  grpc_millis deadline_millis = grpc_timespec_to_millis_round_up(deadline);
+  cq_is_finished_arg is_finished_arg = {
+      gpr_atm_no_barrier_load(&cqd->things_queued_ever),
+      cq,
+      deadline_millis,
+      nullptr,
+      tag,
+      true};
+  ExecCtxPluck exec_ctx(&is_finished_arg);
+  for (;;) {
+    if (is_finished_arg.stolen_completion != nullptr) {
+      gpr_mu_unlock(cq->mu);
+      c = is_finished_arg.stolen_completion;
+      is_finished_arg.stolen_completion = nullptr;
+      ret.type = GRPC_OP_COMPLETE;
+      ret.success = c->next & 1u;
+      ret.tag = c->tag;
+      c->done(c->done_arg, c);
+      break;
+    }
+    prev = &cqd->completed_head;
+    while (
+        (c = (grpc_cq_completion*)(prev->next & ~static_cast<uintptr_t>(1))) !=
+        &cqd->completed_head) {
+      if (c->tag == tag) {
+        prev->next = (prev->next & static_cast<uintptr_t>(1)) |
+                     (c->next & ~static_cast<uintptr_t>(1));
+        if (c == cqd->completed_tail) {
+          cqd->completed_tail = prev;
+        }
+        gpr_mu_unlock(cq->mu);
+        ret.type = GRPC_OP_COMPLETE;
+        ret.success = c->next & 1u;
+        ret.tag = c->tag;
+        c->done(c->done_arg, c);
+        goto done;
+      }
+      prev = c;
+    }
+    if (gpr_atm_no_barrier_load(&cqd->shutdown)) {
+      gpr_mu_unlock(cq->mu);
+      memset(&ret, 0, sizeof(ret));
+      ret.type = GRPC_QUEUE_SHUTDOWN;
+      break;
+    }
+    if (!add_plucker(cq, tag, &worker)) {
+      gpr_log(GPR_DEBUG,
+              "Too many outstanding grpc_completion_queue_pluck calls: maximum "
+              "is %d",
+              GRPC_MAX_COMPLETION_QUEUE_PLUCKERS);
+      gpr_mu_unlock(cq->mu);
+      memset(&ret, 0, sizeof(ret));
+      /* TODO(ctiller): should we use a different result here */
+      ret.type = GRPC_QUEUE_TIMEOUT;
+      dump_pending_tags(cq);
+      break;
+    }
+    if (!is_finished_arg.first_loop &&
+        grpc_core::ExecCtx::Get()->Now() >= deadline_millis) {
+      del_plucker(cq, tag, &worker);
+      gpr_mu_unlock(cq->mu);
+      memset(&ret, 0, sizeof(ret));
+      ret.type = GRPC_QUEUE_TIMEOUT;
+      dump_pending_tags(cq);
+      break;
+    }
+    cq->num_polls++;
+    grpc_error* err =
+        cq->poller_vtable->work(POLLSET_FROM_CQ(cq), &worker, deadline_millis);
+    if (err != GRPC_ERROR_NONE) {
+      del_plucker(cq, tag, &worker);
+      gpr_mu_unlock(cq->mu);
+      const char* msg = grpc_error_string(err);
+      gpr_log(GPR_ERROR, "Completion queue pluck failed: %s", msg);
+
+      GRPC_ERROR_UNREF(err);
+      memset(&ret, 0, sizeof(ret));
+      ret.type = GRPC_QUEUE_TIMEOUT;
+      dump_pending_tags(cq);
+      break;
+    }
+    is_finished_arg.first_loop = false;
+    del_plucker(cq, tag, &worker);
+  }
+done:
+  GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, &ret);
+  GRPC_CQ_INTERNAL_UNREF(cq, "pluck");
+
+  GPR_ASSERT(is_finished_arg.stolen_completion == nullptr);
+
+  return ret;
+}
+
+grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq, void* tag,
+                                       gpr_timespec deadline, void* reserved) {
+  return cq->vtable->pluck(cq, tag, deadline, reserved);
+}
+
+static void cq_finish_shutdown_pluck(grpc_completion_queue* cq) {
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
+
+  GPR_ASSERT(cqd->shutdown_called);
+  GPR_ASSERT(!gpr_atm_no_barrier_load(&cqd->shutdown));
+  gpr_atm_no_barrier_store(&cqd->shutdown, 1);
+
+  cq->poller_vtable->shutdown(POLLSET_FROM_CQ(cq), &cq->pollset_shutdown_done);
+}
+
+/* NOTE: This function is almost exactly identical to cq_shutdown_next() but
+ * merging them is a bit tricky and probably not worth it */
+static void cq_shutdown_pluck(grpc_completion_queue* cq) {
+  cq_pluck_data* cqd = static_cast<cq_pluck_data*> DATA_FROM_CQ(cq);
+
+  /* Need an extra ref for cq here because:
+   * We call cq_finish_shutdown_pluck() below, that would call pollset shutdown.
+   * Pollset shutdown decrements the cq ref count which can potentially destroy
+   * the cq (if that happens to be the last ref).
+   * Creating an extra ref here prevents the cq from getting destroyed while
+   * this function is still active */
+  GRPC_CQ_INTERNAL_REF(cq, "shutting_down (pluck cq)");
+  gpr_mu_lock(cq->mu);
+  if (cqd->shutdown_called) {
+    gpr_mu_unlock(cq->mu);
+    GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (pluck cq)");
+    return;
+  }
+  cqd->shutdown_called = true;
+  if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+    cq_finish_shutdown_pluck(cq);
+  }
+  gpr_mu_unlock(cq->mu);
+  GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (pluck cq)");
+}
+
+static void cq_finish_shutdown_callback(grpc_completion_queue* cq) {
+  cq_callback_data* cqd = static_cast<cq_callback_data*> DATA_FROM_CQ(cq);
+  auto* callback = cqd->shutdown_callback;
+
+  GPR_ASSERT(cqd->shutdown_called);
+
+  cq->poller_vtable->shutdown(POLLSET_FROM_CQ(cq), &cq->pollset_shutdown_done);
+  (*callback->functor_run)(callback, true);
+}
+
+static void cq_shutdown_callback(grpc_completion_queue* cq) {
+  cq_callback_data* cqd = static_cast<cq_callback_data*> DATA_FROM_CQ(cq);
+
+  /* Need an extra ref for cq here because:
+   * We call cq_finish_shutdown_callback() below, which calls pollset shutdown.
+   * Pollset shutdown decrements the cq ref count which can potentially destroy
+   * the cq (if that happens to be the last ref).
+   * Creating an extra ref here prevents the cq from getting destroyed while
+   * this function is still active */
+  GRPC_CQ_INTERNAL_REF(cq, "shutting_down (callback cq)");
+  gpr_mu_lock(cq->mu);
+  if (cqd->shutdown_called) {
+    gpr_mu_unlock(cq->mu);
+    GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (callback cq)");
+    return;
+  }
+  cqd->shutdown_called = true;
+  if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) {
+    gpr_mu_unlock(cq->mu);
+    cq_finish_shutdown_callback(cq);
+  } else {
+    gpr_mu_unlock(cq->mu);
+  }
+  GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (callback cq)");
+}
+
+/* Shutdown simply drops a ref that we reserved at creation time; if we drop
+   to zero here, then enter shutdown mode and wake up any waiters */
+void grpc_completion_queue_shutdown(grpc_completion_queue* cq) {
+  GPR_TIMER_SCOPE("grpc_completion_queue_shutdown", 0);
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE("grpc_completion_queue_shutdown(cq=%p)", 1, (cq));
+  cq->vtable->shutdown(cq);
+}
+
+void grpc_completion_queue_destroy(grpc_completion_queue* cq) {
+  GPR_TIMER_SCOPE("grpc_completion_queue_destroy", 0);
+  GRPC_API_TRACE("grpc_completion_queue_destroy(cq=%p)", 1, (cq));
+  grpc_completion_queue_shutdown(cq);
+
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_CQ_INTERNAL_UNREF(cq, "destroy");
+}
+
+grpc_pollset* grpc_cq_pollset(grpc_completion_queue* cq) {
+  return cq->poller_vtable->can_get_pollset ? POLLSET_FROM_CQ(cq) : nullptr;
+}
+
+bool grpc_cq_can_listen(grpc_completion_queue* cq) {
+  return cq->poller_vtable->can_listen;
+}
diff --git a/third_party/grpc/src/core/lib/surface/completion_queue.h b/third_party/grpc/src/core/lib/surface/completion_queue.h
new file mode 100644
index 0000000..d60fe6d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/completion_queue.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H
+#define GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H
+
+/* Internal API for completion queues */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/iomgr/pollset.h"
+
+/* These trace flags default to 1. The corresponding lines are only traced
+   if grpc_api_trace is also truthy */
+extern grpc_core::TraceFlag grpc_cq_pluck_trace;
+extern grpc_core::TraceFlag grpc_trace_operation_failures;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_pending_tags;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_cq_refcount;
+
+typedef struct grpc_cq_completion {
+  gpr_mpscq_node node;
+
+  /** user supplied tag */
+  void* tag;
+  /** done callback - called when this queue element is no longer
+      needed by the completion queue */
+  void (*done)(void* done_arg, struct grpc_cq_completion* c);
+  void* done_arg;
+  /** next pointer; low bit is used to indicate success or not */
+  uintptr_t next;
+} grpc_cq_completion;
+
+#ifndef NDEBUG
+void grpc_cq_internal_ref(grpc_completion_queue* cc, const char* reason,
+                          const char* file, int line);
+void grpc_cq_internal_unref(grpc_completion_queue* cc, const char* reason,
+                            const char* file, int line);
+#define GRPC_CQ_INTERNAL_REF(cc, reason) \
+  grpc_cq_internal_ref(cc, reason, __FILE__, __LINE__)
+#define GRPC_CQ_INTERNAL_UNREF(cc, reason) \
+  grpc_cq_internal_unref(cc, reason, __FILE__, __LINE__)
+#else
+void grpc_cq_internal_ref(grpc_completion_queue* cc);
+void grpc_cq_internal_unref(grpc_completion_queue* cc);
+#define GRPC_CQ_INTERNAL_REF(cc, reason) grpc_cq_internal_ref(cc)
+#define GRPC_CQ_INTERNAL_UNREF(cc, reason) grpc_cq_internal_unref(cc)
+#endif
+
+/* Initializes global variables used by completion queues */
+void grpc_cq_global_init();
+
+/* Flag that an operation is beginning: the completion channel will not finish
+   shutdown until a corrensponding grpc_cq_end_* call is made.
+   \a tag is currently used only in debug builds. Return true on success, and
+   false if completion_queue has been shutdown. */
+bool grpc_cq_begin_op(grpc_completion_queue* cc, void* tag);
+
+/* Queue a GRPC_OP_COMPLETED operation; tag must correspond to the tag passed to
+   grpc_cq_begin_op */
+void grpc_cq_end_op(grpc_completion_queue* cc, void* tag, grpc_error* error,
+                    void (*done)(void* done_arg, grpc_cq_completion* storage),
+                    void* done_arg, grpc_cq_completion* storage);
+
+grpc_pollset* grpc_cq_pollset(grpc_completion_queue* cc);
+
+bool grpc_cq_can_listen(grpc_completion_queue* cc);
+
+grpc_cq_completion_type grpc_get_cq_completion_type(grpc_completion_queue* cc);
+
+int grpc_get_cq_poll_num(grpc_completion_queue* cc);
+
+grpc_completion_queue* grpc_completion_queue_create_internal(
+    grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type,
+    grpc_experimental_completion_queue_functor* shutdown_callback);
+
+#endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H */
diff --git a/third_party/grpc/src/core/lib/surface/completion_queue_factory.cc b/third_party/grpc/src/core/lib/surface/completion_queue_factory.cc
new file mode 100644
index 0000000..2616c15
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/completion_queue_factory.cc
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/surface/completion_queue_factory.h"
+
+#include <grpc/support/log.h>
+
+/*
+ * == Default completion queue factory implementation ==
+ */
+
+static grpc_completion_queue* default_create(
+    const grpc_completion_queue_factory* factory,
+    const grpc_completion_queue_attributes* attr) {
+  return grpc_completion_queue_create_internal(
+      attr->cq_completion_type, attr->cq_polling_type, attr->cq_shutdown_cb);
+}
+
+static grpc_completion_queue_factory_vtable default_vtable = {default_create};
+
+static const grpc_completion_queue_factory g_default_cq_factory = {
+    "Default Factory", nullptr, &default_vtable};
+
+/*
+ * == Completion queue factory APIs
+ */
+
+const grpc_completion_queue_factory* grpc_completion_queue_factory_lookup(
+    const grpc_completion_queue_attributes* attributes) {
+  GPR_ASSERT(attributes->version >= 1 &&
+             attributes->version <= GRPC_CQ_CURRENT_VERSION);
+
+  /* The default factory can handle version 1 of the attributes structure. We
+     may have to change this as more fields are added to the structure */
+  return &g_default_cq_factory;
+}
+
+/*
+ * == Completion queue creation APIs ==
+ */
+
+grpc_completion_queue* grpc_completion_queue_create_for_next(void* reserved) {
+  GPR_ASSERT(!reserved);
+  grpc_completion_queue_attributes attr = {1, GRPC_CQ_NEXT,
+                                           GRPC_CQ_DEFAULT_POLLING, nullptr};
+  return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr);
+}
+
+grpc_completion_queue* grpc_completion_queue_create_for_pluck(void* reserved) {
+  GPR_ASSERT(!reserved);
+  grpc_completion_queue_attributes attr = {1, GRPC_CQ_PLUCK,
+                                           GRPC_CQ_DEFAULT_POLLING, nullptr};
+  return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr);
+}
+
+grpc_completion_queue* grpc_completion_queue_create_for_callback(
+    grpc_experimental_completion_queue_functor* shutdown_callback,
+    void* reserved) {
+  GPR_ASSERT(!reserved);
+  grpc_completion_queue_attributes attr = {
+      2, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING, shutdown_callback};
+  return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr);
+}
+
+grpc_completion_queue* grpc_completion_queue_create(
+    const grpc_completion_queue_factory* factory,
+    const grpc_completion_queue_attributes* attr, void* reserved) {
+  GPR_ASSERT(!reserved);
+  return factory->vtable->create(factory, attr);
+}
diff --git a/third_party/grpc/src/core/lib/surface/completion_queue_factory.h b/third_party/grpc/src/core/lib/surface/completion_queue_factory.h
new file mode 100644
index 0000000..d2b30a9
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/completion_queue_factory.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H
+#define GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include "src/core/lib/surface/completion_queue.h"
+
+typedef struct grpc_completion_queue_factory_vtable {
+  grpc_completion_queue* (*create)(const grpc_completion_queue_factory*,
+                                   const grpc_completion_queue_attributes*);
+} grpc_completion_queue_factory_vtable;
+
+struct grpc_completion_queue_factory {
+  const char* name;
+  void* data; /* Factory specific data */
+  grpc_completion_queue_factory_vtable* vtable;
+};
+
+#endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H */
diff --git a/third_party/grpc/src/core/lib/surface/event_string.cc b/third_party/grpc/src/core/lib/surface/event_string.cc
new file mode 100644
index 0000000..d639bae
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/event_string.cc
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/event_string.h"
+
+#include <stdio.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/gpr/string.h"
+
+static void addhdr(gpr_strvec* buf, grpc_event* ev) {
+  char* tmp;
+  gpr_asprintf(&tmp, "tag:%p", ev->tag);
+  gpr_strvec_add(buf, tmp);
+}
+
+static const char* errstr(int success) { return success ? "OK" : "ERROR"; }
+
+static void adderr(gpr_strvec* buf, int success) {
+  char* tmp;
+  gpr_asprintf(&tmp, " %s", errstr(success));
+  gpr_strvec_add(buf, tmp);
+}
+
+char* grpc_event_string(grpc_event* ev) {
+  char* out;
+  gpr_strvec buf;
+
+  if (ev == nullptr) return gpr_strdup("null");
+
+  gpr_strvec_init(&buf);
+
+  switch (ev->type) {
+    case GRPC_QUEUE_TIMEOUT:
+      gpr_strvec_add(&buf, gpr_strdup("QUEUE_TIMEOUT"));
+      break;
+    case GRPC_QUEUE_SHUTDOWN:
+      gpr_strvec_add(&buf, gpr_strdup("QUEUE_SHUTDOWN"));
+      break;
+    case GRPC_OP_COMPLETE:
+      gpr_strvec_add(&buf, gpr_strdup("OP_COMPLETE: "));
+      addhdr(&buf, ev);
+      adderr(&buf, ev->success);
+      break;
+  }
+
+  out = gpr_strvec_flatten(&buf, nullptr);
+  gpr_strvec_destroy(&buf);
+  return out;
+}
diff --git a/third_party/grpc/src/core/lib/surface/event_string.h b/third_party/grpc/src/core/lib/surface/event_string.h
new file mode 100644
index 0000000..e609570
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/event_string.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_EVENT_STRING_H
+#define GRPC_CORE_LIB_SURFACE_EVENT_STRING_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+/* Returns a string describing an event. Must be later freed with gpr_free() */
+char* grpc_event_string(grpc_event* ev);
+
+#endif /* GRPC_CORE_LIB_SURFACE_EVENT_STRING_H */
diff --git a/third_party/grpc/src/core/lib/surface/init.cc b/third_party/grpc/src/core/lib/surface/init.cc
new file mode 100644
index 0000000..67cf5d8
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/init.cc
@@ -0,0 +1,197 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <limits.h>
+#include <memory.h>
+
+#include <grpc/fork.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/channelz_registry.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/handshaker_registry.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/fork.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/call_combiner.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/iomgr/resource_quota.h"
+#include "src/core/lib/iomgr/timer_manager.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/surface/init.h"
+#include "src/core/lib/surface/lame_client.h"
+#include "src/core/lib/surface/server.h"
+#include "src/core/lib/transport/bdp_estimator.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/transport_impl.h"
+
+/* (generated) built in registry of plugins */
+extern void grpc_register_built_in_plugins(void);
+
+#define MAX_PLUGINS 128
+
+static gpr_once g_basic_init = GPR_ONCE_INIT;
+static gpr_mu g_init_mu;
+static int g_initializations;
+
+static void do_basic_init(void) {
+  gpr_log_verbosity_init();
+  gpr_mu_init(&g_init_mu);
+  grpc_register_built_in_plugins();
+  grpc_cq_global_init();
+  g_initializations = 0;
+}
+
+static bool append_filter(grpc_channel_stack_builder* builder, void* arg) {
+  return grpc_channel_stack_builder_append_filter(
+      builder, static_cast<const grpc_channel_filter*>(arg), nullptr, nullptr);
+}
+
+static bool prepend_filter(grpc_channel_stack_builder* builder, void* arg) {
+  return grpc_channel_stack_builder_prepend_filter(
+      builder, static_cast<const grpc_channel_filter*>(arg), nullptr, nullptr);
+}
+
+static void register_builtin_channel_init() {
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   grpc_add_connected_filter, nullptr);
+  grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   grpc_add_connected_filter, nullptr);
+  grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   grpc_add_connected_filter, nullptr);
+  grpc_channel_init_register_stage(GRPC_CLIENT_LAME_CHANNEL,
+                                   GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
+                                   append_filter, (void*)&grpc_lame_filter);
+  grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, prepend_filter,
+                                   (void*)&grpc_server_top_filter);
+}
+
+typedef struct grpc_plugin {
+  void (*init)();
+  void (*destroy)();
+} grpc_plugin;
+
+static grpc_plugin g_all_of_the_plugins[MAX_PLUGINS];
+static int g_number_of_plugins = 0;
+
+void grpc_register_plugin(void (*init)(void), void (*destroy)(void)) {
+  GRPC_API_TRACE("grpc_register_plugin(init=%p, destroy=%p)", 2,
+                 ((void*)(intptr_t)init, (void*)(intptr_t)destroy));
+  GPR_ASSERT(g_number_of_plugins != MAX_PLUGINS);
+  g_all_of_the_plugins[g_number_of_plugins].init = init;
+  g_all_of_the_plugins[g_number_of_plugins].destroy = destroy;
+  g_number_of_plugins++;
+}
+
+void grpc_init(void) {
+  int i;
+  gpr_once_init(&g_basic_init, do_basic_init);
+
+  gpr_mu_lock(&g_init_mu);
+  if (++g_initializations == 1) {
+    grpc_core::Fork::GlobalInit();
+    grpc_fork_handlers_auto_register();
+    gpr_time_init();
+    gpr_arena_init();
+    grpc_stats_init();
+    grpc_slice_intern_init();
+    grpc_mdctx_global_init();
+    grpc_channel_init_init();
+    grpc_core::channelz::ChannelzRegistry::Init();
+    grpc_security_pre_init();
+    grpc_core::ExecCtx::GlobalInit();
+    grpc_iomgr_init();
+    gpr_timers_global_init();
+    grpc_handshaker_factory_registry_init();
+    grpc_security_init();
+    for (i = 0; i < g_number_of_plugins; i++) {
+      if (g_all_of_the_plugins[i].init != nullptr) {
+        g_all_of_the_plugins[i].init();
+      }
+    }
+    /* register channel finalization AFTER all plugins, to ensure that it's run
+     * at the appropriate time */
+    grpc_register_security_filters();
+    register_builtin_channel_init();
+    grpc_tracer_init("GRPC_TRACE");
+    /* no more changes to channel init pipelines */
+    grpc_channel_init_finalize();
+    grpc_iomgr_start();
+  }
+  gpr_mu_unlock(&g_init_mu);
+
+  GRPC_API_TRACE("grpc_init(void)", 0, ());
+}
+
+void grpc_shutdown(void) {
+  int i;
+  GRPC_API_TRACE("grpc_shutdown(void)", 0, ());
+  gpr_mu_lock(&g_init_mu);
+  if (--g_initializations == 0) {
+    {
+      grpc_core::ExecCtx exec_ctx(0);
+      grpc_iomgr_shutdown_background_closure();
+      {
+        grpc_timer_manager_set_threading(
+            false);  // shutdown timer_manager thread
+        grpc_executor_shutdown();
+        for (i = g_number_of_plugins; i >= 0; i--) {
+          if (g_all_of_the_plugins[i].destroy != nullptr) {
+            g_all_of_the_plugins[i].destroy();
+          }
+        }
+      }
+      grpc_iomgr_shutdown();
+      gpr_timers_global_destroy();
+      grpc_tracer_shutdown();
+      grpc_mdctx_global_shutdown();
+      grpc_handshaker_factory_registry_shutdown();
+      grpc_slice_intern_shutdown();
+      grpc_core::channelz::ChannelzRegistry::Shutdown();
+      grpc_stats_shutdown();
+      grpc_core::Fork::GlobalShutdown();
+    }
+    grpc_core::ExecCtx::GlobalShutdown();
+  }
+  gpr_mu_unlock(&g_init_mu);
+}
+
+int grpc_is_initialized(void) {
+  int r;
+  gpr_once_init(&g_basic_init, do_basic_init);
+  gpr_mu_lock(&g_init_mu);
+  r = g_initializations > 0;
+  gpr_mu_unlock(&g_init_mu);
+  return r;
+}
diff --git a/third_party/grpc/src/core/lib/surface/init.h b/third_party/grpc/src/core/lib/surface/init.h
new file mode 100644
index 0000000..193f514
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/init.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_INIT_H
+#define GRPC_CORE_LIB_SURFACE_INIT_H
+
+void grpc_register_security_filters(void);
+void grpc_security_pre_init(void);
+void grpc_security_init(void);
+
+#endif /* GRPC_CORE_LIB_SURFACE_INIT_H */
diff --git a/third_party/grpc/src/core/lib/surface/init_secure.cc b/third_party/grpc/src/core/lib/surface/init_secure.cc
new file mode 100644
index 0000000..765350c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/init_secure.cc
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/init.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/credentials/plugin/plugin_credentials.h"
+#include "src/core/lib/security/security_connector/security_connector.h"
+#include "src/core/lib/security/transport/auth_filters.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/security/transport/security_handshaker.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+void grpc_security_pre_init(void) {}
+
+static bool maybe_prepend_client_auth_filter(
+    grpc_channel_stack_builder* builder, void* arg) {
+  const grpc_channel_args* args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  if (args) {
+    for (size_t i = 0; i < args->num_args; i++) {
+      if (0 == strcmp(GRPC_ARG_SECURITY_CONNECTOR, args->args[i].key)) {
+        return grpc_channel_stack_builder_prepend_filter(
+            builder, &grpc_client_auth_filter, nullptr, nullptr);
+      }
+    }
+  }
+  return true;
+}
+
+static bool maybe_prepend_server_auth_filter(
+    grpc_channel_stack_builder* builder, void* arg) {
+  const grpc_channel_args* args =
+      grpc_channel_stack_builder_get_channel_arguments(builder);
+  if (args) {
+    for (size_t i = 0; i < args->num_args; i++) {
+      if (0 == strcmp(GRPC_SERVER_CREDENTIALS_ARG, args->args[i].key)) {
+        return grpc_channel_stack_builder_prepend_filter(
+            builder, &grpc_server_auth_filter, nullptr, nullptr);
+      }
+    }
+  }
+  return true;
+}
+
+void grpc_register_security_filters(void) {
+  // Register the auth client with a priority < INT_MAX to allow the authority
+  // filter -on which the auth filter depends- to be higher on the channel
+  // stack.
+  grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX - 1,
+                                   maybe_prepend_client_auth_filter, nullptr);
+  grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX - 1,
+                                   maybe_prepend_client_auth_filter, nullptr);
+  grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX - 1,
+                                   maybe_prepend_server_auth_filter, nullptr);
+}
+
+void grpc_security_init() { grpc_security_register_handshaker_factories(); }
diff --git a/third_party/grpc/src/core/lib/surface/init_unsecure.cc b/third_party/grpc/src/core/lib/surface/init_unsecure.cc
new file mode 100644
index 0000000..2b3bc64
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/init_unsecure.cc
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/init.h"
+
+void grpc_security_pre_init(void) {}
+
+void grpc_register_security_filters(void) {}
+
+void grpc_security_init(void) {}
diff --git a/third_party/grpc/src/core/lib/surface/lame_client.cc b/third_party/grpc/src/core/lib/surface/lame_client.cc
new file mode 100644
index 0000000..5a84428
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/lame_client.cc
@@ -0,0 +1,180 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gprpp/atomic.h"
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/lame_client.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+namespace grpc_core {
+
+namespace {
+
+struct CallData {
+  grpc_call_combiner* call_combiner;
+  grpc_linked_mdelem status;
+  grpc_linked_mdelem details;
+  grpc_core::atomic<bool> filled_metadata;
+};
+
+struct ChannelData {
+  grpc_status_code error_code;
+  const char* error_message;
+};
+
+static void fill_metadata(grpc_call_element* elem, grpc_metadata_batch* mdb) {
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  bool expected = false;
+  if (!calld->filled_metadata.compare_exchange_strong(
+          expected, true, grpc_core::memory_order_relaxed,
+          grpc_core::memory_order_relaxed)) {
+    return;
+  }
+  ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
+  char tmp[GPR_LTOA_MIN_BUFSIZE];
+  gpr_ltoa(chand->error_code, tmp);
+  calld->status.md = grpc_mdelem_from_slices(
+      GRPC_MDSTR_GRPC_STATUS, grpc_slice_from_copied_string(tmp));
+  calld->details.md = grpc_mdelem_from_slices(
+      GRPC_MDSTR_GRPC_MESSAGE,
+      grpc_slice_from_copied_string(chand->error_message));
+  calld->status.prev = calld->details.next = nullptr;
+  calld->status.next = &calld->details;
+  calld->details.prev = &calld->status;
+  mdb->list.head = &calld->status;
+  mdb->list.tail = &calld->details;
+  mdb->list.count = 2;
+  mdb->deadline = GRPC_MILLIS_INF_FUTURE;
+}
+
+static void lame_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  if (op->recv_initial_metadata) {
+    fill_metadata(elem,
+                  op->payload->recv_initial_metadata.recv_initial_metadata);
+  } else if (op->recv_trailing_metadata) {
+    fill_metadata(elem,
+                  op->payload->recv_trailing_metadata.recv_trailing_metadata);
+  }
+  grpc_transport_stream_op_batch_finish_with_failure(
+      op, GRPC_ERROR_CREATE_FROM_STATIC_STRING("lame client channel"),
+      calld->call_combiner);
+}
+
+static void lame_get_channel_info(grpc_channel_element* elem,
+                                  const grpc_channel_info* channel_info) {}
+
+static void lame_start_transport_op(grpc_channel_element* elem,
+                                    grpc_transport_op* op) {
+  if (op->on_connectivity_state_change) {
+    GPR_ASSERT(*op->connectivity_state != GRPC_CHANNEL_SHUTDOWN);
+    *op->connectivity_state = GRPC_CHANNEL_SHUTDOWN;
+    GRPC_CLOSURE_SCHED(op->on_connectivity_state_change, GRPC_ERROR_NONE);
+  }
+  if (op->send_ping.on_initiate != nullptr) {
+    GRPC_CLOSURE_SCHED(
+        op->send_ping.on_initiate,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("lame client channel"));
+  }
+  if (op->send_ping.on_ack != nullptr) {
+    GRPC_CLOSURE_SCHED(
+        op->send_ping.on_ack,
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("lame client channel"));
+  }
+  GRPC_ERROR_UNREF(op->disconnect_with_error);
+  if (op->on_consumed != nullptr) {
+    GRPC_CLOSURE_SCHED(op->on_consumed, GRPC_ERROR_NONE);
+  }
+}
+
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  CallData* calld = static_cast<CallData*>(elem->call_data);
+  calld->call_combiner = args->call_combiner;
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* then_schedule_closure) {
+  GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE);
+}
+
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  GPR_ASSERT(args->is_first);
+  GPR_ASSERT(args->is_last);
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_channel_elem(grpc_channel_element* elem) {}
+
+}  // namespace
+
+}  // namespace grpc_core
+
+const grpc_channel_filter grpc_lame_filter = {
+    grpc_core::lame_start_transport_stream_op_batch,
+    grpc_core::lame_start_transport_op,
+    sizeof(grpc_core::CallData),
+    grpc_core::init_call_elem,
+    grpc_call_stack_ignore_set_pollset_or_pollset_set,
+    grpc_core::destroy_call_elem,
+    sizeof(grpc_core::ChannelData),
+    grpc_core::init_channel_elem,
+    grpc_core::destroy_channel_elem,
+    grpc_core::lame_get_channel_info,
+    "lame-client",
+};
+
+#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack*)((c) + 1))
+
+grpc_channel* grpc_lame_client_channel_create(const char* target,
+                                              grpc_status_code error_code,
+                                              const char* error_message) {
+  grpc_core::ExecCtx exec_ctx;
+  grpc_channel_element* elem;
+  grpc_channel* channel =
+      grpc_channel_create(target, nullptr, GRPC_CLIENT_LAME_CHANNEL, nullptr);
+  elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
+  GRPC_API_TRACE(
+      "grpc_lame_client_channel_create(target=%s, error_code=%d, "
+      "error_message=%s)",
+      3, (target, (int)error_code, error_message));
+  GPR_ASSERT(elem->filter == &grpc_lame_filter);
+  auto chand = static_cast<grpc_core::ChannelData*>(elem->channel_data);
+  chand->error_code = error_code;
+  chand->error_message = error_message;
+
+  return channel;
+}
diff --git a/third_party/grpc/src/core/lib/surface/lame_client.h b/third_party/grpc/src/core/lib/surface/lame_client.h
new file mode 100644
index 0000000..aefa67c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/lame_client.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H
+#define GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+extern const grpc_channel_filter grpc_lame_filter;
+
+#endif /* GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H */
diff --git a/third_party/grpc/src/core/lib/surface/metadata_array.cc b/third_party/grpc/src/core/lib/surface/metadata_array.cc
new file mode 100644
index 0000000..f794a2b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/metadata_array.cc
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+
+#include <string.h>
+
+#include "src/core/lib/surface/api_trace.h"
+
+void grpc_metadata_array_init(grpc_metadata_array* array) {
+  GRPC_API_TRACE("grpc_metadata_array_init(array=%p)", 1, (array));
+  memset(array, 0, sizeof(*array));
+}
+
+void grpc_metadata_array_destroy(grpc_metadata_array* array) {
+  GRPC_API_TRACE("grpc_metadata_array_destroy(array=%p)", 1, (array));
+  gpr_free(array->metadata);
+}
diff --git a/third_party/grpc/src/core/lib/surface/server.cc b/third_party/grpc/src/core/lib/surface/server.cc
new file mode 100644
index 0000000..7ae6e51
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/server.cc
@@ -0,0 +1,1601 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/surface/server.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include <utility>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/debug/stats.h"
+#include "src/core/lib/gpr/mpscq.h"
+#include "src/core/lib/gpr/spinlock.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/surface/init.h"
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+grpc_core::TraceFlag grpc_server_channel_trace(false, "server_channel");
+
+static void server_on_recv_initial_metadata(void* ptr, grpc_error* error);
+static void server_recv_trailing_metadata_ready(void* user_data,
+                                                grpc_error* error);
+
+namespace {
+struct listener {
+  void* arg;
+  void (*start)(grpc_server* server, void* arg, grpc_pollset** pollsets,
+                size_t pollset_count);
+  void (*destroy)(grpc_server* server, void* arg, grpc_closure* closure);
+  struct listener* next;
+  intptr_t socket_uuid;
+  grpc_closure destroy_done;
+};
+
+enum requested_call_type { BATCH_CALL, REGISTERED_CALL };
+
+struct registered_method;
+
+struct requested_call {
+  gpr_mpscq_node request_link; /* must be first */
+  requested_call_type type;
+  size_t cq_idx;
+  void* tag;
+  grpc_server* server;
+  grpc_completion_queue* cq_bound_to_call;
+  grpc_call** call;
+  grpc_cq_completion completion;
+  grpc_metadata_array* initial_metadata;
+  union {
+    struct {
+      grpc_call_details* details;
+    } batch;
+    struct {
+      registered_method* method;
+      gpr_timespec* deadline;
+      grpc_byte_buffer** optional_payload;
+    } registered;
+  } data;
+};
+
+struct channel_registered_method {
+  registered_method* server_registered_method;
+  uint32_t flags;
+  bool has_host;
+  grpc_slice method;
+  grpc_slice host;
+};
+
+struct channel_data {
+  grpc_server* server;
+  grpc_connectivity_state connectivity_state;
+  grpc_channel* channel;
+  size_t cq_idx;
+  /* linked list of all channels on a server */
+  channel_data* next;
+  channel_data* prev;
+  channel_registered_method* registered_methods;
+  uint32_t registered_method_slots;
+  uint32_t registered_method_max_probes;
+  grpc_closure finish_destroy_channel_closure;
+  grpc_closure channel_connectivity_changed;
+  grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode> socket_node;
+};
+
+typedef struct shutdown_tag {
+  void* tag;
+  grpc_completion_queue* cq;
+  grpc_cq_completion completion;
+} shutdown_tag;
+
+typedef enum {
+  /* waiting for metadata */
+  NOT_STARTED,
+  /* inital metadata read, not flow controlled in yet */
+  PENDING,
+  /* flow controlled in, on completion queue */
+  ACTIVATED,
+  /* cancelled before being queued */
+  ZOMBIED
+} call_state;
+
+typedef struct request_matcher request_matcher;
+
+struct call_data {
+  call_data(grpc_call_element* elem, const grpc_call_element_args& args)
+      : call(grpc_call_from_top_element(elem)),
+        call_combiner(args.call_combiner) {
+    GRPC_CLOSURE_INIT(&server_on_recv_initial_metadata,
+                      ::server_on_recv_initial_metadata, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
+                      server_recv_trailing_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+  }
+  ~call_data() {
+    GPR_ASSERT(state != PENDING);
+    GRPC_ERROR_UNREF(recv_initial_metadata_error);
+    if (host_set) {
+      grpc_slice_unref_internal(host);
+    }
+    if (path_set) {
+      grpc_slice_unref_internal(path);
+    }
+    grpc_metadata_array_destroy(&initial_metadata);
+    grpc_byte_buffer_destroy(payload);
+  }
+
+  grpc_call* call;
+
+  gpr_atm state = NOT_STARTED;
+
+  bool path_set = false;
+  bool host_set = false;
+  grpc_slice path;
+  grpc_slice host;
+  grpc_millis deadline = GRPC_MILLIS_INF_FUTURE;
+
+  grpc_completion_queue* cq_new = nullptr;
+
+  grpc_metadata_batch* recv_initial_metadata = nullptr;
+  uint32_t recv_initial_metadata_flags = 0;
+  grpc_metadata_array initial_metadata =
+      grpc_metadata_array();  // Zero-initialize the C struct.
+
+  request_matcher* matcher = nullptr;
+  grpc_byte_buffer* payload = nullptr;
+
+  grpc_closure got_initial_metadata;
+  grpc_closure server_on_recv_initial_metadata;
+  grpc_closure kill_zombie_closure;
+  grpc_closure* on_done_recv_initial_metadata;
+  grpc_closure recv_trailing_metadata_ready;
+  grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
+  grpc_closure* original_recv_trailing_metadata_ready;
+  grpc_error* recv_trailing_metadata_error = GRPC_ERROR_NONE;
+  bool seen_recv_trailing_metadata_ready = false;
+
+  grpc_closure publish;
+
+  call_data* pending_next = nullptr;
+  grpc_call_combiner* call_combiner;
+};
+
+struct request_matcher {
+  grpc_server* server;
+  call_data* pending_head;
+  call_data* pending_tail;
+  gpr_locked_mpscq* requests_per_cq;
+};
+
+struct registered_method {
+  char* method;
+  char* host;
+  grpc_server_register_method_payload_handling payload_handling;
+  uint32_t flags;
+  /* one request matcher per method */
+  request_matcher matcher;
+  registered_method* next;
+};
+
+typedef struct {
+  grpc_channel** channels;
+  size_t num_channels;
+} channel_broadcaster;
+}  // namespace
+
+struct grpc_server {
+  grpc_channel_args* channel_args;
+
+  grpc_resource_user* default_resource_user;
+
+  grpc_completion_queue** cqs;
+  grpc_pollset** pollsets;
+  size_t cq_count;
+  size_t pollset_count;
+  bool started;
+
+  /* The two following mutexes control access to server-state
+     mu_global controls access to non-call-related state (e.g., channel state)
+     mu_call controls access to call-related state (e.g., the call lists)
+
+     If they are ever required to be nested, you must lock mu_global
+     before mu_call. This is currently used in shutdown processing
+     (grpc_server_shutdown_and_notify and maybe_finish_shutdown) */
+  gpr_mu mu_global; /* mutex for server and channel state */
+  gpr_mu mu_call;   /* mutex for call-specific state */
+
+  /* startup synchronization: flag is protected by mu_global, signals whether
+     we are doing the listener start routine or not */
+  bool starting;
+  gpr_cv starting_cv;
+
+  registered_method* registered_methods;
+  /** one request matcher for unregistered methods */
+  request_matcher unregistered_request_matcher;
+
+  gpr_atm shutdown_flag;
+  uint8_t shutdown_published;
+  size_t num_shutdown_tags;
+  shutdown_tag* shutdown_tags;
+
+  channel_data root_channel_data;
+
+  listener* listeners;
+  int listeners_destroyed;
+  gpr_refcount internal_refcount;
+
+  /** when did we print the last shutdown progress message */
+  gpr_timespec last_shutdown_message_time;
+
+  grpc_core::RefCountedPtr<grpc_core::channelz::ServerNode> channelz_server;
+};
+
+#define SERVER_FROM_CALL_ELEM(elem) \
+  (((channel_data*)(elem)->channel_data)->server)
+
+static void publish_new_rpc(void* calld, grpc_error* error);
+static void fail_call(grpc_server* server, size_t cq_idx, requested_call* rc,
+                      grpc_error* error);
+/* Before calling maybe_finish_shutdown, we must hold mu_global and not
+   hold mu_call */
+static void maybe_finish_shutdown(grpc_server* server);
+
+/*
+ * channel broadcaster
+ */
+
+/* assumes server locked */
+static void channel_broadcaster_init(grpc_server* s, channel_broadcaster* cb) {
+  channel_data* c;
+  size_t count = 0;
+  for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) {
+    count++;
+  }
+  cb->num_channels = count;
+  cb->channels = static_cast<grpc_channel**>(
+      gpr_malloc(sizeof(*cb->channels) * cb->num_channels));
+  count = 0;
+  for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) {
+    cb->channels[count++] = c->channel;
+    GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast");
+  }
+}
+
+struct shutdown_cleanup_args {
+  grpc_closure closure;
+  grpc_slice slice;
+};
+
+static void shutdown_cleanup(void* arg, grpc_error* error) {
+  struct shutdown_cleanup_args* a =
+      static_cast<struct shutdown_cleanup_args*>(arg);
+  grpc_slice_unref_internal(a->slice);
+  gpr_free(a);
+}
+
+static void send_shutdown(grpc_channel* channel, bool send_goaway,
+                          grpc_error* send_disconnect) {
+  struct shutdown_cleanup_args* sc =
+      static_cast<struct shutdown_cleanup_args*>(gpr_malloc(sizeof(*sc)));
+  GRPC_CLOSURE_INIT(&sc->closure, shutdown_cleanup, sc,
+                    grpc_schedule_on_exec_ctx);
+  grpc_transport_op* op = grpc_make_transport_op(&sc->closure);
+  grpc_channel_element* elem;
+
+  op->goaway_error =
+      send_goaway ? grpc_error_set_int(
+                        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"),
+                        GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK)
+                  : GRPC_ERROR_NONE;
+  op->set_accept_stream = true;
+  sc->slice = grpc_slice_from_copied_string("Server shutdown");
+  op->disconnect_with_error = send_disconnect;
+
+  elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
+  elem->filter->start_transport_op(elem, op);
+}
+
+static void channel_broadcaster_shutdown(channel_broadcaster* cb,
+                                         bool send_goaway,
+                                         grpc_error* force_disconnect) {
+  size_t i;
+
+  for (i = 0; i < cb->num_channels; i++) {
+    send_shutdown(cb->channels[i], send_goaway,
+                  GRPC_ERROR_REF(force_disconnect));
+    GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast");
+  }
+  gpr_free(cb->channels);
+  GRPC_ERROR_UNREF(force_disconnect);
+}
+
+/*
+ * request_matcher
+ */
+
+static void request_matcher_init(request_matcher* rm, grpc_server* server) {
+  memset(rm, 0, sizeof(*rm));
+  rm->server = server;
+  rm->requests_per_cq = static_cast<gpr_locked_mpscq*>(
+      gpr_malloc(sizeof(*rm->requests_per_cq) * server->cq_count));
+  for (size_t i = 0; i < server->cq_count; i++) {
+    gpr_locked_mpscq_init(&rm->requests_per_cq[i]);
+  }
+}
+
+static void request_matcher_destroy(request_matcher* rm) {
+  for (size_t i = 0; i < rm->server->cq_count; i++) {
+    GPR_ASSERT(gpr_locked_mpscq_pop(&rm->requests_per_cq[i]) == nullptr);
+    gpr_locked_mpscq_destroy(&rm->requests_per_cq[i]);
+  }
+  gpr_free(rm->requests_per_cq);
+}
+
+static void kill_zombie(void* elem, grpc_error* error) {
+  grpc_call_unref(
+      grpc_call_from_top_element(static_cast<grpc_call_element*>(elem)));
+}
+
+static void request_matcher_zombify_all_pending_calls(request_matcher* rm) {
+  while (rm->pending_head) {
+    call_data* calld = rm->pending_head;
+    rm->pending_head = calld->pending_next;
+    gpr_atm_no_barrier_store(&calld->state, ZOMBIED);
+    GRPC_CLOSURE_INIT(
+        &calld->kill_zombie_closure, kill_zombie,
+        grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0),
+        grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_NONE);
+  }
+}
+
+static void request_matcher_kill_requests(grpc_server* server,
+                                          request_matcher* rm,
+                                          grpc_error* error) {
+  requested_call* rc;
+  for (size_t i = 0; i < server->cq_count; i++) {
+    while ((rc = reinterpret_cast<requested_call*>(
+                gpr_locked_mpscq_pop(&rm->requests_per_cq[i]))) != nullptr) {
+      fail_call(server, i, rc, GRPC_ERROR_REF(error));
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+/*
+ * server proper
+ */
+
+static void server_ref(grpc_server* server) {
+  gpr_ref(&server->internal_refcount);
+}
+
+static void server_delete(grpc_server* server) {
+  registered_method* rm;
+  size_t i;
+  server->channelz_server.reset();
+  grpc_channel_args_destroy(server->channel_args);
+  gpr_mu_destroy(&server->mu_global);
+  gpr_mu_destroy(&server->mu_call);
+  gpr_cv_destroy(&server->starting_cv);
+  while ((rm = server->registered_methods) != nullptr) {
+    server->registered_methods = rm->next;
+    if (server->started) {
+      request_matcher_destroy(&rm->matcher);
+    }
+    gpr_free(rm->method);
+    gpr_free(rm->host);
+    gpr_free(rm);
+  }
+  if (server->started) {
+    request_matcher_destroy(&server->unregistered_request_matcher);
+  }
+  for (i = 0; i < server->cq_count; i++) {
+    GRPC_CQ_INTERNAL_UNREF(server->cqs[i], "server");
+  }
+  gpr_free(server->cqs);
+  gpr_free(server->pollsets);
+  gpr_free(server->shutdown_tags);
+  gpr_free(server);
+}
+
+static void server_unref(grpc_server* server) {
+  if (gpr_unref(&server->internal_refcount)) {
+    server_delete(server);
+  }
+}
+
+static int is_channel_orphaned(channel_data* chand) {
+  return chand->next == chand;
+}
+
+static void orphan_channel(channel_data* chand) {
+  chand->next->prev = chand->prev;
+  chand->prev->next = chand->next;
+  chand->next = chand->prev = chand;
+}
+
+static void finish_destroy_channel(void* cd, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(cd);
+  grpc_server* server = chand->server;
+  GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "server");
+  server_unref(server);
+}
+
+static void destroy_channel(channel_data* chand, grpc_error* error) {
+  if (is_channel_orphaned(chand)) return;
+  GPR_ASSERT(chand->server != nullptr);
+  orphan_channel(chand);
+  server_ref(chand->server);
+  maybe_finish_shutdown(chand->server);
+  GRPC_CLOSURE_INIT(&chand->finish_destroy_channel_closure,
+                    finish_destroy_channel, chand, grpc_schedule_on_exec_ctx);
+
+  if (grpc_server_channel_trace.enabled() && error != GRPC_ERROR_NONE) {
+    const char* msg = grpc_error_string(error);
+    gpr_log(GPR_INFO, "Disconnected client: %s", msg);
+  }
+  GRPC_ERROR_UNREF(error);
+
+  grpc_transport_op* op =
+      grpc_make_transport_op(&chand->finish_destroy_channel_closure);
+  op->set_accept_stream = true;
+  grpc_channel_next_op(grpc_channel_stack_element(
+                           grpc_channel_get_channel_stack(chand->channel), 0),
+                       op);
+}
+
+static void done_request_event(void* req, grpc_cq_completion* c) {
+  gpr_free(req);
+}
+
+static void publish_call(grpc_server* server, call_data* calld, size_t cq_idx,
+                         requested_call* rc) {
+  grpc_call_set_completion_queue(calld->call, rc->cq_bound_to_call);
+  grpc_call* call = calld->call;
+  *rc->call = call;
+  calld->cq_new = server->cqs[cq_idx];
+  GPR_SWAP(grpc_metadata_array, *rc->initial_metadata, calld->initial_metadata);
+  switch (rc->type) {
+    case BATCH_CALL:
+      GPR_ASSERT(calld->host_set);
+      GPR_ASSERT(calld->path_set);
+      rc->data.batch.details->host = grpc_slice_ref_internal(calld->host);
+      rc->data.batch.details->method = grpc_slice_ref_internal(calld->path);
+      rc->data.batch.details->deadline =
+          grpc_millis_to_timespec(calld->deadline, GPR_CLOCK_MONOTONIC);
+      rc->data.batch.details->flags = calld->recv_initial_metadata_flags;
+      break;
+    case REGISTERED_CALL:
+      *rc->data.registered.deadline =
+          grpc_millis_to_timespec(calld->deadline, GPR_CLOCK_MONOTONIC);
+      if (rc->data.registered.optional_payload) {
+        *rc->data.registered.optional_payload = calld->payload;
+        calld->payload = nullptr;
+      }
+      break;
+    default:
+      GPR_UNREACHABLE_CODE(return );
+  }
+
+  grpc_cq_end_op(calld->cq_new, rc->tag, GRPC_ERROR_NONE, done_request_event,
+                 rc, &rc->completion);
+}
+
+static void publish_new_rpc(void* arg, grpc_error* error) {
+  grpc_call_element* call_elem = static_cast<grpc_call_element*>(arg);
+  call_data* calld = static_cast<call_data*>(call_elem->call_data);
+  channel_data* chand = static_cast<channel_data*>(call_elem->channel_data);
+  request_matcher* rm = calld->matcher;
+  grpc_server* server = rm->server;
+
+  if (error != GRPC_ERROR_NONE || gpr_atm_acq_load(&server->shutdown_flag)) {
+    gpr_atm_no_barrier_store(&calld->state, ZOMBIED);
+    GRPC_CLOSURE_INIT(
+        &calld->kill_zombie_closure, kill_zombie,
+        grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0),
+        grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_REF(error));
+    return;
+  }
+
+  for (size_t i = 0; i < server->cq_count; i++) {
+    size_t cq_idx = (chand->cq_idx + i) % server->cq_count;
+    requested_call* rc = reinterpret_cast<requested_call*>(
+        gpr_locked_mpscq_try_pop(&rm->requests_per_cq[cq_idx]));
+    if (rc == nullptr) {
+      continue;
+    } else {
+      GRPC_STATS_INC_SERVER_CQS_CHECKED(i);
+      gpr_atm_no_barrier_store(&calld->state, ACTIVATED);
+      publish_call(server, calld, cq_idx, rc);
+      return; /* early out */
+    }
+  }
+
+  /* no cq to take the request found: queue it on the slow list */
+  GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED();
+  gpr_mu_lock(&server->mu_call);
+
+  // We need to ensure that all the queues are empty.  We do this under
+  // the server mu_call lock to ensure that if something is added to
+  // an empty request queue, it will block until the call is actually
+  // added to the pending list.
+  for (size_t i = 0; i < server->cq_count; i++) {
+    size_t cq_idx = (chand->cq_idx + i) % server->cq_count;
+    requested_call* rc = reinterpret_cast<requested_call*>(
+        gpr_locked_mpscq_pop(&rm->requests_per_cq[cq_idx]));
+    if (rc == nullptr) {
+      continue;
+    } else {
+      gpr_mu_unlock(&server->mu_call);
+      GRPC_STATS_INC_SERVER_CQS_CHECKED(i + server->cq_count);
+      gpr_atm_no_barrier_store(&calld->state, ACTIVATED);
+      publish_call(server, calld, cq_idx, rc);
+      return; /* early out */
+    }
+  }
+
+  gpr_atm_no_barrier_store(&calld->state, PENDING);
+  if (rm->pending_head == nullptr) {
+    rm->pending_tail = rm->pending_head = calld;
+  } else {
+    rm->pending_tail->pending_next = calld;
+    rm->pending_tail = calld;
+  }
+  calld->pending_next = nullptr;
+  gpr_mu_unlock(&server->mu_call);
+}
+
+static void finish_start_new_rpc(
+    grpc_server* server, grpc_call_element* elem, request_matcher* rm,
+    grpc_server_register_method_payload_handling payload_handling) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+
+  if (gpr_atm_acq_load(&server->shutdown_flag)) {
+    gpr_atm_no_barrier_store(&calld->state, ZOMBIED);
+    GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_NONE);
+    return;
+  }
+
+  calld->matcher = rm;
+
+  switch (payload_handling) {
+    case GRPC_SRM_PAYLOAD_NONE:
+      publish_new_rpc(elem, GRPC_ERROR_NONE);
+      break;
+    case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: {
+      grpc_op op;
+      memset(&op, 0, sizeof(op));
+      op.op = GRPC_OP_RECV_MESSAGE;
+      op.data.recv_message.recv_message = &calld->payload;
+      GRPC_CLOSURE_INIT(&calld->publish, publish_new_rpc, elem,
+                        grpc_schedule_on_exec_ctx);
+      grpc_call_start_batch_and_execute(calld->call, &op, 1, &calld->publish);
+      break;
+    }
+  }
+}
+
+static void start_new_rpc(grpc_call_element* elem) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_server* server = chand->server;
+  uint32_t i;
+  uint32_t hash;
+  channel_registered_method* rm;
+
+  if (chand->registered_methods && calld->path_set && calld->host_set) {
+    /* TODO(ctiller): unify these two searches */
+    /* check for an exact match with host */
+    hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(calld->host),
+                              grpc_slice_hash(calld->path));
+    for (i = 0; i <= chand->registered_method_max_probes; i++) {
+      rm = &chand->registered_methods[(hash + i) %
+                                      chand->registered_method_slots];
+      if (!rm) break;
+      if (!rm->has_host) continue;
+      if (!grpc_slice_eq(rm->host, calld->host)) continue;
+      if (!grpc_slice_eq(rm->method, calld->path)) continue;
+      if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
+          0 == (calld->recv_initial_metadata_flags &
+                GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) {
+        continue;
+      }
+      finish_start_new_rpc(server, elem, &rm->server_registered_method->matcher,
+                           rm->server_registered_method->payload_handling);
+      return;
+    }
+    /* check for a wildcard method definition (no host set) */
+    hash = GRPC_MDSTR_KV_HASH(0, grpc_slice_hash(calld->path));
+    for (i = 0; i <= chand->registered_method_max_probes; i++) {
+      rm = &chand->registered_methods[(hash + i) %
+                                      chand->registered_method_slots];
+      if (!rm) break;
+      if (rm->has_host) continue;
+      if (!grpc_slice_eq(rm->method, calld->path)) continue;
+      if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
+          0 == (calld->recv_initial_metadata_flags &
+                GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) {
+        continue;
+      }
+      finish_start_new_rpc(server, elem, &rm->server_registered_method->matcher,
+                           rm->server_registered_method->payload_handling);
+      return;
+    }
+  }
+  finish_start_new_rpc(server, elem, &server->unregistered_request_matcher,
+                       GRPC_SRM_PAYLOAD_NONE);
+}
+
+static int num_listeners(grpc_server* server) {
+  listener* l;
+  int n = 0;
+  for (l = server->listeners; l; l = l->next) {
+    n++;
+  }
+  return n;
+}
+
+static void done_shutdown_event(void* server, grpc_cq_completion* completion) {
+  server_unref(static_cast<grpc_server*>(server));
+}
+
+static int num_channels(grpc_server* server) {
+  channel_data* chand;
+  int n = 0;
+  for (chand = server->root_channel_data.next;
+       chand != &server->root_channel_data; chand = chand->next) {
+    n++;
+  }
+  return n;
+}
+
+static void kill_pending_work_locked(grpc_server* server, grpc_error* error) {
+  if (server->started) {
+    request_matcher_kill_requests(server, &server->unregistered_request_matcher,
+                                  GRPC_ERROR_REF(error));
+    request_matcher_zombify_all_pending_calls(
+        &server->unregistered_request_matcher);
+    for (registered_method* rm = server->registered_methods; rm;
+         rm = rm->next) {
+      request_matcher_kill_requests(server, &rm->matcher,
+                                    GRPC_ERROR_REF(error));
+      request_matcher_zombify_all_pending_calls(&rm->matcher);
+    }
+  }
+  GRPC_ERROR_UNREF(error);
+}
+
+static void maybe_finish_shutdown(grpc_server* server) {
+  size_t i;
+  if (!gpr_atm_acq_load(&server->shutdown_flag) || server->shutdown_published) {
+    return;
+  }
+
+  kill_pending_work_locked(
+      server, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
+
+  if (server->root_channel_data.next != &server->root_channel_data ||
+      server->listeners_destroyed < num_listeners(server)) {
+    if (gpr_time_cmp(gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME),
+                                  server->last_shutdown_message_time),
+                     gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) {
+      server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME);
+      gpr_log(GPR_DEBUG,
+              "Waiting for %d channels and %d/%d listeners to be destroyed"
+              " before shutting down server",
+              num_channels(server),
+              num_listeners(server) - server->listeners_destroyed,
+              num_listeners(server));
+    }
+    return;
+  }
+  server->shutdown_published = 1;
+  for (i = 0; i < server->num_shutdown_tags; i++) {
+    server_ref(server);
+    grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag,
+                   GRPC_ERROR_NONE, done_shutdown_event, server,
+                   &server->shutdown_tags[i].completion);
+  }
+}
+
+static void server_on_recv_initial_metadata(void* ptr, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(ptr);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_millis op_deadline;
+
+  if (error == GRPC_ERROR_NONE) {
+    GPR_ASSERT(calld->recv_initial_metadata->idx.named.path != nullptr);
+    GPR_ASSERT(calld->recv_initial_metadata->idx.named.authority != nullptr);
+    calld->path = grpc_slice_ref_internal(
+        GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.path->md));
+    calld->host = grpc_slice_ref_internal(
+        GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.authority->md));
+    calld->path_set = true;
+    calld->host_set = true;
+    grpc_metadata_batch_remove(calld->recv_initial_metadata,
+                               calld->recv_initial_metadata->idx.named.path);
+    grpc_metadata_batch_remove(
+        calld->recv_initial_metadata,
+        calld->recv_initial_metadata->idx.named.authority);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  op_deadline = calld->recv_initial_metadata->deadline;
+  if (op_deadline != GRPC_MILLIS_INF_FUTURE) {
+    calld->deadline = op_deadline;
+  }
+  if (calld->host_set && calld->path_set) {
+    /* do nothing */
+  } else {
+    /* Pass the error reference to calld->recv_initial_metadata_error */
+    grpc_error* src_error = error;
+    error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
+        "Missing :authority or :path", &src_error, 1);
+    GRPC_ERROR_UNREF(src_error);
+    calld->recv_initial_metadata_error = GRPC_ERROR_REF(error);
+  }
+  grpc_closure* closure = calld->on_done_recv_initial_metadata;
+  calld->on_done_recv_initial_metadata = nullptr;
+  if (calld->seen_recv_trailing_metadata_ready) {
+    GRPC_CALL_COMBINER_START(calld->call_combiner,
+                             &calld->recv_trailing_metadata_ready,
+                             calld->recv_trailing_metadata_error,
+                             "continue server_recv_trailing_metadata_ready");
+  }
+  GRPC_CLOSURE_RUN(closure, error);
+}
+
+static void server_recv_trailing_metadata_ready(void* user_data,
+                                                grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (calld->on_done_recv_initial_metadata != nullptr) {
+    calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error);
+    calld->seen_recv_trailing_metadata_ready = true;
+    GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready,
+                      server_recv_trailing_metadata_ready, elem,
+                      grpc_schedule_on_exec_ctx);
+    GRPC_CALL_COMBINER_STOP(calld->call_combiner,
+                            "deferring server_recv_trailing_metadata_ready "
+                            "until after server_on_recv_initial_metadata");
+    return;
+  }
+  error =
+      grpc_error_add_child(GRPC_ERROR_REF(error),
+                           GRPC_ERROR_REF(calld->recv_initial_metadata_error));
+  GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error);
+}
+
+static void server_mutate_op(grpc_call_element* elem,
+                             grpc_transport_stream_op_batch* op) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+
+  if (op->recv_initial_metadata) {
+    GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags == nullptr);
+    calld->recv_initial_metadata =
+        op->payload->recv_initial_metadata.recv_initial_metadata;
+    calld->on_done_recv_initial_metadata =
+        op->payload->recv_initial_metadata.recv_initial_metadata_ready;
+    op->payload->recv_initial_metadata.recv_initial_metadata_ready =
+        &calld->server_on_recv_initial_metadata;
+    op->payload->recv_initial_metadata.recv_flags =
+        &calld->recv_initial_metadata_flags;
+  }
+  if (op->recv_trailing_metadata) {
+    calld->original_recv_trailing_metadata_ready =
+        op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &calld->recv_trailing_metadata_ready;
+  }
+}
+
+static void server_start_transport_stream_op_batch(
+    grpc_call_element* elem, grpc_transport_stream_op_batch* op) {
+  server_mutate_op(elem, op);
+  grpc_call_next_op(elem, op);
+}
+
+static void got_initial_metadata(void* ptr, grpc_error* error) {
+  grpc_call_element* elem = static_cast<grpc_call_element*>(ptr);
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  if (error == GRPC_ERROR_NONE) {
+    start_new_rpc(elem);
+  } else {
+    if (gpr_atm_full_cas(&calld->state, NOT_STARTED, ZOMBIED)) {
+      GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem,
+                        grpc_schedule_on_exec_ctx);
+      GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_NONE);
+    } else if (gpr_atm_full_cas(&calld->state, PENDING, ZOMBIED)) {
+      /* zombied call will be destroyed when it's removed from the pending
+         queue... later */
+    }
+  }
+}
+
+static void accept_stream(void* cd, grpc_transport* transport,
+                          const void* transport_server_data) {
+  channel_data* chand = static_cast<channel_data*>(cd);
+  /* create a call */
+  grpc_call_create_args args;
+  args.channel = chand->channel;
+  args.server = chand->server;
+  args.parent = nullptr;
+  args.propagation_mask = 0;
+  args.cq = nullptr;
+  args.pollset_set_alternative = nullptr;
+  args.server_transport_data = transport_server_data;
+  args.add_initial_metadata = nullptr;
+  args.add_initial_metadata_count = 0;
+  args.send_deadline = GRPC_MILLIS_INF_FUTURE;
+  grpc_call* call;
+  grpc_error* error = grpc_call_create(&args, &call);
+  grpc_call_element* elem =
+      grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
+  if (error != GRPC_ERROR_NONE) {
+    got_initial_metadata(elem, error);
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  grpc_op op;
+  op.op = GRPC_OP_RECV_INITIAL_METADATA;
+  op.flags = 0;
+  op.reserved = nullptr;
+  op.data.recv_initial_metadata.recv_initial_metadata =
+      &calld->initial_metadata;
+  GRPC_CLOSURE_INIT(&calld->got_initial_metadata, got_initial_metadata, elem,
+                    grpc_schedule_on_exec_ctx);
+  grpc_call_start_batch_and_execute(call, &op, 1, &calld->got_initial_metadata);
+}
+
+static void channel_connectivity_changed(void* cd, grpc_error* error) {
+  channel_data* chand = static_cast<channel_data*>(cd);
+  grpc_server* server = chand->server;
+  if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) {
+    grpc_transport_op* op = grpc_make_transport_op(nullptr);
+    op->on_connectivity_state_change = &chand->channel_connectivity_changed;
+    op->connectivity_state = &chand->connectivity_state;
+    grpc_channel_next_op(grpc_channel_stack_element(
+                             grpc_channel_get_channel_stack(chand->channel), 0),
+                         op);
+  } else {
+    gpr_mu_lock(&server->mu_global);
+    destroy_channel(chand, GRPC_ERROR_REF(error));
+    gpr_mu_unlock(&server->mu_global);
+    GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "connectivity");
+  }
+}
+
+static grpc_error* init_call_elem(grpc_call_element* elem,
+                                  const grpc_call_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  server_ref(chand->server);
+  new (elem->call_data) call_data(elem, *args);
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_call_elem(grpc_call_element* elem,
+                              const grpc_call_final_info* final_info,
+                              grpc_closure* ignored) {
+  call_data* calld = static_cast<call_data*>(elem->call_data);
+  calld->~call_data();
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  server_unref(chand->server);
+}
+
+static grpc_error* init_channel_elem(grpc_channel_element* elem,
+                                     grpc_channel_element_args* args) {
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  GPR_ASSERT(args->is_first);
+  GPR_ASSERT(!args->is_last);
+  chand->server = nullptr;
+  chand->channel = nullptr;
+  chand->next = chand->prev = chand;
+  chand->registered_methods = nullptr;
+  chand->connectivity_state = GRPC_CHANNEL_IDLE;
+  GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed,
+                    channel_connectivity_changed, chand,
+                    grpc_schedule_on_exec_ctx);
+  return GRPC_ERROR_NONE;
+}
+
+static void destroy_channel_elem(grpc_channel_element* elem) {
+  size_t i;
+  channel_data* chand = static_cast<channel_data*>(elem->channel_data);
+  chand->socket_node.reset();
+  if (chand->registered_methods) {
+    for (i = 0; i < chand->registered_method_slots; i++) {
+      grpc_slice_unref_internal(chand->registered_methods[i].method);
+      if (chand->registered_methods[i].has_host) {
+        grpc_slice_unref_internal(chand->registered_methods[i].host);
+      }
+    }
+    gpr_free(chand->registered_methods);
+  }
+  if (chand->server) {
+    gpr_mu_lock(&chand->server->mu_global);
+    chand->next->prev = chand->prev;
+    chand->prev->next = chand->next;
+    chand->next = chand->prev = chand;
+    maybe_finish_shutdown(chand->server);
+    gpr_mu_unlock(&chand->server->mu_global);
+    server_unref(chand->server);
+  }
+}
+
+const grpc_channel_filter grpc_server_top_filter = {
+    server_start_transport_stream_op_batch,
+    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_channel_next_get_info,
+    "server",
+};
+
+static void register_completion_queue(grpc_server* server,
+                                      grpc_completion_queue* cq,
+                                      void* reserved) {
+  size_t i, n;
+  GPR_ASSERT(!reserved);
+  for (i = 0; i < server->cq_count; i++) {
+    if (server->cqs[i] == cq) return;
+  }
+
+  GRPC_CQ_INTERNAL_REF(cq, "server");
+  n = server->cq_count++;
+  server->cqs = static_cast<grpc_completion_queue**>(gpr_realloc(
+      server->cqs, server->cq_count * sizeof(grpc_completion_queue*)));
+  server->cqs[n] = cq;
+}
+
+void grpc_server_register_completion_queue(grpc_server* server,
+                                           grpc_completion_queue* cq,
+                                           void* reserved) {
+  GRPC_API_TRACE(
+      "grpc_server_register_completion_queue(server=%p, cq=%p, reserved=%p)", 3,
+      (server, cq, reserved));
+
+  if (grpc_get_cq_completion_type(cq) != GRPC_CQ_NEXT) {
+    gpr_log(GPR_INFO,
+            "Completion queue which is not of type GRPC_CQ_NEXT is being "
+            "registered as a server-completion-queue");
+    /* Ideally we should log an error and abort but ruby-wrapped-language API
+       calls grpc_completion_queue_pluck() on server completion queues */
+  }
+
+  register_completion_queue(server, cq, reserved);
+}
+
+grpc_server* grpc_server_create(const grpc_channel_args* args, void* reserved) {
+  grpc_core::ExecCtx exec_ctx;
+  GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved));
+
+  grpc_server* server =
+      static_cast<grpc_server*>(gpr_zalloc(sizeof(grpc_server)));
+
+  gpr_mu_init(&server->mu_global);
+  gpr_mu_init(&server->mu_call);
+  gpr_cv_init(&server->starting_cv);
+
+  /* decremented by grpc_server_destroy */
+  gpr_ref_init(&server->internal_refcount, 1);
+  server->root_channel_data.next = server->root_channel_data.prev =
+      &server->root_channel_data;
+
+  server->channel_args = grpc_channel_args_copy(args);
+
+  const grpc_arg* arg = grpc_channel_args_find(args, GRPC_ARG_ENABLE_CHANNELZ);
+  if (grpc_channel_arg_get_bool(arg, GRPC_ENABLE_CHANNELZ_DEFAULT)) {
+    arg = grpc_channel_args_find(
+        args, GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE);
+    size_t channel_tracer_max_memory = grpc_channel_arg_get_integer(
+        arg,
+        {GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX});
+    server->channelz_server =
+        grpc_core::MakeRefCounted<grpc_core::channelz::ServerNode>(
+            server, channel_tracer_max_memory);
+    server->channelz_server->AddTraceEvent(
+        grpc_core::channelz::ChannelTrace::Severity::Info,
+        grpc_slice_from_static_string("Server created"));
+  }
+
+  if (args != nullptr) {
+    grpc_resource_quota* resource_quota =
+        grpc_resource_quota_from_channel_args(args, false /* create */);
+    if (resource_quota != nullptr) {
+      server->default_resource_user =
+          grpc_resource_user_create(resource_quota, "default");
+    }
+  }
+
+  return server;
+}
+
+static int streq(const char* a, const char* b) {
+  if (a == nullptr && b == nullptr) return 1;
+  if (a == nullptr) return 0;
+  if (b == nullptr) return 0;
+  return 0 == strcmp(a, b);
+}
+
+void* grpc_server_register_method(
+    grpc_server* server, const char* method, const char* host,
+    grpc_server_register_method_payload_handling payload_handling,
+    uint32_t flags) {
+  registered_method* m;
+  GRPC_API_TRACE(
+      "grpc_server_register_method(server=%p, method=%s, host=%s, "
+      "flags=0x%08x)",
+      4, (server, method, host, flags));
+  if (!method) {
+    gpr_log(GPR_ERROR,
+            "grpc_server_register_method method string cannot be NULL");
+    return nullptr;
+  }
+  for (m = server->registered_methods; m; m = m->next) {
+    if (streq(m->method, method) && streq(m->host, host)) {
+      gpr_log(GPR_ERROR, "duplicate registration for %s@%s", method,
+              host ? host : "*");
+      return nullptr;
+    }
+  }
+  if ((flags & ~GRPC_INITIAL_METADATA_USED_MASK) != 0) {
+    gpr_log(GPR_ERROR, "grpc_server_register_method invalid flags 0x%08x",
+            flags);
+    return nullptr;
+  }
+  m = static_cast<registered_method*>(gpr_zalloc(sizeof(registered_method)));
+  m->method = gpr_strdup(method);
+  m->host = gpr_strdup(host);
+  m->next = server->registered_methods;
+  m->payload_handling = payload_handling;
+  m->flags = flags;
+  server->registered_methods = m;
+  return m;
+}
+
+static void start_listeners(void* s, grpc_error* error) {
+  grpc_server* server = static_cast<grpc_server*>(s);
+  for (listener* l = server->listeners; l; l = l->next) {
+    l->start(server, l->arg, server->pollsets, server->pollset_count);
+  }
+
+  gpr_mu_lock(&server->mu_global);
+  server->starting = false;
+  gpr_cv_signal(&server->starting_cv);
+  gpr_mu_unlock(&server->mu_global);
+
+  server_unref(server);
+}
+
+void grpc_server_start(grpc_server* server) {
+  size_t i;
+  grpc_core::ExecCtx exec_ctx;
+
+  GRPC_API_TRACE("grpc_server_start(server=%p)", 1, (server));
+
+  server->started = true;
+  server->pollset_count = 0;
+  server->pollsets = static_cast<grpc_pollset**>(
+      gpr_malloc(sizeof(grpc_pollset*) * server->cq_count));
+  for (i = 0; i < server->cq_count; i++) {
+    if (grpc_cq_can_listen(server->cqs[i])) {
+      server->pollsets[server->pollset_count++] =
+          grpc_cq_pollset(server->cqs[i]);
+    }
+  }
+  request_matcher_init(&server->unregistered_request_matcher, server);
+  for (registered_method* rm = server->registered_methods; rm; rm = rm->next) {
+    request_matcher_init(&rm->matcher, server);
+  }
+
+  server_ref(server);
+  server->starting = true;
+  GRPC_CLOSURE_SCHED(
+      GRPC_CLOSURE_CREATE(start_listeners, server,
+                          grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)),
+      GRPC_ERROR_NONE);
+}
+
+void grpc_server_get_pollsets(grpc_server* server, grpc_pollset*** pollsets,
+                              size_t* pollset_count) {
+  *pollset_count = server->pollset_count;
+  *pollsets = server->pollsets;
+}
+
+void grpc_server_setup_transport(
+    grpc_server* s, grpc_transport* transport, grpc_pollset* accepting_pollset,
+    const grpc_channel_args* args,
+    grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode> socket_node,
+    grpc_resource_user* resource_user) {
+  size_t num_registered_methods;
+  size_t alloc;
+  registered_method* rm;
+  channel_registered_method* crm;
+  grpc_channel* channel;
+  channel_data* chand;
+  uint32_t hash;
+  size_t slots;
+  uint32_t probes;
+  uint32_t max_probes = 0;
+  grpc_transport_op* op = nullptr;
+
+  channel = grpc_channel_create(nullptr, args, GRPC_SERVER_CHANNEL, transport,
+                                resource_user);
+  chand = static_cast<channel_data*>(
+      grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0)
+          ->channel_data);
+  chand->server = s;
+  server_ref(s);
+  chand->channel = channel;
+  chand->socket_node = std::move(socket_node);
+
+  size_t cq_idx;
+  for (cq_idx = 0; cq_idx < s->cq_count; cq_idx++) {
+    if (grpc_cq_pollset(s->cqs[cq_idx]) == accepting_pollset) break;
+  }
+  if (cq_idx == s->cq_count) {
+    /* completion queue not found: pick a random one to publish new calls to */
+    cq_idx = static_cast<size_t>(rand()) % s->cq_count;
+  }
+  chand->cq_idx = cq_idx;
+
+  num_registered_methods = 0;
+  for (rm = s->registered_methods; rm; rm = rm->next) {
+    num_registered_methods++;
+  }
+  /* build a lookup table phrased in terms of mdstr's in this channels context
+     to quickly find registered methods */
+  if (num_registered_methods > 0) {
+    slots = 2 * num_registered_methods;
+    alloc = sizeof(channel_registered_method) * slots;
+    chand->registered_methods =
+        static_cast<channel_registered_method*>(gpr_zalloc(alloc));
+    for (rm = s->registered_methods; rm; rm = rm->next) {
+      grpc_slice host;
+      bool has_host;
+      grpc_slice method;
+      if (rm->host != nullptr) {
+        host = grpc_slice_intern(grpc_slice_from_static_string(rm->host));
+        has_host = true;
+      } else {
+        has_host = false;
+      }
+      method = grpc_slice_intern(grpc_slice_from_static_string(rm->method));
+      hash = GRPC_MDSTR_KV_HASH(has_host ? grpc_slice_hash(host) : 0,
+                                grpc_slice_hash(method));
+      for (probes = 0; chand->registered_methods[(hash + probes) % slots]
+                           .server_registered_method != nullptr;
+           probes++)
+        ;
+      if (probes > max_probes) max_probes = probes;
+      crm = &chand->registered_methods[(hash + probes) % slots];
+      crm->server_registered_method = rm;
+      crm->flags = rm->flags;
+      crm->has_host = has_host;
+      if (has_host) {
+        crm->host = host;
+      }
+      crm->method = method;
+    }
+    GPR_ASSERT(slots <= UINT32_MAX);
+    chand->registered_method_slots = static_cast<uint32_t>(slots);
+    chand->registered_method_max_probes = max_probes;
+  }
+
+  gpr_mu_lock(&s->mu_global);
+  chand->next = &s->root_channel_data;
+  chand->prev = chand->next->prev;
+  chand->next->prev = chand->prev->next = chand;
+  gpr_mu_unlock(&s->mu_global);
+
+  GRPC_CHANNEL_INTERNAL_REF(channel, "connectivity");
+  op = grpc_make_transport_op(nullptr);
+  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_FROM_STATIC_STRING("Server shutdown");
+  }
+  grpc_transport_perform_op(transport, op);
+}
+
+void grpc_server_populate_server_sockets(
+    grpc_server* s, grpc_core::channelz::ChildSocketsList* server_sockets,
+    intptr_t start_idx) {
+  gpr_mu_lock(&s->mu_global);
+  channel_data* c = nullptr;
+  for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) {
+    if (c->socket_node != nullptr && c->socket_node->uuid() >= start_idx) {
+      server_sockets->push_back(c->socket_node.get());
+    }
+  }
+  gpr_mu_unlock(&s->mu_global);
+}
+
+void grpc_server_populate_listen_sockets(
+    grpc_server* server, grpc_core::channelz::ChildRefsList* listen_sockets) {
+  gpr_mu_lock(&server->mu_global);
+  for (listener* l = server->listeners; l != nullptr; l = l->next) {
+    listen_sockets->push_back(l->socket_uuid);
+  }
+  gpr_mu_unlock(&server->mu_global);
+}
+
+void done_published_shutdown(void* done_arg, grpc_cq_completion* storage) {
+  (void)done_arg;
+  gpr_free(storage);
+}
+
+static void listener_destroy_done(void* s, grpc_error* error) {
+  grpc_server* server = static_cast<grpc_server*>(s);
+  gpr_mu_lock(&server->mu_global);
+  server->listeners_destroyed++;
+  maybe_finish_shutdown(server);
+  gpr_mu_unlock(&server->mu_global);
+}
+
+/*
+  - Kills all pending requests-for-incoming-RPC-calls (i.e the requests made via
+    grpc_server_request_call and grpc_server_request_registered call will now be
+    cancelled). See 'kill_pending_work_locked()'
+
+  - Shuts down the listeners (i.e the server will no longer listen on the port
+    for new incoming channels).
+
+  - Iterates through all channels on the server and sends shutdown msg (see
+    'channel_broadcaster_shutdown()' for details) to the clients via the
+    transport layer. The transport layer then guarantees the following:
+     -- Sends shutdown to the client (for eg: HTTP2 transport sends GOAWAY)
+     -- If the server has outstanding calls that are in the process, the
+        connection is NOT closed until the server is done with all those calls
+     -- Once, there are no more calls in progress, the channel is closed
+ */
+void grpc_server_shutdown_and_notify(grpc_server* server,
+                                     grpc_completion_queue* cq, void* tag) {
+  listener* l;
+  shutdown_tag* sdt;
+  channel_broadcaster broadcaster;
+  grpc_core::ExecCtx exec_ctx;
+
+  GRPC_API_TRACE("grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", 3,
+                 (server, cq, tag));
+
+  /* wait for startup to be finished: locks mu_global */
+  gpr_mu_lock(&server->mu_global);
+  while (server->starting) {
+    gpr_cv_wait(&server->starting_cv, &server->mu_global,
+                gpr_inf_future(GPR_CLOCK_MONOTONIC));
+  }
+
+  /* stay locked, and gather up some stuff to do */
+  GPR_ASSERT(grpc_cq_begin_op(cq, tag));
+  if (server->shutdown_published) {
+    grpc_cq_end_op(cq, tag, GRPC_ERROR_NONE, done_published_shutdown, nullptr,
+                   static_cast<grpc_cq_completion*>(
+                       gpr_malloc(sizeof(grpc_cq_completion))));
+    gpr_mu_unlock(&server->mu_global);
+    return;
+  }
+  server->shutdown_tags = static_cast<shutdown_tag*>(
+      gpr_realloc(server->shutdown_tags,
+                  sizeof(shutdown_tag) * (server->num_shutdown_tags + 1)));
+  sdt = &server->shutdown_tags[server->num_shutdown_tags++];
+  sdt->tag = tag;
+  sdt->cq = cq;
+  if (gpr_atm_acq_load(&server->shutdown_flag)) {
+    gpr_mu_unlock(&server->mu_global);
+    return;
+  }
+
+  server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME);
+
+  channel_broadcaster_init(server, &broadcaster);
+
+  gpr_atm_rel_store(&server->shutdown_flag, 1);
+
+  /* collect all unregistered then registered calls */
+  gpr_mu_lock(&server->mu_call);
+  kill_pending_work_locked(
+      server, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
+  gpr_mu_unlock(&server->mu_call);
+
+  maybe_finish_shutdown(server);
+  gpr_mu_unlock(&server->mu_global);
+
+  /* Shutdown listeners */
+  for (l = server->listeners; l; l = l->next) {
+    GRPC_CLOSURE_INIT(&l->destroy_done, listener_destroy_done, server,
+                      grpc_schedule_on_exec_ctx);
+    l->destroy(server, l->arg, &l->destroy_done);
+  }
+
+  channel_broadcaster_shutdown(&broadcaster, true /* send_goaway */,
+                               GRPC_ERROR_NONE);
+
+  if (server->default_resource_user != nullptr) {
+    grpc_resource_quota_unref(
+        grpc_resource_user_quota(server->default_resource_user));
+    grpc_resource_user_shutdown(server->default_resource_user);
+    grpc_resource_user_unref(server->default_resource_user);
+  }
+}
+
+void grpc_server_cancel_all_calls(grpc_server* server) {
+  channel_broadcaster broadcaster;
+  grpc_core::ExecCtx exec_ctx;
+
+  GRPC_API_TRACE("grpc_server_cancel_all_calls(server=%p)", 1, (server));
+
+  gpr_mu_lock(&server->mu_global);
+  channel_broadcaster_init(server, &broadcaster);
+  gpr_mu_unlock(&server->mu_global);
+
+  channel_broadcaster_shutdown(
+      &broadcaster, false /* send_goaway */,
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Cancelling all calls"));
+}
+
+void grpc_server_destroy(grpc_server* server) {
+  listener* l;
+  grpc_core::ExecCtx exec_ctx;
+
+  GRPC_API_TRACE("grpc_server_destroy(server=%p)", 1, (server));
+
+  gpr_mu_lock(&server->mu_global);
+  GPR_ASSERT(gpr_atm_acq_load(&server->shutdown_flag) || !server->listeners);
+  GPR_ASSERT(server->listeners_destroyed == num_listeners(server));
+
+  while (server->listeners) {
+    l = server->listeners;
+    server->listeners = l->next;
+    gpr_free(l);
+  }
+
+  gpr_mu_unlock(&server->mu_global);
+
+  server_unref(server);
+}
+
+void grpc_server_add_listener(grpc_server* server, void* arg,
+                              void (*start)(grpc_server* server, void* arg,
+                                            grpc_pollset** pollsets,
+                                            size_t pollset_count),
+                              void (*destroy)(grpc_server* server, void* arg,
+                                              grpc_closure* on_done),
+                              intptr_t socket_uuid) {
+  listener* l = static_cast<listener*>(gpr_malloc(sizeof(listener)));
+  l->arg = arg;
+  l->start = start;
+  l->destroy = destroy;
+  l->socket_uuid = socket_uuid;
+  l->next = server->listeners;
+  server->listeners = l;
+}
+
+static grpc_call_error queue_call_request(grpc_server* server, size_t cq_idx,
+                                          requested_call* rc) {
+  call_data* calld = nullptr;
+  request_matcher* rm = nullptr;
+  if (gpr_atm_acq_load(&server->shutdown_flag)) {
+    fail_call(server, cq_idx, rc,
+              GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown"));
+    return GRPC_CALL_OK;
+  }
+  switch (rc->type) {
+    case BATCH_CALL:
+      rm = &server->unregistered_request_matcher;
+      break;
+    case REGISTERED_CALL:
+      rm = &rc->data.registered.method->matcher;
+      break;
+  }
+  if (gpr_locked_mpscq_push(&rm->requests_per_cq[cq_idx], &rc->request_link)) {
+    /* this was the first queued request: we need to lock and start
+       matching calls */
+    gpr_mu_lock(&server->mu_call);
+    while ((calld = rm->pending_head) != nullptr) {
+      rc = reinterpret_cast<requested_call*>(
+          gpr_locked_mpscq_pop(&rm->requests_per_cq[cq_idx]));
+      if (rc == nullptr) break;
+      rm->pending_head = calld->pending_next;
+      gpr_mu_unlock(&server->mu_call);
+      if (!gpr_atm_full_cas(&calld->state, PENDING, ACTIVATED)) {
+        // Zombied Call
+        GRPC_CLOSURE_INIT(
+            &calld->kill_zombie_closure, kill_zombie,
+            grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0),
+            grpc_schedule_on_exec_ctx);
+        GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_NONE);
+      } else {
+        publish_call(server, calld, cq_idx, rc);
+      }
+      gpr_mu_lock(&server->mu_call);
+    }
+    gpr_mu_unlock(&server->mu_call);
+  }
+  return GRPC_CALL_OK;
+}
+
+grpc_call_error grpc_server_request_call(
+    grpc_server* server, grpc_call** call, grpc_call_details* details,
+    grpc_metadata_array* initial_metadata,
+    grpc_completion_queue* cq_bound_to_call,
+    grpc_completion_queue* cq_for_notification, void* tag) {
+  grpc_call_error error;
+  grpc_core::ExecCtx exec_ctx;
+  requested_call* rc = static_cast<requested_call*>(gpr_malloc(sizeof(*rc)));
+  GRPC_STATS_INC_SERVER_REQUESTED_CALLS();
+  GRPC_API_TRACE(
+      "grpc_server_request_call("
+      "server=%p, call=%p, details=%p, initial_metadata=%p, "
+      "cq_bound_to_call=%p, cq_for_notification=%p, tag=%p)",
+      7,
+      (server, call, details, initial_metadata, cq_bound_to_call,
+       cq_for_notification, tag));
+  size_t cq_idx;
+  for (cq_idx = 0; cq_idx < server->cq_count; cq_idx++) {
+    if (server->cqs[cq_idx] == cq_for_notification) {
+      break;
+    }
+  }
+  if (cq_idx == server->cq_count) {
+    gpr_free(rc);
+    error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
+    goto done;
+  }
+  if (grpc_cq_begin_op(cq_for_notification, tag) == false) {
+    gpr_free(rc);
+    error = GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN;
+    goto done;
+  }
+  details->reserved = nullptr;
+  rc->cq_idx = cq_idx;
+  rc->type = BATCH_CALL;
+  rc->server = server;
+  rc->tag = tag;
+  rc->cq_bound_to_call = cq_bound_to_call;
+  rc->call = call;
+  rc->data.batch.details = details;
+  rc->initial_metadata = initial_metadata;
+  error = queue_call_request(server, cq_idx, rc);
+done:
+
+  return error;
+}
+
+grpc_call_error grpc_server_request_registered_call(
+    grpc_server* server, void* rmp, grpc_call** call, gpr_timespec* deadline,
+    grpc_metadata_array* initial_metadata, grpc_byte_buffer** optional_payload,
+    grpc_completion_queue* cq_bound_to_call,
+    grpc_completion_queue* cq_for_notification, void* tag) {
+  grpc_call_error error;
+  grpc_core::ExecCtx exec_ctx;
+  requested_call* rc = static_cast<requested_call*>(gpr_malloc(sizeof(*rc)));
+  registered_method* rm = static_cast<registered_method*>(rmp);
+  GRPC_STATS_INC_SERVER_REQUESTED_CALLS();
+  GRPC_API_TRACE(
+      "grpc_server_request_registered_call("
+      "server=%p, rmp=%p, call=%p, deadline=%p, initial_metadata=%p, "
+      "optional_payload=%p, cq_bound_to_call=%p, cq_for_notification=%p, "
+      "tag=%p)",
+      9,
+      (server, rmp, call, deadline, initial_metadata, optional_payload,
+       cq_bound_to_call, cq_for_notification, tag));
+
+  size_t cq_idx;
+  for (cq_idx = 0; cq_idx < server->cq_count; cq_idx++) {
+    if (server->cqs[cq_idx] == cq_for_notification) {
+      break;
+    }
+  }
+  if (cq_idx == server->cq_count) {
+    gpr_free(rc);
+    error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
+    goto done;
+  }
+  if ((optional_payload == nullptr) !=
+      (rm->payload_handling == GRPC_SRM_PAYLOAD_NONE)) {
+    gpr_free(rc);
+    error = GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH;
+    goto done;
+  }
+  if (grpc_cq_begin_op(cq_for_notification, tag) == false) {
+    gpr_free(rc);
+    error = GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN;
+    goto done;
+  }
+  rc->cq_idx = cq_idx;
+  rc->type = REGISTERED_CALL;
+  rc->server = server;
+  rc->tag = tag;
+  rc->cq_bound_to_call = cq_bound_to_call;
+  rc->call = call;
+  rc->data.registered.method = rm;
+  rc->data.registered.deadline = deadline;
+  rc->initial_metadata = initial_metadata;
+  rc->data.registered.optional_payload = optional_payload;
+  error = queue_call_request(server, cq_idx, rc);
+done:
+
+  return error;
+}
+
+static void fail_call(grpc_server* server, size_t cq_idx, requested_call* rc,
+                      grpc_error* error) {
+  *rc->call = nullptr;
+  rc->initial_metadata->count = 0;
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+
+  grpc_cq_end_op(server->cqs[cq_idx], rc->tag, error, done_request_event, rc,
+                 &rc->completion);
+}
+
+const grpc_channel_args* grpc_server_get_channel_args(grpc_server* server) {
+  return server->channel_args;
+}
+
+grpc_resource_user* grpc_server_get_default_resource_user(grpc_server* server) {
+  return server->default_resource_user;
+}
+
+int grpc_server_has_open_connections(grpc_server* server) {
+  int r;
+  gpr_mu_lock(&server->mu_global);
+  r = server->root_channel_data.next != &server->root_channel_data;
+  gpr_mu_unlock(&server->mu_global);
+  return r;
+}
+
+grpc_core::channelz::ServerNode* grpc_server_get_channelz_node(
+    grpc_server* server) {
+  if (server == nullptr) {
+    return nullptr;
+  }
+  return server->channelz_server.get();
+}
diff --git a/third_party/grpc/src/core/lib/surface/server.h b/third_party/grpc/src/core/lib/surface/server.h
new file mode 100644
index 0000000..393bb24
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/server.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_SERVER_H
+#define GRPC_CORE_LIB_SURFACE_SERVER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/channelz.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/transport/transport.h"
+
+extern const grpc_channel_filter grpc_server_top_filter;
+
+/** Lightweight tracing of server channel state */
+extern grpc_core::TraceFlag grpc_server_channel_trace;
+
+/* Add a listener to the server: when the server starts, it will call start,
+   and when it shuts down, it will call destroy */
+void grpc_server_add_listener(grpc_server* server, void* listener,
+                              void (*start)(grpc_server* server, void* arg,
+                                            grpc_pollset** pollsets,
+                                            size_t npollsets),
+                              void (*destroy)(grpc_server* server, void* arg,
+                                              grpc_closure* on_done),
+                              intptr_t socket_uuid);
+
+/* Setup a transport - creates a channel stack, binds the transport to the
+   server */
+void grpc_server_setup_transport(
+    grpc_server* server, grpc_transport* transport,
+    grpc_pollset* accepting_pollset, const grpc_channel_args* args,
+    grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode> socket_node,
+    grpc_resource_user* resource_user = nullptr);
+
+/* fills in the uuids of all sockets used for connections on this server */
+void grpc_server_populate_server_sockets(
+    grpc_server* server, grpc_core::channelz::ChildSocketsList* server_sockets,
+    intptr_t start_idx);
+
+/* fills in the uuids of all listen sockets on this server */
+void grpc_server_populate_listen_sockets(
+    grpc_server* server, grpc_core::channelz::ChildRefsList* listen_sockets);
+
+grpc_core::channelz::ServerNode* grpc_server_get_channelz_node(
+    grpc_server* server);
+
+const grpc_channel_args* grpc_server_get_channel_args(grpc_server* server);
+
+grpc_resource_user* grpc_server_get_default_resource_user(grpc_server* server);
+
+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/third_party/grpc/src/core/lib/surface/validate_metadata.cc b/third_party/grpc/src/core/lib/surface/validate_metadata.cc
new file mode 100644
index 0000000..2dd18f3
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/validate_metadata.cc
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/surface/validate_metadata.h"
+
+static grpc_error* conforms_to(grpc_slice slice, const uint8_t* legal_bits,
+                               const char* err_desc) {
+  const uint8_t* p = GRPC_SLICE_START_PTR(slice);
+  const uint8_t* e = GRPC_SLICE_END_PTR(slice);
+  for (; p != e; p++) {
+    int idx = *p;
+    int byte = idx / 8;
+    int bit = idx % 8;
+    if ((legal_bits[byte] & (1 << bit)) == 0) {
+      char* dump = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+      grpc_error* error = grpc_error_set_str(
+          grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_desc),
+                             GRPC_ERROR_INT_OFFSET,
+                             p - GRPC_SLICE_START_PTR(slice)),
+          GRPC_ERROR_STR_RAW_BYTES, grpc_slice_from_copied_string(dump));
+      gpr_free(dump);
+      return error;
+    }
+  }
+  return GRPC_ERROR_NONE;
+}
+
+static int error2int(grpc_error* error) {
+  int r = (error == GRPC_ERROR_NONE);
+  GRPC_ERROR_UNREF(error);
+  return r;
+}
+
+grpc_error* grpc_validate_header_key_is_legal(grpc_slice slice) {
+  static const uint8_t legal_header_bits[256 / 8] = {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0x00, 0x00, 0x00,
+      0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  if (GRPC_SLICE_LENGTH(slice) == 0) {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Metadata keys cannot be zero length");
+  }
+  if (GRPC_SLICE_START_PTR(slice)[0] == ':') {
+    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+        "Metadata keys cannot start with :");
+  }
+  return conforms_to(slice, legal_header_bits, "Illegal header key");
+}
+
+int grpc_header_key_is_legal(grpc_slice slice) {
+  return error2int(grpc_validate_header_key_is_legal(slice));
+}
+
+grpc_error* grpc_validate_header_nonbin_value_is_legal(grpc_slice slice) {
+  static const uint8_t legal_header_bits[256 / 8] = {
+      0x00, 0x00, 0x00, 0x00, 0xff, 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};
+  return conforms_to(slice, legal_header_bits, "Illegal header value");
+}
+
+int grpc_header_nonbin_value_is_legal(grpc_slice slice) {
+  return error2int(grpc_validate_header_nonbin_value_is_legal(slice));
+}
+
+int grpc_is_binary_header(grpc_slice slice) {
+  if (GRPC_SLICE_LENGTH(slice) < 5) return 0;
+  return 0 == memcmp(GRPC_SLICE_END_PTR(slice) - 4, "-bin", 4);
+}
diff --git a/third_party/grpc/src/core/lib/surface/validate_metadata.h b/third_party/grpc/src/core/lib/surface/validate_metadata.h
new file mode 100644
index 0000000..e87fb7b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/validate_metadata.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H
+#define GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include "src/core/lib/iomgr/error.h"
+
+grpc_error* grpc_validate_header_key_is_legal(grpc_slice slice);
+grpc_error* grpc_validate_header_nonbin_value_is_legal(grpc_slice slice);
+
+#endif /* GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H */
diff --git a/third_party/grpc/src/core/lib/surface/version.cc b/third_party/grpc/src/core/lib/surface/version.cc
new file mode 100644
index 0000000..fac9389
--- /dev/null
+++ b/third_party/grpc/src/core/lib/surface/version.cc
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* This file is autogenerated from:
+   templates/src/core/surface/version.c.template */
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+const char* grpc_version_string(void) { return "7.0.0"; }
+
+const char* grpc_g_stands_for(void) { return "goose"; }
diff --git a/third_party/grpc/src/core/lib/transport/README.md b/third_party/grpc/src/core/lib/transport/README.md
new file mode 100644
index 0000000..e7e135e
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/README.md
@@ -0,0 +1,7 @@
+# Transport
+
+Common implementation details for gRPC Transports.
+
+Transports multiplex messages across some single connection. In ext/ there are
+implementations atop [a custom http2 implementation](/src/core/ext/transport/chttp2/README.md)
+and atop [cronet](/src/core/ext/transport/cronet/README.md).
diff --git a/third_party/grpc/src/core/lib/transport/bdp_estimator.cc b/third_party/grpc/src/core/lib/transport/bdp_estimator.cc
new file mode 100644
index 0000000..8e71f86
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/bdp_estimator.cc
@@ -0,0 +1,87 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/bdp_estimator.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+grpc_core::TraceFlag grpc_bdp_estimator_trace(false, "bdp_estimator");
+
+namespace grpc_core {
+
+BdpEstimator::BdpEstimator(const char* name)
+    : ping_state_(PingState::UNSCHEDULED),
+      accumulator_(0),
+      estimate_(65536),
+      ping_start_time_(gpr_time_0(GPR_CLOCK_MONOTONIC)),
+      inter_ping_delay_(100.0),  // start at 100ms
+      stable_estimate_count_(0),
+      bw_est_(0),
+      name_(name) {}
+
+grpc_millis BdpEstimator::CompletePing() {
+  gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
+  gpr_timespec dt_ts = gpr_time_sub(now, ping_start_time_);
+  double dt = static_cast<double>(dt_ts.tv_sec) +
+              1e-9 * static_cast<double>(dt_ts.tv_nsec);
+  double bw = dt > 0 ? (static_cast<double>(accumulator_) / dt) : 0;
+  int start_inter_ping_delay = inter_ping_delay_;
+  if (grpc_bdp_estimator_trace.enabled()) {
+    gpr_log(GPR_INFO,
+            "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64
+            " dt=%lf bw=%lfMbs bw_est=%lfMbs",
+            name_, accumulator_, estimate_, dt, bw / 125000.0,
+            bw_est_ / 125000.0);
+  }
+  GPR_ASSERT(ping_state_ == PingState::STARTED);
+  if (accumulator_ > 2 * estimate_ / 3 && bw > bw_est_) {
+    estimate_ = GPR_MAX(accumulator_, estimate_ * 2);
+    bw_est_ = bw;
+    if (grpc_bdp_estimator_trace.enabled()) {
+      gpr_log(GPR_INFO, "bdp[%s]: estimate increased to %" PRId64, name_,
+              estimate_);
+    }
+    inter_ping_delay_ /= 2;  // if the ping estimate changes,
+                             // exponentially get faster at probing
+  } else if (inter_ping_delay_ < 10000) {
+    stable_estimate_count_++;
+    if (stable_estimate_count_ >= 2) {
+      inter_ping_delay_ +=
+          100 + static_cast<int>(rand() * 100.0 /
+                                 RAND_MAX);  // if the ping estimate is steady,
+                                             // slowly ramp down the probe time
+    }
+  }
+  if (start_inter_ping_delay != inter_ping_delay_) {
+    stable_estimate_count_ = 0;
+    if (grpc_bdp_estimator_trace.enabled()) {
+      gpr_log(GPR_INFO, "bdp[%s]:update_inter_time to %dms", name_,
+              inter_ping_delay_);
+    }
+  }
+  ping_state_ = PingState::UNSCHEDULED;
+  accumulator_ = 0;
+  return grpc_core::ExecCtx::Get()->Now() + inter_ping_delay_;
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/transport/bdp_estimator.h b/third_party/grpc/src/core/lib/transport/bdp_estimator.h
new file mode 100644
index 0000000..ab13ae4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/bdp_estimator.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H
+#define GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+extern grpc_core::TraceFlag grpc_bdp_estimator_trace;
+
+namespace grpc_core {
+
+class BdpEstimator {
+ public:
+  explicit BdpEstimator(const char* name);
+  ~BdpEstimator() {}
+
+  int64_t EstimateBdp() const { return estimate_; }
+  double EstimateBandwidth() const { return bw_est_; }
+
+  void AddIncomingBytes(int64_t num_bytes) { accumulator_ += num_bytes; }
+
+  // Schedule a ping: call in response to receiving a true from
+  // grpc_bdp_estimator_add_incoming_bytes once a ping has been scheduled by a
+  // transport (but not necessarily started)
+  void SchedulePing() {
+    if (grpc_bdp_estimator_trace.enabled()) {
+      gpr_log(GPR_INFO, "bdp[%s]:sched acc=%" PRId64 " est=%" PRId64, name_,
+              accumulator_, estimate_);
+    }
+    GPR_ASSERT(ping_state_ == PingState::UNSCHEDULED);
+    ping_state_ = PingState::SCHEDULED;
+    accumulator_ = 0;
+  }
+
+  // Start a ping: call after calling grpc_bdp_estimator_schedule_ping and
+  // once
+  // the ping is on the wire
+  void StartPing() {
+    if (grpc_bdp_estimator_trace.enabled()) {
+      gpr_log(GPR_INFO, "bdp[%s]:start acc=%" PRId64 " est=%" PRId64, name_,
+              accumulator_, estimate_);
+    }
+    GPR_ASSERT(ping_state_ == PingState::SCHEDULED);
+    ping_state_ = PingState::STARTED;
+    accumulator_ = 0;
+    ping_start_time_ = gpr_now(GPR_CLOCK_MONOTONIC);
+  }
+
+  // Completes a previously started ping, returns when to schedule the next one
+  grpc_millis CompletePing();
+
+ private:
+  enum class PingState { UNSCHEDULED, SCHEDULED, STARTED };
+
+  PingState ping_state_;
+  int64_t accumulator_;
+  int64_t estimate_;
+  // when was the current ping started?
+  gpr_timespec ping_start_time_;
+  int inter_ping_delay_;
+  int stable_estimate_count_;
+  double bw_est_;
+  const char* name_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H */
diff --git a/third_party/grpc/src/core/lib/transport/byte_stream.cc b/third_party/grpc/src/core/lib/transport/byte_stream.cc
new file mode 100644
index 0000000..16b85ca
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/byte_stream.cc
@@ -0,0 +1,160 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/byte_stream.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+namespace grpc_core {
+
+//
+// SliceBufferByteStream
+//
+
+SliceBufferByteStream::SliceBufferByteStream(grpc_slice_buffer* slice_buffer,
+                                             uint32_t flags)
+    : ByteStream(static_cast<uint32_t>(slice_buffer->length), flags) {
+  GPR_ASSERT(slice_buffer->length <= UINT32_MAX);
+  grpc_slice_buffer_init(&backing_buffer_);
+  grpc_slice_buffer_swap(slice_buffer, &backing_buffer_);
+}
+
+SliceBufferByteStream::~SliceBufferByteStream() {}
+
+void SliceBufferByteStream::Orphan() {
+  grpc_slice_buffer_destroy_internal(&backing_buffer_);
+  GRPC_ERROR_UNREF(shutdown_error_);
+  // Note: We do not actually delete the object here, since
+  // SliceBufferByteStream is usually allocated as part of a larger
+  // object and has an OrphanablePtr of itself passed down through the
+  // filter stack.
+}
+
+bool SliceBufferByteStream::Next(size_t max_size_hint,
+                                 grpc_closure* on_complete) {
+  GPR_ASSERT(cursor_ < backing_buffer_.count);
+  return true;
+}
+
+grpc_error* SliceBufferByteStream::Pull(grpc_slice* slice) {
+  if (shutdown_error_ != GRPC_ERROR_NONE) {
+    return GRPC_ERROR_REF(shutdown_error_);
+  }
+  GPR_ASSERT(cursor_ < backing_buffer_.count);
+  *slice = grpc_slice_ref_internal(backing_buffer_.slices[cursor_]);
+  ++cursor_;
+  return GRPC_ERROR_NONE;
+}
+
+void SliceBufferByteStream::Shutdown(grpc_error* error) {
+  GRPC_ERROR_UNREF(shutdown_error_);
+  shutdown_error_ = error;
+}
+
+//
+// ByteStreamCache
+//
+
+ByteStreamCache::ByteStreamCache(OrphanablePtr<ByteStream> underlying_stream)
+    : underlying_stream_(std::move(underlying_stream)),
+      length_(underlying_stream_->length()),
+      flags_(underlying_stream_->flags()) {
+  grpc_slice_buffer_init(&cache_buffer_);
+}
+
+ByteStreamCache::~ByteStreamCache() { Destroy(); }
+
+void ByteStreamCache::Destroy() {
+  underlying_stream_.reset();
+  if (cache_buffer_.length > 0) {
+    grpc_slice_buffer_destroy_internal(&cache_buffer_);
+  }
+}
+
+//
+// ByteStreamCache::CachingByteStream
+//
+
+ByteStreamCache::CachingByteStream::CachingByteStream(ByteStreamCache* cache)
+    : ByteStream(cache->length_, cache->flags_), cache_(cache) {}
+
+ByteStreamCache::CachingByteStream::~CachingByteStream() {}
+
+void ByteStreamCache::CachingByteStream::Orphan() {
+  GRPC_ERROR_UNREF(shutdown_error_);
+  // Note: We do not actually delete the object here, since
+  // CachingByteStream is usually allocated as part of a larger
+  // object and has an OrphanablePtr of itself passed down through the
+  // filter stack.
+}
+
+bool ByteStreamCache::CachingByteStream::Next(size_t max_size_hint,
+                                              grpc_closure* on_complete) {
+  if (shutdown_error_ != GRPC_ERROR_NONE) return true;
+  if (cursor_ < cache_->cache_buffer_.count) return true;
+  GPR_ASSERT(cache_->underlying_stream_ != nullptr);
+  return cache_->underlying_stream_->Next(max_size_hint, on_complete);
+}
+
+grpc_error* ByteStreamCache::CachingByteStream::Pull(grpc_slice* slice) {
+  if (shutdown_error_ != GRPC_ERROR_NONE) {
+    return GRPC_ERROR_REF(shutdown_error_);
+  }
+  if (cursor_ < cache_->cache_buffer_.count) {
+    *slice = grpc_slice_ref_internal(cache_->cache_buffer_.slices[cursor_]);
+    ++cursor_;
+    offset_ += GRPC_SLICE_LENGTH(*slice);
+    return GRPC_ERROR_NONE;
+  }
+  GPR_ASSERT(cache_->underlying_stream_ != nullptr);
+  grpc_error* error = cache_->underlying_stream_->Pull(slice);
+  if (error == GRPC_ERROR_NONE) {
+    grpc_slice_buffer_add(&cache_->cache_buffer_,
+                          grpc_slice_ref_internal(*slice));
+    ++cursor_;
+    offset_ += GRPC_SLICE_LENGTH(*slice);
+    // Orphan the underlying stream if it's been drained.
+    if (offset_ == cache_->underlying_stream_->length()) {
+      cache_->underlying_stream_.reset();
+    }
+  }
+  return error;
+}
+
+void ByteStreamCache::CachingByteStream::Shutdown(grpc_error* error) {
+  GRPC_ERROR_UNREF(shutdown_error_);
+  shutdown_error_ = GRPC_ERROR_REF(error);
+  if (cache_->underlying_stream_ != nullptr) {
+    cache_->underlying_stream_->Shutdown(error);
+  }
+}
+
+void ByteStreamCache::CachingByteStream::Reset() {
+  cursor_ = 0;
+  offset_ = 0;
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/transport/byte_stream.h b/third_party/grpc/src/core/lib/transport/byte_stream.h
new file mode 100644
index 0000000..eff8325
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/byte_stream.h
@@ -0,0 +1,164 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H
+#define GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+#include "src/core/lib/gprpp/abstract.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/iomgr/closure.h"
+
+/** Internal bit flag for grpc_begin_message's \a flags signaling the use of
+ * compression for the message */
+#define GRPC_WRITE_INTERNAL_COMPRESS (0x80000000u)
+/** Mask of all valid internal flags. */
+#define GRPC_WRITE_INTERNAL_USED_MASK (GRPC_WRITE_INTERNAL_COMPRESS)
+
+namespace grpc_core {
+
+class ByteStream : public Orphanable {
+ public:
+  virtual ~ByteStream() {}
+
+  // Returns true if the bytes are available immediately (in which case
+  // on_complete will not be called), or false if the bytes will be available
+  // asynchronously (in which case on_complete will be called when they
+  // are available).
+  //
+  // max_size_hint can be set as a hint as to the maximum number
+  // of bytes that would be acceptable to read.
+  virtual bool Next(size_t max_size_hint,
+                    grpc_closure* on_complete) GRPC_ABSTRACT;
+
+  // Returns the next slice in the byte stream when it is available, as
+  // indicated by Next().
+  //
+  // Once a slice is returned into *slice, it is owned by the caller.
+  virtual grpc_error* Pull(grpc_slice* slice) GRPC_ABSTRACT;
+
+  // Shuts down the byte stream.
+  //
+  // If there is a pending call to on_complete from Next(), it will be
+  // invoked with the error passed to Shutdown().
+  //
+  // The next call to Pull() (if any) will return the error passed to
+  // Shutdown().
+  virtual void Shutdown(grpc_error* error) GRPC_ABSTRACT;
+
+  uint32_t length() const { return length_; }
+  uint32_t flags() const { return flags_; }
+
+  void set_flags(uint32_t flags) { flags_ = flags; }
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  ByteStream(uint32_t length, uint32_t flags)
+      : length_(length), flags_(flags) {}
+
+ private:
+  const uint32_t length_;
+  uint32_t flags_;
+};
+
+//
+// SliceBufferByteStream
+//
+// A ByteStream that wraps a slice buffer.
+//
+
+class SliceBufferByteStream : public ByteStream {
+ public:
+  // Removes all slices in slice_buffer, leaving it empty.
+  SliceBufferByteStream(grpc_slice_buffer* slice_buffer, uint32_t flags);
+
+  ~SliceBufferByteStream();
+
+  void Orphan() override;
+
+  bool Next(size_t max_size_hint, grpc_closure* on_complete) override;
+  grpc_error* Pull(grpc_slice* slice) override;
+  void Shutdown(grpc_error* error) override;
+
+ private:
+  grpc_slice_buffer backing_buffer_;
+  size_t cursor_ = 0;
+  grpc_error* shutdown_error_ = GRPC_ERROR_NONE;
+};
+
+//
+// CachingByteStream
+//
+// A ByteStream that that wraps an underlying byte stream but caches
+// the resulting slices in a slice buffer.  If an initial attempt fails
+// without fully draining the underlying stream, a new caching stream
+// can be created from the same underlying cache, in which case it will
+// return whatever is in the backing buffer before continuing to read the
+// underlying stream.
+//
+// NOTE: No synchronization is done, so it is not safe to have multiple
+// CachingByteStreams simultaneously drawing from the same underlying
+// ByteStreamCache at the same time.
+//
+
+class ByteStreamCache {
+ public:
+  class CachingByteStream : public ByteStream {
+   public:
+    explicit CachingByteStream(ByteStreamCache* cache);
+
+    ~CachingByteStream();
+
+    void Orphan() override;
+
+    bool Next(size_t max_size_hint, grpc_closure* on_complete) override;
+    grpc_error* Pull(grpc_slice* slice) override;
+    void Shutdown(grpc_error* error) override;
+
+    // Resets the byte stream to the start of the underlying stream.
+    void Reset();
+
+   private:
+    ByteStreamCache* cache_;
+    size_t cursor_ = 0;
+    size_t offset_ = 0;
+    grpc_error* shutdown_error_ = GRPC_ERROR_NONE;
+  };
+
+  explicit ByteStreamCache(OrphanablePtr<ByteStream> underlying_stream);
+
+  ~ByteStreamCache();
+
+  // Must not be destroyed while still in use by a CachingByteStream.
+  void Destroy();
+
+  grpc_slice_buffer* cache_buffer() { return &cache_buffer_; }
+
+ private:
+  OrphanablePtr<ByteStream> underlying_stream_;
+  uint32_t length_;
+  uint32_t flags_;
+  grpc_slice_buffer cache_buffer_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H */
diff --git a/third_party/grpc/src/core/lib/transport/connectivity_state.cc b/third_party/grpc/src/core/lib/transport/connectivity_state.cc
new file mode 100644
index 0000000..db6b6c0
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/connectivity_state.cc
@@ -0,0 +1,195 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/connectivity_state.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+grpc_core::TraceFlag grpc_connectivity_state_trace(false, "connectivity_state");
+
+const char* grpc_connectivity_state_name(grpc_connectivity_state state) {
+  switch (state) {
+    case GRPC_CHANNEL_IDLE:
+      return "IDLE";
+    case GRPC_CHANNEL_CONNECTING:
+      return "CONNECTING";
+    case GRPC_CHANNEL_READY:
+      return "READY";
+    case GRPC_CHANNEL_TRANSIENT_FAILURE:
+      return "TRANSIENT_FAILURE";
+    case GRPC_CHANNEL_SHUTDOWN:
+      return "SHUTDOWN";
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
+
+void grpc_connectivity_state_init(grpc_connectivity_state_tracker* tracker,
+                                  grpc_connectivity_state init_state,
+                                  const char* name) {
+  gpr_atm_no_barrier_store(&tracker->current_state_atm, init_state);
+  tracker->current_error = GRPC_ERROR_NONE;
+  tracker->watchers = nullptr;
+  tracker->name = gpr_strdup(name);
+}
+
+void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker* tracker) {
+  grpc_error* error;
+  grpc_connectivity_state_watcher* w;
+  while ((w = tracker->watchers)) {
+    tracker->watchers = w->next;
+
+    if (GRPC_CHANNEL_SHUTDOWN != *w->current) {
+      *w->current = GRPC_CHANNEL_SHUTDOWN;
+      error = GRPC_ERROR_NONE;
+    } else {
+      error =
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutdown connectivity owner");
+    }
+    GRPC_CLOSURE_SCHED(w->notify, error);
+    gpr_free(w);
+  }
+  GRPC_ERROR_UNREF(tracker->current_error);
+  gpr_free(tracker->name);
+}
+
+grpc_connectivity_state grpc_connectivity_state_check(
+    grpc_connectivity_state_tracker* tracker) {
+  grpc_connectivity_state cur = static_cast<grpc_connectivity_state>(
+      gpr_atm_no_barrier_load(&tracker->current_state_atm));
+  if (grpc_connectivity_state_trace.enabled()) {
+    gpr_log(GPR_INFO, "CONWATCH: %p %s: get %s", tracker, tracker->name,
+            grpc_connectivity_state_name(cur));
+  }
+  return cur;
+}
+
+grpc_connectivity_state grpc_connectivity_state_get(
+    grpc_connectivity_state_tracker* tracker, grpc_error** error) {
+  grpc_connectivity_state cur = static_cast<grpc_connectivity_state>(
+      gpr_atm_no_barrier_load(&tracker->current_state_atm));
+  if (grpc_connectivity_state_trace.enabled()) {
+    gpr_log(GPR_INFO, "CONWATCH: %p %s: get %s", tracker, tracker->name,
+            grpc_connectivity_state_name(cur));
+  }
+  if (error != nullptr) {
+    *error = GRPC_ERROR_REF(tracker->current_error);
+  }
+  return cur;
+}
+
+bool grpc_connectivity_state_has_watchers(
+    grpc_connectivity_state_tracker* connectivity_state) {
+  return connectivity_state->watchers != nullptr;
+}
+
+bool grpc_connectivity_state_notify_on_state_change(
+    grpc_connectivity_state_tracker* tracker, grpc_connectivity_state* current,
+    grpc_closure* notify) {
+  grpc_connectivity_state cur = static_cast<grpc_connectivity_state>(
+      gpr_atm_no_barrier_load(&tracker->current_state_atm));
+  if (grpc_connectivity_state_trace.enabled()) {
+    if (current == nullptr) {
+      gpr_log(GPR_INFO, "CONWATCH: %p %s: unsubscribe notify=%p", tracker,
+              tracker->name, notify);
+    } else {
+      gpr_log(GPR_INFO, "CONWATCH: %p %s: from %s [cur=%s] notify=%p", tracker,
+              tracker->name, grpc_connectivity_state_name(*current),
+              grpc_connectivity_state_name(cur), notify);
+    }
+  }
+  if (current == nullptr) {
+    grpc_connectivity_state_watcher* w = tracker->watchers;
+    if (w != nullptr && w->notify == notify) {
+      GRPC_CLOSURE_SCHED(notify, GRPC_ERROR_CANCELLED);
+      tracker->watchers = w->next;
+      gpr_free(w);
+      return false;
+    }
+    while (w != nullptr) {
+      grpc_connectivity_state_watcher* rm_candidate = w->next;
+      if (rm_candidate != nullptr && rm_candidate->notify == notify) {
+        GRPC_CLOSURE_SCHED(notify, GRPC_ERROR_CANCELLED);
+        w->next = w->next->next;
+        gpr_free(rm_candidate);
+        return false;
+      }
+      w = w->next;
+    }
+    return false;
+  } else {
+    if (cur != *current) {
+      *current = cur;
+      GRPC_CLOSURE_SCHED(notify, GRPC_ERROR_REF(tracker->current_error));
+    } else {
+      grpc_connectivity_state_watcher* w =
+          static_cast<grpc_connectivity_state_watcher*>(gpr_malloc(sizeof(*w)));
+      w->current = current;
+      w->notify = notify;
+      w->next = tracker->watchers;
+      tracker->watchers = w;
+    }
+    return cur == GRPC_CHANNEL_IDLE;
+  }
+}
+
+void grpc_connectivity_state_set(grpc_connectivity_state_tracker* tracker,
+                                 grpc_connectivity_state state,
+                                 grpc_error* error, const char* reason) {
+  grpc_connectivity_state cur = static_cast<grpc_connectivity_state>(
+      gpr_atm_no_barrier_load(&tracker->current_state_atm));
+  grpc_connectivity_state_watcher* w;
+  if (grpc_connectivity_state_trace.enabled()) {
+    const char* error_string = grpc_error_string(error);
+    gpr_log(GPR_INFO, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker,
+            tracker->name, grpc_connectivity_state_name(cur),
+            grpc_connectivity_state_name(state), reason, error, error_string);
+  }
+  switch (state) {
+    case GRPC_CHANNEL_CONNECTING:
+    case GRPC_CHANNEL_IDLE:
+    case GRPC_CHANNEL_READY:
+      GPR_ASSERT(error == GRPC_ERROR_NONE);
+      break;
+    case GRPC_CHANNEL_SHUTDOWN:
+    case GRPC_CHANNEL_TRANSIENT_FAILURE:
+      GPR_ASSERT(error != GRPC_ERROR_NONE);
+      break;
+  }
+  GRPC_ERROR_UNREF(tracker->current_error);
+  tracker->current_error = error;
+  if (cur == state) {
+    return;
+  }
+  GPR_ASSERT(cur != GRPC_CHANNEL_SHUTDOWN);
+  gpr_atm_no_barrier_store(&tracker->current_state_atm, state);
+  while ((w = tracker->watchers) != nullptr) {
+    *w->current = state;
+    tracker->watchers = w->next;
+    if (grpc_connectivity_state_trace.enabled()) {
+      gpr_log(GPR_INFO, "NOTIFY: %p %s: %p", tracker, tracker->name, w->notify);
+    }
+    GRPC_CLOSURE_SCHED(w->notify, GRPC_ERROR_REF(tracker->current_error));
+    gpr_free(w);
+  }
+}
diff --git a/third_party/grpc/src/core/lib/transport/connectivity_state.h b/third_party/grpc/src/core/lib/transport/connectivity_state.h
new file mode 100644
index 0000000..ecb083c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/connectivity_state.h
@@ -0,0 +1,87 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H
+#define GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/closure.h"
+
+typedef struct grpc_connectivity_state_watcher {
+  /** we keep watchers in a linked list */
+  struct grpc_connectivity_state_watcher* next;
+  /** closure to notify on change */
+  grpc_closure* notify;
+  /** the current state as believed by the watcher */
+  grpc_connectivity_state* current;
+} grpc_connectivity_state_watcher;
+
+typedef struct {
+  /** current grpc_connectivity_state */
+  gpr_atm current_state_atm;
+  /** error associated with state */
+  grpc_error* current_error;
+  /** all our watchers */
+  grpc_connectivity_state_watcher* watchers;
+  /** a name to help debugging */
+  char* name;
+} grpc_connectivity_state_tracker;
+
+extern grpc_core::TraceFlag grpc_connectivity_state_trace;
+
+/** enum --> string conversion */
+const char* grpc_connectivity_state_name(grpc_connectivity_state state);
+
+void grpc_connectivity_state_init(grpc_connectivity_state_tracker* tracker,
+                                  grpc_connectivity_state init_state,
+                                  const char* name);
+void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker* tracker);
+
+/** Set connectivity state; not thread safe; access must be serialized with an
+ *  external lock */
+void grpc_connectivity_state_set(grpc_connectivity_state_tracker* tracker,
+                                 grpc_connectivity_state state,
+                                 grpc_error* associated_error,
+                                 const char* reason);
+
+/** Return true if this connectivity state has watchers.
+    Access must be serialized with an external lock. */
+bool grpc_connectivity_state_has_watchers(
+    grpc_connectivity_state_tracker* tracker);
+
+/** Return the last seen connectivity state. No need to synchronize access. */
+grpc_connectivity_state grpc_connectivity_state_check(
+    grpc_connectivity_state_tracker* tracker);
+
+/** Return the last seen connectivity state, and the associated error.
+    Access must be serialized with an external lock. */
+grpc_connectivity_state grpc_connectivity_state_get(
+    grpc_connectivity_state_tracker* tracker, grpc_error** error);
+
+/** Return 1 if the channel should start connecting, 0 otherwise.
+    If current==NULL cancel notify if it is already queued (success==0 in that
+    case).
+    Access must be serialized with an external lock. */
+bool grpc_connectivity_state_notify_on_state_change(
+    grpc_connectivity_state_tracker* tracker, grpc_connectivity_state* current,
+    grpc_closure* notify);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H */
diff --git a/third_party/grpc/src/core/lib/transport/error_utils.cc b/third_party/grpc/src/core/lib/transport/error_utils.cc
new file mode 100644
index 0000000..558f1d4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/error_utils.cc
@@ -0,0 +1,120 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/error_utils.h"
+
+#include <grpc/support/string_util.h>
+#include "src/core/lib/iomgr/error_internal.h"
+#include "src/core/lib/transport/status_conversion.h"
+
+static grpc_error* recursively_find_error_with_field(grpc_error* error,
+                                                     grpc_error_ints which) {
+  intptr_t unused;
+  // If the error itself has a status code, return it.
+  if (grpc_error_get_int(error, which, &unused)) {
+    return error;
+  }
+  if (grpc_error_is_special(error)) return nullptr;
+  // Otherwise, search through its children.
+  uint8_t slot = error->first_err;
+  while (slot != UINT8_MAX) {
+    grpc_linked_error* lerr =
+        reinterpret_cast<grpc_linked_error*>(error->arena + slot);
+    grpc_error* result = recursively_find_error_with_field(lerr->err, which);
+    if (result) return result;
+    slot = lerr->next;
+  }
+  return nullptr;
+}
+
+void grpc_error_get_status(grpc_error* error, grpc_millis deadline,
+                           grpc_status_code* code, grpc_slice* slice,
+                           grpc_http2_error_code* http_error,
+                           const char** error_string) {
+  // Start with the parent error and recurse through the tree of children
+  // until we find the first one that has a status code.
+  grpc_error* found_error =
+      recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS);
+  if (found_error == nullptr) {
+    /// If no grpc-status exists, retry through the tree to find a http2 error
+    /// code
+    found_error =
+        recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR);
+  }
+
+  // If we found an error with a status code above, use that; otherwise,
+  // fall back to using the parent error.
+  if (found_error == nullptr) found_error = error;
+
+  grpc_status_code status = GRPC_STATUS_UNKNOWN;
+  intptr_t integer;
+  if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) {
+    status = static_cast<grpc_status_code>(integer);
+  } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR,
+                                &integer)) {
+    status = grpc_http2_error_to_grpc_status(
+        static_cast<grpc_http2_error_code>(integer), deadline);
+  }
+  if (code != nullptr) *code = status;
+
+  if (error_string != nullptr && status != GRPC_STATUS_OK) {
+    *error_string = gpr_strdup(grpc_error_string(error));
+  }
+
+  if (http_error != nullptr) {
+    if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) {
+      *http_error = static_cast<grpc_http2_error_code>(integer);
+    } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS,
+                                  &integer)) {
+      *http_error =
+          grpc_status_to_http2_error(static_cast<grpc_status_code>(integer));
+    } else {
+      *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR
+                                                   : GRPC_HTTP2_INTERNAL_ERROR;
+    }
+  }
+
+  // If the error has a status message, use it.  Otherwise, fall back to
+  // the error description.
+  if (slice != nullptr) {
+    if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE, slice)) {
+      if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION, slice)) {
+        *slice = grpc_slice_from_static_string("unknown error");
+      }
+    }
+  }
+}
+
+bool grpc_error_has_clear_grpc_status(grpc_error* error) {
+  intptr_t unused;
+  if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &unused)) {
+    return true;
+  }
+  uint8_t slot = error->first_err;
+  while (slot != UINT8_MAX) {
+    grpc_linked_error* lerr =
+        reinterpret_cast<grpc_linked_error*>(error->arena + slot);
+    if (grpc_error_has_clear_grpc_status(lerr->err)) {
+      return true;
+    }
+    slot = lerr->next;
+  }
+  return false;
+}
diff --git a/third_party/grpc/src/core/lib/transport/error_utils.h b/third_party/grpc/src/core/lib/transport/error_utils.h
new file mode 100644
index 0000000..9a46267
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/error_utils.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H
+#define GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+/// 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.
+/// All attributes are pulled from the same child error. error_string will
+/// be populated with the entire error string. If any of the attributes (code,
+/// msg, http_status, error_string) are unneeded, they can be passed as
+/// NULL.
+void grpc_error_get_status(grpc_error* error, grpc_millis deadline,
+                           grpc_status_code* code, grpc_slice* slice,
+                           grpc_http2_error_code* http_status,
+                           const char** error_string);
+
+/// A utility function to check whether there is a clear status code that
+/// doesn't need to be guessed in \a error. This means that \a error or some
+/// child has GRPC_ERROR_INT_GRPC_STATUS set, or that it is GRPC_ERROR_NONE or
+/// GRPC_ERROR_CANCELLED
+bool grpc_error_has_clear_grpc_status(grpc_error* error);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H */
diff --git a/third_party/grpc/src/core/lib/transport/http2_errors.h b/third_party/grpc/src/core/lib/transport/http2_errors.h
new file mode 100644
index 0000000..d412c5d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/http2_errors.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_HTTP2_ERRORS_H
+#define GRPC_CORE_LIB_TRANSPORT_HTTP2_ERRORS_H
+
+/* error codes for RST_STREAM from http2 draft 14 section 7 */
+typedef enum {
+  GRPC_HTTP2_NO_ERROR = 0x0,
+  GRPC_HTTP2_PROTOCOL_ERROR = 0x1,
+  GRPC_HTTP2_INTERNAL_ERROR = 0x2,
+  GRPC_HTTP2_FLOW_CONTROL_ERROR = 0x3,
+  GRPC_HTTP2_SETTINGS_TIMEOUT = 0x4,
+  GRPC_HTTP2_STREAM_CLOSED = 0x5,
+  GRPC_HTTP2_FRAME_SIZE_ERROR = 0x6,
+  GRPC_HTTP2_REFUSED_STREAM = 0x7,
+  GRPC_HTTP2_CANCEL = 0x8,
+  GRPC_HTTP2_COMPRESSION_ERROR = 0x9,
+  GRPC_HTTP2_CONNECT_ERROR = 0xa,
+  GRPC_HTTP2_ENHANCE_YOUR_CALM = 0xb,
+  GRPC_HTTP2_INADEQUATE_SECURITY = 0xc,
+  /* force use of a default clause */
+  GRPC_HTTP2__ERROR_DO_NOT_USE = -1
+} grpc_http2_error_code;
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_HTTP2_ERRORS_H */
diff --git a/third_party/grpc/src/core/lib/transport/metadata.cc b/third_party/grpc/src/core/lib/transport/metadata.cc
new file mode 100644
index 0000000..30482a1
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/metadata.cc
@@ -0,0 +1,523 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/metadata.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gpr/murmur_hash.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+/* There are two kinds of mdelem and mdstr instances.
+ * Static instances are declared in static_metadata.{h,c} and
+ * are initialized by grpc_mdctx_global_init().
+ * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
+ * by internal_string and internal_element structures.
+ * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
+ * used to determine which kind of element a pointer refers to.
+ */
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata");
+
+#ifndef NDEBUG
+#define DEBUG_ARGS , const char *file, int line
+#define FWD_DEBUG_ARGS , file, line
+#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
+#else
+#define DEBUG_ARGS
+#define FWD_DEBUG_ARGS
+#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
+#endif
+
+#define INITIAL_SHARD_CAPACITY 8
+#define LOG2_SHARD_COUNT 4
+#define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT))
+
+#define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity))
+#define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1))
+
+typedef void (*destroy_user_data_func)(void* user_data);
+
+/* Shadow structure for grpc_mdelem_data for interned elements */
+typedef struct interned_metadata {
+  /* must be byte compatible with grpc_mdelem_data */
+  grpc_slice key;
+  grpc_slice value;
+
+  /* private only data */
+  gpr_atm refcnt;
+
+  gpr_mu mu_user_data;
+  gpr_atm destroy_user_data;
+  gpr_atm user_data;
+
+  struct interned_metadata* bucket_next;
+} interned_metadata;
+
+/* Shadow structure for grpc_mdelem_data for allocated elements */
+typedef struct allocated_metadata {
+  /* must be byte compatible with grpc_mdelem_data */
+  grpc_slice key;
+  grpc_slice value;
+
+  /* private only data */
+  gpr_atm refcnt;
+} allocated_metadata;
+
+typedef struct mdtab_shard {
+  gpr_mu mu;
+  interned_metadata** elems;
+  size_t count;
+  size_t capacity;
+  /** Estimate of the number of unreferenced mdelems in the hash table.
+      This will eventually converge to the exact number, but it's instantaneous
+      accuracy is not guaranteed */
+  gpr_atm free_estimate;
+} mdtab_shard;
+
+static mdtab_shard g_shards[SHARD_COUNT];
+
+static void gc_mdtab(mdtab_shard* shard);
+
+void grpc_mdctx_global_init(void) {
+  /* initialize shards */
+  for (size_t i = 0; i < SHARD_COUNT; i++) {
+    mdtab_shard* shard = &g_shards[i];
+    gpr_mu_init(&shard->mu);
+    shard->count = 0;
+    gpr_atm_no_barrier_store(&shard->free_estimate, 0);
+    shard->capacity = INITIAL_SHARD_CAPACITY;
+    shard->elems = static_cast<interned_metadata**>(
+        gpr_zalloc(sizeof(*shard->elems) * shard->capacity));
+  }
+}
+
+void grpc_mdctx_global_shutdown() {
+  for (size_t i = 0; i < SHARD_COUNT; i++) {
+    mdtab_shard* shard = &g_shards[i];
+    gpr_mu_destroy(&shard->mu);
+    gc_mdtab(shard);
+    /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
+    if (shard->count != 0) {
+      gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked",
+              shard->count);
+      if (grpc_iomgr_abort_on_leaks()) {
+        abort();
+      }
+    }
+    gpr_free(shard->elems);
+  }
+}
+
+static int is_mdelem_static(grpc_mdelem e) {
+  return GRPC_MDELEM_DATA(e) >= &grpc_static_mdelem_table[0] &&
+         GRPC_MDELEM_DATA(e) <
+             &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+}
+
+static void ref_md_locked(mdtab_shard* shard,
+                          interned_metadata* md DEBUG_ARGS) {
+#ifndef NDEBUG
+  if (grpc_trace_metadata.enabled()) {
+    char* key_str = grpc_slice_to_c_string(md->key);
+    char* value_str = grpc_slice_to_c_string(md->value);
+    gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+            "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", (void*)md,
+            gpr_atm_no_barrier_load(&md->refcnt),
+            gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
+    gpr_free(key_str);
+    gpr_free(value_str);
+  }
+#endif
+  if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
+    gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
+  }
+}
+
+static void gc_mdtab(mdtab_shard* shard) {
+  GPR_TIMER_SCOPE("gc_mdtab", 0);
+
+  size_t i;
+  interned_metadata** prev_next;
+  interned_metadata *md, *next;
+  gpr_atm num_freed = 0;
+
+  for (i = 0; i < shard->capacity; i++) {
+    prev_next = &shard->elems[i];
+    for (md = shard->elems[i]; md; md = next) {
+      void* user_data = (void*)gpr_atm_no_barrier_load(&md->user_data);
+      next = md->bucket_next;
+      if (gpr_atm_acq_load(&md->refcnt) == 0) {
+        grpc_slice_unref_internal(md->key);
+        grpc_slice_unref_internal(md->value);
+        if (md->user_data) {
+          ((destroy_user_data_func)gpr_atm_no_barrier_load(
+              &md->destroy_user_data))(user_data);
+        }
+        gpr_mu_destroy(&md->mu_user_data);
+        gpr_free(md);
+        *prev_next = next;
+        num_freed++;
+        shard->count--;
+      } else {
+        prev_next = &md->bucket_next;
+      }
+    }
+  }
+  gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
+}
+
+static void grow_mdtab(mdtab_shard* shard) {
+  GPR_TIMER_SCOPE("grow_mdtab", 0);
+
+  size_t capacity = shard->capacity * 2;
+  size_t i;
+  interned_metadata** mdtab;
+  interned_metadata *md, *next;
+  uint32_t hash;
+
+  mdtab = static_cast<interned_metadata**>(
+      gpr_zalloc(sizeof(interned_metadata*) * capacity));
+
+  for (i = 0; i < shard->capacity; i++) {
+    for (md = shard->elems[i]; md; md = next) {
+      size_t idx;
+      hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
+                                grpc_slice_hash(md->value));
+      next = md->bucket_next;
+      idx = TABLE_IDX(hash, capacity);
+      md->bucket_next = mdtab[idx];
+      mdtab[idx] = md;
+    }
+  }
+  gpr_free(shard->elems);
+  shard->elems = mdtab;
+  shard->capacity = capacity;
+}
+
+static void rehash_mdtab(mdtab_shard* shard) {
+  if (gpr_atm_no_barrier_load(&shard->free_estimate) >
+      static_cast<gpr_atm>(shard->capacity / 4)) {
+    gc_mdtab(shard);
+  } else {
+    grow_mdtab(shard);
+  }
+}
+
+grpc_mdelem grpc_mdelem_create(
+    const grpc_slice& key, const grpc_slice& value,
+    grpc_mdelem_data* compatible_external_backing_store) {
+  if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) {
+    if (compatible_external_backing_store != nullptr) {
+      return GRPC_MAKE_MDELEM(compatible_external_backing_store,
+                              GRPC_MDELEM_STORAGE_EXTERNAL);
+    }
+
+    allocated_metadata* allocated =
+        static_cast<allocated_metadata*>(gpr_malloc(sizeof(*allocated)));
+    allocated->key = grpc_slice_ref_internal(key);
+    allocated->value = grpc_slice_ref_internal(value);
+    gpr_atm_rel_store(&allocated->refcnt, 1);
+#ifndef NDEBUG
+    if (grpc_trace_metadata.enabled()) {
+      char* key_str = grpc_slice_to_c_string(allocated->key);
+      char* value_str = grpc_slice_to_c_string(allocated->value);
+      gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'",
+              (void*)allocated, gpr_atm_no_barrier_load(&allocated->refcnt),
+              key_str, value_str);
+      gpr_free(key_str);
+      gpr_free(value_str);
+    }
+#endif
+    return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED);
+  }
+
+  if (GRPC_IS_STATIC_METADATA_STRING(key) &&
+      GRPC_IS_STATIC_METADATA_STRING(value)) {
+    grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings(
+        GRPC_STATIC_METADATA_INDEX(key), GRPC_STATIC_METADATA_INDEX(value));
+    if (!GRPC_MDISNULL(static_elem)) {
+      return static_elem;
+    }
+  }
+
+  uint32_t hash =
+      GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value));
+  interned_metadata* md;
+  mdtab_shard* shard = &g_shards[SHARD_IDX(hash)];
+  size_t idx;
+
+  GPR_TIMER_SCOPE("grpc_mdelem_from_metadata_strings", 0);
+
+  gpr_mu_lock(&shard->mu);
+
+  idx = TABLE_IDX(hash, shard->capacity);
+  /* search for an existing pair */
+  for (md = shard->elems[idx]; md; md = md->bucket_next) {
+    if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) {
+      REF_MD_LOCKED(shard, md);
+      gpr_mu_unlock(&shard->mu);
+      return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
+    }
+  }
+
+  /* not found: create a new pair */
+  md = static_cast<interned_metadata*>(gpr_malloc(sizeof(interned_metadata)));
+  gpr_atm_rel_store(&md->refcnt, 1);
+  md->key = grpc_slice_ref_internal(key);
+  md->value = grpc_slice_ref_internal(value);
+  md->user_data = 0;
+  md->destroy_user_data = 0;
+  md->bucket_next = shard->elems[idx];
+  shard->elems[idx] = md;
+  gpr_mu_init(&md->mu_user_data);
+#ifndef NDEBUG
+  if (grpc_trace_metadata.enabled()) {
+    char* key_str = grpc_slice_to_c_string(md->key);
+    char* value_str = grpc_slice_to_c_string(md->value);
+    gpr_log(GPR_DEBUG, "ELM   NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void*)md,
+            gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str);
+    gpr_free(key_str);
+    gpr_free(value_str);
+  }
+#endif
+  shard->count++;
+
+  if (shard->count > shard->capacity * 2) {
+    rehash_mdtab(shard);
+  }
+
+  gpr_mu_unlock(&shard->mu);
+
+  return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
+}
+
+grpc_mdelem grpc_mdelem_from_slices(const grpc_slice& key,
+                                    const grpc_slice& value) {
+  grpc_mdelem out = grpc_mdelem_create(key, value, nullptr);
+  grpc_slice_unref_internal(key);
+  grpc_slice_unref_internal(value);
+  return out;
+}
+
+grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata) {
+  bool changed = false;
+  grpc_slice key_slice =
+      grpc_slice_maybe_static_intern(metadata->key, &changed);
+  grpc_slice value_slice =
+      grpc_slice_maybe_static_intern(metadata->value, &changed);
+  return grpc_mdelem_create(
+      key_slice, value_slice,
+      changed ? nullptr : reinterpret_cast<grpc_mdelem_data*>(metadata));
+}
+
+grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) {
+  switch (GRPC_MDELEM_STORAGE(gmd)) {
+    case GRPC_MDELEM_STORAGE_EXTERNAL:
+    case GRPC_MDELEM_STORAGE_STATIC:
+      break;
+    case GRPC_MDELEM_STORAGE_INTERNED: {
+      interned_metadata* md =
+          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(gmd);
+#ifndef NDEBUG
+      if (grpc_trace_metadata.enabled()) {
+        char* key_str = grpc_slice_to_c_string(md->key);
+        char* value_str = grpc_slice_to_c_string(md->value);
+        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+                "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
+                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
+                gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
+        gpr_free(key_str);
+        gpr_free(value_str);
+      }
+#endif
+      /* we can assume the ref count is >= 1 as the application is calling
+         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 */
+      GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
+      gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
+      break;
+    }
+    case GRPC_MDELEM_STORAGE_ALLOCATED: {
+      allocated_metadata* md =
+          reinterpret_cast<allocated_metadata*> GRPC_MDELEM_DATA(gmd);
+#ifndef NDEBUG
+      if (grpc_trace_metadata.enabled()) {
+        char* key_str = grpc_slice_to_c_string(md->key);
+        char* value_str = grpc_slice_to_c_string(md->value);
+        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+                "ELM   REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
+                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
+                gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str);
+        gpr_free(key_str);
+        gpr_free(value_str);
+      }
+#endif
+      /* we can assume the ref count is >= 1 as the application is calling
+         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 */
+      gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
+      break;
+    }
+  }
+  return gmd;
+}
+
+void grpc_mdelem_unref(grpc_mdelem gmd DEBUG_ARGS) {
+  switch (GRPC_MDELEM_STORAGE(gmd)) {
+    case GRPC_MDELEM_STORAGE_EXTERNAL:
+    case GRPC_MDELEM_STORAGE_STATIC:
+      break;
+    case GRPC_MDELEM_STORAGE_INTERNED: {
+      interned_metadata* md =
+          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(gmd);
+#ifndef NDEBUG
+      if (grpc_trace_metadata.enabled()) {
+        char* key_str = grpc_slice_to_c_string(md->key);
+        char* value_str = grpc_slice_to_c_string(md->value);
+        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+                "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
+                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
+                gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
+        gpr_free(key_str);
+        gpr_free(value_str);
+      }
+#endif
+      uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
+                                         grpc_slice_hash(md->value));
+      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 = &g_shards[SHARD_IDX(hash)];
+        gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
+      }
+      break;
+    }
+    case GRPC_MDELEM_STORAGE_ALLOCATED: {
+      allocated_metadata* md =
+          reinterpret_cast<allocated_metadata*> GRPC_MDELEM_DATA(gmd);
+#ifndef NDEBUG
+      if (grpc_trace_metadata.enabled()) {
+        char* key_str = grpc_slice_to_c_string(md->key);
+        char* value_str = grpc_slice_to_c_string(md->value);
+        gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
+                "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'",
+                (void*)md, gpr_atm_no_barrier_load(&md->refcnt),
+                gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str);
+        gpr_free(key_str);
+        gpr_free(value_str);
+      }
+#endif
+      const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1);
+      GPR_ASSERT(prev_refcount >= 1);
+      if (1 == prev_refcount) {
+        grpc_slice_unref_internal(md->key);
+        grpc_slice_unref_internal(md->value);
+        gpr_free(md);
+      }
+      break;
+    }
+  }
+}
+
+void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void*)) {
+  switch (GRPC_MDELEM_STORAGE(md)) {
+    case GRPC_MDELEM_STORAGE_EXTERNAL:
+    case GRPC_MDELEM_STORAGE_ALLOCATED:
+      return nullptr;
+    case GRPC_MDELEM_STORAGE_STATIC:
+      return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
+                                                 grpc_static_mdelem_table];
+    case GRPC_MDELEM_STORAGE_INTERNED: {
+      interned_metadata* im =
+          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(md);
+      void* result;
+      if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
+        return (void*)gpr_atm_no_barrier_load(&im->user_data);
+      } else {
+        return nullptr;
+      }
+      return result;
+    }
+  }
+  GPR_UNREACHABLE_CODE(return nullptr);
+}
+
+void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
+                                void* user_data) {
+  switch (GRPC_MDELEM_STORAGE(md)) {
+    case GRPC_MDELEM_STORAGE_EXTERNAL:
+    case GRPC_MDELEM_STORAGE_ALLOCATED:
+      destroy_func(user_data);
+      return nullptr;
+    case GRPC_MDELEM_STORAGE_STATIC:
+      destroy_func(user_data);
+      return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) -
+                                                 grpc_static_mdelem_table];
+    case GRPC_MDELEM_STORAGE_INTERNED: {
+      interned_metadata* im =
+          reinterpret_cast<interned_metadata*> GRPC_MDELEM_DATA(md);
+      GPR_ASSERT(!is_mdelem_static(md));
+      GPR_ASSERT((user_data == nullptr) == (destroy_func == nullptr));
+      gpr_mu_lock(&im->mu_user_data);
+      if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
+        /* user data can only be set once */
+        gpr_mu_unlock(&im->mu_user_data);
+        if (destroy_func != nullptr) {
+          destroy_func(user_data);
+        }
+        return (void*)gpr_atm_no_barrier_load(&im->user_data);
+      }
+      gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data);
+      gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func);
+      gpr_mu_unlock(&im->mu_user_data);
+      return user_data;
+    }
+  }
+  GPR_UNREACHABLE_CODE(return nullptr);
+}
+
+bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) {
+  if (a.payload == b.payload) return true;
+  if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false;
+  if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false;
+  return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) &&
+         grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b));
+}
diff --git a/third_party/grpc/src/core/lib/transport/metadata.h b/third_party/grpc/src/core/lib/transport/metadata.h
new file mode 100644
index 0000000..989c754
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/metadata.h
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_H
+#define GRPC_CORE_LIB_TRANSPORT_METADATA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/slice.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gpr/useful.h"
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_metadata;
+
+/* This file provides a mechanism for tracking metadata through the grpc stack.
+   It's not intended for consumption outside of the library.
+
+   Metadata is tracked in the context of a grpc_mdctx. For the time being there
+   is one of these per-channel, avoiding cross channel interference with memory
+   use and lock contention.
+
+   The context tracks unique strings (grpc_mdstr) and pairs of strings
+   (grpc_mdelem). Any of these objects can be checked for equality by comparing
+   their pointers. These objects are reference counted.
+
+   grpc_mdelem can additionally store a (non-NULL) user data pointer. This
+   pointer is intended to be used to cache semantic meaning of a metadata
+   element. For example, an OAuth token may cache the credentials it represents
+   and the time at which it expires in the mdelem user data.
+
+   Combining this metadata cache and the hpack compression table allows us to
+   simply lookup complete preparsed objects quickly, incurring a few atomic
+   ops per metadata element on the fast path.
+
+   grpc_mdelem instances MAY live longer than their refcount implies, and are
+   garbage collected periodically, meaning cached data can easily outlive a
+   single request.
+
+   STATIC METADATA: in static_metadata.h we declare a set of static metadata.
+   These mdelems and mdstrs are available via pre-declared code generated macros
+   and are available to code anywhere between grpc_init() and grpc_shutdown().
+   They are not refcounted, but can be passed to _ref and _unref functions
+   declared here - in which case those functions are effectively no-ops. */
+
+/* Forward declarations */
+typedef struct grpc_mdelem grpc_mdelem;
+
+/* if changing this, make identical changes in:
+   - interned_metadata, allocated_metadata in metadata.c
+   - grpc_metadata in grpc_types.h */
+typedef struct grpc_mdelem_data {
+  const grpc_slice key;
+  const grpc_slice value;
+  /* there is a private part to this in metadata.c */
+} grpc_mdelem_data;
+
+/* GRPC_MDELEM_STORAGE_* enum values that can be treated as interned always have
+   this bit set in their integer value */
+#define GRPC_MDELEM_STORAGE_INTERNED_BIT 1
+
+typedef enum {
+  /* memory pointed to by grpc_mdelem::payload is owned by an external system */
+  GRPC_MDELEM_STORAGE_EXTERNAL = 0,
+  /* memory pointed to by grpc_mdelem::payload is interned by the metadata
+     system */
+  GRPC_MDELEM_STORAGE_INTERNED = GRPC_MDELEM_STORAGE_INTERNED_BIT,
+  /* memory pointed to by grpc_mdelem::payload is allocated by the metadata
+     system */
+  GRPC_MDELEM_STORAGE_ALLOCATED = 2,
+  /* memory is in the static metadata table */
+  GRPC_MDELEM_STORAGE_STATIC = 2 | GRPC_MDELEM_STORAGE_INTERNED_BIT,
+} grpc_mdelem_data_storage;
+
+struct grpc_mdelem {
+  /* a grpc_mdelem_data* generally, with the two lower bits signalling memory
+     ownership as per grpc_mdelem_data_storage */
+  uintptr_t payload;
+};
+
+#define GRPC_MDELEM_DATA(md) ((grpc_mdelem_data*)((md).payload & ~(uintptr_t)3))
+#define GRPC_MDELEM_STORAGE(md) \
+  ((grpc_mdelem_data_storage)((md).payload & (uintptr_t)3))
+#ifdef __cplusplus
+#define GRPC_MAKE_MDELEM(data, storage) \
+  (grpc_mdelem{((uintptr_t)(data)) | ((uintptr_t)storage)})
+#else
+#define GRPC_MAKE_MDELEM(data, storage) \
+  ((grpc_mdelem){((uintptr_t)(data)) | ((uintptr_t)storage)})
+#endif
+#define GRPC_MDELEM_IS_INTERNED(md)          \
+  ((grpc_mdelem_data_storage)((md).payload & \
+                              (uintptr_t)GRPC_MDELEM_STORAGE_INTERNED_BIT))
+
+/* Unrefs the slices. */
+grpc_mdelem grpc_mdelem_from_slices(const grpc_slice& key,
+                                    const grpc_slice& value);
+
+/* Cheaply convert a grpc_metadata to a grpc_mdelem; may use the grpc_metadata
+   object as backing storage (so lifetimes should align) */
+grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata);
+
+/* Does not unref the slices; if a new non-interned mdelem is needed, allocates
+   one if compatible_external_backing_store is NULL, or uses
+   compatible_external_backing_store if it is non-NULL (in which case it's the
+   users responsibility to ensure that it outlives usage) */
+grpc_mdelem grpc_mdelem_create(
+    const grpc_slice& key, const grpc_slice& value,
+    grpc_mdelem_data* compatible_external_backing_store);
+
+bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b);
+
+/* Mutator and accessor for grpc_mdelem user data. The destructor function
+   is used as a type tag and is checked during user_data fetch. */
+void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*if_destroy_func)(void*));
+void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
+                                void* user_data);
+
+#ifndef NDEBUG
+#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__)
+#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __LINE__)
+grpc_mdelem grpc_mdelem_ref(grpc_mdelem md, const char* file, int line);
+void grpc_mdelem_unref(grpc_mdelem md, const char* file, int line);
+#else
+#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
+#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s))
+grpc_mdelem grpc_mdelem_ref(grpc_mdelem md);
+void grpc_mdelem_unref(grpc_mdelem md);
+#endif
+
+#define GRPC_MDKEY(md) (GRPC_MDELEM_DATA(md)->key)
+#define GRPC_MDVALUE(md) (GRPC_MDELEM_DATA(md)->value)
+
+#define GRPC_MDNULL GRPC_MAKE_MDELEM(NULL, GRPC_MDELEM_STORAGE_EXTERNAL)
+#define GRPC_MDISNULL(md) (GRPC_MDELEM_DATA(md) == NULL)
+
+/* We add 32 bytes of padding as per RFC-7540 section 6.5.2. */
+#define GRPC_MDELEM_LENGTH(e)                                                  \
+  (GRPC_SLICE_LENGTH(GRPC_MDKEY((e))) + GRPC_SLICE_LENGTH(GRPC_MDVALUE((e))) + \
+   32)
+
+#define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash))
+
+void grpc_mdctx_global_init(void);
+void grpc_mdctx_global_shutdown();
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_H */
diff --git a/third_party/grpc/src/core/lib/transport/metadata_batch.cc b/third_party/grpc/src/core/lib/transport/metadata_batch.cc
new file mode 100644
index 0000000..928ed73
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/metadata_batch.cc
@@ -0,0 +1,330 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/metadata_batch.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+static void assert_valid_list(grpc_mdelem_list* list) {
+#ifndef NDEBUG
+  grpc_linked_mdelem* l;
+
+  GPR_ASSERT((list->head == nullptr) == (list->tail == nullptr));
+  if (!list->head) return;
+  GPR_ASSERT(list->head->prev == nullptr);
+  GPR_ASSERT(list->tail->next == nullptr);
+  GPR_ASSERT((list->head == list->tail) == (list->head->next == nullptr));
+
+  size_t verified_count = 0;
+  for (l = list->head; l; l = l->next) {
+    GPR_ASSERT(!GRPC_MDISNULL(l->md));
+    GPR_ASSERT((l->prev == nullptr) == (l == list->head));
+    GPR_ASSERT((l->next == nullptr) == (l == list->tail));
+    if (l->next) GPR_ASSERT(l->next->prev == l);
+    if (l->prev) GPR_ASSERT(l->prev->next == l);
+    verified_count++;
+  }
+  GPR_ASSERT(list->count == verified_count);
+#endif /* NDEBUG */
+}
+
+static void assert_valid_callouts(grpc_metadata_batch* batch) {
+#ifndef NDEBUG
+  for (grpc_linked_mdelem* l = batch->list.head; l != nullptr; l = l->next) {
+    grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md));
+    grpc_metadata_batch_callouts_index callout_idx =
+        GRPC_BATCH_INDEX_OF(key_interned);
+    if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) {
+      GPR_ASSERT(batch->idx.array[callout_idx] == l);
+    }
+    grpc_slice_unref_internal(key_interned);
+  }
+#endif
+}
+
+#ifndef NDEBUG
+void grpc_metadata_batch_assert_ok(grpc_metadata_batch* batch) {
+  assert_valid_list(&batch->list);
+}
+#endif /* NDEBUG */
+
+void grpc_metadata_batch_init(grpc_metadata_batch* batch) {
+  memset(batch, 0, sizeof(*batch));
+  batch->deadline = GRPC_MILLIS_INF_FUTURE;
+}
+
+void grpc_metadata_batch_destroy(grpc_metadata_batch* batch) {
+  grpc_linked_mdelem* l;
+  for (l = batch->list.head; l; l = l->next) {
+    GRPC_MDELEM_UNREF(l->md);
+  }
+}
+
+grpc_error* grpc_attach_md_to_error(grpc_error* src, grpc_mdelem md) {
+  grpc_error* out = grpc_error_set_str(
+      grpc_error_set_str(src, GRPC_ERROR_STR_KEY,
+                         grpc_slice_ref_internal(GRPC_MDKEY(md))),
+      GRPC_ERROR_STR_VALUE, grpc_slice_ref_internal(GRPC_MDVALUE(md)));
+  return out;
+}
+
+static grpc_error* maybe_link_callout(grpc_metadata_batch* batch,
+                                      grpc_linked_mdelem* storage)
+    GRPC_MUST_USE_RESULT;
+
+static grpc_error* maybe_link_callout(grpc_metadata_batch* batch,
+                                      grpc_linked_mdelem* storage) {
+  grpc_metadata_batch_callouts_index idx =
+      GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
+  if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
+    return GRPC_ERROR_NONE;
+  }
+  if (batch->idx.array[idx] == nullptr) {
+    ++batch->list.default_count;
+    batch->idx.array[idx] = storage;
+    return GRPC_ERROR_NONE;
+  }
+  return grpc_attach_md_to_error(
+      GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"),
+      storage->md);
+}
+
+static void maybe_unlink_callout(grpc_metadata_batch* batch,
+                                 grpc_linked_mdelem* storage) {
+  grpc_metadata_batch_callouts_index idx =
+      GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
+  if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
+    return;
+  }
+  --batch->list.default_count;
+  GPR_ASSERT(batch->idx.array[idx] != nullptr);
+  batch->idx.array[idx] = nullptr;
+}
+
+grpc_error* grpc_metadata_batch_add_head(grpc_metadata_batch* batch,
+                                         grpc_linked_mdelem* storage,
+                                         grpc_mdelem elem_to_add) {
+  GPR_ASSERT(!GRPC_MDISNULL(elem_to_add));
+  storage->md = elem_to_add;
+  return grpc_metadata_batch_link_head(batch, storage);
+}
+
+static void link_head(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
+  assert_valid_list(list);
+  GPR_ASSERT(!GRPC_MDISNULL(storage->md));
+  storage->prev = nullptr;
+  storage->next = list->head;
+  storage->reserved = nullptr;
+  if (list->head != nullptr) {
+    list->head->prev = storage;
+  } else {
+    list->tail = storage;
+  }
+  list->head = storage;
+  list->count++;
+  assert_valid_list(list);
+}
+
+grpc_error* grpc_metadata_batch_link_head(grpc_metadata_batch* batch,
+                                          grpc_linked_mdelem* storage) {
+  assert_valid_callouts(batch);
+  grpc_error* err = maybe_link_callout(batch, storage);
+  if (err != GRPC_ERROR_NONE) {
+    assert_valid_callouts(batch);
+    return err;
+  }
+  link_head(&batch->list, storage);
+  assert_valid_callouts(batch);
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error* grpc_metadata_batch_add_tail(grpc_metadata_batch* batch,
+                                         grpc_linked_mdelem* storage,
+                                         grpc_mdelem elem_to_add) {
+  GPR_ASSERT(!GRPC_MDISNULL(elem_to_add));
+  storage->md = elem_to_add;
+  return grpc_metadata_batch_link_tail(batch, storage);
+}
+
+static void link_tail(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
+  assert_valid_list(list);
+  GPR_ASSERT(!GRPC_MDISNULL(storage->md));
+  storage->prev = list->tail;
+  storage->next = nullptr;
+  storage->reserved = nullptr;
+  if (list->tail != nullptr) {
+    list->tail->next = storage;
+  } else {
+    list->head = storage;
+  }
+  list->tail = storage;
+  list->count++;
+  assert_valid_list(list);
+}
+
+grpc_error* grpc_metadata_batch_link_tail(grpc_metadata_batch* batch,
+                                          grpc_linked_mdelem* storage) {
+  assert_valid_callouts(batch);
+  grpc_error* err = maybe_link_callout(batch, storage);
+  if (err != GRPC_ERROR_NONE) {
+    assert_valid_callouts(batch);
+    return err;
+  }
+  link_tail(&batch->list, storage);
+  assert_valid_callouts(batch);
+  return GRPC_ERROR_NONE;
+}
+
+static void unlink_storage(grpc_mdelem_list* list,
+                           grpc_linked_mdelem* storage) {
+  assert_valid_list(list);
+  if (storage->prev != nullptr) {
+    storage->prev->next = storage->next;
+  } else {
+    list->head = storage->next;
+  }
+  if (storage->next != nullptr) {
+    storage->next->prev = storage->prev;
+  } else {
+    list->tail = storage->prev;
+  }
+  list->count--;
+  assert_valid_list(list);
+}
+
+void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
+                                grpc_linked_mdelem* storage) {
+  assert_valid_callouts(batch);
+  maybe_unlink_callout(batch, storage);
+  unlink_storage(&batch->list, storage);
+  GRPC_MDELEM_UNREF(storage->md);
+  assert_valid_callouts(batch);
+}
+
+void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage,
+                                   grpc_slice value) {
+  grpc_mdelem old_mdelem = storage->md;
+  grpc_mdelem new_mdelem = grpc_mdelem_from_slices(
+      grpc_slice_ref_internal(GRPC_MDKEY(old_mdelem)), value);
+  storage->md = new_mdelem;
+  GRPC_MDELEM_UNREF(old_mdelem);
+}
+
+grpc_error* grpc_metadata_batch_substitute(grpc_metadata_batch* batch,
+                                           grpc_linked_mdelem* storage,
+                                           grpc_mdelem new_mdelem) {
+  assert_valid_callouts(batch);
+  grpc_error* error = GRPC_ERROR_NONE;
+  grpc_mdelem old_mdelem = storage->md;
+  if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) {
+    maybe_unlink_callout(batch, storage);
+    storage->md = new_mdelem;
+    error = maybe_link_callout(batch, storage);
+    if (error != GRPC_ERROR_NONE) {
+      unlink_storage(&batch->list, storage);
+      GRPC_MDELEM_UNREF(storage->md);
+    }
+  } else {
+    storage->md = new_mdelem;
+  }
+  GRPC_MDELEM_UNREF(old_mdelem);
+  assert_valid_callouts(batch);
+  return error;
+}
+
+void grpc_metadata_batch_clear(grpc_metadata_batch* batch) {
+  grpc_metadata_batch_destroy(batch);
+  grpc_metadata_batch_init(batch);
+}
+
+bool grpc_metadata_batch_is_empty(grpc_metadata_batch* batch) {
+  return batch->list.head == nullptr &&
+         batch->deadline == GRPC_MILLIS_INF_FUTURE;
+}
+
+size_t grpc_metadata_batch_size(grpc_metadata_batch* batch) {
+  size_t size = 0;
+  for (grpc_linked_mdelem* elem = batch->list.head; elem != nullptr;
+       elem = elem->next) {
+    size += GRPC_MDELEM_LENGTH(elem->md);
+  }
+  return size;
+}
+
+static void add_error(grpc_error** composite, grpc_error* error,
+                      const char* composite_error_string) {
+  if (error == GRPC_ERROR_NONE) return;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(composite_error_string);
+  }
+  *composite = grpc_error_add_child(*composite, error);
+}
+
+grpc_error* grpc_metadata_batch_filter(grpc_metadata_batch* batch,
+                                       grpc_metadata_batch_filter_func func,
+                                       void* user_data,
+                                       const char* composite_error_string) {
+  grpc_linked_mdelem* l = batch->list.head;
+  grpc_error* error = GRPC_ERROR_NONE;
+  while (l) {
+    grpc_linked_mdelem* next = l->next;
+    grpc_filtered_mdelem new_mdelem = func(user_data, l->md);
+    add_error(&error, new_mdelem.error, composite_error_string);
+    if (GRPC_MDISNULL(new_mdelem.md)) {
+      grpc_metadata_batch_remove(batch, l);
+    } else if (new_mdelem.md.payload != l->md.payload) {
+      grpc_metadata_batch_substitute(batch, l, new_mdelem.md);
+    }
+    l = next;
+  }
+  return error;
+}
+
+void grpc_metadata_batch_copy(grpc_metadata_batch* src,
+                              grpc_metadata_batch* dst,
+                              grpc_linked_mdelem* storage) {
+  grpc_metadata_batch_init(dst);
+  dst->deadline = src->deadline;
+  size_t i = 0;
+  for (grpc_linked_mdelem* elem = src->list.head; elem != nullptr;
+       elem = elem->next) {
+    grpc_error* error = grpc_metadata_batch_add_tail(dst, &storage[i++],
+                                                     GRPC_MDELEM_REF(elem->md));
+    // The only way that grpc_metadata_batch_add_tail() can fail is if
+    // there's a duplicate entry for a callout.  However, that can't be
+    // the case here, because we would not have been allowed to create
+    // a source batch that had that kind of conflict.
+    GPR_ASSERT(error == GRPC_ERROR_NONE);
+  }
+}
+
+void grpc_metadata_batch_move(grpc_metadata_batch* src,
+                              grpc_metadata_batch* dst) {
+  *dst = *src;
+  grpc_metadata_batch_init(src);
+}
diff --git a/third_party/grpc/src/core/lib/transport/metadata_batch.h b/third_party/grpc/src/core/lib/transport/metadata_batch.h
new file mode 100644
index 0000000..f6e8bbf
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/metadata_batch.h
@@ -0,0 +1,154 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H
+#define GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include <grpc/grpc.h>
+#include <grpc/slice.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+typedef struct grpc_linked_mdelem {
+  grpc_linked_mdelem() {}
+
+  grpc_mdelem md;
+  struct grpc_linked_mdelem* next = nullptr;
+  struct grpc_linked_mdelem* prev = nullptr;
+  void* reserved;
+} grpc_linked_mdelem;
+
+typedef struct grpc_mdelem_list {
+  size_t count;
+  size_t default_count;  // Number of default keys.
+  grpc_linked_mdelem* head;
+  grpc_linked_mdelem* tail;
+} grpc_mdelem_list;
+
+typedef struct grpc_metadata_batch {
+  /** Metadata elements in this batch */
+  grpc_mdelem_list list;
+  grpc_metadata_batch_callouts idx;
+  /** Used to calculate grpc-timeout at the point of sending,
+      or GRPC_MILLIS_INF_FUTURE if this batch does not need to send a
+      grpc-timeout */
+  grpc_millis deadline;
+} grpc_metadata_batch;
+
+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);
+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);
+
+/** Remove \a storage from the batch, unreffing the mdelem contained */
+void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
+                                grpc_linked_mdelem* storage);
+
+/** Substitute a new mdelem for an old value */
+grpc_error* grpc_metadata_batch_substitute(grpc_metadata_batch* batch,
+                                           grpc_linked_mdelem* storage,
+                                           grpc_mdelem new_value);
+
+void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage,
+                                   grpc_slice value);
+
+/** Add \a storage to the beginning of \a batch. storage->md is
+    assumed to be valid.
+    \a storage is owned by the caller and must survive for the
+    lifetime of batch. This usually means it should be around
+    for the lifetime of the call. */
+grpc_error* grpc_metadata_batch_link_head(grpc_metadata_batch* batch,
+                                          grpc_linked_mdelem* storage)
+    GRPC_MUST_USE_RESULT;
+
+/** Add \a storage to the end of \a batch. storage->md is
+    assumed to be valid.
+    \a storage is owned by the caller and must survive for the
+    lifetime of batch. This usually means it should be around
+    for the lifetime of the call. */
+grpc_error* grpc_metadata_batch_link_tail(grpc_metadata_batch* batch,
+                                          grpc_linked_mdelem* storage)
+    GRPC_MUST_USE_RESULT;
+
+/** Add \a elem_to_add as the first element in \a batch, using
+    \a storage as backing storage for the linked list element.
+    \a storage is owned by the caller and must survive for the
+    lifetime of batch. This usually means it should be around
+    for the lifetime of the call.
+    Takes ownership of \a elem_to_add */
+grpc_error* grpc_metadata_batch_add_head(
+    grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
+    grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT;
+
+/** Add \a elem_to_add as the last element in \a batch, using
+    \a storage as backing storage for the linked list element.
+    \a storage is owned by the caller and must survive for the
+    lifetime of batch. This usually means it should be around
+    for the lifetime of the call.
+    Takes ownership of \a elem_to_add */
+grpc_error* grpc_metadata_batch_add_tail(
+    grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
+    grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT;
+
+grpc_error* grpc_attach_md_to_error(grpc_error* src, grpc_mdelem md);
+
+typedef struct {
+  grpc_error* error;
+  grpc_mdelem md;
+} grpc_filtered_mdelem;
+
+#define GRPC_FILTERED_ERROR(error) \
+  { (error), GRPC_MDNULL }
+#define GRPC_FILTERED_MDELEM(md) \
+  { GRPC_ERROR_NONE, (md) }
+#define GRPC_FILTERED_REMOVE() \
+  { GRPC_ERROR_NONE, GRPC_MDNULL }
+
+typedef grpc_filtered_mdelem (*grpc_metadata_batch_filter_func)(
+    void* user_data, grpc_mdelem elem);
+grpc_error* grpc_metadata_batch_filter(
+    grpc_metadata_batch* batch, grpc_metadata_batch_filter_func func,
+    void* user_data, const char* composite_error_string) GRPC_MUST_USE_RESULT;
+
+#ifndef NDEBUG
+void grpc_metadata_batch_assert_ok(grpc_metadata_batch* comd);
+#else
+#define grpc_metadata_batch_assert_ok(comd) \
+  do {                                      \
+  } while (0)
+#endif
+
+/// Copies \a src to \a dst.  \a storage must point to an array of
+/// \a grpc_linked_mdelem structs of at least the same size as \a src.
+void grpc_metadata_batch_copy(grpc_metadata_batch* src,
+                              grpc_metadata_batch* dst,
+                              grpc_linked_mdelem* storage);
+
+void grpc_metadata_batch_move(grpc_metadata_batch* src,
+                              grpc_metadata_batch* dst);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H */
diff --git a/third_party/grpc/src/core/lib/transport/pid_controller.cc b/third_party/grpc/src/core/lib/transport/pid_controller.cc
new file mode 100644
index 0000000..dbc98f4
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/pid_controller.cc
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/pid_controller.h"
+
+#include "src/core/lib/gpr/useful.h"
+
+namespace grpc_core {
+
+PidController::PidController(const Args& args)
+    : last_control_value_(args.initial_control_value()), args_(args) {}
+
+double PidController::Update(double error, double dt) {
+  if (dt <= 0) return last_control_value_;
+  /* integrate error using the trapezoid rule */
+  error_integral_ += dt * (last_error_ + error) * 0.5;
+  error_integral_ = GPR_CLAMP(error_integral_, -args_.integral_range(),
+                              args_.integral_range());
+  double diff_error = (error - last_error_) / dt;
+  /* calculate derivative of control value vs time */
+  double dc_dt = args_.gain_p() * error + args_.gain_i() * error_integral_ +
+                 args_.gain_d() * diff_error;
+  /* and perform trapezoidal integration */
+  double new_control_value =
+      last_control_value_ + dt * (last_dc_dt_ + dc_dt) * 0.5;
+  new_control_value = GPR_CLAMP(new_control_value, args_.min_control_value(),
+                                args_.max_control_value());
+  last_error_ = error;
+  last_dc_dt_ = dc_dt;
+  last_control_value_ = new_control_value;
+  return new_control_value;
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/transport/pid_controller.h b/third_party/grpc/src/core/lib/transport/pid_controller.h
new file mode 100644
index 0000000..e26205b
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/pid_controller.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H
+#define GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <limits>
+
+/* \file Simple PID controller.
+   Implements a proportional-integral-derivative controller.
+   Used when we want to iteratively control a variable to converge some other
+   observed value to a 'set-point'.
+   Gains can be set to adjust sensitivity to current error (p), the integral
+   of error (i), and the derivative of error (d). */
+
+namespace grpc_core {
+
+class PidController {
+ public:
+  class Args {
+   public:
+    double gain_p() const { return gain_p_; }
+    double gain_i() const { return gain_i_; }
+    double gain_d() const { return gain_d_; }
+    double initial_control_value() const { return initial_control_value_; }
+    double min_control_value() const { return min_control_value_; }
+    double max_control_value() const { return max_control_value_; }
+    double integral_range() const { return integral_range_; }
+
+    Args& set_gain_p(double gain_p) {
+      gain_p_ = gain_p;
+      return *this;
+    }
+    Args& set_gain_i(double gain_i) {
+      gain_i_ = gain_i;
+      return *this;
+    }
+    Args& set_gain_d(double gain_d) {
+      gain_d_ = gain_d;
+      return *this;
+    }
+    Args& set_initial_control_value(double initial_control_value) {
+      initial_control_value_ = initial_control_value;
+      return *this;
+    }
+    Args& set_min_control_value(double min_control_value) {
+      min_control_value_ = min_control_value;
+      return *this;
+    }
+    Args& set_max_control_value(double max_control_value) {
+      max_control_value_ = max_control_value;
+      return *this;
+    }
+    Args& set_integral_range(double integral_range) {
+      integral_range_ = integral_range;
+      return *this;
+    }
+
+   private:
+    double gain_p_ = 0.0;
+    double gain_i_ = 0.0;
+    double gain_d_ = 0.0;
+    double initial_control_value_ = 0.0;
+    double min_control_value_ = std::numeric_limits<double>::min();
+    double max_control_value_ = std::numeric_limits<double>::max();
+    double integral_range_ = std::numeric_limits<double>::max();
+  };
+
+  explicit PidController(const Args& args);
+
+  /// Reset the controller internal state: useful when the environment has
+  /// changed significantly
+  void Reset() {
+    last_error_ = 0.0;
+    last_dc_dt_ = 0.0;
+    error_integral_ = 0.0;
+  }
+
+  /// Update the controller: given a current error estimate, and the time since
+  /// the last update, returns a new control value
+  double Update(double error, double dt);
+
+  /// Returns the last control value calculated
+  double last_control_value() const { return last_control_value_; }
+
+  /// Returns the current error integral (mostly for testing)
+  double error_integral() const { return error_integral_; }
+
+ private:
+  double last_error_ = 0.0;
+  double error_integral_ = 0.0;
+  double last_control_value_;
+  double last_dc_dt_ = 0.0;
+  const Args args_;
+};
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H */
diff --git a/third_party/grpc/src/core/lib/transport/service_config.cc b/third_party/grpc/src/core/lib/transport/service_config.cc
new file mode 100644
index 0000000..405e336
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/service_config.cc
@@ -0,0 +1,106 @@
+//
+// Copyright 2015 gRPC authors.
+//
+// Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/service_config.h"
+
+#include <string.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/slice/slice_hash_table.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+namespace grpc_core {
+
+UniquePtr<ServiceConfig> ServiceConfig::Create(const char* json) {
+  UniquePtr<char> json_string(gpr_strdup(json));
+  grpc_json* json_tree = grpc_json_parse_string(json_string.get());
+  if (json_tree == nullptr) {
+    gpr_log(GPR_INFO, "failed to parse JSON for service config");
+    return nullptr;
+  }
+  return MakeUnique<ServiceConfig>(std::move(json_string), json_tree);
+}
+
+ServiceConfig::ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree)
+    : json_string_(std::move(json_string)), json_tree_(json_tree) {}
+
+ServiceConfig::~ServiceConfig() { grpc_json_destroy(json_tree_); }
+
+const char* ServiceConfig::GetLoadBalancingPolicyName() const {
+  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
+    return nullptr;
+  }
+  const char* lb_policy_name = nullptr;
+  for (grpc_json* field = json_tree_->child; field != nullptr;
+       field = field->next) {
+    if (field->key == nullptr) return nullptr;
+    if (strcmp(field->key, "loadBalancingPolicy") == 0) {
+      if (lb_policy_name != nullptr) return nullptr;  // Duplicate.
+      if (field->type != GRPC_JSON_STRING) return nullptr;
+      lb_policy_name = field->value;
+    }
+  }
+  return lb_policy_name;
+}
+
+int ServiceConfig::CountNamesInMethodConfig(grpc_json* json) {
+  int num_names = 0;
+  for (grpc_json* field = json->child; field != nullptr; field = field->next) {
+    if (field->key != nullptr && strcmp(field->key, "name") == 0) {
+      if (field->type != GRPC_JSON_ARRAY) return -1;
+      for (grpc_json* name = field->child; name != nullptr; name = name->next) {
+        if (name->type != GRPC_JSON_OBJECT) return -1;
+        ++num_names;
+      }
+    }
+  }
+  return num_names;
+}
+
+UniquePtr<char> ServiceConfig::ParseJsonMethodName(grpc_json* json) {
+  if (json->type != GRPC_JSON_OBJECT) return nullptr;
+  const char* service_name = nullptr;
+  const char* method_name = nullptr;
+  for (grpc_json* child = json->child; child != nullptr; child = child->next) {
+    if (child->key == nullptr) return nullptr;
+    if (child->type != GRPC_JSON_STRING) return nullptr;
+    if (strcmp(child->key, "service") == 0) {
+      if (service_name != nullptr) return nullptr;  // Duplicate.
+      if (child->value == nullptr) return nullptr;
+      service_name = child->value;
+    } else if (strcmp(child->key, "method") == 0) {
+      if (method_name != nullptr) return nullptr;  // Duplicate.
+      if (child->value == nullptr) return nullptr;
+      method_name = child->value;
+    }
+  }
+  if (service_name == nullptr) return nullptr;  // Required field.
+  char* path;
+  gpr_asprintf(&path, "/%s/%s", service_name,
+               method_name == nullptr ? "*" : method_name);
+  return UniquePtr<char>(path);
+}
+
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/lib/transport/service_config.h b/third_party/grpc/src/core/lib/transport/service_config.h
new file mode 100644
index 0000000..2c0dd75
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/service_config.h
@@ -0,0 +1,249 @@
+//
+// Copyright 2016 gRPC authors.
+//
+// Licensed 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.
+//
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H
+#define GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gprpp/inlined_vector.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/slice/slice_hash_table.h"
+
+// The main purpose of the code here is to parse the service config in
+// JSON form, which will look like this:
+//
+// {
+//   "loadBalancingPolicy": "string",  // optional
+//   "methodConfig": [  // array of one or more method_config objects
+//     {
+//       "name": [  // array of one or more name objects
+//         {
+//           "service": "string",  // required
+//           "method": "string",  // optional
+//         }
+//       ],
+//       // remaining fields are optional.
+//       // see
+//       https://developers.google.com/protocol-buffers/docs/proto3#json
+//       // for format details.
+//       "waitForReady": bool,
+//       "timeout": "duration_string",
+//       "maxRequestMessageBytes": "int64_string",
+//       "maxResponseMessageBytes": "int64_string",
+//     }
+//   ]
+// }
+
+namespace grpc_core {
+
+class ServiceConfig {
+ public:
+  /// Creates a new service config from parsing \a json_string.
+  /// Returns null on parse error.
+  static UniquePtr<ServiceConfig> Create(const char* json);
+
+  ~ServiceConfig();
+
+  /// Invokes \a process_json() for each global parameter in the service
+  /// config.  \a arg is passed as the second argument to \a process_json().
+  template <typename T>
+  using ProcessJson = void (*)(const grpc_json*, T*);
+  template <typename T>
+  void ParseGlobalParams(ProcessJson<T> process_json, T* arg) const;
+
+  /// Gets the LB policy name from \a service_config.
+  /// Returns NULL if no LB policy name was specified.
+  /// Caller does NOT take ownership.
+  const char* GetLoadBalancingPolicyName() const;
+
+  /// Creates a method config table based on the data in \a json.
+  /// The table's keys are request paths.  The table's value type is
+  /// returned by \a create_value(), based on data parsed from the JSON tree.
+  /// Returns null on error.
+  template <typename T>
+  using CreateValue = RefCountedPtr<T> (*)(const grpc_json* method_config_json);
+  template <typename T>
+  RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> CreateMethodConfigTable(
+      CreateValue<T> create_value);
+
+  /// A helper function for looking up values in the table returned by
+  /// \a CreateMethodConfigTable().
+  /// Gets the method config for the specified \a path, which should be of
+  /// the form "/service/method".
+  /// Returns null if the method has no config.
+  /// Caller does NOT own a reference to the result.
+  template <typename T>
+  static RefCountedPtr<T> MethodConfigTableLookup(
+      const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path);
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T, typename... Args>
+  friend T* New(Args&&... args);
+
+  // Takes ownership of \a json_tree.
+  ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree);
+
+  // Returns the number of names specified in the method config \a json.
+  static int CountNamesInMethodConfig(grpc_json* json);
+
+  // Returns a path string for the JSON name object specified by \a json.
+  // Returns null on error.
+  static UniquePtr<char> ParseJsonMethodName(grpc_json* json);
+
+  // Parses the method config from \a json.  Adds an entry to \a entries for
+  // each name found, incrementing \a idx for each entry added.
+  // Returns false on error.
+  template <typename T>
+  static bool ParseJsonMethodConfig(
+      grpc_json* json, CreateValue<T> create_value,
+      typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx);
+
+  UniquePtr<char> json_string_;  // Underlying storage for json_tree.
+  grpc_json* json_tree_;
+};
+
+//
+// implementation -- no user-serviceable parts below
+//
+
+template <typename T>
+void ServiceConfig::ParseGlobalParams(ProcessJson<T> process_json,
+                                      T* arg) const {
+  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
+    return;
+  }
+  for (grpc_json* field = json_tree_->child; field != nullptr;
+       field = field->next) {
+    if (field->key == nullptr) return;
+    if (strcmp(field->key, "methodConfig") == 0) continue;
+    process_json(field, arg);
+  }
+}
+
+template <typename T>
+bool ServiceConfig::ParseJsonMethodConfig(
+    grpc_json* json, CreateValue<T> create_value,
+    typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx) {
+  // Construct value.
+  RefCountedPtr<T> method_config = create_value(json);
+  if (method_config == nullptr) return false;
+  // Construct list of paths.
+  InlinedVector<UniquePtr<char>, 10> paths;
+  for (grpc_json* child = json->child; child != nullptr; child = child->next) {
+    if (child->key == nullptr) continue;
+    if (strcmp(child->key, "name") == 0) {
+      if (child->type != GRPC_JSON_ARRAY) return false;
+      for (grpc_json* name = child->child; name != nullptr; name = name->next) {
+        UniquePtr<char> path = ParseJsonMethodName(name);
+        if (path == nullptr) return false;
+        paths.push_back(std::move(path));
+      }
+    }
+  }
+  if (paths.size() == 0) return false;  // No names specified.
+  // Add entry for each path.
+  for (size_t i = 0; i < paths.size(); ++i) {
+    entries[*idx].key = grpc_slice_from_copied_string(paths[i].get());
+    entries[*idx].value = method_config;  // Takes a new ref.
+    ++*idx;
+  }
+  // Success.
+  return true;
+}
+
+template <typename T>
+RefCountedPtr<SliceHashTable<RefCountedPtr<T>>>
+ServiceConfig::CreateMethodConfigTable(CreateValue<T> create_value) {
+  // Traverse parsed JSON tree.
+  if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
+    return nullptr;
+  }
+  size_t num_entries = 0;
+  typename SliceHashTable<RefCountedPtr<T>>::Entry* entries = nullptr;
+  for (grpc_json* field = json_tree_->child; field != nullptr;
+       field = field->next) {
+    if (field->key == nullptr) return nullptr;
+    if (strcmp(field->key, "methodConfig") == 0) {
+      if (entries != nullptr) return nullptr;  // Duplicate.
+      if (field->type != GRPC_JSON_ARRAY) return nullptr;
+      // Find number of entries.
+      for (grpc_json* method = field->child; method != nullptr;
+           method = method->next) {
+        int count = CountNamesInMethodConfig(method);
+        if (count <= 0) return nullptr;
+        num_entries += static_cast<size_t>(count);
+      }
+      // Populate method config table entries.
+      entries = static_cast<typename SliceHashTable<RefCountedPtr<T>>::Entry*>(
+          gpr_zalloc(num_entries *
+                     sizeof(typename SliceHashTable<RefCountedPtr<T>>::Entry)));
+      size_t idx = 0;
+      for (grpc_json* method = field->child; method != nullptr;
+           method = method->next) {
+        if (!ParseJsonMethodConfig(method, create_value, entries, &idx)) {
+          for (size_t i = 0; i < idx; ++i) {
+            grpc_slice_unref_internal(entries[i].key);
+            entries[i].value.reset();
+          }
+          gpr_free(entries);
+          return nullptr;
+        }
+      }
+      GPR_ASSERT(idx == num_entries);
+    }
+  }
+  // Instantiate method config table.
+  RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> method_config_table;
+  if (entries != nullptr) {
+    method_config_table =
+        SliceHashTable<RefCountedPtr<T>>::Create(num_entries, entries, nullptr);
+    gpr_free(entries);
+  }
+  return method_config_table;
+}
+
+template <typename T>
+RefCountedPtr<T> ServiceConfig::MethodConfigTableLookup(
+    const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path) {
+  const RefCountedPtr<T>* value = table.Get(path);
+  // If we didn't find a match for the path, try looking for a wildcard
+  // entry (i.e., change "/service/method" to "/service/*").
+  if (value == nullptr) {
+    char* path_str = grpc_slice_to_c_string(path);
+    const char* sep = strrchr(path_str, '/') + 1;
+    const size_t len = (size_t)(sep - path_str);
+    char* buf = (char*)gpr_malloc(len + 2);  // '*' and NUL
+    memcpy(buf, path_str, len);
+    buf[len] = '*';
+    buf[len + 1] = '\0';
+    grpc_slice wildcard_path = grpc_slice_from_copied_string(buf);
+    gpr_free(buf);
+    value = table.Get(wildcard_path);
+    grpc_slice_unref_internal(wildcard_path);
+    gpr_free(path_str);
+  }
+  return RefCountedPtr<T>(*value);
+}
+
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */
diff --git a/third_party/grpc/src/core/lib/transport/static_metadata.cc b/third_party/grpc/src/core/lib/transport/static_metadata.cc
new file mode 100644
index 0000000..3dfaaaa
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/static_metadata.cc
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ */
+
+/*
+ * WARNING: Auto-generated code.
+ *
+ * To make changes to this file, change
+ * tools/codegen/core/gen_static_metadata.py, and then re-run it.
+ *
+ * See metadata.h for an explanation of the interface here, and metadata.cc for
+ * an explanation of what's going on.
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/static_metadata.h"
+
+#include "src/core/lib/slice/slice_internal.h"
+
+static uint8_t g_bytes[] = {
+    58,  112, 97,  116, 104, 58,  109, 101, 116, 104, 111, 100, 58,  115, 116,
+    97,  116, 117, 115, 58,  97,  117, 116, 104, 111, 114, 105, 116, 121, 58,
+    115, 99,  104, 101, 109, 101, 116, 101, 103, 114, 112, 99,  45,  109, 101,
+    115, 115, 97,  103, 101, 103, 114, 112, 99,  45,  115, 116, 97,  116, 117,
+    115, 103, 114, 112, 99,  45,  112, 97,  121, 108, 111, 97,  100, 45,  98,
+    105, 110, 103, 114, 112, 99,  45,  101, 110, 99,  111, 100, 105, 110, 103,
+    103, 114, 112, 99,  45,  97,  99,  99,  101, 112, 116, 45,  101, 110, 99,
+    111, 100, 105, 110, 103, 103, 114, 112, 99,  45,  115, 101, 114, 118, 101,
+    114, 45,  115, 116, 97,  116, 115, 45,  98,  105, 110, 103, 114, 112, 99,
+    45,  116, 97,  103, 115, 45,  98,  105, 110, 103, 114, 112, 99,  45,  116,
+    114, 97,  99,  101, 45,  98,  105, 110, 99,  111, 110, 116, 101, 110, 116,
+    45,  116, 121, 112, 101, 99,  111, 110, 116, 101, 110, 116, 45,  101, 110,
+    99,  111, 100, 105, 110, 103, 97,  99,  99,  101, 112, 116, 45,  101, 110,
+    99,  111, 100, 105, 110, 103, 103, 114, 112, 99,  45,  105, 110, 116, 101,
+    114, 110, 97,  108, 45,  101, 110, 99,  111, 100, 105, 110, 103, 45,  114,
+    101, 113, 117, 101, 115, 116, 103, 114, 112, 99,  45,  105, 110, 116, 101,
+    114, 110, 97,  108, 45,  115, 116, 114, 101, 97,  109, 45,  101, 110, 99,
+    111, 100, 105, 110, 103, 45,  114, 101, 113, 117, 101, 115, 116, 117, 115,
+    101, 114, 45,  97,  103, 101, 110, 116, 104, 111, 115, 116, 108, 98,  45,
+    116, 111, 107, 101, 110, 103, 114, 112, 99,  45,  112, 114, 101, 118, 105,
+    111, 117, 115, 45,  114, 112, 99,  45,  97,  116, 116, 101, 109, 112, 116,
+    115, 103, 114, 112, 99,  45,  114, 101, 116, 114, 121, 45,  112, 117, 115,
+    104, 98,  97,  99,  107, 45,  109, 115, 103, 114, 112, 99,  45,  116, 105,
+    109, 101, 111, 117, 116, 49,  50,  51,  52,  103, 114, 112, 99,  46,  119,
+    97,  105, 116, 95,  102, 111, 114, 95,  114, 101, 97,  100, 121, 103, 114,
+    112, 99,  46,  116, 105, 109, 101, 111, 117, 116, 103, 114, 112, 99,  46,
+    109, 97,  120, 95,  114, 101, 113, 117, 101, 115, 116, 95,  109, 101, 115,
+    115, 97,  103, 101, 95,  98,  121, 116, 101, 115, 103, 114, 112, 99,  46,
+    109, 97,  120, 95,  114, 101, 115, 112, 111, 110, 115, 101, 95,  109, 101,
+    115, 115, 97,  103, 101, 95,  98,  121, 116, 101, 115, 47,  103, 114, 112,
+    99,  46,  108, 98,  46,  118, 49,  46,  76,  111, 97,  100, 66,  97,  108,
+    97,  110, 99,  101, 114, 47,  66,  97,  108, 97,  110, 99,  101, 76,  111,
+    97,  100, 47,  103, 114, 112, 99,  46,  104, 101, 97,  108, 116, 104, 46,
+    118, 49,  46,  72,  101, 97,  108, 116, 104, 47,  87,  97,  116, 99,  104,
+    47,  101, 110, 118, 111, 121, 46,  115, 101, 114, 118, 105, 99,  101, 46,
+    100, 105, 115, 99,  111, 118, 101, 114, 121, 46,  118, 50,  46,  65,  103,
+    103, 114, 101, 103, 97,  116, 101, 100, 68,  105, 115, 99,  111, 118, 101,
+    114, 121, 83,  101, 114, 118, 105, 99,  101, 47,  83,  116, 114, 101, 97,
+    109, 65,  103, 103, 114, 101, 103, 97,  116, 101, 100, 82,  101, 115, 111,
+    117, 114, 99,  101, 115, 100, 101, 102, 108, 97,  116, 101, 103, 122, 105,
+    112, 115, 116, 114, 101, 97,  109, 47,  103, 122, 105, 112, 71,  69,  84,
+    80,  79,  83,  84,  47,  47,  105, 110, 100, 101, 120, 46,  104, 116, 109,
+    108, 104, 116, 116, 112, 104, 116, 116, 112, 115, 50,  48,  48,  50,  48,
+    52,  50,  48,  54,  51,  48,  52,  52,  48,  48,  52,  48,  52,  53,  48,
+    48,  97,  99,  99,  101, 112, 116, 45,  99,  104, 97,  114, 115, 101, 116,
+    103, 122, 105, 112, 44,  32,  100, 101, 102, 108, 97,  116, 101, 97,  99,
+    99,  101, 112, 116, 45,  108, 97,  110, 103, 117, 97,  103, 101, 97,  99,
+    99,  101, 112, 116, 45,  114, 97,  110, 103, 101, 115, 97,  99,  99,  101,
+    112, 116, 97,  99,  99,  101, 115, 115, 45,  99,  111, 110, 116, 114, 111,
+    108, 45,  97,  108, 108, 111, 119, 45,  111, 114, 105, 103, 105, 110, 97,
+    103, 101, 97,  108, 108, 111, 119, 97,  117, 116, 104, 111, 114, 105, 122,
+    97,  116, 105, 111, 110, 99,  97,  99,  104, 101, 45,  99,  111, 110, 116,
+    114, 111, 108, 99,  111, 110, 116, 101, 110, 116, 45,  100, 105, 115, 112,
+    111, 115, 105, 116, 105, 111, 110, 99,  111, 110, 116, 101, 110, 116, 45,
+    108, 97,  110, 103, 117, 97,  103, 101, 99,  111, 110, 116, 101, 110, 116,
+    45,  108, 101, 110, 103, 116, 104, 99,  111, 110, 116, 101, 110, 116, 45,
+    108, 111, 99,  97,  116, 105, 111, 110, 99,  111, 110, 116, 101, 110, 116,
+    45,  114, 97,  110, 103, 101, 99,  111, 111, 107, 105, 101, 100, 97,  116,
+    101, 101, 116, 97,  103, 101, 120, 112, 101, 99,  116, 101, 120, 112, 105,
+    114, 101, 115, 102, 114, 111, 109, 105, 102, 45,  109, 97,  116, 99,  104,
+    105, 102, 45,  109, 111, 100, 105, 102, 105, 101, 100, 45,  115, 105, 110,
+    99,  101, 105, 102, 45,  110, 111, 110, 101, 45,  109, 97,  116, 99,  104,
+    105, 102, 45,  114, 97,  110, 103, 101, 105, 102, 45,  117, 110, 109, 111,
+    100, 105, 102, 105, 101, 100, 45,  115, 105, 110, 99,  101, 108, 97,  115,
+    116, 45,  109, 111, 100, 105, 102, 105, 101, 100, 108, 105, 110, 107, 108,
+    111, 99,  97,  116, 105, 111, 110, 109, 97,  120, 45,  102, 111, 114, 119,
+    97,  114, 100, 115, 112, 114, 111, 120, 121, 45,  97,  117, 116, 104, 101,
+    110, 116, 105, 99,  97,  116, 101, 112, 114, 111, 120, 121, 45,  97,  117,
+    116, 104, 111, 114, 105, 122, 97,  116, 105, 111, 110, 114, 97,  110, 103,
+    101, 114, 101, 102, 101, 114, 101, 114, 114, 101, 102, 114, 101, 115, 104,
+    114, 101, 116, 114, 121, 45,  97,  102, 116, 101, 114, 115, 101, 114, 118,
+    101, 114, 115, 101, 116, 45,  99,  111, 111, 107, 105, 101, 115, 116, 114,
+    105, 99,  116, 45,  116, 114, 97,  110, 115, 112, 111, 114, 116, 45,  115,
+    101, 99,  117, 114, 105, 116, 121, 116, 114, 97,  110, 115, 102, 101, 114,
+    45,  101, 110, 99,  111, 100, 105, 110, 103, 118, 97,  114, 121, 118, 105,
+    97,  119, 119, 119, 45,  97,  117, 116, 104, 101, 110, 116, 105, 99,  97,
+    116, 101, 48,  105, 100, 101, 110, 116, 105, 116, 121, 116, 114, 97,  105,
+    108, 101, 114, 115, 97,  112, 112, 108, 105, 99,  97,  116, 105, 111, 110,
+    47,  103, 114, 112, 99,  103, 114, 112, 99,  80,  85,  84,  108, 98,  45,
+    99,  111, 115, 116, 45,  98,  105, 110, 105, 100, 101, 110, 116, 105, 116,
+    121, 44,  100, 101, 102, 108, 97,  116, 101, 105, 100, 101, 110, 116, 105,
+    116, 121, 44,  103, 122, 105, 112, 100, 101, 102, 108, 97,  116, 101, 44,
+    103, 122, 105, 112, 105, 100, 101, 110, 116, 105, 116, 121, 44,  100, 101,
+    102, 108, 97,  116, 101, 44,  103, 122, 105, 112};
+
+static void static_ref(void* unused) {}
+static void static_unref(void* unused) {}
+static const grpc_slice_refcount_vtable static_sub_vtable = {
+    static_ref, static_unref, grpc_slice_default_eq_impl,
+    grpc_slice_default_hash_impl};
+const grpc_slice_refcount_vtable grpc_static_metadata_vtable = {
+    static_ref, static_unref, grpc_static_slice_eq, grpc_static_slice_hash};
+static grpc_slice_refcount static_sub_refcnt = {&static_sub_vtable,
+                                                &static_sub_refcnt};
+grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = {
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+    {&grpc_static_metadata_vtable, &static_sub_refcnt},
+};
+
+const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = {
+    {&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}},
+    {&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}},
+    {&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
+    {&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}},
+    {&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}},
+    {&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}},
+    {&grpc_static_metadata_refcounts[6], {{g_bytes + 38, 12}}},
+    {&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}},
+    {&grpc_static_metadata_refcounts[8], {{g_bytes + 61, 16}}},
+    {&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}},
+    {&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
+    {&grpc_static_metadata_refcounts[11], {{g_bytes + 110, 21}}},
+    {&grpc_static_metadata_refcounts[12], {{g_bytes + 131, 13}}},
+    {&grpc_static_metadata_refcounts[13], {{g_bytes + 144, 14}}},
+    {&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}},
+    {&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}},
+    {&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
+    {&grpc_static_metadata_refcounts[17], {{g_bytes + 201, 30}}},
+    {&grpc_static_metadata_refcounts[18], {{g_bytes + 231, 37}}},
+    {&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}},
+    {&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}},
+    {&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}},
+    {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 26}}},
+    {&grpc_static_metadata_refcounts[23], {{g_bytes + 316, 22}}},
+    {&grpc_static_metadata_refcounts[24], {{g_bytes + 338, 12}}},
+    {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}},
+    {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}},
+    {&grpc_static_metadata_refcounts[27], {{g_bytes + 352, 1}}},
+    {&grpc_static_metadata_refcounts[28], {{g_bytes + 353, 1}}},
+    {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}},
+    {&grpc_static_metadata_refcounts[30], {{g_bytes + 354, 19}}},
+    {&grpc_static_metadata_refcounts[31], {{g_bytes + 373, 12}}},
+    {&grpc_static_metadata_refcounts[32], {{g_bytes + 385, 30}}},
+    {&grpc_static_metadata_refcounts[33], {{g_bytes + 415, 31}}},
+    {&grpc_static_metadata_refcounts[34], {{g_bytes + 446, 36}}},
+    {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 28}}},
+    {&grpc_static_metadata_refcounts[36], {{g_bytes + 510, 80}}},
+    {&grpc_static_metadata_refcounts[37], {{g_bytes + 590, 7}}},
+    {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}},
+    {&grpc_static_metadata_refcounts[39], {{g_bytes + 601, 11}}},
+    {&grpc_static_metadata_refcounts[40], {{g_bytes + 612, 3}}},
+    {&grpc_static_metadata_refcounts[41], {{g_bytes + 615, 4}}},
+    {&grpc_static_metadata_refcounts[42], {{g_bytes + 619, 1}}},
+    {&grpc_static_metadata_refcounts[43], {{g_bytes + 620, 11}}},
+    {&grpc_static_metadata_refcounts[44], {{g_bytes + 631, 4}}},
+    {&grpc_static_metadata_refcounts[45], {{g_bytes + 635, 5}}},
+    {&grpc_static_metadata_refcounts[46], {{g_bytes + 640, 3}}},
+    {&grpc_static_metadata_refcounts[47], {{g_bytes + 643, 3}}},
+    {&grpc_static_metadata_refcounts[48], {{g_bytes + 646, 3}}},
+    {&grpc_static_metadata_refcounts[49], {{g_bytes + 649, 3}}},
+    {&grpc_static_metadata_refcounts[50], {{g_bytes + 652, 3}}},
+    {&grpc_static_metadata_refcounts[51], {{g_bytes + 655, 3}}},
+    {&grpc_static_metadata_refcounts[52], {{g_bytes + 658, 3}}},
+    {&grpc_static_metadata_refcounts[53], {{g_bytes + 661, 14}}},
+    {&grpc_static_metadata_refcounts[54], {{g_bytes + 675, 13}}},
+    {&grpc_static_metadata_refcounts[55], {{g_bytes + 688, 15}}},
+    {&grpc_static_metadata_refcounts[56], {{g_bytes + 703, 13}}},
+    {&grpc_static_metadata_refcounts[57], {{g_bytes + 716, 6}}},
+    {&grpc_static_metadata_refcounts[58], {{g_bytes + 722, 27}}},
+    {&grpc_static_metadata_refcounts[59], {{g_bytes + 749, 3}}},
+    {&grpc_static_metadata_refcounts[60], {{g_bytes + 752, 5}}},
+    {&grpc_static_metadata_refcounts[61], {{g_bytes + 757, 13}}},
+    {&grpc_static_metadata_refcounts[62], {{g_bytes + 770, 13}}},
+    {&grpc_static_metadata_refcounts[63], {{g_bytes + 783, 19}}},
+    {&grpc_static_metadata_refcounts[64], {{g_bytes + 802, 16}}},
+    {&grpc_static_metadata_refcounts[65], {{g_bytes + 818, 14}}},
+    {&grpc_static_metadata_refcounts[66], {{g_bytes + 832, 16}}},
+    {&grpc_static_metadata_refcounts[67], {{g_bytes + 848, 13}}},
+    {&grpc_static_metadata_refcounts[68], {{g_bytes + 861, 6}}},
+    {&grpc_static_metadata_refcounts[69], {{g_bytes + 867, 4}}},
+    {&grpc_static_metadata_refcounts[70], {{g_bytes + 871, 4}}},
+    {&grpc_static_metadata_refcounts[71], {{g_bytes + 875, 6}}},
+    {&grpc_static_metadata_refcounts[72], {{g_bytes + 881, 7}}},
+    {&grpc_static_metadata_refcounts[73], {{g_bytes + 888, 4}}},
+    {&grpc_static_metadata_refcounts[74], {{g_bytes + 892, 8}}},
+    {&grpc_static_metadata_refcounts[75], {{g_bytes + 900, 17}}},
+    {&grpc_static_metadata_refcounts[76], {{g_bytes + 917, 13}}},
+    {&grpc_static_metadata_refcounts[77], {{g_bytes + 930, 8}}},
+    {&grpc_static_metadata_refcounts[78], {{g_bytes + 938, 19}}},
+    {&grpc_static_metadata_refcounts[79], {{g_bytes + 957, 13}}},
+    {&grpc_static_metadata_refcounts[80], {{g_bytes + 970, 4}}},
+    {&grpc_static_metadata_refcounts[81], {{g_bytes + 974, 8}}},
+    {&grpc_static_metadata_refcounts[82], {{g_bytes + 982, 12}}},
+    {&grpc_static_metadata_refcounts[83], {{g_bytes + 994, 18}}},
+    {&grpc_static_metadata_refcounts[84], {{g_bytes + 1012, 19}}},
+    {&grpc_static_metadata_refcounts[85], {{g_bytes + 1031, 5}}},
+    {&grpc_static_metadata_refcounts[86], {{g_bytes + 1036, 7}}},
+    {&grpc_static_metadata_refcounts[87], {{g_bytes + 1043, 7}}},
+    {&grpc_static_metadata_refcounts[88], {{g_bytes + 1050, 11}}},
+    {&grpc_static_metadata_refcounts[89], {{g_bytes + 1061, 6}}},
+    {&grpc_static_metadata_refcounts[90], {{g_bytes + 1067, 10}}},
+    {&grpc_static_metadata_refcounts[91], {{g_bytes + 1077, 25}}},
+    {&grpc_static_metadata_refcounts[92], {{g_bytes + 1102, 17}}},
+    {&grpc_static_metadata_refcounts[93], {{g_bytes + 1119, 4}}},
+    {&grpc_static_metadata_refcounts[94], {{g_bytes + 1123, 3}}},
+    {&grpc_static_metadata_refcounts[95], {{g_bytes + 1126, 16}}},
+    {&grpc_static_metadata_refcounts[96], {{g_bytes + 1142, 1}}},
+    {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}},
+    {&grpc_static_metadata_refcounts[98], {{g_bytes + 1151, 8}}},
+    {&grpc_static_metadata_refcounts[99], {{g_bytes + 1159, 16}}},
+    {&grpc_static_metadata_refcounts[100], {{g_bytes + 1175, 4}}},
+    {&grpc_static_metadata_refcounts[101], {{g_bytes + 1179, 3}}},
+    {&grpc_static_metadata_refcounts[102], {{g_bytes + 1182, 11}}},
+    {&grpc_static_metadata_refcounts[103], {{g_bytes + 1193, 16}}},
+    {&grpc_static_metadata_refcounts[104], {{g_bytes + 1209, 13}}},
+    {&grpc_static_metadata_refcounts[105], {{g_bytes + 1222, 12}}},
+    {&grpc_static_metadata_refcounts[106], {{g_bytes + 1234, 21}}},
+};
+
+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, 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, 2, 4, 4, 6, 6, 8, 8, 2, 4, 4};
+
+static const int8_t elems_r[] = {
+    15, 10,  -8, 0,  2,  -42, -81, -43, 0,   6,  -8,  0,   0,   0,   2,
+    -3, -10, 0,  0,  1,  0,   -1,  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,   -64, 0,  -67, -68, -69, -70, 0,
+    35, 34,  33, 32, 31, 30,  29,  28,  27,  26, 25,  24,  23,  22,  21,
+    20, 19,  18, 17, 16, 15,  14,  13,  12,  11, 10,  9,   8,   7,   6,
+    5,  4,   5,  4,  4,  8,   8,   0,   0,   0,  0,   0,   0,   -5,  0};
+static uint32_t elems_phash(uint32_t i) {
+  i -= 42;
+  uint32_t x = i % 105;
+  uint32_t y = i / 105;
+  uint32_t h = x;
+  if (y < GPR_ARRAY_SIZE(elems_r)) {
+    uint32_t delta = (uint32_t)elems_r[y];
+    h += delta;
+  }
+  return h;
+}
+
+static const uint16_t elem_keys[] = {
+    260,  261,  262,  263,  264,  265,  266,   1107, 1108,  1741,  147,  148,
+    472,  473,  1634, 42,   43,   1527, 1750,  1000, 1001,  774,   775,  1643,
+    633,  845,  2062, 2169, 2276, 5700, 5914,  6021, 6128,  6235,  1766, 6342,
+    6449, 6556, 6663, 6770, 6877, 6984, 7091,  7198, 7305,  7412,  7519, 7626,
+    7733, 7840, 7947, 8054, 8161, 8268, 8375,  8482, 8589,  8696,  8803, 8910,
+    9017, 9124, 9231, 9338, 9445, 9552, 9659,  1167, 528,   9766,  9873, 208,
+    9980, 1173, 1174, 1175, 1176, 1809, 10087, 1060, 10194, 10943, 1702, 0,
+    1816, 0,    0,    1597, 0,    0,    350,   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};
+static const uint8_t elem_idxs[] = {
+    7,  8,  9,  10,  11, 12,  13,  77, 79,  71,  1,  2,  5,  6,  25, 3,
+    4,  30, 84, 66,  65, 62,  63,  73, 67,  61,  57, 37, 74, 14, 16, 17,
+    18, 19, 15, 20,  21, 22,  23,  24, 26,  27,  28, 29, 31, 32, 33, 34,
+    35, 36, 38, 39,  40, 41,  42,  43, 44,  45,  46, 47, 48, 49, 50, 51,
+    52, 53, 54, 76,  69, 55,  56,  70, 58,  78,  80, 81, 82, 83, 59, 64,
+    60, 75, 72, 255, 85, 255, 255, 68, 255, 255, 0};
+
+grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) {
+  if (a == -1 || b == -1) return GRPC_MDNULL;
+  uint32_t k = (uint32_t)(a * 107 + b);
+  uint32_t h = elems_phash(k);
+  return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k &&
+                 elem_idxs[h] != 255
+             ? GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[elem_idxs[h]],
+                                GRPC_MDELEM_STORAGE_STATIC)
+             : GRPC_MDNULL;
+}
+
+grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = {
+    {{&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}},
+     {&grpc_static_metadata_refcounts[40], {{g_bytes + 612, 3}}}},
+    {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}},
+     {&grpc_static_metadata_refcounts[41], {{g_bytes + 615, 4}}}},
+    {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}},
+     {&grpc_static_metadata_refcounts[42], {{g_bytes + 619, 1}}}},
+    {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}},
+     {&grpc_static_metadata_refcounts[43], {{g_bytes + 620, 11}}}},
+    {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}},
+     {&grpc_static_metadata_refcounts[44], {{g_bytes + 631, 4}}}},
+    {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}},
+     {&grpc_static_metadata_refcounts[45], {{g_bytes + 635, 5}}}},
+    {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
+     {&grpc_static_metadata_refcounts[46], {{g_bytes + 640, 3}}}},
+    {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
+     {&grpc_static_metadata_refcounts[47], {{g_bytes + 643, 3}}}},
+    {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
+     {&grpc_static_metadata_refcounts[48], {{g_bytes + 646, 3}}}},
+    {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
+     {&grpc_static_metadata_refcounts[49], {{g_bytes + 649, 3}}}},
+    {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
+     {&grpc_static_metadata_refcounts[50], {{g_bytes + 652, 3}}}},
+    {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
+     {&grpc_static_metadata_refcounts[51], {{g_bytes + 655, 3}}}},
+    {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}},
+     {&grpc_static_metadata_refcounts[52], {{g_bytes + 658, 3}}}},
+    {{&grpc_static_metadata_refcounts[53], {{g_bytes + 661, 14}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
+     {&grpc_static_metadata_refcounts[54], {{g_bytes + 675, 13}}}},
+    {{&grpc_static_metadata_refcounts[55], {{g_bytes + 688, 15}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[56], {{g_bytes + 703, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[57], {{g_bytes + 716, 6}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[58], {{g_bytes + 722, 27}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[59], {{g_bytes + 749, 3}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[60], {{g_bytes + 752, 5}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[61], {{g_bytes + 757, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[62], {{g_bytes + 770, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[63], {{g_bytes + 783, 19}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[64], {{g_bytes + 802, 16}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[65], {{g_bytes + 818, 14}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[66], {{g_bytes + 832, 16}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[67], {{g_bytes + 848, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[68], {{g_bytes + 861, 6}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[69], {{g_bytes + 867, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[70], {{g_bytes + 871, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[71], {{g_bytes + 875, 6}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[72], {{g_bytes + 881, 7}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[73], {{g_bytes + 888, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[74], {{g_bytes + 892, 8}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[75], {{g_bytes + 900, 17}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[76], {{g_bytes + 917, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[77], {{g_bytes + 930, 8}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[78], {{g_bytes + 938, 19}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[79], {{g_bytes + 957, 13}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[80], {{g_bytes + 970, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[81], {{g_bytes + 974, 8}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[82], {{g_bytes + 982, 12}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[83], {{g_bytes + 994, 18}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[84], {{g_bytes + 1012, 19}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[85], {{g_bytes + 1031, 5}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[86], {{g_bytes + 1036, 7}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[87], {{g_bytes + 1043, 7}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[88], {{g_bytes + 1050, 11}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[89], {{g_bytes + 1061, 6}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[90], {{g_bytes + 1067, 10}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[91], {{g_bytes + 1077, 25}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[92], {{g_bytes + 1102, 17}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[93], {{g_bytes + 1119, 4}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1123, 3}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1126, 16}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}},
+     {&grpc_static_metadata_refcounts[96], {{g_bytes + 1142, 1}}}},
+    {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}},
+     {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}}},
+    {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}},
+     {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}}},
+    {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}},
+     {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}},
+    {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}},
+     {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}},
+    {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}},
+     {&grpc_static_metadata_refcounts[37], {{g_bytes + 590, 7}}}},
+    {{&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}},
+     {&grpc_static_metadata_refcounts[98], {{g_bytes + 1151, 8}}}},
+    {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}},
+     {&grpc_static_metadata_refcounts[99], {{g_bytes + 1159, 16}}}},
+    {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}},
+     {&grpc_static_metadata_refcounts[100], {{g_bytes + 1175, 4}}}},
+    {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}},
+     {&grpc_static_metadata_refcounts[101], {{g_bytes + 1179, 3}}}},
+    {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}},
+     {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}},
+    {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}},
+     {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}},
+    {{&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[102], {{g_bytes + 1182, 11}}},
+     {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}},
+    {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
+     {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}},
+    {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
+     {&grpc_static_metadata_refcounts[37], {{g_bytes + 590, 7}}}},
+    {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
+     {&grpc_static_metadata_refcounts[103], {{g_bytes + 1193, 16}}}},
+    {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
+     {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}},
+    {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
+     {&grpc_static_metadata_refcounts[104], {{g_bytes + 1209, 13}}}},
+    {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
+     {&grpc_static_metadata_refcounts[105], {{g_bytes + 1222, 12}}}},
+    {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}},
+     {&grpc_static_metadata_refcounts[106], {{g_bytes + 1234, 21}}}},
+    {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
+     {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}},
+    {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
+     {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}},
+    {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}},
+     {&grpc_static_metadata_refcounts[104], {{g_bytes + 1209, 13}}}},
+};
+const uint8_t grpc_static_accept_encoding_metadata[8] = {0,  76, 77, 78,
+                                                         79, 80, 81, 82};
+
+const uint8_t grpc_static_accept_stream_encoding_metadata[4] = {0, 83, 84, 85};
diff --git a/third_party/grpc/src/core/lib/transport/static_metadata.h b/third_party/grpc/src/core/lib/transport/static_metadata.h
new file mode 100644
index 0000000..4f96702
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/static_metadata.h
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ */
+
+/*
+ * WARNING: Auto-generated code.
+ *
+ * To make changes to this file, change
+ * tools/codegen/core/gen_static_metadata.py, and then re-run it.
+ *
+ * See metadata.h for an explanation of the interface here, and metadata.cc for
+ * an explanation of what's going on.
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H
+#define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/metadata.h"
+
+#define GRPC_STATIC_MDSTR_COUNT 107
+extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT];
+/* ":path" */
+#define GRPC_MDSTR_PATH (grpc_static_slice_table[0])
+/* ":method" */
+#define GRPC_MDSTR_METHOD (grpc_static_slice_table[1])
+/* ":status" */
+#define GRPC_MDSTR_STATUS (grpc_static_slice_table[2])
+/* ":authority" */
+#define GRPC_MDSTR_AUTHORITY (grpc_static_slice_table[3])
+/* ":scheme" */
+#define GRPC_MDSTR_SCHEME (grpc_static_slice_table[4])
+/* "te" */
+#define GRPC_MDSTR_TE (grpc_static_slice_table[5])
+/* "grpc-message" */
+#define GRPC_MDSTR_GRPC_MESSAGE (grpc_static_slice_table[6])
+/* "grpc-status" */
+#define GRPC_MDSTR_GRPC_STATUS (grpc_static_slice_table[7])
+/* "grpc-payload-bin" */
+#define GRPC_MDSTR_GRPC_PAYLOAD_BIN (grpc_static_slice_table[8])
+/* "grpc-encoding" */
+#define GRPC_MDSTR_GRPC_ENCODING (grpc_static_slice_table[9])
+/* "grpc-accept-encoding" */
+#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (grpc_static_slice_table[10])
+/* "grpc-server-stats-bin" */
+#define GRPC_MDSTR_GRPC_SERVER_STATS_BIN (grpc_static_slice_table[11])
+/* "grpc-tags-bin" */
+#define GRPC_MDSTR_GRPC_TAGS_BIN (grpc_static_slice_table[12])
+/* "grpc-trace-bin" */
+#define GRPC_MDSTR_GRPC_TRACE_BIN (grpc_static_slice_table[13])
+/* "content-type" */
+#define GRPC_MDSTR_CONTENT_TYPE (grpc_static_slice_table[14])
+/* "content-encoding" */
+#define GRPC_MDSTR_CONTENT_ENCODING (grpc_static_slice_table[15])
+/* "accept-encoding" */
+#define GRPC_MDSTR_ACCEPT_ENCODING (grpc_static_slice_table[16])
+/* "grpc-internal-encoding-request" */
+#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (grpc_static_slice_table[17])
+/* "grpc-internal-stream-encoding-request" */
+#define GRPC_MDSTR_GRPC_INTERNAL_STREAM_ENCODING_REQUEST \
+  (grpc_static_slice_table[18])
+/* "user-agent" */
+#define GRPC_MDSTR_USER_AGENT (grpc_static_slice_table[19])
+/* "host" */
+#define GRPC_MDSTR_HOST (grpc_static_slice_table[20])
+/* "lb-token" */
+#define GRPC_MDSTR_LB_TOKEN (grpc_static_slice_table[21])
+/* "grpc-previous-rpc-attempts" */
+#define GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS (grpc_static_slice_table[22])
+/* "grpc-retry-pushback-ms" */
+#define GRPC_MDSTR_GRPC_RETRY_PUSHBACK_MS (grpc_static_slice_table[23])
+/* "grpc-timeout" */
+#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[24])
+/* "1" */
+#define GRPC_MDSTR_1 (grpc_static_slice_table[25])
+/* "2" */
+#define GRPC_MDSTR_2 (grpc_static_slice_table[26])
+/* "3" */
+#define GRPC_MDSTR_3 (grpc_static_slice_table[27])
+/* "4" */
+#define GRPC_MDSTR_4 (grpc_static_slice_table[28])
+/* "" */
+#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[29])
+/* "grpc.wait_for_ready" */
+#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[30])
+/* "grpc.timeout" */
+#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[31])
+/* "grpc.max_request_message_bytes" */
+#define GRPC_MDSTR_GRPC_DOT_MAX_REQUEST_MESSAGE_BYTES \
+  (grpc_static_slice_table[32])
+/* "grpc.max_response_message_bytes" */
+#define GRPC_MDSTR_GRPC_DOT_MAX_RESPONSE_MESSAGE_BYTES \
+  (grpc_static_slice_table[33])
+/* "/grpc.lb.v1.LoadBalancer/BalanceLoad" */
+#define GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD \
+  (grpc_static_slice_table[34])
+/* "/grpc.health.v1.Health/Watch" */
+#define GRPC_MDSTR_SLASH_GRPC_DOT_HEALTH_DOT_V1_DOT_HEALTH_SLASH_WATCH \
+  (grpc_static_slice_table[35])
+/* "/envoy.service.discovery.v2.AggregatedDiscoveryService/StreamAggregatedResources"
+ */
+#define GRPC_MDSTR_SLASH_ENVOY_DOT_SERVICE_DOT_DISCOVERY_DOT_V2_DOT_AGGREGATEDDISCOVERYSERVICE_SLASH_STREAMAGGREGATEDRESOURCES \
+  (grpc_static_slice_table[36])
+/* "deflate" */
+#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[37])
+/* "gzip" */
+#define GRPC_MDSTR_GZIP (grpc_static_slice_table[38])
+/* "stream/gzip" */
+#define GRPC_MDSTR_STREAM_SLASH_GZIP (grpc_static_slice_table[39])
+/* "GET" */
+#define GRPC_MDSTR_GET (grpc_static_slice_table[40])
+/* "POST" */
+#define GRPC_MDSTR_POST (grpc_static_slice_table[41])
+/* "/" */
+#define GRPC_MDSTR_SLASH (grpc_static_slice_table[42])
+/* "/index.html" */
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (grpc_static_slice_table[43])
+/* "http" */
+#define GRPC_MDSTR_HTTP (grpc_static_slice_table[44])
+/* "https" */
+#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[45])
+/* "200" */
+#define GRPC_MDSTR_200 (grpc_static_slice_table[46])
+/* "204" */
+#define GRPC_MDSTR_204 (grpc_static_slice_table[47])
+/* "206" */
+#define GRPC_MDSTR_206 (grpc_static_slice_table[48])
+/* "304" */
+#define GRPC_MDSTR_304 (grpc_static_slice_table[49])
+/* "400" */
+#define GRPC_MDSTR_400 (grpc_static_slice_table[50])
+/* "404" */
+#define GRPC_MDSTR_404 (grpc_static_slice_table[51])
+/* "500" */
+#define GRPC_MDSTR_500 (grpc_static_slice_table[52])
+/* "accept-charset" */
+#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[53])
+/* "gzip, deflate" */
+#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[54])
+/* "accept-language" */
+#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[55])
+/* "accept-ranges" */
+#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[56])
+/* "accept" */
+#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[57])
+/* "access-control-allow-origin" */
+#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[58])
+/* "age" */
+#define GRPC_MDSTR_AGE (grpc_static_slice_table[59])
+/* "allow" */
+#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[60])
+/* "authorization" */
+#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[61])
+/* "cache-control" */
+#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[62])
+/* "content-disposition" */
+#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[63])
+/* "content-language" */
+#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[64])
+/* "content-length" */
+#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[65])
+/* "content-location" */
+#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[66])
+/* "content-range" */
+#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[67])
+/* "cookie" */
+#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[68])
+/* "date" */
+#define GRPC_MDSTR_DATE (grpc_static_slice_table[69])
+/* "etag" */
+#define GRPC_MDSTR_ETAG (grpc_static_slice_table[70])
+/* "expect" */
+#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[71])
+/* "expires" */
+#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[72])
+/* "from" */
+#define GRPC_MDSTR_FROM (grpc_static_slice_table[73])
+/* "if-match" */
+#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[74])
+/* "if-modified-since" */
+#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[75])
+/* "if-none-match" */
+#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[76])
+/* "if-range" */
+#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[77])
+/* "if-unmodified-since" */
+#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[78])
+/* "last-modified" */
+#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[79])
+/* "link" */
+#define GRPC_MDSTR_LINK (grpc_static_slice_table[80])
+/* "location" */
+#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[81])
+/* "max-forwards" */
+#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[82])
+/* "proxy-authenticate" */
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[83])
+/* "proxy-authorization" */
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[84])
+/* "range" */
+#define GRPC_MDSTR_RANGE (grpc_static_slice_table[85])
+/* "referer" */
+#define GRPC_MDSTR_REFERER (grpc_static_slice_table[86])
+/* "refresh" */
+#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[87])
+/* "retry-after" */
+#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[88])
+/* "server" */
+#define GRPC_MDSTR_SERVER (grpc_static_slice_table[89])
+/* "set-cookie" */
+#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[90])
+/* "strict-transport-security" */
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[91])
+/* "transfer-encoding" */
+#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[92])
+/* "vary" */
+#define GRPC_MDSTR_VARY (grpc_static_slice_table[93])
+/* "via" */
+#define GRPC_MDSTR_VIA (grpc_static_slice_table[94])
+/* "www-authenticate" */
+#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[95])
+/* "0" */
+#define GRPC_MDSTR_0 (grpc_static_slice_table[96])
+/* "identity" */
+#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[97])
+/* "trailers" */
+#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[98])
+/* "application/grpc" */
+#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[99])
+/* "grpc" */
+#define GRPC_MDSTR_GRPC (grpc_static_slice_table[100])
+/* "PUT" */
+#define GRPC_MDSTR_PUT (grpc_static_slice_table[101])
+/* "lb-cost-bin" */
+#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[102])
+/* "identity,deflate" */
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[103])
+/* "identity,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[104])
+/* "deflate,gzip" */
+#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[105])
+/* "identity,deflate,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
+  (grpc_static_slice_table[106])
+
+extern const grpc_slice_refcount_vtable grpc_static_metadata_vtable;
+extern grpc_slice_refcount
+    grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT];
+#define GRPC_IS_STATIC_METADATA_STRING(slice) \
+  ((slice).refcount != NULL &&                \
+   (slice).refcount->vtable == &grpc_static_metadata_vtable)
+
+#define GRPC_STATIC_METADATA_INDEX(static_slice) \
+  ((int)((static_slice).refcount - grpc_static_metadata_refcounts))
+
+#define GRPC_STATIC_MDELEM_COUNT 86
+extern grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
+/* ":authority": "" */
+#define GRPC_MDELEM_AUTHORITY_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[0], GRPC_MDELEM_STORAGE_STATIC))
+/* ":method": "GET" */
+#define GRPC_MDELEM_METHOD_GET \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[1], GRPC_MDELEM_STORAGE_STATIC))
+/* ":method": "POST" */
+#define GRPC_MDELEM_METHOD_POST \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[2], GRPC_MDELEM_STORAGE_STATIC))
+/* ":path": "/" */
+#define GRPC_MDELEM_PATH_SLASH \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[3], GRPC_MDELEM_STORAGE_STATIC))
+/* ":path": "/index.html" */
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[4], GRPC_MDELEM_STORAGE_STATIC))
+/* ":scheme": "http" */
+#define GRPC_MDELEM_SCHEME_HTTP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[5], GRPC_MDELEM_STORAGE_STATIC))
+/* ":scheme": "https" */
+#define GRPC_MDELEM_SCHEME_HTTPS \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[6], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "200" */
+#define GRPC_MDELEM_STATUS_200 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[7], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "204" */
+#define GRPC_MDELEM_STATUS_204 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[8], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "206" */
+#define GRPC_MDELEM_STATUS_206 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[9], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "304" */
+#define GRPC_MDELEM_STATUS_304 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[10], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "400" */
+#define GRPC_MDELEM_STATUS_400 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[11], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "404" */
+#define GRPC_MDELEM_STATUS_404 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[12], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "500" */
+#define GRPC_MDELEM_STATUS_500 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[13], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-charset": "" */
+#define GRPC_MDELEM_ACCEPT_CHARSET_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[14], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-encoding": "gzip, deflate" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_GZIP_COMMA_DEFLATE \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[15], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-language": "" */
+#define GRPC_MDELEM_ACCEPT_LANGUAGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[16], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-ranges": "" */
+#define GRPC_MDELEM_ACCEPT_RANGES_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[17], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept": "" */
+#define GRPC_MDELEM_ACCEPT_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[18], GRPC_MDELEM_STORAGE_STATIC))
+/* "access-control-allow-origin": "" */
+#define GRPC_MDELEM_ACCESS_CONTROL_ALLOW_ORIGIN_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[19], GRPC_MDELEM_STORAGE_STATIC))
+/* "age": "" */
+#define GRPC_MDELEM_AGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[20], GRPC_MDELEM_STORAGE_STATIC))
+/* "allow": "" */
+#define GRPC_MDELEM_ALLOW_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[21], GRPC_MDELEM_STORAGE_STATIC))
+/* "authorization": "" */
+#define GRPC_MDELEM_AUTHORIZATION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[22], GRPC_MDELEM_STORAGE_STATIC))
+/* "cache-control": "" */
+#define GRPC_MDELEM_CACHE_CONTROL_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[23], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-disposition": "" */
+#define GRPC_MDELEM_CONTENT_DISPOSITION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[24], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-encoding": "" */
+#define GRPC_MDELEM_CONTENT_ENCODING_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[25], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-language": "" */
+#define GRPC_MDELEM_CONTENT_LANGUAGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[26], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-length": "" */
+#define GRPC_MDELEM_CONTENT_LENGTH_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[27], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-location": "" */
+#define GRPC_MDELEM_CONTENT_LOCATION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[28], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-range": "" */
+#define GRPC_MDELEM_CONTENT_RANGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[29], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-type": "" */
+#define GRPC_MDELEM_CONTENT_TYPE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[30], GRPC_MDELEM_STORAGE_STATIC))
+/* "cookie": "" */
+#define GRPC_MDELEM_COOKIE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[31], GRPC_MDELEM_STORAGE_STATIC))
+/* "date": "" */
+#define GRPC_MDELEM_DATE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[32], GRPC_MDELEM_STORAGE_STATIC))
+/* "etag": "" */
+#define GRPC_MDELEM_ETAG_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[33], GRPC_MDELEM_STORAGE_STATIC))
+/* "expect": "" */
+#define GRPC_MDELEM_EXPECT_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[34], GRPC_MDELEM_STORAGE_STATIC))
+/* "expires": "" */
+#define GRPC_MDELEM_EXPIRES_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[35], GRPC_MDELEM_STORAGE_STATIC))
+/* "from": "" */
+#define GRPC_MDELEM_FROM_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[36], GRPC_MDELEM_STORAGE_STATIC))
+/* "host": "" */
+#define GRPC_MDELEM_HOST_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[37], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-match": "" */
+#define GRPC_MDELEM_IF_MATCH_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[38], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-modified-since": "" */
+#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[39], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-none-match": "" */
+#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[40], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-range": "" */
+#define GRPC_MDELEM_IF_RANGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[41], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-unmodified-since": "" */
+#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[42], GRPC_MDELEM_STORAGE_STATIC))
+/* "last-modified": "" */
+#define GRPC_MDELEM_LAST_MODIFIED_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[43], GRPC_MDELEM_STORAGE_STATIC))
+/* "link": "" */
+#define GRPC_MDELEM_LINK_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[44], GRPC_MDELEM_STORAGE_STATIC))
+/* "location": "" */
+#define GRPC_MDELEM_LOCATION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[45], GRPC_MDELEM_STORAGE_STATIC))
+/* "max-forwards": "" */
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[46], GRPC_MDELEM_STORAGE_STATIC))
+/* "proxy-authenticate": "" */
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[47], GRPC_MDELEM_STORAGE_STATIC))
+/* "proxy-authorization": "" */
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[48], GRPC_MDELEM_STORAGE_STATIC))
+/* "range": "" */
+#define GRPC_MDELEM_RANGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[49], GRPC_MDELEM_STORAGE_STATIC))
+/* "referer": "" */
+#define GRPC_MDELEM_REFERER_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[50], GRPC_MDELEM_STORAGE_STATIC))
+/* "refresh": "" */
+#define GRPC_MDELEM_REFRESH_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[51], GRPC_MDELEM_STORAGE_STATIC))
+/* "retry-after": "" */
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[52], GRPC_MDELEM_STORAGE_STATIC))
+/* "server": "" */
+#define GRPC_MDELEM_SERVER_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[53], GRPC_MDELEM_STORAGE_STATIC))
+/* "set-cookie": "" */
+#define GRPC_MDELEM_SET_COOKIE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[54], GRPC_MDELEM_STORAGE_STATIC))
+/* "strict-transport-security": "" */
+#define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[55], GRPC_MDELEM_STORAGE_STATIC))
+/* "transfer-encoding": "" */
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[56], GRPC_MDELEM_STORAGE_STATIC))
+/* "user-agent": "" */
+#define GRPC_MDELEM_USER_AGENT_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[57], GRPC_MDELEM_STORAGE_STATIC))
+/* "vary": "" */
+#define GRPC_MDELEM_VARY_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[58], GRPC_MDELEM_STORAGE_STATIC))
+/* "via": "" */
+#define GRPC_MDELEM_VIA_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[59], GRPC_MDELEM_STORAGE_STATIC))
+/* "www-authenticate": "" */
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[60], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-status": "0" */
+#define GRPC_MDELEM_GRPC_STATUS_0 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[61], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-status": "1" */
+#define GRPC_MDELEM_GRPC_STATUS_1 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[62], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-status": "2" */
+#define GRPC_MDELEM_GRPC_STATUS_2 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[63], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-encoding": "identity" */
+#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[64], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-encoding": "gzip" */
+#define GRPC_MDELEM_GRPC_ENCODING_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[65], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-encoding": "deflate" */
+#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[66], GRPC_MDELEM_STORAGE_STATIC))
+/* "te": "trailers" */
+#define GRPC_MDELEM_TE_TRAILERS \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[67], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-type": "application/grpc" */
+#define GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[68], GRPC_MDELEM_STORAGE_STATIC))
+/* ":scheme": "grpc" */
+#define GRPC_MDELEM_SCHEME_GRPC \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[69], GRPC_MDELEM_STORAGE_STATIC))
+/* ":method": "PUT" */
+#define GRPC_MDELEM_METHOD_PUT \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[70], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-encoding": "" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[71], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-encoding": "identity" */
+#define GRPC_MDELEM_CONTENT_ENCODING_IDENTITY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[72], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-encoding": "gzip" */
+#define GRPC_MDELEM_CONTENT_ENCODING_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[73], GRPC_MDELEM_STORAGE_STATIC))
+/* "lb-token": "" */
+#define GRPC_MDELEM_LB_TOKEN_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[74], GRPC_MDELEM_STORAGE_STATIC))
+/* "lb-cost-bin": "" */
+#define GRPC_MDELEM_LB_COST_BIN_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[75], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "identity" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[76], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "deflate" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[77], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "identity,deflate" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[78], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[79], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "identity,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[80], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "deflate,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE_COMMA_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[81], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "identity,deflate,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[82], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-encoding": "identity" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_IDENTITY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[83], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-encoding": "gzip" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[84], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-encoding": "identity,gzip" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_IDENTITY_COMMA_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[85], GRPC_MDELEM_STORAGE_STATIC))
+
+grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b);
+typedef enum {
+  GRPC_BATCH_PATH,
+  GRPC_BATCH_METHOD,
+  GRPC_BATCH_STATUS,
+  GRPC_BATCH_AUTHORITY,
+  GRPC_BATCH_SCHEME,
+  GRPC_BATCH_TE,
+  GRPC_BATCH_GRPC_MESSAGE,
+  GRPC_BATCH_GRPC_STATUS,
+  GRPC_BATCH_GRPC_PAYLOAD_BIN,
+  GRPC_BATCH_GRPC_ENCODING,
+  GRPC_BATCH_GRPC_ACCEPT_ENCODING,
+  GRPC_BATCH_GRPC_SERVER_STATS_BIN,
+  GRPC_BATCH_GRPC_TAGS_BIN,
+  GRPC_BATCH_GRPC_TRACE_BIN,
+  GRPC_BATCH_CONTENT_TYPE,
+  GRPC_BATCH_CONTENT_ENCODING,
+  GRPC_BATCH_ACCEPT_ENCODING,
+  GRPC_BATCH_GRPC_INTERNAL_ENCODING_REQUEST,
+  GRPC_BATCH_GRPC_INTERNAL_STREAM_ENCODING_REQUEST,
+  GRPC_BATCH_USER_AGENT,
+  GRPC_BATCH_HOST,
+  GRPC_BATCH_LB_TOKEN,
+  GRPC_BATCH_GRPC_PREVIOUS_RPC_ATTEMPTS,
+  GRPC_BATCH_GRPC_RETRY_PUSHBACK_MS,
+  GRPC_BATCH_CALLOUTS_COUNT
+} grpc_metadata_batch_callouts_index;
+
+typedef union {
+  struct grpc_linked_mdelem* array[GRPC_BATCH_CALLOUTS_COUNT];
+  struct {
+    struct grpc_linked_mdelem* path;
+    struct grpc_linked_mdelem* method;
+    struct grpc_linked_mdelem* status;
+    struct grpc_linked_mdelem* authority;
+    struct grpc_linked_mdelem* scheme;
+    struct grpc_linked_mdelem* te;
+    struct grpc_linked_mdelem* grpc_message;
+    struct grpc_linked_mdelem* grpc_status;
+    struct grpc_linked_mdelem* grpc_payload_bin;
+    struct grpc_linked_mdelem* grpc_encoding;
+    struct grpc_linked_mdelem* grpc_accept_encoding;
+    struct grpc_linked_mdelem* grpc_server_stats_bin;
+    struct grpc_linked_mdelem* grpc_tags_bin;
+    struct grpc_linked_mdelem* grpc_trace_bin;
+    struct grpc_linked_mdelem* content_type;
+    struct grpc_linked_mdelem* content_encoding;
+    struct grpc_linked_mdelem* accept_encoding;
+    struct grpc_linked_mdelem* grpc_internal_encoding_request;
+    struct grpc_linked_mdelem* grpc_internal_stream_encoding_request;
+    struct grpc_linked_mdelem* user_agent;
+    struct grpc_linked_mdelem* host;
+    struct grpc_linked_mdelem* lb_token;
+    struct grpc_linked_mdelem* grpc_previous_rpc_attempts;
+    struct grpc_linked_mdelem* grpc_retry_pushback_ms;
+  } named;
+} grpc_metadata_batch_callouts;
+
+#define GRPC_BATCH_INDEX_OF(slice)                      \
+  (GRPC_IS_STATIC_METADATA_STRING((slice))              \
+       ? (grpc_metadata_batch_callouts_index)GPR_CLAMP( \
+             GRPC_STATIC_METADATA_INDEX((slice)), 0,    \
+             GRPC_BATCH_CALLOUTS_COUNT)                 \
+       : GRPC_BATCH_CALLOUTS_COUNT)
+
+extern const uint8_t grpc_static_accept_encoding_metadata[8];
+#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs)                       \
+  (GRPC_MAKE_MDELEM(                                                           \
+      &grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]], \
+      GRPC_MDELEM_STORAGE_STATIC))
+
+extern const uint8_t grpc_static_accept_stream_encoding_metadata[4];
+#define GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS(algs)                \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table                                  \
+                        [grpc_static_accept_stream_encoding_metadata[(algs)]], \
+                    GRPC_MDELEM_STORAGE_STATIC))
+#endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */
diff --git a/third_party/grpc/src/core/lib/transport/status_conversion.cc b/third_party/grpc/src/core/lib/transport/status_conversion.cc
new file mode 100644
index 0000000..e58bef5
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/status_conversion.cc
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/status_conversion.h"
+
+grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status) {
+  switch (status) {
+    case GRPC_STATUS_OK:
+      return GRPC_HTTP2_NO_ERROR;
+    case GRPC_STATUS_CANCELLED:
+      return GRPC_HTTP2_CANCEL;
+    case GRPC_STATUS_DEADLINE_EXCEEDED:
+      return GRPC_HTTP2_CANCEL;
+    case GRPC_STATUS_RESOURCE_EXHAUSTED:
+      return GRPC_HTTP2_ENHANCE_YOUR_CALM;
+    case GRPC_STATUS_PERMISSION_DENIED:
+      return GRPC_HTTP2_INADEQUATE_SECURITY;
+    case GRPC_STATUS_UNAVAILABLE:
+      return GRPC_HTTP2_REFUSED_STREAM;
+    default:
+      return GRPC_HTTP2_INTERNAL_ERROR;
+  }
+}
+
+grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error,
+                                                 grpc_millis deadline) {
+  switch (error) {
+    case GRPC_HTTP2_NO_ERROR:
+      /* should never be received */
+      return GRPC_STATUS_INTERNAL;
+    case GRPC_HTTP2_CANCEL:
+      /* http2 cancel translates to STATUS_CANCELLED iff deadline hasn't been
+       * exceeded */
+      return grpc_core::ExecCtx::Get()->Now() > deadline
+                 ? GRPC_STATUS_DEADLINE_EXCEEDED
+                 : GRPC_STATUS_CANCELLED;
+    case GRPC_HTTP2_ENHANCE_YOUR_CALM:
+      return GRPC_STATUS_RESOURCE_EXHAUSTED;
+    case GRPC_HTTP2_INADEQUATE_SECURITY:
+      return GRPC_STATUS_PERMISSION_DENIED;
+    case GRPC_HTTP2_REFUSED_STREAM:
+      return GRPC_STATUS_UNAVAILABLE;
+    default:
+      return GRPC_STATUS_INTERNAL;
+  }
+}
+
+grpc_status_code grpc_http2_status_to_grpc_status(int status) {
+  switch (status) {
+    /* these HTTP2 status codes are called out explicitly in status.proto */
+    case 200:
+      return GRPC_STATUS_OK;
+    case 400:
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    case 401:
+      return GRPC_STATUS_UNAUTHENTICATED;
+    case 403:
+      return GRPC_STATUS_PERMISSION_DENIED;
+    case 404:
+      return GRPC_STATUS_NOT_FOUND;
+    case 409:
+      return GRPC_STATUS_ABORTED;
+    case 412:
+      return GRPC_STATUS_FAILED_PRECONDITION;
+    case 429:
+      return GRPC_STATUS_RESOURCE_EXHAUSTED;
+    case 499:
+      return GRPC_STATUS_CANCELLED;
+    case 500:
+      return GRPC_STATUS_UNKNOWN;
+    case 501:
+      return GRPC_STATUS_UNIMPLEMENTED;
+    case 503:
+      return GRPC_STATUS_UNAVAILABLE;
+    case 504:
+      return GRPC_STATUS_DEADLINE_EXCEEDED;
+    /* everything else is unknown */
+    default:
+      return GRPC_STATUS_UNKNOWN;
+  }
+}
+
+int grpc_status_to_http2_status(grpc_status_code status) { return 200; }
diff --git a/third_party/grpc/src/core/lib/transport/status_conversion.h b/third_party/grpc/src/core/lib/transport/status_conversion.h
new file mode 100644
index 0000000..487f00c
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/status_conversion.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H
+#define GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+/* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */
+grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status);
+grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error,
+                                                 grpc_millis deadline);
+
+/* Conversion of HTTP status codes (:status) to grpc status codes */
+grpc_status_code grpc_http2_status_to_grpc_status(int status);
+int grpc_status_to_http2_status(grpc_status_code status);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H */
diff --git a/third_party/grpc/src/core/lib/transport/status_metadata.cc b/third_party/grpc/src/core/lib/transport/status_metadata.cc
new file mode 100644
index 0000000..f896053
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/status_metadata.cc
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/status_metadata.h"
+
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/static_metadata.h"
+
+/* 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
+   */
+#define STATUS_OFFSET 1
+
+static void destroy_status(void* ignored) {}
+
+grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md) {
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) {
+    return GRPC_STATUS_OK;
+  }
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) {
+    return GRPC_STATUS_CANCELLED;
+  }
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) {
+    return GRPC_STATUS_UNKNOWN;
+  }
+  void* user_data = grpc_mdelem_get_user_data(md, destroy_status);
+  if (user_data != nullptr) {
+    return static_cast<grpc_status_code>((intptr_t)user_data - STATUS_OFFSET);
+  }
+  uint32_t status;
+  if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) {
+    status = GRPC_STATUS_UNKNOWN; /* could not parse status code */
+  }
+  grpc_mdelem_set_user_data(
+      md, destroy_status, (void*)static_cast<intptr_t>(status + STATUS_OFFSET));
+  return static_cast<grpc_status_code>(status);
+}
diff --git a/third_party/grpc/src/core/lib/transport/status_metadata.h b/third_party/grpc/src/core/lib/transport/status_metadata.h
new file mode 100644
index 0000000..aed9c7a
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/status_metadata.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H
+#define GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/status.h>
+
+#include "src/core/lib/transport/metadata.h"
+
+grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H */
diff --git a/third_party/grpc/src/core/lib/transport/timeout_encoding.cc b/third_party/grpc/src/core/lib/transport/timeout_encoding.cc
new file mode 100644
index 0000000..c372499
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/timeout_encoding.cc
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/timeout_encoding.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/lib/gpr/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_millis(char* buffer, int64_t x) {
+  x = round_up_to_three_sig_figs(x);
+  if (x < GPR_MS_PER_SEC) {
+    enc_ext(buffer, x, 'm');
+  } else {
+    if (x % GPR_MS_PER_SEC == 0) {
+      enc_seconds(buffer, x / GPR_MS_PER_SEC);
+    } else {
+      enc_ext(buffer, x, 'm');
+    }
+  }
+}
+
+void grpc_http2_encode_timeout(grpc_millis timeout, char* buffer) {
+  if (timeout <= 0) {
+    enc_tiny(buffer);
+  } else if (timeout < 1000 * GPR_MS_PER_SEC) {
+    enc_millis(buffer, timeout);
+  } else {
+    enc_seconds(buffer,
+                timeout / GPR_MS_PER_SEC + (timeout % GPR_MS_PER_SEC != 0));
+  }
+}
+
+static int is_all_whitespace(const char* p, const char* end) {
+  while (p != end && *p == ' ') p++;
+  return p == end;
+}
+
+int grpc_http2_decode_timeout(grpc_slice text, grpc_millis* timeout) {
+  grpc_millis x = 0;
+  const uint8_t* p = GRPC_SLICE_START_PTR(text);
+  const uint8_t* end = GRPC_SLICE_END_PTR(text);
+  int have_digit = 0;
+  /* skip whitespace */
+  for (; p != end && *p == ' '; p++)
+    ;
+  /* decode numeric part */
+  for (; p != end && *p >= '0' && *p <= '9'; p++) {
+    int32_t digit = static_cast<int32_t>(*p - static_cast<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 = GRPC_MILLIS_INF_FUTURE;
+        return 1;
+      }
+    }
+    x = x * 10 + digit;
+  }
+  if (!have_digit) return 0;
+  /* skip whitespace */
+  for (; p != end && *p == ' '; p++)
+    ;
+  if (p == end) return 0;
+  /* decode unit specifier */
+  switch (*p) {
+    case 'n':
+      *timeout = x / GPR_NS_PER_MS + (x % GPR_NS_PER_MS != 0);
+      break;
+    case 'u':
+      *timeout = x / GPR_US_PER_MS + (x % GPR_US_PER_MS != 0);
+      break;
+    case 'm':
+      *timeout = x;
+      break;
+    case 'S':
+      *timeout = x * GPR_MS_PER_SEC;
+      break;
+    case 'M':
+      *timeout = x * 60 * GPR_MS_PER_SEC;
+      break;
+    case 'H':
+      *timeout = x * 60 * 60 * GPR_MS_PER_SEC;
+      break;
+    default:
+      return 0;
+  }
+  p++;
+  return is_all_whitespace(reinterpret_cast<const char*>(p),
+                           reinterpret_cast<const char*>(end));
+}
diff --git a/third_party/grpc/src/core/lib/transport/timeout_encoding.h b/third_party/grpc/src/core/lib/transport/timeout_encoding.h
new file mode 100644
index 0000000..8505e32
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/timeout_encoding.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
+#define GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/exec_ctx.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(grpc_millis timeout, char* buffer);
+int grpc_http2_decode_timeout(grpc_slice text, grpc_millis* timeout);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */
diff --git a/third_party/grpc/src/core/lib/transport/transport.cc b/third_party/grpc/src/core/lib/transport/transport.cc
new file mode 100644
index 0000000..b32f9c6
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/transport.cc
@@ -0,0 +1,291 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#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/gpr/alloc.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/transport_impl.h"
+
+grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount(false,
+                                                         "stream_refcount");
+
+#ifndef NDEBUG
+void grpc_stream_ref(grpc_stream_refcount* refcount, const char* reason) {
+  if (grpc_trace_stream_refcount.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
+    gpr_log(GPR_DEBUG, "%s %p:%p   REF %" PRIdPTR "->%" PRIdPTR " %s",
+            refcount->object_type, refcount, refcount->destroy.cb_arg, val,
+            val + 1, reason);
+  }
+#else
+void grpc_stream_ref(grpc_stream_refcount* refcount) {
+#endif
+  gpr_ref_non_zero(&refcount->refs);
+}
+
+#ifndef NDEBUG
+void grpc_stream_unref(grpc_stream_refcount* refcount, const char* reason) {
+  if (grpc_trace_stream_refcount.enabled()) {
+    gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
+    gpr_log(GPR_DEBUG, "%s %p:%p UNREF %" PRIdPTR "->%" PRIdPTR " %s",
+            refcount->object_type, refcount, refcount->destroy.cb_arg, val,
+            val - 1, reason);
+  }
+#else
+void grpc_stream_unref(grpc_stream_refcount* refcount) {
+#endif
+  if (gpr_unref(&refcount->refs)) {
+    if (grpc_core::ExecCtx::Get()->flags() &
+        GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP) {
+      /* Ick.
+         The thread we're running on MAY be owned (indirectly) by a call-stack.
+         If that's the case, destroying the call-stack MAY try to destroy the
+         thread, which is a tangled mess that we just don't want to ever have to
+         cope with.
+         Throw this over to the executor (on a core-owned thread) and process it
+         there. */
+      refcount->destroy.scheduler =
+          grpc_executor_scheduler(GRPC_EXECUTOR_SHORT);
+    }
+    GRPC_CLOSURE_SCHED(&refcount->destroy, GRPC_ERROR_NONE);
+  }
+}
+
+#define STREAM_REF_FROM_SLICE_REF(p)       \
+  ((grpc_stream_refcount*)(((uint8_t*)p) - \
+                           offsetof(grpc_stream_refcount, slice_refcount)))
+
+static void slice_stream_ref(void* p) {
+#ifndef NDEBUG
+  grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p), "slice");
+#else
+  grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p));
+#endif
+}
+
+static void slice_stream_unref(void* p) {
+#ifndef NDEBUG
+  grpc_stream_unref(STREAM_REF_FROM_SLICE_REF(p), "slice");
+#else
+  grpc_stream_unref(STREAM_REF_FROM_SLICE_REF(p));
+#endif
+}
+
+grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount,
+                                               void* buffer, size_t length) {
+  slice_stream_ref(&refcount->slice_refcount);
+  grpc_slice res;
+  res.refcount = &refcount->slice_refcount;
+  res.data.refcounted.bytes = static_cast<uint8_t*>(buffer);
+  res.data.refcounted.length = length;
+  return res;
+}
+
+static const grpc_slice_refcount_vtable stream_ref_slice_vtable = {
+    slice_stream_ref,            /* ref */
+    slice_stream_unref,          /* unref */
+    grpc_slice_default_eq_impl,  /* eq */
+    grpc_slice_default_hash_impl /* hash */
+};
+
+#ifndef NDEBUG
+void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
+                          grpc_iomgr_cb_func cb, void* cb_arg,
+                          const char* object_type) {
+  refcount->object_type = object_type;
+#else
+void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
+                          grpc_iomgr_cb_func cb, void* cb_arg) {
+#endif
+  gpr_ref_init(&refcount->refs, initial_refs);
+  GRPC_CLOSURE_INIT(&refcount->destroy, cb, cb_arg, grpc_schedule_on_exec_ctx);
+  refcount->slice_refcount.vtable = &stream_ref_slice_vtable;
+  refcount->slice_refcount.sub_refcount = &refcount->slice_refcount;
+}
+
+static void move64(uint64_t* from, uint64_t* to) {
+  *to += *from;
+  *from = 0;
+}
+
+void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats* from,
+                                       grpc_transport_one_way_stats* to) {
+  move64(&from->framing_bytes, &to->framing_bytes);
+  move64(&from->data_bytes, &to->data_bytes);
+  move64(&from->header_bytes, &to->header_bytes);
+}
+
+void grpc_transport_move_stats(grpc_transport_stream_stats* from,
+                               grpc_transport_stream_stats* to) {
+  grpc_transport_move_one_way_stats(&from->incoming, &to->incoming);
+  grpc_transport_move_one_way_stats(&from->outgoing, &to->outgoing);
+}
+
+size_t grpc_transport_stream_size(grpc_transport* transport) {
+  return GPR_ROUND_UP_TO_ALIGNMENT_SIZE(transport->vtable->sizeof_stream);
+}
+
+void grpc_transport_destroy(grpc_transport* transport) {
+  transport->vtable->destroy(transport);
+}
+
+int grpc_transport_init_stream(grpc_transport* transport, grpc_stream* stream,
+                               grpc_stream_refcount* refcount,
+                               const void* server_data, gpr_arena* arena) {
+  return transport->vtable->init_stream(transport, stream, refcount,
+                                        server_data, arena);
+}
+
+void grpc_transport_perform_stream_op(grpc_transport* transport,
+                                      grpc_stream* stream,
+                                      grpc_transport_stream_op_batch* op) {
+  transport->vtable->perform_stream_op(transport, stream, op);
+}
+
+void grpc_transport_perform_op(grpc_transport* transport,
+                               grpc_transport_op* op) {
+  transport->vtable->perform_op(transport, op);
+}
+
+void grpc_transport_set_pops(grpc_transport* transport, grpc_stream* stream,
+                             grpc_polling_entity* pollent) {
+  grpc_pollset* pollset;
+  grpc_pollset_set* pollset_set;
+  if ((pollset = grpc_polling_entity_pollset(pollent)) != nullptr) {
+    transport->vtable->set_pollset(transport, stream, pollset);
+  } else if ((pollset_set = grpc_polling_entity_pollset_set(pollent)) !=
+             nullptr) {
+    transport->vtable->set_pollset_set(transport, stream, pollset_set);
+  } else {
+    // No-op for empty pollset. Empty pollset is possible when using
+    // non-fd-based event engines such as CFStream.
+  }
+}
+
+void grpc_transport_destroy_stream(grpc_transport* transport,
+                                   grpc_stream* stream,
+                                   grpc_closure* then_schedule_closure) {
+  transport->vtable->destroy_stream(transport, stream, then_schedule_closure);
+}
+
+grpc_endpoint* grpc_transport_get_endpoint(grpc_transport* transport) {
+  return transport->vtable->get_endpoint(transport);
+}
+
+// This comment should be sung to the tune of
+// "Supercalifragilisticexpialidocious":
+//
+// grpc_transport_stream_op_batch_finish_with_failure
+// is a function that must always unref cancel_error
+// though it lives in lib, it handles transport stream ops sure
+// it's grpc_transport_stream_op_batch_finish_with_failure
+void grpc_transport_stream_op_batch_finish_with_failure(
+    grpc_transport_stream_op_batch* batch, grpc_error* error,
+    grpc_call_combiner* call_combiner) {
+  if (batch->send_message) {
+    batch->payload->send_message.send_message.reset();
+  }
+  if (batch->cancel_stream) {
+    GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error);
+  }
+  // Construct a list of closures to execute.
+  grpc_core::CallCombinerClosureList closures;
+  if (batch->recv_initial_metadata) {
+    closures.Add(
+        batch->payload->recv_initial_metadata.recv_initial_metadata_ready,
+        GRPC_ERROR_REF(error), "failing recv_initial_metadata_ready");
+  }
+  if (batch->recv_message) {
+    closures.Add(batch->payload->recv_message.recv_message_ready,
+                 GRPC_ERROR_REF(error), "failing recv_message_ready");
+  }
+  if (batch->recv_trailing_metadata) {
+    closures.Add(
+        batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready,
+        GRPC_ERROR_REF(error), "failing recv_trailing_metadata_ready");
+  }
+  if (batch->on_complete != nullptr) {
+    closures.Add(batch->on_complete, GRPC_ERROR_REF(error),
+                 "failing on_complete");
+  }
+  // Execute closures.
+  closures.RunClosures(call_combiner);
+  GRPC_ERROR_UNREF(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(void* arg, grpc_error* error) {
+  made_transport_op* op = static_cast<made_transport_op*>(arg);
+  GRPC_CLOSURE_SCHED(op->inner_on_complete, GRPC_ERROR_REF(error));
+  gpr_free(op);
+}
+
+grpc_transport_op* grpc_make_transport_op(grpc_closure* on_complete) {
+  made_transport_op* op =
+      static_cast<made_transport_op*>(gpr_malloc(sizeof(*op)));
+  GRPC_CLOSURE_INIT(&op->outer_on_complete, destroy_made_transport_op, op,
+                    grpc_schedule_on_exec_ctx);
+  op->inner_on_complete = on_complete;
+  memset(&op->op, 0, sizeof(op->op));
+  op->op.on_consumed = &op->outer_on_complete;
+  return &op->op;
+}
+
+typedef struct {
+  grpc_closure outer_on_complete;
+  grpc_closure* inner_on_complete;
+  grpc_transport_stream_op_batch op;
+  grpc_transport_stream_op_batch_payload payload;
+} made_transport_stream_op;
+
+static void destroy_made_transport_stream_op(void* arg, grpc_error* error) {
+  made_transport_stream_op* op = static_cast<made_transport_stream_op*>(arg);
+  grpc_closure* c = op->inner_on_complete;
+  gpr_free(op);
+  GRPC_CLOSURE_RUN(c, GRPC_ERROR_REF(error));
+}
+
+grpc_transport_stream_op_batch* grpc_make_transport_stream_op(
+    grpc_closure* on_complete) {
+  made_transport_stream_op* op =
+      static_cast<made_transport_stream_op*>(gpr_zalloc(sizeof(*op)));
+  op->op.payload = &op->payload;
+  GRPC_CLOSURE_INIT(&op->outer_on_complete, destroy_made_transport_stream_op,
+                    op, grpc_schedule_on_exec_ctx);
+  op->inner_on_complete = on_complete;
+  op->op.on_complete = &op->outer_on_complete;
+  return &op->op;
+}
diff --git a/third_party/grpc/src/core/lib/transport/transport.h b/third_party/grpc/src/core/lib/transport/transport.h
new file mode 100644
index 0000000..5ce5688
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/transport.h
@@ -0,0 +1,400 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H
+#define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+#include "src/core/lib/channel/context.h"
+#include "src/core/lib/gpr/arena.h"
+#include "src/core/lib/iomgr/call_combiner.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/transport/byte_stream.h"
+#include "src/core/lib/transport/metadata_batch.h"
+
+/* Minimum and maximum protocol accepted versions. */
+#define GRPC_PROTOCOL_VERSION_MAX_MAJOR 2
+#define GRPC_PROTOCOL_VERSION_MAX_MINOR 1
+#define GRPC_PROTOCOL_VERSION_MIN_MAJOR 2
+#define GRPC_PROTOCOL_VERSION_MIN_MINOR 1
+
+/* forward declarations */
+
+typedef struct grpc_transport grpc_transport;
+
+/* grpc_stream doesn't actually exist. It's used as a typesafe
+   opaque pointer for whatever data the transport wants to track
+   for a stream. */
+typedef struct grpc_stream grpc_stream;
+
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount;
+
+typedef struct grpc_stream_refcount {
+  gpr_refcount refs;
+  grpc_closure destroy;
+#ifndef NDEBUG
+  const char* object_type;
+#endif
+  grpc_slice_refcount slice_refcount;
+} grpc_stream_refcount;
+
+#ifndef NDEBUG
+void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
+                          grpc_iomgr_cb_func cb, void* cb_arg,
+                          const char* object_type);
+void grpc_stream_ref(grpc_stream_refcount* refcount, const char* reason);
+void grpc_stream_unref(grpc_stream_refcount* refcount, const char* reason);
+#define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \
+  grpc_stream_ref_init(rc, ir, cb, cb_arg, objtype)
+#else
+void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs,
+                          grpc_iomgr_cb_func cb, void* cb_arg);
+void grpc_stream_ref(grpc_stream_refcount* refcount);
+void grpc_stream_unref(grpc_stream_refcount* refcount);
+#define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \
+  grpc_stream_ref_init(rc, ir, cb, cb_arg)
+#endif
+
+/* Wrap a buffer that is owned by some stream object into a slice that shares
+   the same refcount */
+grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount,
+                                               void* buffer, size_t length);
+
+struct grpc_transport_one_way_stats {
+  uint64_t framing_bytes = 0;
+  uint64_t data_bytes = 0;
+  uint64_t header_bytes = 0;
+};
+
+struct grpc_transport_stream_stats {
+  grpc_transport_one_way_stats incoming;
+  grpc_transport_one_way_stats outgoing;
+};
+
+void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats* from,
+                                       grpc_transport_one_way_stats* to);
+
+void grpc_transport_move_stats(grpc_transport_stream_stats* from,
+                               grpc_transport_stream_stats* to);
+
+// This struct (which is present in both grpc_transport_stream_op_batch
+// and grpc_transport_op_batch) is a convenience to allow filters or
+// transports to schedule a closure related to a particular batch without
+// having to allocate memory.  The general pattern is to initialize the
+// closure with the callback arg set to the batch and extra_arg set to
+// whatever state is associated with the handler (e.g., the call element
+// or the transport stream object).
+//
+// Note that this can only be used by the current handler of a given
+// batch on the way down the stack (i.e., whichever filter or transport is
+// currently handling the batch).  Once a filter or transport passes control
+// of the batch to the next handler, it cannot depend on the contents of
+// this struct anymore, because the next handler may reuse it.
+typedef struct {
+  void* extra_arg;
+  grpc_closure closure;
+} grpc_handler_private_op_data;
+
+typedef struct grpc_transport_stream_op_batch_payload
+    grpc_transport_stream_op_batch_payload;
+
+/* Transport stream op: a set of operations to perform on a transport
+   against a single stream */
+struct grpc_transport_stream_op_batch {
+  grpc_transport_stream_op_batch()
+      : send_initial_metadata(false),
+        send_trailing_metadata(false),
+        send_message(false),
+        recv_initial_metadata(false),
+        recv_message(false),
+        recv_trailing_metadata(false),
+        cancel_stream(false),
+        is_traced(false) {}
+
+  /** Should be scheduled when all of the non-recv operations in the batch
+      are complete.
+
+      The recv ops (recv_initial_metadata, recv_message, and
+      recv_trailing_metadata) each have their own callbacks.  If a batch
+      contains both recv ops and non-recv ops, on_complete should be
+      scheduled as soon as the non-recv ops are complete, regardless of
+      whether or not the recv ops are complete.  If a batch contains
+      only recv ops, on_complete can be null. */
+  grpc_closure* on_complete = nullptr;
+
+  /** Values for the stream op (fields set are determined by flags above) */
+  grpc_transport_stream_op_batch_payload* payload = nullptr;
+
+  /** Send initial metadata to the peer, from the provided metadata batch. */
+  bool send_initial_metadata : 1;
+
+  /** Send trailing metadata to the peer, from the provided metadata batch. */
+  bool send_trailing_metadata : 1;
+
+  /** Send message data to the peer, from the provided byte stream. */
+  bool send_message : 1;
+
+  /** Receive initial metadata from the stream, into provided metadata batch. */
+  bool recv_initial_metadata : 1;
+
+  /** Receive message data from the stream, into provided byte stream. */
+  bool recv_message : 1;
+
+  /** Receive trailing metadata from the stream, into provided metadata batch.
+   */
+  bool recv_trailing_metadata : 1;
+
+  /** Cancel this stream with the provided error */
+  bool cancel_stream : 1;
+
+  /** Is this stream traced */
+  bool is_traced : 1;
+
+  /***************************************************************************
+   * remaining fields are initialized and used at the discretion of the
+   * current handler of the op */
+
+  grpc_handler_private_op_data handler_private;
+};
+
+struct grpc_transport_stream_op_batch_payload {
+  explicit grpc_transport_stream_op_batch_payload(
+      grpc_call_context_element* context)
+      : context(context) {}
+  ~grpc_transport_stream_op_batch_payload() {
+    // We don't really own `send_message`, so release ownership and let the
+    // owner clean the data.
+    send_message.send_message.release();
+  }
+
+  struct {
+    grpc_metadata_batch* send_initial_metadata = nullptr;
+    /** Iff send_initial_metadata != NULL, flags associated with
+        send_initial_metadata: a bitfield of GRPC_INITIAL_METADATA_xxx */
+    uint32_t send_initial_metadata_flags = 0;
+    // If non-NULL, will be set by the transport to the peer string (a char*).
+    // The transport retains ownership of the string.
+    // Note: This pointer may be used by the transport after the
+    // send_initial_metadata op is completed.  It must remain valid
+    // until the call is destroyed.
+    gpr_atm* peer_string = nullptr;
+  } send_initial_metadata;
+
+  struct {
+    grpc_metadata_batch* send_trailing_metadata = nullptr;
+  } send_trailing_metadata;
+
+  struct {
+    // The transport (or a filter that decides to return a failure before
+    // the op gets down to the transport) takes ownership.
+    // The batch's on_complete will not be called until after the byte
+    // stream is orphaned.
+    grpc_core::OrphanablePtr<grpc_core::ByteStream> send_message;
+  } send_message;
+
+  struct {
+    grpc_metadata_batch* recv_initial_metadata = nullptr;
+    // Flags are used only on the server side.  If non-null, will be set to
+    // a bitfield of the GRPC_INITIAL_METADATA_xxx macros (e.g., to
+    // indicate if the call is idempotent).
+    uint32_t* recv_flags = nullptr;
+    /** Should be enqueued when initial metadata is ready to be processed. */
+    grpc_closure* recv_initial_metadata_ready = nullptr;
+    // If not NULL, will be set to true if trailing metadata is
+    // immediately available.  This may be a signal that we received a
+    // Trailers-Only response.
+    bool* trailing_metadata_available = nullptr;
+    // If non-NULL, will be set by the transport to the peer string (a char*).
+    // The transport retains ownership of the string.
+    // Note: This pointer may be used by the transport after the
+    // recv_initial_metadata op is completed.  It must remain valid
+    // until the call is destroyed.
+    gpr_atm* peer_string = nullptr;
+  } recv_initial_metadata;
+
+  struct {
+    // Will be set by the transport to point to the byte stream
+    // containing a received message.
+    // Will be NULL if trailing metadata is received instead of a message.
+    grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message = nullptr;
+    /** Should be enqueued when one message is ready to be processed. */
+    grpc_closure* recv_message_ready = nullptr;
+  } recv_message;
+
+  struct {
+    grpc_metadata_batch* recv_trailing_metadata = nullptr;
+    grpc_transport_stream_stats* collect_stats = nullptr;
+    /** Should be enqueued when initial metadata is ready to be processed. */
+    grpc_closure* recv_trailing_metadata_ready = nullptr;
+  } recv_trailing_metadata;
+
+  /** Forcefully close this stream.
+      The HTTP2 semantics should be:
+      - server side: if cancel_error has GRPC_ERROR_INT_GRPC_STATUS, and
+        trailing metadata has not been sent, send trailing metadata with status
+        and message from cancel_error (use grpc_error_get_status) followed by
+        a RST_STREAM with error=GRPC_CHTTP2_NO_ERROR to force a full close
+      - at all other times: use grpc_error_get_status to get a status code, and
+        convert to a HTTP2 error code using
+        grpc_chttp2_grpc_status_to_http2_error. Send a RST_STREAM with this
+        error. */
+  struct {
+    // Error contract: the transport that gets this op must cause cancel_error
+    //                 to be unref'ed after processing it
+    grpc_error* cancel_error = GRPC_ERROR_NONE;
+  } cancel_stream;
+
+  /* Indexes correspond to grpc_context_index enum values */
+  grpc_call_context_element* context;
+};
+
+/** Transport op: a set of operations to perform on a transport as a whole */
+typedef struct grpc_transport_op {
+  /** Called when processing of this op is done. */
+  grpc_closure* on_consumed;
+  /** connectivity monitoring - set connectivity_state to NULL to unsubscribe */
+  grpc_closure* on_connectivity_state_change;
+  grpc_connectivity_state* connectivity_state;
+  /** should the transport be disconnected
+   * Error contract: the transport that gets this op must cause
+   *                 disconnect_with_error to be unref'ed after processing it */
+  grpc_error* disconnect_with_error;
+  /** what should the goaway contain?
+   * Error contract: the transport that gets this op must cause
+   *                 goaway_error to be unref'ed after processing it */
+  grpc_error* goaway_error;
+  /** set the callback for accepting new streams;
+      this is a permanent callback, unlike the other one-shot closures.
+      If true, the callback is set to set_accept_stream_fn, with its
+      user_data argument set to set_accept_stream_user_data */
+  bool set_accept_stream;
+  void (*set_accept_stream_fn)(void* user_data, grpc_transport* transport,
+                               const void* server_data);
+  void* set_accept_stream_user_data;
+  /** add this transport to a pollset */
+  grpc_pollset* bind_pollset;
+  /** add this transport to a pollset_set */
+  grpc_pollset_set* bind_pollset_set;
+  /** send a ping, if either on_initiate or on_ack is not NULL */
+  struct {
+    /** Ping may be delayed by the transport, on_initiate callback will be
+        called when the ping is actually being sent. */
+    grpc_closure* on_initiate;
+    /** Called when the ping ack is received */
+    grpc_closure* on_ack;
+  } send_ping;
+  // If true, will reset the channel's connection backoff.
+  bool reset_connect_backoff;
+
+  /***************************************************************************
+   * remaining fields are initialized and used at the discretion of the
+   * transport implementation */
+
+  grpc_handler_private_op_data handler_private;
+} grpc_transport_op;
+
+/* Returns the amount of memory required to store a grpc_stream for this
+   transport */
+size_t grpc_transport_stream_size(grpc_transport* transport);
+
+/* Initialize transport data for a stream.
+
+   Returns 0 on success, any other (transport-defined) value for failure.
+   May assume that stream contains all-zeros.
+
+   Arguments:
+     transport   - the transport on which to create this stream
+     stream      - a pointer to uninitialized memory to initialize
+     server_data - either NULL for a client initiated stream, or a pointer
+                   supplied from the accept_stream callback function */
+int grpc_transport_init_stream(grpc_transport* transport, grpc_stream* stream,
+                               grpc_stream_refcount* refcount,
+                               const void* server_data, gpr_arena* arena);
+
+void grpc_transport_set_pops(grpc_transport* transport, grpc_stream* stream,
+                             grpc_polling_entity* pollent);
+
+/* Destroy transport data for a stream.
+
+   Requires: a recv_batch with final_state == GRPC_STREAM_CLOSED has been
+   received by the up-layer. Must not be called in the same call stack as
+   recv_frame.
+
+   Arguments:
+     transport - the transport on which to create this stream
+     stream    - the grpc_stream to destroy (memory is still owned by the
+                 caller, but any child memory must be cleaned up) */
+void grpc_transport_destroy_stream(grpc_transport* transport,
+                                   grpc_stream* stream,
+                                   grpc_closure* then_schedule_closure);
+
+void grpc_transport_stream_op_batch_finish_with_failure(
+    grpc_transport_stream_op_batch* op, grpc_error* error,
+    grpc_call_combiner* call_combiner);
+
+char* grpc_transport_stream_op_batch_string(grpc_transport_stream_op_batch* op);
+char* grpc_transport_op_string(grpc_transport_op* op);
+
+/* Send a batch of operations on a transport
+
+   Takes ownership of any objects contained in ops.
+
+   Arguments:
+     transport - the transport on which to initiate the stream
+     stream    - the stream on which to send the operations. This must be
+                 non-NULL and previously initialized by the same transport.
+     op        - a grpc_transport_stream_op_batch specifying the op to perform
+   */
+void grpc_transport_perform_stream_op(grpc_transport* transport,
+                                      grpc_stream* stream,
+                                      grpc_transport_stream_op_batch* op);
+
+void grpc_transport_perform_op(grpc_transport* transport,
+                               grpc_transport_op* op);
+
+/* Send a ping on a transport
+
+   Calls cb with user data when a response is received. */
+void grpc_transport_ping(grpc_transport* transport, grpc_closure* cb);
+
+/* Advise peer of pending connection termination. */
+void grpc_transport_goaway(grpc_transport* transport, grpc_status_code status,
+                           grpc_slice debug_data);
+
+/* Destroy the transport */
+void grpc_transport_destroy(grpc_transport* transport);
+
+/* Get the endpoint used by \a transport */
+grpc_endpoint* grpc_transport_get_endpoint(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);
+/* Allocate a grpc_transport_stream_op_batch, and preconfigure the on_consumed
+   closure
+   to \a on_consumed and then delete the returned transport op */
+grpc_transport_stream_op_batch* grpc_make_transport_stream_op(
+    grpc_closure* on_consumed);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H */
diff --git a/third_party/grpc/src/core/lib/transport/transport_impl.h b/third_party/grpc/src/core/lib/transport/transport_impl.h
new file mode 100644
index 0000000..ba5e05d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/transport_impl.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H
+#define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/transport/transport.h"
+
+typedef struct grpc_transport_vtable {
+  /* Memory required for a single stream element - this is allocated by upper
+     layers and initialized by the transport */
+  size_t sizeof_stream; /* = sizeof(transport stream) */
+
+  /* name of this transport implementation */
+  const char* name;
+
+  /* implementation of grpc_transport_init_stream */
+  int (*init_stream)(grpc_transport* self, grpc_stream* stream,
+                     grpc_stream_refcount* refcount, const void* server_data,
+                     gpr_arena* arena);
+
+  /* implementation of grpc_transport_set_pollset */
+  void (*set_pollset)(grpc_transport* self, grpc_stream* stream,
+                      grpc_pollset* pollset);
+
+  /* implementation of grpc_transport_set_pollset */
+  void (*set_pollset_set)(grpc_transport* self, grpc_stream* stream,
+                          grpc_pollset_set* pollset_set);
+
+  /* implementation of grpc_transport_perform_stream_op */
+  void (*perform_stream_op)(grpc_transport* self, grpc_stream* stream,
+                            grpc_transport_stream_op_batch* op);
+
+  /* implementation of grpc_transport_perform_op */
+  void (*perform_op)(grpc_transport* self, grpc_transport_op* op);
+
+  /* implementation of grpc_transport_destroy_stream */
+  void (*destroy_stream)(grpc_transport* self, grpc_stream* stream,
+                         grpc_closure* then_schedule_closure);
+
+  /* implementation of grpc_transport_destroy */
+  void (*destroy)(grpc_transport* self);
+
+  /* implementation of grpc_transport_get_endpoint */
+  grpc_endpoint* (*get_endpoint)(grpc_transport* self);
+} grpc_transport_vtable;
+
+/* an instance of a grpc transport */
+struct grpc_transport {
+  /* pointer to a vtable defining operations on this transport */
+  const grpc_transport_vtable* vtable;
+};
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H */
diff --git a/third_party/grpc/src/core/lib/transport/transport_op_string.cc b/third_party/grpc/src/core/lib/transport/transport_op_string.cc
new file mode 100644
index 0000000..8c7db64
--- /dev/null
+++ b/third_party/grpc/src/core/lib/transport/transport_op_string.cc
@@ -0,0 +1,207 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/connectivity_state.h"
+
+/* These routines are here to facilitate debugging - they produce string
+   representations of various transport data structures */
+
+static void put_metadata(gpr_strvec* b, grpc_mdelem md) {
+  gpr_strvec_add(b, gpr_strdup("key="));
+  gpr_strvec_add(
+      b, grpc_dump_slice(GRPC_MDKEY(md), GPR_DUMP_HEX | GPR_DUMP_ASCII));
+
+  gpr_strvec_add(b, gpr_strdup(" value="));
+  gpr_strvec_add(
+      b, grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII));
+}
+
+static void put_metadata_list(gpr_strvec* b, grpc_metadata_batch md) {
+  grpc_linked_mdelem* m;
+  for (m = md.list.head; m != nullptr; m = m->next) {
+    if (m != md.list.head) gpr_strvec_add(b, gpr_strdup(", "));
+    put_metadata(b, m->md);
+  }
+  if (md.deadline != GRPC_MILLIS_INF_FUTURE) {
+    char* tmp;
+    gpr_asprintf(&tmp, " deadline=%" PRId64, md.deadline);
+    gpr_strvec_add(b, tmp);
+  }
+}
+
+char* grpc_transport_stream_op_batch_string(
+    grpc_transport_stream_op_batch* op) {
+  char* tmp;
+  char* out;
+
+  gpr_strvec b;
+  gpr_strvec_init(&b);
+
+  if (op->send_initial_metadata) {
+    gpr_strvec_add(&b, gpr_strdup(" "));
+    gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA{"));
+    put_metadata_list(
+        &b, *op->payload->send_initial_metadata.send_initial_metadata);
+    gpr_strvec_add(&b, gpr_strdup("}"));
+  }
+
+  if (op->send_message) {
+    gpr_strvec_add(&b, gpr_strdup(" "));
+    if (op->payload->send_message.send_message != nullptr) {
+      gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d",
+                   op->payload->send_message.send_message->flags(),
+                   op->payload->send_message.send_message->length());
+    } else {
+      // This can happen when we check a batch after the transport has
+      // processed and cleared the send_message op.
+      tmp =
+          gpr_strdup("SEND_MESSAGE(flag and length unknown, already orphaned)");
+    }
+    gpr_strvec_add(&b, tmp);
+  }
+
+  if (op->send_trailing_metadata) {
+    gpr_strvec_add(&b, gpr_strdup(" "));
+    gpr_strvec_add(&b, gpr_strdup("SEND_TRAILING_METADATA{"));
+    put_metadata_list(
+        &b, *op->payload->send_trailing_metadata.send_trailing_metadata);
+    gpr_strvec_add(&b, gpr_strdup("}"));
+  }
+
+  if (op->recv_initial_metadata) {
+    gpr_strvec_add(&b, gpr_strdup(" "));
+    gpr_strvec_add(&b, gpr_strdup("RECV_INITIAL_METADATA"));
+  }
+
+  if (op->recv_message) {
+    gpr_strvec_add(&b, gpr_strdup(" "));
+    gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE"));
+  }
+
+  if (op->recv_trailing_metadata) {
+    gpr_strvec_add(&b, gpr_strdup(" "));
+    gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA"));
+  }
+
+  if (op->cancel_stream) {
+    gpr_strvec_add(&b, gpr_strdup(" "));
+    const char* msg =
+        grpc_error_string(op->payload->cancel_stream.cancel_error);
+    gpr_asprintf(&tmp, "CANCEL:%s", msg);
+
+    gpr_strvec_add(&b, tmp);
+  }
+
+  out = gpr_strvec_flatten(&b, nullptr);
+  gpr_strvec_destroy(&b);
+
+  return out;
+}
+
+char* grpc_transport_op_string(grpc_transport_op* op) {
+  char* tmp;
+  char* out;
+  bool first = true;
+
+  gpr_strvec b;
+  gpr_strvec_init(&b);
+
+  if (op->on_connectivity_state_change != nullptr) {
+    if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+    first = false;
+    if (op->connectivity_state != nullptr) {
+      gpr_asprintf(&tmp, "ON_CONNECTIVITY_STATE_CHANGE:p=%p:from=%s",
+                   op->on_connectivity_state_change,
+                   grpc_connectivity_state_name(*op->connectivity_state));
+      gpr_strvec_add(&b, tmp);
+    } else {
+      gpr_asprintf(&tmp, "ON_CONNECTIVITY_STATE_CHANGE:p=%p:unsubscribe",
+                   op->on_connectivity_state_change);
+      gpr_strvec_add(&b, tmp);
+    }
+  }
+
+  if (op->disconnect_with_error != GRPC_ERROR_NONE) {
+    if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+    first = false;
+    const char* err = grpc_error_string(op->disconnect_with_error);
+    gpr_asprintf(&tmp, "DISCONNECT:%s", err);
+    gpr_strvec_add(&b, tmp);
+  }
+
+  if (op->goaway_error) {
+    if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+    first = false;
+    const char* msg = grpc_error_string(op->goaway_error);
+    gpr_asprintf(&tmp, "SEND_GOAWAY:%s", msg);
+
+    gpr_strvec_add(&b, tmp);
+  }
+
+  if (op->set_accept_stream) {
+    if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+    first = false;
+    gpr_asprintf(&tmp, "SET_ACCEPT_STREAM:%p(%p,...)", op->set_accept_stream_fn,
+                 op->set_accept_stream_user_data);
+    gpr_strvec_add(&b, tmp);
+  }
+
+  if (op->bind_pollset != nullptr) {
+    if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+    first = false;
+    gpr_strvec_add(&b, gpr_strdup("BIND_POLLSET"));
+  }
+
+  if (op->bind_pollset_set != nullptr) {
+    if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+    first = false;
+    gpr_strvec_add(&b, gpr_strdup("BIND_POLLSET_SET"));
+  }
+
+  if (op->send_ping.on_initiate != nullptr || op->send_ping.on_ack != nullptr) {
+    if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+    // first = false;
+    gpr_strvec_add(&b, gpr_strdup("SEND_PING"));
+  }
+
+  out = gpr_strvec_flatten(&b, nullptr);
+  gpr_strvec_destroy(&b);
+
+  return out;
+}
+
+void grpc_call_log_op(const char* file, int line, gpr_log_severity severity,
+                      grpc_call_element* elem,
+                      grpc_transport_stream_op_batch* op) {
+  char* str = grpc_transport_stream_op_batch_string(op);
+  gpr_log(file, line, severity, "OP[%s:%p]: %s", elem->filter->name, elem, str);
+  gpr_free(str);
+}
diff --git a/third_party/grpc/src/core/lib/uri/uri_parser.cc b/third_party/grpc/src/core/lib/uri/uri_parser.cc
new file mode 100644
index 0000000..f212c7d
--- /dev/null
+++ b/third_party/grpc/src/core/lib/uri/uri_parser.cc
@@ -0,0 +1,314 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/uri/uri_parser.h"
+
+#include <string.h>
+
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/slice/percent_encoding.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+
+/** a size_t default value... maps to all 1's */
+#define NOT_SET (~(size_t)0)
+
+static grpc_uri* bad_uri(const char* uri_text, size_t pos, const char* section,
+                         bool suppress_errors) {
+  char* line_prefix;
+  size_t pfx_len;
+
+  if (!suppress_errors) {
+    gpr_asprintf(&line_prefix, "bad uri.%s: '", section);
+    pfx_len = strlen(line_prefix) + pos;
+    gpr_log(GPR_ERROR, "%s%s'", line_prefix, uri_text);
+    gpr_free(line_prefix);
+
+    line_prefix = static_cast<char*>(gpr_malloc(pfx_len + 1));
+    memset(line_prefix, ' ', pfx_len);
+    line_prefix[pfx_len] = 0;
+    gpr_log(GPR_ERROR, "%s^ here", line_prefix);
+    gpr_free(line_prefix);
+  }
+
+  return nullptr;
+}
+
+/** Returns a copy of percent decoded \a src[begin, end) */
+static char* decode_and_copy_component(const char* src, size_t begin,
+                                       size_t end) {
+  grpc_slice component =
+      (begin == NOT_SET || end == NOT_SET)
+          ? grpc_empty_slice()
+          : grpc_slice_from_copied_buffer(src + begin, end - begin);
+  grpc_slice decoded_component =
+      grpc_permissive_percent_decode_slice(component);
+  char* out = grpc_dump_slice(decoded_component, GPR_DUMP_ASCII);
+  grpc_slice_unref_internal(component);
+  grpc_slice_unref_internal(decoded_component);
+  return out;
+}
+
+static bool valid_hex(char c) {
+  return ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) ||
+         ((c >= '0') && (c <= '9'));
+}
+
+/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar
+ * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent
+ * sign not followed by two hex digits), NOT_SET is returned. */
+static size_t parse_pchar(const char* uri_text, size_t i) {
+  /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+   * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+   * pct-encoded = "%" HEXDIG HEXDIG
+   * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+   / "*" / "+" / "," / ";" / "=" */
+  char c = uri_text[i];
+  switch (c) {
+    default:
+      if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ||
+          ((c >= '0') && (c <= '9'))) {
+        return 1;
+      }
+      break;
+    case ':':
+    case '@':
+    case '-':
+    case '.':
+    case '_':
+    case '~':
+    case '!':
+    case '$':
+    case '&':
+    case '\'':
+    case '(':
+    case ')':
+    case '*':
+    case '+':
+    case ',':
+    case ';':
+    case '=':
+      return 1;
+    case '%': /* pct-encoded */
+      if (valid_hex(uri_text[i + 1]) && valid_hex(uri_text[i + 2])) {
+        return 2;
+      }
+      return NOT_SET;
+  }
+  return 0;
+}
+
+/* *( pchar / "?" / "/" ) */
+static int parse_fragment_or_query(const char* uri_text, size_t* i) {
+  char c;
+  while ((c = uri_text[*i]) != 0) {
+    const size_t advance = parse_pchar(uri_text, *i); /* pchar */
+    switch (advance) {
+      case 0: /* uri_text[i] isn't in pchar */
+        /* maybe it's ? or / */
+        if (uri_text[*i] == '?' || uri_text[*i] == '/') {
+          (*i)++;
+          break;
+        } else {
+          return 1;
+        }
+        GPR_UNREACHABLE_CODE(return 0);
+      default:
+        (*i) += advance;
+        break;
+      case NOT_SET: /* uri_text[i] introduces an invalid URI */
+        return 0;
+    }
+  }
+  /* *i is the first uri_text position past the \a query production, maybe \0 */
+  return 1;
+}
+
+static void parse_query_parts(grpc_uri* uri) {
+  static const char* QUERY_PARTS_SEPARATOR = "&";
+  static const char* QUERY_PARTS_VALUE_SEPARATOR = "=";
+  GPR_ASSERT(uri->query != nullptr);
+  if (uri->query[0] == '\0') {
+    uri->query_parts = nullptr;
+    uri->query_parts_values = nullptr;
+    uri->num_query_parts = 0;
+    return;
+  }
+
+  gpr_string_split(uri->query, QUERY_PARTS_SEPARATOR, &uri->query_parts,
+                   &uri->num_query_parts);
+  uri->query_parts_values =
+      static_cast<char**>(gpr_malloc(uri->num_query_parts * sizeof(char**)));
+  for (size_t i = 0; i < uri->num_query_parts; i++) {
+    char** query_param_parts;
+    size_t num_query_param_parts;
+    char* full = uri->query_parts[i];
+    gpr_string_split(full, QUERY_PARTS_VALUE_SEPARATOR, &query_param_parts,
+                     &num_query_param_parts);
+    GPR_ASSERT(num_query_param_parts > 0);
+    uri->query_parts[i] = query_param_parts[0];
+    if (num_query_param_parts > 1) {
+      /* TODO(dgq): only the first value after the separator is considered.
+       * Perhaps all chars after the first separator for the query part should
+       * be included, even if they include the separator. */
+      uri->query_parts_values[i] = query_param_parts[1];
+    } else {
+      uri->query_parts_values[i] = nullptr;
+    }
+    for (size_t j = 2; j < num_query_param_parts; j++) {
+      gpr_free(query_param_parts[j]);
+    }
+    gpr_free(query_param_parts);
+    gpr_free(full);
+  }
+}
+
+grpc_uri* grpc_uri_parse(const char* uri_text, bool suppress_errors) {
+  grpc_uri* uri;
+  size_t scheme_begin = 0;
+  size_t scheme_end = NOT_SET;
+  size_t authority_begin = NOT_SET;
+  size_t authority_end = NOT_SET;
+  size_t path_begin = NOT_SET;
+  size_t path_end = NOT_SET;
+  size_t query_begin = NOT_SET;
+  size_t query_end = NOT_SET;
+  size_t fragment_begin = NOT_SET;
+  size_t fragment_end = NOT_SET;
+  size_t i;
+
+  for (i = scheme_begin; uri_text[i] != 0; i++) {
+    if (uri_text[i] == ':') {
+      scheme_end = i;
+      break;
+    }
+    if (uri_text[i] >= 'a' && uri_text[i] <= 'z') continue;
+    if (uri_text[i] >= 'A' && uri_text[i] <= 'Z') continue;
+    if (i != scheme_begin) {
+      if (uri_text[i] >= '0' && uri_text[i] <= '9') continue;
+      if (uri_text[i] == '+') continue;
+      if (uri_text[i] == '-') continue;
+      if (uri_text[i] == '.') continue;
+    }
+    break;
+  }
+  if (scheme_end == NOT_SET) {
+    return bad_uri(uri_text, i, "scheme", suppress_errors);
+  }
+
+  if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') {
+    authority_begin = scheme_end + 3;
+    for (i = authority_begin; uri_text[i] != 0 && authority_end == NOT_SET;
+         i++) {
+      if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') {
+        authority_end = i;
+      }
+    }
+    if (authority_end == NOT_SET && uri_text[i] == 0) {
+      authority_end = i;
+    }
+    if (authority_end == NOT_SET) {
+      return bad_uri(uri_text, i, "authority", suppress_errors);
+    }
+    /* TODO(ctiller): parse the authority correctly */
+    path_begin = authority_end;
+  } else {
+    path_begin = scheme_end + 1;
+  }
+
+  for (i = path_begin; uri_text[i] != 0; i++) {
+    if (uri_text[i] == '?' || uri_text[i] == '#') {
+      path_end = i;
+      break;
+    }
+  }
+  if (path_end == NOT_SET && uri_text[i] == 0) {
+    path_end = i;
+  }
+  if (path_end == NOT_SET) {
+    return bad_uri(uri_text, i, "path", suppress_errors);
+  }
+
+  if (uri_text[i] == '?') {
+    query_begin = ++i;
+    if (!parse_fragment_or_query(uri_text, &i)) {
+      return bad_uri(uri_text, i, "query", suppress_errors);
+    } else if (uri_text[i] != 0 && uri_text[i] != '#') {
+      /* We must be at the end or at the beginning of a fragment */
+      return bad_uri(uri_text, i, "query", suppress_errors);
+    }
+    query_end = i;
+  }
+  if (uri_text[i] == '#') {
+    fragment_begin = ++i;
+    if (!parse_fragment_or_query(uri_text, &i)) {
+      return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors);
+    } else if (uri_text[i] != 0) {
+      /* We must be at the end */
+      return bad_uri(uri_text, i, "fragment", suppress_errors);
+    }
+    fragment_end = i;
+  }
+
+  uri = static_cast<grpc_uri*>(gpr_zalloc(sizeof(*uri)));
+  uri->scheme = decode_and_copy_component(uri_text, scheme_begin, scheme_end);
+  uri->authority =
+      decode_and_copy_component(uri_text, authority_begin, authority_end);
+  uri->path = decode_and_copy_component(uri_text, path_begin, path_end);
+  uri->query = decode_and_copy_component(uri_text, query_begin, query_end);
+  uri->fragment =
+      decode_and_copy_component(uri_text, fragment_begin, fragment_end);
+  parse_query_parts(uri);
+
+  return uri;
+}
+
+const char* grpc_uri_get_query_arg(const grpc_uri* uri, const char* key) {
+  GPR_ASSERT(key != nullptr);
+  if (key[0] == '\0') return nullptr;
+
+  for (size_t i = 0; i < uri->num_query_parts; ++i) {
+    if (0 == strcmp(key, uri->query_parts[i])) {
+      return uri->query_parts_values[i];
+    }
+  }
+  return nullptr;
+}
+
+void grpc_uri_destroy(grpc_uri* uri) {
+  if (!uri) return;
+  gpr_free(uri->scheme);
+  gpr_free(uri->authority);
+  gpr_free(uri->path);
+  gpr_free(uri->query);
+  for (size_t i = 0; i < uri->num_query_parts; ++i) {
+    gpr_free(uri->query_parts[i]);
+    gpr_free(uri->query_parts_values[i]);
+  }
+  gpr_free(uri->query_parts);
+  gpr_free(uri->query_parts_values);
+  gpr_free(uri->fragment);
+  gpr_free(uri);
+}
diff --git a/third_party/grpc/src/core/lib/uri/uri_parser.h b/third_party/grpc/src/core/lib/uri/uri_parser.h
new file mode 100644
index 0000000..b6771bb
--- /dev/null
+++ b/third_party/grpc/src/core/lib/uri/uri_parser.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_URI_URI_PARSER_H
+#define GRPC_CORE_LIB_URI_URI_PARSER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+typedef struct {
+  char* scheme;
+  char* authority;
+  char* path;
+  char* query;
+  /** Query substrings separated by '&' */
+  char** query_parts;
+  /** Number of elements in \a query_parts and \a query_parts_values */
+  size_t num_query_parts;
+  /** Split each query part by '='. NULL if not present. */
+  char** query_parts_values;
+  char* fragment;
+} grpc_uri;
+
+/** parse a uri, return NULL on failure */
+grpc_uri* grpc_uri_parse(const char* uri_text, bool suppress_errors);
+
+/** return the part of a query string after the '=' in "?key=xxx&...", or NULL
+ * if key is not present */
+const char* grpc_uri_get_query_arg(const grpc_uri* uri, const char* key);
+
+/** destroy a uri */
+void grpc_uri_destroy(grpc_uri* uri);
+
+#endif /* GRPC_CORE_LIB_URI_URI_PARSER_H */
diff --git a/third_party/grpc/src/core/plugin_registry/grpc_cronet_plugin_registry.cc b/third_party/grpc/src/core/plugin_registry/grpc_cronet_plugin_registry.cc
new file mode 100644
index 0000000..92085d3
--- /dev/null
+++ b/third_party/grpc/src/core/plugin_registry/grpc_cronet_plugin_registry.cc
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+void grpc_http_filters_init(void);
+void grpc_http_filters_shutdown(void);
+void grpc_chttp2_plugin_init(void);
+void grpc_chttp2_plugin_shutdown(void);
+void grpc_deadline_filter_init(void);
+void grpc_deadline_filter_shutdown(void);
+void grpc_client_channel_init(void);
+void grpc_client_channel_shutdown(void);
+
+void grpc_register_built_in_plugins(void) {
+  grpc_register_plugin(grpc_http_filters_init,
+                       grpc_http_filters_shutdown);
+  grpc_register_plugin(grpc_chttp2_plugin_init,
+                       grpc_chttp2_plugin_shutdown);
+  grpc_register_plugin(grpc_deadline_filter_init,
+                       grpc_deadline_filter_shutdown);
+  grpc_register_plugin(grpc_client_channel_init,
+                       grpc_client_channel_shutdown);
+}
diff --git a/third_party/grpc/src/core/plugin_registry/grpc_plugin_registry.cc b/third_party/grpc/src/core/plugin_registry/grpc_plugin_registry.cc
new file mode 100644
index 0000000..cde40ef
--- /dev/null
+++ b/third_party/grpc/src/core/plugin_registry/grpc_plugin_registry.cc
@@ -0,0 +1,93 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+void grpc_http_filters_init(void);
+void grpc_http_filters_shutdown(void);
+void grpc_chttp2_plugin_init(void);
+void grpc_chttp2_plugin_shutdown(void);
+void grpc_deadline_filter_init(void);
+void grpc_deadline_filter_shutdown(void);
+void grpc_client_channel_init(void);
+void grpc_client_channel_shutdown(void);
+void grpc_inproc_plugin_init(void);
+void grpc_inproc_plugin_shutdown(void);
+void grpc_resolver_fake_init(void);
+void grpc_resolver_fake_shutdown(void);
+void grpc_lb_policy_grpclb_init(void);
+void grpc_lb_policy_grpclb_shutdown(void);
+void grpc_lb_policy_xds_init(void);
+void grpc_lb_policy_xds_shutdown(void);
+void grpc_lb_policy_pick_first_init(void);
+void grpc_lb_policy_pick_first_shutdown(void);
+void grpc_lb_policy_round_robin_init(void);
+void grpc_lb_policy_round_robin_shutdown(void);
+void grpc_resolver_dns_ares_init(void);
+void grpc_resolver_dns_ares_shutdown(void);
+void grpc_resolver_dns_native_init(void);
+void grpc_resolver_dns_native_shutdown(void);
+void grpc_resolver_sockaddr_init(void);
+void grpc_resolver_sockaddr_shutdown(void);
+void grpc_max_age_filter_init(void);
+void grpc_max_age_filter_shutdown(void);
+void grpc_message_size_filter_init(void);
+void grpc_message_size_filter_shutdown(void);
+void grpc_client_authority_filter_init(void);
+void grpc_client_authority_filter_shutdown(void);
+void grpc_workaround_cronet_compression_filter_init(void);
+void grpc_workaround_cronet_compression_filter_shutdown(void);
+
+void grpc_register_built_in_plugins(void) {
+  grpc_register_plugin(grpc_http_filters_init,
+                       grpc_http_filters_shutdown);
+  grpc_register_plugin(grpc_chttp2_plugin_init,
+                       grpc_chttp2_plugin_shutdown);
+  grpc_register_plugin(grpc_deadline_filter_init,
+                       grpc_deadline_filter_shutdown);
+  grpc_register_plugin(grpc_client_channel_init,
+                       grpc_client_channel_shutdown);
+  grpc_register_plugin(grpc_inproc_plugin_init,
+                       grpc_inproc_plugin_shutdown);
+  grpc_register_plugin(grpc_resolver_fake_init,
+                       grpc_resolver_fake_shutdown);
+  grpc_register_plugin(grpc_lb_policy_grpclb_init,
+                       grpc_lb_policy_grpclb_shutdown);
+  grpc_register_plugin(grpc_lb_policy_xds_init,
+                       grpc_lb_policy_xds_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,
+                       grpc_lb_policy_round_robin_shutdown);
+  grpc_register_plugin(grpc_resolver_dns_ares_init,
+                       grpc_resolver_dns_ares_shutdown);
+  grpc_register_plugin(grpc_resolver_dns_native_init,
+                       grpc_resolver_dns_native_shutdown);
+  grpc_register_plugin(grpc_resolver_sockaddr_init,
+                       grpc_resolver_sockaddr_shutdown);
+  grpc_register_plugin(grpc_max_age_filter_init,
+                       grpc_max_age_filter_shutdown);
+  grpc_register_plugin(grpc_message_size_filter_init,
+                       grpc_message_size_filter_shutdown);
+  grpc_register_plugin(grpc_client_authority_filter_init,
+                       grpc_client_authority_filter_shutdown);
+  grpc_register_plugin(grpc_workaround_cronet_compression_filter_init,
+                       grpc_workaround_cronet_compression_filter_shutdown);
+}
diff --git a/third_party/grpc/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc b/third_party/grpc/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
new file mode 100644
index 0000000..5749ab6
--- /dev/null
+++ b/third_party/grpc/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc
@@ -0,0 +1,93 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+void grpc_http_filters_init(void);
+void grpc_http_filters_shutdown(void);
+void grpc_chttp2_plugin_init(void);
+void grpc_chttp2_plugin_shutdown(void);
+void grpc_deadline_filter_init(void);
+void grpc_deadline_filter_shutdown(void);
+void grpc_client_channel_init(void);
+void grpc_client_channel_shutdown(void);
+void grpc_inproc_plugin_init(void);
+void grpc_inproc_plugin_shutdown(void);
+void grpc_resolver_dns_ares_init(void);
+void grpc_resolver_dns_ares_shutdown(void);
+void grpc_resolver_dns_native_init(void);
+void grpc_resolver_dns_native_shutdown(void);
+void grpc_resolver_sockaddr_init(void);
+void grpc_resolver_sockaddr_shutdown(void);
+void grpc_resolver_fake_init(void);
+void grpc_resolver_fake_shutdown(void);
+void grpc_lb_policy_grpclb_init(void);
+void grpc_lb_policy_grpclb_shutdown(void);
+void grpc_lb_policy_xds_init(void);
+void grpc_lb_policy_xds_shutdown(void);
+void grpc_lb_policy_pick_first_init(void);
+void grpc_lb_policy_pick_first_shutdown(void);
+void grpc_lb_policy_round_robin_init(void);
+void grpc_lb_policy_round_robin_shutdown(void);
+void grpc_max_age_filter_init(void);
+void grpc_max_age_filter_shutdown(void);
+void grpc_message_size_filter_init(void);
+void grpc_message_size_filter_shutdown(void);
+void grpc_client_authority_filter_init(void);
+void grpc_client_authority_filter_shutdown(void);
+void grpc_workaround_cronet_compression_filter_init(void);
+void grpc_workaround_cronet_compression_filter_shutdown(void);
+
+void grpc_register_built_in_plugins(void) {
+  grpc_register_plugin(grpc_http_filters_init,
+                       grpc_http_filters_shutdown);
+  grpc_register_plugin(grpc_chttp2_plugin_init,
+                       grpc_chttp2_plugin_shutdown);
+  grpc_register_plugin(grpc_deadline_filter_init,
+                       grpc_deadline_filter_shutdown);
+  grpc_register_plugin(grpc_client_channel_init,
+                       grpc_client_channel_shutdown);
+  grpc_register_plugin(grpc_inproc_plugin_init,
+                       grpc_inproc_plugin_shutdown);
+  grpc_register_plugin(grpc_resolver_dns_ares_init,
+                       grpc_resolver_dns_ares_shutdown);
+  grpc_register_plugin(grpc_resolver_dns_native_init,
+                       grpc_resolver_dns_native_shutdown);
+  grpc_register_plugin(grpc_resolver_sockaddr_init,
+                       grpc_resolver_sockaddr_shutdown);
+  grpc_register_plugin(grpc_resolver_fake_init,
+                       grpc_resolver_fake_shutdown);
+  grpc_register_plugin(grpc_lb_policy_grpclb_init,
+                       grpc_lb_policy_grpclb_shutdown);
+  grpc_register_plugin(grpc_lb_policy_xds_init,
+                       grpc_lb_policy_xds_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,
+                       grpc_lb_policy_round_robin_shutdown);
+  grpc_register_plugin(grpc_max_age_filter_init,
+                       grpc_max_age_filter_shutdown);
+  grpc_register_plugin(grpc_message_size_filter_init,
+                       grpc_message_size_filter_shutdown);
+  grpc_register_plugin(grpc_client_authority_filter_init,
+                       grpc_client_authority_filter_shutdown);
+  grpc_register_plugin(grpc_workaround_cronet_compression_filter_init,
+                       grpc_workaround_cronet_compression_filter_shutdown);
+}
diff --git a/third_party/grpc/src/core/tsi/README.md b/third_party/grpc/src/core/tsi/README.md
new file mode 100644
index 0000000..3ca3c1e
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/README.md
@@ -0,0 +1,2 @@
+# Transport Security Interface
+An abstraction library over crypto and auth modules (typically OpenSSL)
diff --git a/third_party/grpc/src/core/tsi/alts/crypt/aes_gcm.cc b/third_party/grpc/src/core/tsi/alts/crypt/aes_gcm.cc
new file mode 100644
index 0000000..c638ce7
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/crypt/aes_gcm.cc
@@ -0,0 +1,689 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/grpc_shadow_boringssl.h"
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+constexpr size_t kKdfKeyLen = 32;
+constexpr size_t kKdfCounterLen = 6;
+constexpr size_t kKdfCounterOffset = 2;
+constexpr size_t kRekeyAeadKeyLen = kAes128GcmKeyLength;
+
+/* Struct for additional data required if rekeying is enabled. */
+struct gsec_aes_gcm_aead_rekey_data {
+  uint8_t kdf_counter[kKdfCounterLen];
+  uint8_t nonce_mask[kAesGcmNonceLength];
+};
+
+/* Main struct for AES_GCM crypter interface. */
+struct gsec_aes_gcm_aead_crypter {
+  gsec_aead_crypter crypter;
+  size_t key_length;
+  size_t nonce_length;
+  size_t tag_length;
+  uint8_t* key;
+  gsec_aes_gcm_aead_rekey_data* rekey_data;
+  EVP_CIPHER_CTX* ctx;
+};
+
+static char* aes_gcm_get_openssl_errors() {
+  BIO* bio = BIO_new(BIO_s_mem());
+  ERR_print_errors(bio);
+  BUF_MEM* mem = nullptr;
+  char* error_msg = nullptr;
+  BIO_get_mem_ptr(bio, &mem);
+  if (mem != nullptr) {
+    error_msg = static_cast<char*>(gpr_malloc(mem->length + 1));
+    memcpy(error_msg, mem->data, mem->length);
+    error_msg[mem->length] = '\0';
+  }
+  BIO_free_all(bio);
+  return error_msg;
+}
+
+static void aes_gcm_format_errors(const char* error_msg, char** error_details) {
+  if (error_details == nullptr) {
+    return;
+  }
+  unsigned long error = ERR_get_error();
+  if (error == 0 && error_msg != nullptr) {
+    *error_details = static_cast<char*>(gpr_malloc(strlen(error_msg) + 1));
+    memcpy(*error_details, error_msg, strlen(error_msg) + 1);
+    return;
+  }
+  char* openssl_errors = aes_gcm_get_openssl_errors();
+  if (openssl_errors != nullptr && error_msg != nullptr) {
+    size_t len = strlen(error_msg) + strlen(openssl_errors) + 2; /* ", " */
+    *error_details = static_cast<char*>(gpr_malloc(len + 1));
+    snprintf(*error_details, len + 1, "%s, %s", error_msg, openssl_errors);
+    gpr_free(openssl_errors);
+  }
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length(
+    const gsec_aead_crypter* crypter, size_t plaintext_length,
+    size_t* max_ciphertext_and_tag_length, char** error_details) {
+  if (max_ciphertext_and_tag_length == nullptr) {
+    aes_gcm_format_errors("max_ciphertext_and_tag_length is nullptr.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  *max_ciphertext_and_tag_length =
+      plaintext_length + aes_gcm_crypter->tag_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_max_plaintext_length(
+    const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
+    size_t* max_plaintext_length, char** error_details) {
+  if (max_plaintext_length == nullptr) {
+    aes_gcm_format_errors("max_plaintext_length is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  if (ciphertext_and_tag_length < aes_gcm_crypter->tag_length) {
+    *max_plaintext_length = 0;
+    aes_gcm_format_errors(
+        "ciphertext_and_tag_length is smaller than tag_length.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  *max_plaintext_length =
+      ciphertext_and_tag_length - aes_gcm_crypter->tag_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_nonce_length(
+    const gsec_aead_crypter* crypter, size_t* nonce_length,
+    char** error_details) {
+  if (nonce_length == nullptr) {
+    aes_gcm_format_errors("nonce_length is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  *nonce_length = aes_gcm_crypter->nonce_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_key_length(
+    const gsec_aead_crypter* crypter, size_t* key_length,
+    char** error_details) {
+  if (key_length == nullptr) {
+    aes_gcm_format_errors("key_length is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  *key_length = aes_gcm_crypter->key_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_tag_length(
+    const gsec_aead_crypter* crypter, size_t* tag_length,
+    char** error_details) {
+  if (tag_length == nullptr) {
+    aes_gcm_format_errors("tag_length is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  *tag_length = aes_gcm_crypter->tag_length;
+  return GRPC_STATUS_OK;
+}
+
+static void aes_gcm_mask_nonce(uint8_t* dst, const uint8_t* nonce,
+                               const uint8_t* mask) {
+  uint64_t mask1;
+  uint32_t mask2;
+  memcpy(&mask1, mask, sizeof(mask1));
+  memcpy(&mask2, mask + sizeof(mask1), sizeof(mask2));
+  uint64_t nonce1;
+  uint32_t nonce2;
+  memcpy(&nonce1, nonce, sizeof(nonce1));
+  memcpy(&nonce2, nonce + sizeof(nonce1), sizeof(nonce2));
+  nonce1 ^= mask1;
+  nonce2 ^= mask2;
+  memcpy(dst, &nonce1, sizeof(nonce1));
+  memcpy(dst + sizeof(nonce1), &nonce2, sizeof(nonce2));
+}
+
+static grpc_status_code aes_gcm_derive_aead_key(uint8_t* dst,
+                                                const uint8_t* kdf_key,
+                                                const uint8_t* kdf_counter) {
+  unsigned char buf[EVP_MAX_MD_SIZE];
+  unsigned char ctr = 1;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+  HMAC_CTX hmac;
+  HMAC_CTX_init(&hmac);
+  if (!HMAC_Init_ex(&hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) ||
+      !HMAC_Update(&hmac, kdf_counter, kKdfCounterLen) ||
+      !HMAC_Update(&hmac, &ctr, 1) || !HMAC_Final(&hmac, buf, nullptr)) {
+    HMAC_CTX_cleanup(&hmac);
+    return GRPC_STATUS_INTERNAL;
+  }
+  HMAC_CTX_cleanup(&hmac);
+#else
+  HMAC_CTX* hmac = HMAC_CTX_new();
+  if (hmac == nullptr) {
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (!HMAC_Init_ex(hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) ||
+      !HMAC_Update(hmac, kdf_counter, kKdfCounterLen) ||
+      !HMAC_Update(hmac, &ctr, 1) || !HMAC_Final(hmac, buf, nullptr)) {
+    HMAC_CTX_free(hmac);
+    return GRPC_STATUS_INTERNAL;
+  }
+  HMAC_CTX_free(hmac);
+#endif
+  memcpy(dst, buf, kRekeyAeadKeyLen);
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code aes_gcm_rekey_if_required(
+    gsec_aes_gcm_aead_crypter* aes_gcm_crypter, const uint8_t* nonce,
+    char** error_details) {
+  // If rekey_data is nullptr, then rekeying is not supported and not required.
+  // If bytes 2-7 of kdf_counter differ from the (per message) nonce, then the
+  // encryption key is recomputed from a new kdf_counter to ensure that we don't
+  // encrypt more than 2^16 messages per encryption key (in each direction).
+  if (aes_gcm_crypter->rekey_data == nullptr ||
+      memcmp(aes_gcm_crypter->rekey_data->kdf_counter,
+             nonce + kKdfCounterOffset, kKdfCounterLen) == 0) {
+    return GRPC_STATUS_OK;
+  }
+  memcpy(aes_gcm_crypter->rekey_data->kdf_counter, nonce + kKdfCounterOffset,
+         kKdfCounterLen);
+  uint8_t aead_key[kRekeyAeadKeyLen];
+  if (aes_gcm_derive_aead_key(aead_key, aes_gcm_crypter->key,
+                              aes_gcm_crypter->rekey_data->kdf_counter) !=
+      GRPC_STATUS_OK) {
+    aes_gcm_format_errors("Rekeying failed in key derivation.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, aead_key,
+                          nullptr)) {
+    aes_gcm_format_errors("Rekeying failed in context update.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_encrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* plaintext_vec, size_t plaintext_vec_length,
+    struct iovec ciphertext_vec, size_t* ciphertext_bytes_written,
+    char** error_details) {
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(crypter);
+  // Input checks
+  if (nonce == nullptr) {
+    aes_gcm_format_errors("Nonce buffer is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (kAesGcmNonceLength != nonce_length) {
+    aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (aad_vec_length > 0 && aad_vec == nullptr) {
+    aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (plaintext_vec_length > 0 && plaintext_vec == nullptr) {
+    aes_gcm_format_errors(
+        "Non-zero plaintext_vec_length but plaintext_vec is nullptr.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (ciphertext_bytes_written == nullptr) {
+    aes_gcm_format_errors("bytes_written is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  *ciphertext_bytes_written = 0;
+  // rekey if required
+  if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) !=
+      GRPC_STATUS_OK) {
+    return GRPC_STATUS_INTERNAL;
+  }
+  // mask nonce if required
+  const uint8_t* nonce_aead = nonce;
+  uint8_t nonce_masked[kAesGcmNonceLength];
+  if (aes_gcm_crypter->rekey_data != nullptr) {
+    aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask,
+                       nonce);
+    nonce_aead = nonce_masked;
+  }
+  // init openssl context
+  if (!EVP_EncryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr,
+                          nonce_aead)) {
+    aes_gcm_format_errors("Initializing nonce failed", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  // process aad
+  size_t i;
+  for (i = 0; i < aad_vec_length; i++) {
+    const uint8_t* aad = static_cast<uint8_t*>(aad_vec[i].iov_base);
+    size_t aad_length = aad_vec[i].iov_len;
+    if (aad_length == 0) {
+      continue;
+    }
+    size_t aad_bytes_read = 0;
+    if (aad == nullptr) {
+      aes_gcm_format_errors("aad is nullptr.", error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, nullptr,
+                           reinterpret_cast<int*>(&aad_bytes_read), aad,
+                           static_cast<int>(aad_length)) ||
+        aad_bytes_read != aad_length) {
+      aes_gcm_format_errors("Setting authenticated associated data failed",
+                            error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+  }
+  uint8_t* ciphertext = static_cast<uint8_t*>(ciphertext_vec.iov_base);
+  size_t ciphertext_length = ciphertext_vec.iov_len;
+  if (ciphertext == nullptr) {
+    aes_gcm_format_errors("ciphertext is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  // process plaintext
+  for (i = 0; i < plaintext_vec_length; i++) {
+    const uint8_t* plaintext = static_cast<uint8_t*>(plaintext_vec[i].iov_base);
+    size_t plaintext_length = plaintext_vec[i].iov_len;
+    if (plaintext == nullptr) {
+      if (plaintext_length == 0) {
+        continue;
+      }
+      aes_gcm_format_errors("plaintext is nullptr.", error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    if (ciphertext_length < plaintext_length) {
+      aes_gcm_format_errors(
+          "ciphertext is not large enough to hold the result.", error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    int bytes_written = 0;
+    int bytes_to_write = static_cast<int>(plaintext_length);
+    if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, ciphertext, &bytes_written,
+                           plaintext, bytes_to_write)) {
+      aes_gcm_format_errors("Encrypting plaintext failed.", error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+    if (bytes_written > bytes_to_write) {
+      aes_gcm_format_errors("More bytes written than expected.", error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+    ciphertext += bytes_written;
+    ciphertext_length -= bytes_written;
+  }
+  int bytes_written_temp = 0;
+  if (!EVP_EncryptFinal_ex(aes_gcm_crypter->ctx, nullptr,
+                           &bytes_written_temp)) {
+    aes_gcm_format_errors("Finalizing encryption failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (bytes_written_temp != 0) {
+    aes_gcm_format_errors("Openssl wrote some unexpected bytes.",
+                          error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (ciphertext_length < kAesGcmTagLength) {
+    aes_gcm_format_errors("ciphertext is too small to hold a tag.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+
+  if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_GET_TAG,
+                           kAesGcmTagLength, ciphertext)) {
+    aes_gcm_format_errors("Writing tag failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  ciphertext += kAesGcmTagLength;
+  ciphertext_length -= kAesGcmTagLength;
+  *ciphertext_bytes_written = ciphertext_vec.iov_len - ciphertext_length;
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code gsec_aes_gcm_aead_crypter_decrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* ciphertext_vec, size_t ciphertext_vec_length,
+    struct iovec plaintext_vec, size_t* plaintext_bytes_written,
+    char** error_details) {
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  if (nonce == nullptr) {
+    aes_gcm_format_errors("Nonce buffer is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (kAesGcmNonceLength != nonce_length) {
+    aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (aad_vec_length > 0 && aad_vec == nullptr) {
+    aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (ciphertext_vec_length > 0 && ciphertext_vec == nullptr) {
+    aes_gcm_format_errors(
+        "Non-zero plaintext_vec_length but plaintext_vec is nullptr.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  // Compute the total length so we can ensure we don't pass the tag into
+  // EVP_decrypt.
+  size_t total_ciphertext_length = 0;
+  size_t i;
+  for (i = 0; i < ciphertext_vec_length; i++) {
+    total_ciphertext_length += ciphertext_vec[i].iov_len;
+  }
+  if (total_ciphertext_length < kAesGcmTagLength) {
+    aes_gcm_format_errors("ciphertext is too small to hold a tag.",
+                          error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (plaintext_bytes_written == nullptr) {
+    aes_gcm_format_errors("bytes_written is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  *plaintext_bytes_written = 0;
+  // rekey if required
+  if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) !=
+      GRPC_STATUS_OK) {
+    aes_gcm_format_errors("Rekeying failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  // mask nonce if required
+  const uint8_t* nonce_aead = nonce;
+  uint8_t nonce_masked[kAesGcmNonceLength];
+  if (aes_gcm_crypter->rekey_data != nullptr) {
+    aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask,
+                       nonce);
+    nonce_aead = nonce_masked;
+  }
+  // init openssl context
+  if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr,
+                          nonce_aead)) {
+    aes_gcm_format_errors("Initializing nonce failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  // process aad
+  for (i = 0; i < aad_vec_length; i++) {
+    const uint8_t* aad = static_cast<uint8_t*>(aad_vec[i].iov_base);
+    size_t aad_length = aad_vec[i].iov_len;
+    if (aad_length == 0) {
+      continue;
+    }
+    size_t aad_bytes_read = 0;
+    if (aad == nullptr) {
+      aes_gcm_format_errors("aad is nullptr.", error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, nullptr,
+                           reinterpret_cast<int*>(&aad_bytes_read), aad,
+                           static_cast<int>(aad_length)) ||
+        aad_bytes_read != aad_length) {
+      aes_gcm_format_errors("Setting authenticated associated data failed.",
+                            error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+  }
+  // process ciphertext
+  uint8_t* plaintext = static_cast<uint8_t*>(plaintext_vec.iov_base);
+  size_t plaintext_length = plaintext_vec.iov_len;
+  if (plaintext_length > 0 && plaintext == nullptr) {
+    aes_gcm_format_errors(
+        "plaintext is nullptr, but plaintext_length is positive.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  const uint8_t* ciphertext = nullptr;
+  size_t ciphertext_length = 0;
+  for (i = 0;
+       i < ciphertext_vec_length && total_ciphertext_length > kAesGcmTagLength;
+       i++) {
+    ciphertext = static_cast<uint8_t*>(ciphertext_vec[i].iov_base);
+    ciphertext_length = ciphertext_vec[i].iov_len;
+    if (ciphertext == nullptr) {
+      if (ciphertext_length == 0) {
+        continue;
+      }
+      aes_gcm_format_errors("ciphertext is nullptr.", error_details);
+      memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    size_t bytes_written = 0;
+    size_t bytes_to_write = ciphertext_length;
+    // Don't include the tag
+    if (bytes_to_write > total_ciphertext_length - kAesGcmTagLength) {
+      bytes_to_write = total_ciphertext_length - kAesGcmTagLength;
+    }
+    if (plaintext_length < bytes_to_write) {
+      aes_gcm_format_errors(
+          "Not enough plaintext buffer to hold encrypted ciphertext.",
+          error_details);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, plaintext,
+                           reinterpret_cast<int*>(&bytes_written), ciphertext,
+                           static_cast<int>(bytes_to_write))) {
+      aes_gcm_format_errors("Decrypting ciphertext failed.", error_details);
+      memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+      return GRPC_STATUS_INTERNAL;
+    }
+    if (bytes_written > ciphertext_length) {
+      aes_gcm_format_errors("More bytes written than expected.", error_details);
+      memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+      return GRPC_STATUS_INTERNAL;
+    }
+    ciphertext += bytes_written;
+    ciphertext_length -= bytes_written;
+    total_ciphertext_length -= bytes_written;
+    plaintext += bytes_written;
+    plaintext_length -= bytes_written;
+  }
+  if (total_ciphertext_length > kAesGcmTagLength) {
+    aes_gcm_format_errors(
+        "Not enough plaintext buffer to hold encrypted ciphertext.",
+        error_details);
+    memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  uint8_t tag[kAesGcmTagLength];
+  uint8_t* tag_tmp = tag;
+  if (ciphertext_length > 0) {
+    memcpy(tag_tmp, ciphertext, ciphertext_length);
+    tag_tmp += ciphertext_length;
+    total_ciphertext_length -= ciphertext_length;
+  }
+  for (; i < ciphertext_vec_length; i++) {
+    ciphertext = static_cast<uint8_t*>(ciphertext_vec[i].iov_base);
+    ciphertext_length = ciphertext_vec[i].iov_len;
+    if (ciphertext == nullptr) {
+      if (ciphertext_length == 0) {
+        continue;
+      }
+      aes_gcm_format_errors("ciphertext is nullptr.", error_details);
+      memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+      return GRPC_STATUS_INVALID_ARGUMENT;
+    }
+    memcpy(tag_tmp, ciphertext, ciphertext_length);
+    tag_tmp += ciphertext_length;
+    total_ciphertext_length -= ciphertext_length;
+  }
+  if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_TAG,
+                           kAesGcmTagLength, reinterpret_cast<void*>(tag))) {
+    aes_gcm_format_errors("Setting tag failed.", error_details);
+    memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+    return GRPC_STATUS_INTERNAL;
+  }
+  int bytes_written_temp = 0;
+  if (!EVP_DecryptFinal_ex(aes_gcm_crypter->ctx, nullptr,
+                           &bytes_written_temp)) {
+    aes_gcm_format_errors("Checking tag failed.", error_details);
+    memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (bytes_written_temp != 0) {
+    aes_gcm_format_errors("Openssl wrote some unexpected bytes.",
+                          error_details);
+    memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len);
+    return GRPC_STATUS_INTERNAL;
+  }
+  *plaintext_bytes_written = plaintext_vec.iov_len - plaintext_length;
+  return GRPC_STATUS_OK;
+}
+
+static void gsec_aes_gcm_aead_crypter_destroy(gsec_aead_crypter* crypter) {
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      reinterpret_cast<gsec_aes_gcm_aead_crypter*>(
+          const_cast<gsec_aead_crypter*>(crypter));
+  gpr_free(aes_gcm_crypter->key);
+  gpr_free(aes_gcm_crypter->rekey_data);
+  EVP_CIPHER_CTX_free(aes_gcm_crypter->ctx);
+}
+
+static const gsec_aead_crypter_vtable vtable = {
+    gsec_aes_gcm_aead_crypter_encrypt_iovec,
+    gsec_aes_gcm_aead_crypter_decrypt_iovec,
+    gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length,
+    gsec_aes_gcm_aead_crypter_max_plaintext_length,
+    gsec_aes_gcm_aead_crypter_nonce_length,
+    gsec_aes_gcm_aead_crypter_key_length,
+    gsec_aes_gcm_aead_crypter_tag_length,
+    gsec_aes_gcm_aead_crypter_destroy};
+
+static grpc_status_code aes_gcm_new_evp_cipher_ctx(
+    gsec_aes_gcm_aead_crypter* aes_gcm_crypter, char** error_details) {
+  const EVP_CIPHER* cipher = nullptr;
+  bool is_rekey = aes_gcm_crypter->rekey_data != nullptr;
+  switch (is_rekey ? kRekeyAeadKeyLen : aes_gcm_crypter->key_length) {
+    case kAes128GcmKeyLength:
+      cipher = EVP_aes_128_gcm();
+      break;
+    case kAes256GcmKeyLength:
+      cipher = EVP_aes_256_gcm();
+      break;
+  }
+  const uint8_t* aead_key = aes_gcm_crypter->key;
+  uint8_t aead_key_rekey[kRekeyAeadKeyLen];
+  if (is_rekey) {
+    if (aes_gcm_derive_aead_key(aead_key_rekey, aes_gcm_crypter->key,
+                                aes_gcm_crypter->rekey_data->kdf_counter) !=
+        GRPC_STATUS_OK) {
+      aes_gcm_format_errors("Deriving key failed.", error_details);
+      return GRPC_STATUS_INTERNAL;
+    }
+    aead_key = aead_key_rekey;
+  }
+  if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, cipher, nullptr, aead_key,
+                          nullptr)) {
+    aes_gcm_format_errors("Setting key failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_IVLEN,
+                           static_cast<int>(aes_gcm_crypter->nonce_length),
+                           nullptr)) {
+    aes_gcm_format_errors("Setting nonce length failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key,
+                                                  size_t key_length,
+                                                  size_t nonce_length,
+                                                  size_t tag_length, bool rekey,
+                                                  gsec_aead_crypter** crypter,
+                                                  char** error_details) {
+  if (key == nullptr) {
+    aes_gcm_format_errors("key is nullptr.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (crypter == nullptr) {
+    aes_gcm_format_errors("crypter is nullptr.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  *crypter = nullptr;
+  if ((rekey && key_length != kAes128GcmRekeyKeyLength) ||
+      (!rekey && key_length != kAes128GcmKeyLength &&
+       key_length != kAes256GcmKeyLength) ||
+      (tag_length != kAesGcmTagLength) ||
+      (nonce_length != kAesGcmNonceLength)) {
+    aes_gcm_format_errors(
+        "Invalid key and/or nonce and/or tag length are provided at AEAD "
+        "crypter instance construction time.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  gsec_aes_gcm_aead_crypter* aes_gcm_crypter =
+      static_cast<gsec_aes_gcm_aead_crypter*>(
+          gpr_malloc(sizeof(gsec_aes_gcm_aead_crypter)));
+  aes_gcm_crypter->crypter.vtable = &vtable;
+  aes_gcm_crypter->nonce_length = nonce_length;
+  aes_gcm_crypter->tag_length = tag_length;
+  if (rekey) {
+    aes_gcm_crypter->key_length = kKdfKeyLen;
+    aes_gcm_crypter->rekey_data = static_cast<gsec_aes_gcm_aead_rekey_data*>(
+        gpr_malloc(sizeof(gsec_aes_gcm_aead_rekey_data)));
+    memcpy(aes_gcm_crypter->rekey_data->nonce_mask, key + kKdfKeyLen,
+           kAesGcmNonceLength);
+    // Set kdf_counter to all-zero for initial key derivation.
+    memset(aes_gcm_crypter->rekey_data->kdf_counter, 0, kKdfCounterLen);
+  } else {
+    aes_gcm_crypter->key_length = key_length;
+    aes_gcm_crypter->rekey_data = nullptr;
+  }
+  aes_gcm_crypter->key =
+      static_cast<uint8_t*>(gpr_malloc(aes_gcm_crypter->key_length));
+  memcpy(aes_gcm_crypter->key, key, aes_gcm_crypter->key_length);
+  aes_gcm_crypter->ctx = EVP_CIPHER_CTX_new();
+  grpc_status_code status =
+      aes_gcm_new_evp_cipher_ctx(aes_gcm_crypter, error_details);
+  if (status != GRPC_STATUS_OK) {
+    gsec_aes_gcm_aead_crypter_destroy(&aes_gcm_crypter->crypter);
+    gpr_free(aes_gcm_crypter);
+    return status;
+  }
+  *crypter = &aes_gcm_crypter->crypter;
+  return GRPC_STATUS_OK;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/crypt/gsec.cc b/third_party/grpc/src/core/tsi/alts/crypt/gsec.cc
new file mode 100644
index 0000000..6236591
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/crypt/gsec.cc
@@ -0,0 +1,189 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+static const char vtable_error_msg[] =
+    "crypter or crypter->vtable has not been initialized properly";
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+grpc_status_code gsec_aead_crypter_encrypt(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const uint8_t* aad, size_t aad_length, const uint8_t* plaintext,
+    size_t plaintext_length, uint8_t* ciphertext_and_tag,
+    size_t ciphertext_and_tag_length, size_t* bytes_written,
+    char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->encrypt_iovec != nullptr) {
+    struct iovec aad_vec = {(void*)aad, aad_length};
+    struct iovec plaintext_vec = {(void*)plaintext, plaintext_length};
+    struct iovec ciphertext_vec = {ciphertext_and_tag,
+                                   ciphertext_and_tag_length};
+    return crypter->vtable->encrypt_iovec(
+        crypter, nonce, nonce_length, &aad_vec, 1, &plaintext_vec, 1,
+        ciphertext_vec, bytes_written, error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_encrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* plaintext_vec, size_t plaintext_vec_length,
+    struct iovec ciphertext_vec, size_t* ciphertext_bytes_written,
+    char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->encrypt_iovec != nullptr) {
+    return crypter->vtable->encrypt_iovec(
+        crypter, nonce, nonce_length, aad_vec, aad_vec_length, plaintext_vec,
+        plaintext_vec_length, ciphertext_vec, ciphertext_bytes_written,
+        error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_decrypt(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag,
+    size_t ciphertext_and_tag_length, uint8_t* plaintext,
+    size_t plaintext_length, size_t* bytes_written, char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->decrypt_iovec != nullptr) {
+    struct iovec aad_vec = {(void*)aad, aad_length};
+    struct iovec ciphertext_vec = {(void*)ciphertext_and_tag,
+                                   ciphertext_and_tag_length};
+    struct iovec plaintext_vec = {plaintext, plaintext_length};
+    return crypter->vtable->decrypt_iovec(
+        crypter, nonce, nonce_length, &aad_vec, 1, &ciphertext_vec, 1,
+        plaintext_vec, bytes_written, error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_decrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* ciphertext_vec, size_t ciphertext_vec_length,
+    struct iovec plaintext_vec, size_t* plaintext_bytes_written,
+    char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->encrypt_iovec != nullptr) {
+    return crypter->vtable->decrypt_iovec(
+        crypter, nonce, nonce_length, aad_vec, aad_vec_length, ciphertext_vec,
+        ciphertext_vec_length, plaintext_vec, plaintext_bytes_written,
+        error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length(
+    const gsec_aead_crypter* crypter, size_t plaintext_length,
+    size_t* max_ciphertext_and_tag_length_to_return, char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->max_ciphertext_and_tag_length != nullptr) {
+    return crypter->vtable->max_ciphertext_and_tag_length(
+        crypter, plaintext_length, max_ciphertext_and_tag_length_to_return,
+        error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_max_plaintext_length(
+    const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
+    size_t* max_plaintext_length_to_return, char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->max_plaintext_length != nullptr) {
+    return crypter->vtable->max_plaintext_length(
+        crypter, ciphertext_and_tag_length, max_plaintext_length_to_return,
+        error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_nonce_length(
+    const gsec_aead_crypter* crypter, size_t* nonce_length_to_return,
+    char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->nonce_length != nullptr) {
+    return crypter->vtable->nonce_length(crypter, nonce_length_to_return,
+                                         error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter,
+                                              size_t* key_length_to_return,
+                                              char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->key_length != nullptr) {
+    return crypter->vtable->key_length(crypter, key_length_to_return,
+                                       error_details);
+  }
+  /* An error occurred */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter,
+                                              size_t* tag_length_to_return,
+                                              char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->tag_length != nullptr) {
+    return crypter->vtable->tag_length(crypter, tag_length_to_return,
+                                       error_details);
+  }
+  /* An error occurred. */
+  maybe_copy_error_msg(vtable_error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter) {
+  if (crypter != nullptr) {
+    if (crypter->vtable != nullptr && crypter->vtable->destruct != nullptr) {
+      crypter->vtable->destruct(crypter);
+    }
+    gpr_free(crypter);
+  }
+}
diff --git a/third_party/grpc/src/core/tsi/alts/crypt/gsec.h b/third_party/grpc/src/core/tsi/alts/crypt/gsec.h
new file mode 100644
index 0000000..4d65caa
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/crypt/gsec.h
@@ -0,0 +1,454 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H
+#define GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <grpc/grpc.h>
+
+struct iovec {
+  void* iov_base;
+  size_t iov_len;
+};
+
+/**
+ * A gsec interface for AEAD encryption schemes. The API is thread-compatible.
+ * Each implementation of this interface should specify supported values for
+ * key, nonce, and tag lengths.
+ */
+
+/* Key, nonce, and tag length in bytes */
+const size_t kAesGcmNonceLength = 12;
+const size_t kAesGcmTagLength = 16;
+const size_t kAes128GcmKeyLength = 16;
+const size_t kAes256GcmKeyLength = 32;
+
+// The first 32 bytes are used as a KDF key and the remaining 12 bytes are used
+// to mask the nonce.
+const size_t kAes128GcmRekeyKeyLength = 44;
+
+typedef struct gsec_aead_crypter gsec_aead_crypter;
+
+/**
+ * The gsec_aead_crypter is an API for different AEAD implementations such as
+ * AES_GCM. It encapsulates all AEAD-related operations in the format of
+ * V-table that stores pointers to functions implementing those operations.
+ * It also provides helper functions to wrap each of those function pointers.
+ *
+ * A typical usage of this object would be:
+ *
+ *------------------------------------------------------------------------------
+ * // Declare a gsec_aead_crypter object, and create and assign an instance
+ * // of specific AEAD implementation e.g., AES_GCM to it. We assume both
+ * // key and nonce contain cryptographically secure random bytes, and the key
+ * // can be derived from an upper-layer application.
+ * gsec_aead_crypter* crypter;
+ * char* error_in_creation;
+ * // User can populate the message with any 100 bytes data.
+ * uint8_t* message = gpr_malloc(100);
+ * grpc_status_code creation_status = gsec_aes_gcm_aead_crypter_create(key,
+ *                                                      kAes128GcmKeyLength,
+ *                                                      kAesGcmNonceLength,
+ *                                                      kAesGcmTagLength,
+ *                                                      &crypter,
+ *                                                      false,
+ *                                                      0
+ *                                                      &error_in_creation);
+ *
+ * if (creation_status == GRPC_STATUS_OK) {
+ *    // Allocate a correct amount of memory to hold a ciphertext.
+ *    size_t clength = 0;
+ *    gsec_aead_crypter_max_ciphertext_and_tag_length(crypter, 100, &clength,
+ *                                                    nullptr);
+ *    uint8_t* ciphertext = gpr_malloc(clength);
+ *
+ *    // Perform encryption
+ *    size_t num_encrypted_bytes = 0;
+ *    char* error_in_encryption = nullptr;
+ *    grpc_status_code status = gsec_aead_crypter_encrypt(crypter, nonce,
+ *                                                        kAesGcmNonceLength,
+ *                                                        nullptr, 0, message,
+ *                                                        100, ciphertext,
+ *                                                        clength,
+ *                                                        &num_encrypted_bytes,
+ *                                                        &error_in_encryption);
+ * if (status == GRPC_STATUS_OK) {
+ *       // Allocate a correct amount of memory to hold a plaintext.
+ *       size_t plength = 0;
+ *       gsec_aead_crypter_max_plaintext_length(crypter, num_encrypted_bytes,
+ *                                              &plength, nullptr);
+ *       uint8_t* plaintext = gpr_malloc(plength);
+ *
+ *       // Perform decryption.
+ *       size_t num_decrypted_bytes = 0;
+ *       char* error_in_decryption = nullptr;
+ *       status = gsec_aead_crypter_decrypt(crypter, nonce,
+ *                                          kAesGcmNonceLength, nullptr, 0,
+ *                                          ciphertext, num_encrypted_bytes,
+ *                                          plaintext, plength,
+ *                                          &num_decrypted_bytes,
+ *                                          &error_in_decryption);
+ *       if (status != GRPC_STATUS_OK) {
+ *         fprintf(stderr, "AEAD decrypt operation failed with error code:"
+ *                         "%d, message: %s\n", status, error_in_decryption);
+ *       }
+ *       ...
+ *       gpr_free(plaintext);
+ *       gpr_free(error_in_decryption);
+ *    } else {
+ *        fprintf(stderr, "AEAD encrypt operation failed with error code:"
+ *                        "%d, message: %s\n", status, error_in_encryption);
+ *    }
+ *    ...
+ *    gpr_free(ciphertext);
+ *    gpr_free(error_in_encryption);
+ * } else {
+ *   fprintf(stderr, "Creation of AEAD crypter instance failed with error code:"
+ *                   "%d, message: %s\n", creation_status, error_in_creation);
+ * }
+ *
+ * // Destruct AEAD crypter instance.
+ * if (creation_status == GRPC_STATUS_OK) {
+ *   gsec_aead_crypter_destroy(crypter);
+ * }
+ * gpr_free(error_in_creation);
+ * gpr_free(message);
+ * -----------------------------------------------------------------------------
+ */
+
+/* V-table for gsec AEAD operations */
+typedef struct gsec_aead_crypter_vtable {
+  grpc_status_code (*encrypt_iovec)(
+      gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+      const struct iovec* aad_vec, size_t aad_vec_length,
+      const struct iovec* plaintext_vec, size_t plaintext_vec_length,
+      struct iovec ciphertext_vec, size_t* ciphertext_bytes_written,
+      char** error_details);
+  grpc_status_code (*decrypt_iovec)(
+      gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+      const struct iovec* aad_vec, size_t aad_vec_length,
+      const struct iovec* ciphertext_vec, size_t ciphertext_vec_length,
+      struct iovec plaintext_vec, size_t* plaintext_bytes_written,
+      char** error_details);
+  grpc_status_code (*max_ciphertext_and_tag_length)(
+      const gsec_aead_crypter* crypter, size_t plaintext_length,
+      size_t* max_ciphertext_and_tag_length_to_return, char** error_details);
+  grpc_status_code (*max_plaintext_length)(
+      const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
+      size_t* max_plaintext_length_to_return, char** error_details);
+  grpc_status_code (*nonce_length)(const gsec_aead_crypter* crypter,
+                                   size_t* nonce_length_to_return,
+                                   char** error_details);
+  grpc_status_code (*key_length)(const gsec_aead_crypter* crypter,
+                                 size_t* key_length_to_return,
+                                 char** error_details);
+  grpc_status_code (*tag_length)(const gsec_aead_crypter* crypter,
+                                 size_t* tag_length_to_return,
+                                 char** error_details);
+  void (*destruct)(gsec_aead_crypter* crypter);
+} gsec_aead_crypter_vtable;
+
+/* Main struct for gsec interface */
+struct gsec_aead_crypter {
+  const struct gsec_aead_crypter_vtable* vtable;
+};
+
+/**
+ * This method performs an AEAD encrypt operation.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce: buffer containing a nonce with its size equal to nonce_length.
+ * - nonce_length: size of nonce buffer, and must be equal to the value returned
+ *   from method gsec_aead_crypter_nonce_length.
+ * - aad: buffer containing data that needs to be authenticated but not
+ *   encrypted with its size equal to aad_length.
+ * - aad_length: size of aad buffer, which should be zero if the buffer is
+ *   nullptr.
+ * - plaintext: buffer containing data that needs to be both encrypted and
+ *   authenticated with its size equal to plaintext_length.
+ * - plaintext_length: size of plaintext buffer, which should be zero if
+ *   plaintext is nullptr.
+ * - ciphertext_and_tag: buffer that will contain ciphertext and tags the method
+ *   produced. The buffer should not overlap the plaintext buffer, and pointers
+ *   to those buffers should not be equal. Also if the ciphertext+tag buffer is
+ *   nullptr, the plaintext_length should be zero.
+ * - ciphertext_and_tag_length: size of ciphertext+tag buffer, which should be
+ *   at least as long as the one returned from method
+ *   gsec_aead_crypter_max_ciphertext_and_tag_length.
+ * - bytes_written: the actual number of bytes written to the ciphertext+tag
+ *   buffer. If bytes_written is nullptr, the plaintext_length should be zero.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ *
+ */
+grpc_status_code gsec_aead_crypter_encrypt(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const uint8_t* aad, size_t aad_length, const uint8_t* plaintext,
+    size_t plaintext_length, uint8_t* ciphertext_and_tag,
+    size_t ciphertext_and_tag_length, size_t* bytes_written,
+    char** error_details);
+
+/**
+ * This method performs an AEAD encrypt operation.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce: buffer containing a nonce with its size equal to nonce_length.
+ * - nonce_length: size of nonce buffer, and must be equal to the value returned
+ *   from method gsec_aead_crypter_nonce_length.
+ * - aad_vec: an iovec array containing data that needs to be authenticated but
+ *   not encrypted.
+ * - aad_vec_length: the array length of aad_vec.
+ * - plaintext_vec: an iovec array containing data that needs to be both
+ *   encrypted and authenticated.
+ * - plaintext_vec_length: the array length of plaintext_vec.
+ * - ciphertext_vec: an iovec containing a ciphertext buffer. The buffer should
+ *   not overlap the plaintext buffer.
+ * - ciphertext_bytes_written: the actual number of bytes written to
+ *   ciphertext_vec.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ *
+ */
+grpc_status_code gsec_aead_crypter_encrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* plaintext_vec, size_t plaintext_vec_length,
+    struct iovec ciphertext_vec, size_t* ciphertext_bytes_written,
+    char** error_details);
+
+/**
+ * This method performs an AEAD decrypt operation.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce: buffer containing a nonce with its size equal to nonce_length.
+ * - nonce_length: size of nonce buffer, and must be equal to the value returned
+ *   from method gsec_aead_crypter_nonce_length.
+ * - aad: buffer containing data that needs to be authenticated only.
+ * - aad_length: size of aad buffer, which should be zero if the buffer is
+ *   nullptr.
+ * - ciphertext_and_tag: buffer containing ciphertext and tag.
+ * - ciphertext_and_tag_length: length of ciphertext and tag. It should be zero
+ *   if any of plaintext, ciphertext_and_tag, or bytes_written is nullptr. Also,
+ *   ciphertext_and_tag_length should be at least as large as the tag length set
+ *   at AEAD crypter instance construction time.
+ * - plaintext: buffer containing decrypted and authenticated data the method
+ *   produced. The buffer should not overlap with the ciphertext+tag buffer, and
+ *   pointers to those buffers should not be equal.
+ * - plaintext_length: size of plaintext buffer, which should be at least as
+ *   long as the one returned from gsec_aead_crypter_max_plaintext_length
+ *   method.
+ * - bytes_written: the actual number of bytes written to the plaintext
+ *   buffer.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_decrypt(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag,
+    size_t ciphertext_and_tag_length, uint8_t* plaintext,
+    size_t plaintext_length, size_t* bytes_written, char** error_details);
+
+/**
+ * This method performs an AEAD decrypt operation.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce: buffer containing a nonce with its size equal to nonce_length.
+ * - nonce_length: size of nonce buffer, and must be equal to the value returned
+ *   from method gsec_aead_crypter_nonce_length.
+ * - aad_vec: an iovec array containing data that needs to be authenticated but
+ *   not encrypted.
+ * - aad_vec_length: the array length of aad_vec.
+ * - ciphertext_vec: an iovec array containing the ciphertext and tag.
+ * - ciphertext_vec_length: the array length of ciphertext_vec.
+ * - plaintext_vec: an iovec containing a plaintext buffer. The buffer should
+ *   not overlap the ciphertext buffer.
+ * - plaintext_bytes_written: the actual number of bytes written to
+ *   plaintext_vec.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_decrypt_iovec(
+    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
+    const struct iovec* aad_vec, size_t aad_vec_length,
+    const struct iovec* ciphertext_vec, size_t ciphertext_vec_length,
+    struct iovec plaintext_vec, size_t* plaintext_bytes_written,
+    char** error_details);
+
+/**
+ * This method computes the size of ciphertext+tag buffer that must be passed to
+ * gsec_aead_crypter_encrypt function to ensure correct encryption of a
+ * plaintext. The actual size of ciphertext+tag written to the buffer could be
+ * smaller.
+ *
+ * - crypter: AEAD crypter instance.
+ * - plaintext_length: length of plaintext.
+ * - max_ciphertext_and_tag_length_to_return: the size of ciphertext+tag buffer
+ *   the method returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length(
+    const gsec_aead_crypter* crypter, size_t plaintext_length,
+    size_t* max_ciphertext_and_tag_length_to_return, char** error_details);
+
+/**
+ * This method computes the size of plaintext buffer that must be passed to
+ * gsec_aead_crypter_decrypt function to ensure correct decryption of a
+ * ciphertext. The actual size of plaintext written to the buffer could be
+ * smaller.
+ *
+ * - crypter: AEAD crypter instance.
+ * - ciphertext_and_tag_length: length of ciphertext and tag.
+ * - max_plaintext_length_to_return: the size of plaintext buffer the method
+ *   returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_max_plaintext_length(
+    const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
+    size_t* max_plaintext_length_to_return, char** error_details);
+
+/**
+ * This method returns a valid size of nonce array used at the construction of
+ * AEAD crypter instance. It is also the size that should be passed to encrypt
+ * and decrypt methods executed on the instance.
+ *
+ * - crypter: AEAD crypter instance.
+ * - nonce_length_to_return: the length of nonce array the method returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_nonce_length(
+    const gsec_aead_crypter* crypter, size_t* nonce_length_to_return,
+    char** error_details);
+
+/**
+ * This method returns a valid size of key array used at the construction of
+ * AEAD crypter instance. It is also the size that should be passed to encrypt
+ * and decrypt methods executed on the instance.
+ *
+ * - crypter: AEAD crypter instance.
+ * - key_length_to_return: the length of key array the method returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter,
+                                              size_t* key_length_to_return,
+                                              char** error_details);
+/**
+ * This method returns a valid size of tag array used at the construction of
+ * AEAD crypter instance. It is also the size that should be passed to encrypt
+ * and decrypt methods executed on the instance.
+ *
+ * - crypter: AEAD crypter instance.
+ * - tag_length_to_return: the length of tag array the method returns.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter,
+                                              size_t* tag_length_to_return,
+                                              char** error_details);
+
+/**
+ * This method destroys an AEAD crypter instance by de-allocating all of its
+ * occupied memory.
+ *
+ * - crypter: AEAD crypter instance that needs to be destroyed.
+ */
+void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter);
+
+/**
+ * This method creates an AEAD crypter instance of AES-GCM encryption scheme
+ * which supports 16 and 32 bytes long keys, 12 and 16 bytes long nonces, and
+ * 16 bytes long tags. It should be noted that once the lengths of key, nonce,
+ * and tag are determined at construction time, they cannot be modified later.
+ *
+ * - key: buffer containing a key which is binded with AEAD crypter instance.
+ * - key_length: length of a key in bytes, which should be 44 if rekeying is
+ *   enabled and 16 or 32 otherwise.
+ * - nonce_length: length of a nonce in bytes, which should be either 12 or 16.
+ * - tag_length: length of a tag in bytes, which should be always 16.
+ * - rekey: enable nonce-based rekeying and nonce-masking.
+ * - crypter: address of AES_GCM crypter instance returned from the method.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success of instance creation, it stores the address of instance at
+ * crypter. Otherwise, it returns an error status code together with its details
+ * specified in error_details.
+ */
+grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key,
+                                                  size_t key_length,
+                                                  size_t nonce_length,
+                                                  size_t tag_length, bool rekey,
+                                                  gsec_aead_crypter** crypter,
+                                                  char** error_details);
+
+#endif /* GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H */
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.cc
new file mode 100644
index 0000000..de163e3
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.cc
@@ -0,0 +1,118 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+grpc_status_code alts_counter_create(bool is_client, size_t counter_size,
+                                     size_t overflow_size,
+                                     alts_counter** crypter_counter,
+                                     char** error_details) {
+  /* Perform input sanity check. */
+  if (counter_size == 0) {
+    const char error_msg[] = "counter_size is invalid.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (overflow_size == 0 || overflow_size >= counter_size) {
+    const char error_msg[] = "overflow_size is invalid.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (crypter_counter == nullptr) {
+    const char error_msg[] = "crypter_counter is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  *crypter_counter =
+      static_cast<alts_counter*>(gpr_malloc(sizeof(**crypter_counter)));
+  (*crypter_counter)->size = counter_size;
+  (*crypter_counter)->overflow_size = overflow_size;
+  (*crypter_counter)->counter =
+      static_cast<unsigned char*>(gpr_zalloc(counter_size));
+  if (is_client) {
+    ((*crypter_counter)->counter)[counter_size - 1] = 0x80;
+  }
+  return GRPC_STATUS_OK;
+}
+
+grpc_status_code alts_counter_increment(alts_counter* crypter_counter,
+                                        bool* is_overflow,
+                                        char** error_details) {
+  /* Perform input sanity check. */
+  if (crypter_counter == nullptr) {
+    const char error_msg[] = "crypter_counter is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (is_overflow == nullptr) {
+    const char error_msg[] = "is_overflow is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Increment the internal counter. */
+  size_t i = 0;
+  for (; i < crypter_counter->overflow_size; i++) {
+    (crypter_counter->counter)[i]++;
+    if ((crypter_counter->counter)[i] != 0x00) {
+      break;
+    }
+  }
+  /**
+   * If the lower overflow_size bytes are all zero, the counter has overflowed.
+   */
+  if (i == crypter_counter->overflow_size) {
+    *is_overflow = true;
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  *is_overflow = false;
+  return GRPC_STATUS_OK;
+}
+
+size_t alts_counter_get_size(alts_counter* crypter_counter) {
+  if (crypter_counter == nullptr) {
+    return 0;
+  }
+  return crypter_counter->size;
+}
+
+unsigned char* alts_counter_get_counter(alts_counter* crypter_counter) {
+  if (crypter_counter == nullptr) {
+    return nullptr;
+  }
+  return crypter_counter->counter;
+}
+
+void alts_counter_destroy(alts_counter* crypter_counter) {
+  if (crypter_counter != nullptr) {
+    gpr_free(crypter_counter->counter);
+    gpr_free(crypter_counter);
+  }
+}
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.h b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.h
new file mode 100644
index 0000000..d705638
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.h
@@ -0,0 +1,98 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <grpc/grpc.h>
+
+/* Main struct for a crypter counter managed within seal/unseal operations. */
+typedef struct alts_counter {
+  size_t size;
+  size_t overflow_size;
+  unsigned char* counter;
+} alts_counter;
+
+/**
+ * This method creates and initializes an alts_counter instance.
+ *
+ * - is_client: a flag indicating if the alts_counter instance will be used
+ *   at client (is_client = true) or server (is_client = false) side.
+ * - counter_size: size of buffer holding the counter value.
+ * - overflow_size: overflow size in bytes. The counter instance can be used
+ *   to produce at most 2^(overflow_size*8) frames.
+ * - crypter_counter: an alts_counter instance to be returned from the method.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_counter_create(bool is_client, size_t counter_size,
+                                     size_t overflow_size,
+                                     alts_counter** crypter_counter,
+                                     char** error_details);
+
+/**
+ * This method increments the internal counter.
+ *
+ * - crypter_counter: an alts_counter instance.
+ * - is_overflow: after incrementing the internal counter, if an overflow
+ *   occurs, is_overflow is set to true, and no further calls to
+ *   alts_counter_increment() should be made. Otherwise, is_overflow is set to
+ *   false.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_counter_increment(alts_counter* crypter_counter,
+                                        bool* is_overflow,
+                                        char** error_details);
+
+/**
+ * This method returns the size of counter buffer.
+ *
+ * - crypter_counter: an alts_counter instance.
+ */
+size_t alts_counter_get_size(alts_counter* crypter_counter);
+
+/**
+ * This method returns the counter buffer.
+ *
+ * - crypter_counter: an alts_counter instance.
+ */
+unsigned char* alts_counter_get_counter(alts_counter* crypter_counter);
+
+/**
+ * This method de-allocates all memory allocated to an alts_coutner instance.
+ * - crypter_counter: an alts_counter instance.
+ */
+void alts_counter_destroy(alts_counter* crypter_counter);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H */
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.cc
new file mode 100644
index 0000000..56f0512
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.cc
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+grpc_status_code alts_crypter_process_in_place(
+    alts_crypter* crypter, unsigned char* data, size_t data_allocated_size,
+    size_t data_size, size_t* output_size, char** error_details) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->process_in_place != nullptr) {
+    return crypter->vtable->process_in_place(crypter, data, data_allocated_size,
+                                             data_size, output_size,
+                                             error_details);
+  }
+  /* An error occurred. */
+  const char error_msg[] =
+      "crypter or crypter->vtable has not been initialized properly.";
+  maybe_copy_error_msg(error_msg, error_details);
+  return GRPC_STATUS_INVALID_ARGUMENT;
+}
+
+size_t alts_crypter_num_overhead_bytes(const alts_crypter* crypter) {
+  if (crypter != nullptr && crypter->vtable != nullptr &&
+      crypter->vtable->num_overhead_bytes != nullptr) {
+    return crypter->vtable->num_overhead_bytes(crypter);
+  }
+  /* An error occurred. */
+  return 0;
+}
+
+void alts_crypter_destroy(alts_crypter* crypter) {
+  if (crypter != nullptr) {
+    if (crypter->vtable != nullptr && crypter->vtable->destruct != nullptr) {
+      crypter->vtable->destruct(crypter);
+    }
+    gpr_free(crypter);
+  }
+}
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.h b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.h
new file mode 100644
index 0000000..3140778
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.h
@@ -0,0 +1,255 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+/**
+ * An alts_crypter interface for an ALTS record protocol providing
+ * seal/unseal functionality. The interface is thread-compatible.
+ */
+
+typedef struct alts_crypter alts_crypter;
+
+/**
+ * A typical usage of the interface would be
+ *------------------------------------------------------------------------------
+ * // Perform a seal operation. We assume the gsec_aead_crypter instance -
+ * // client_aead_crypter is created beforehand with a 16-byte key and 12-byte
+ * // nonce length.
+ *
+ * alts_crypter* client = nullptr;
+ * char* client_error_in_creation = nullptr;
+ * unsigned char* data = nullptr;
+ * grpc_status_code client_status =
+ *                 alts_seal_crypter_create(client_aead_crypter, 1, 5, &client,
+ *                                          &client_error_in_creation);
+ * if (client_status == GRPC_STATUS_OK) {
+ *   size_t data_size = 100;
+ *   size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(client);
+ *   size_t data_allocated_size = data_size + num_overhead_bytes;
+ *   data = gpr_malloc(data_allocated_size);
+ *   char* client_error_in_seal = nullptr;
+ *   // Client performs a seal operation.
+ *   client_status = alts_crypter_process_in_place(client, data,
+ *                                                 data_allocated_size,
+ *                                                 &data_size,
+ *                                                 &client_error_in_seal);
+ *   if (client_status != GRPC_STATUS_OK) {
+ *     fprintf(stderr, "seal operation failed with error code:"
+ *                     "%d, message: %s\n", client_status,
+ *                      client_error_in_seal);
+ *    }
+ *    gpr_free(client_error_in_seal);
+ * } else {
+ *     fprintf(stderr, "alts_crypter instance creation failed with error"
+ *                     "code: %d, message: %s\n", client_status,
+ *                      client_error_in_creation);
+ * }
+ *
+ * ...
+ *
+ * gpr_free(client_error_in_creation);
+ * alts_crypter_destroy(client);
+ *
+ * ...
+ *
+ * // Perform an unseal operation. We assume the gsec_aead_crypter instance -
+ * // server_aead_crypter is created beforehand with a 16-byte key and 12-byte
+ * // nonce length. The key used in the creation of gsec_aead_crypter instances
+ * // at server and client sides should be identical.
+ *
+ * alts_crypter* server = nullptr;
+ * char* server_error_in_creation = nullptr;
+ * grpc_status_code server_status =
+ *               alts_unseal_crypter_create(server_aead_crypter, 0, 5, &server,
+ *                                          &server_error_in_creation);
+ * if (server_status == GRPC_STATUS_OK) {
+ *   size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(server);
+ *   size_t data_size = 100 + num_overhead_bytes;
+ *   size_t data_allocated_size = data_size;
+ *   char* server_error_in_unseal = nullptr;
+ *   // Server performs an unseal operation.
+ *   server_status = alts_crypter_process_in_place(server, data,
+ *                                                 data_allocated_size,
+ *                                                 &data_size,
+ *                                                 &server_error_in_unseal);
+ *   if (server_status != GRPC_STATUS_OK) {
+ *     fprintf(stderr, "unseal operation failed with error code:"
+ *                     "%d, message: %s\n", server_status,
+ *                      server_error_in_unseal);
+ *   }
+ *   gpr_free(server_error_in_unseal);
+ * } else {
+ *     fprintf(stderr, "alts_crypter instance creation failed with error"
+ *                     "code: %d, message: %s\n", server_status,
+ *                      server_error_in_creation);
+ * }
+ *
+ * ...
+ *
+ * gpr_free(data);
+ * gpr_free(server_error_in_creation);
+ * alts_crypter_destroy(server);
+ *
+ * ...
+ *------------------------------------------------------------------------------
+ */
+
+/* V-table for alts_crypter operations */
+typedef struct alts_crypter_vtable {
+  size_t (*num_overhead_bytes)(const alts_crypter* crypter);
+  grpc_status_code (*process_in_place)(alts_crypter* crypter,
+                                       unsigned char* data,
+                                       size_t data_allocated_size,
+                                       size_t data_size, size_t* output_size,
+                                       char** error_details);
+  void (*destruct)(alts_crypter* crypter);
+} alts_crypter_vtable;
+
+/* Main struct for alts_crypter interface */
+struct alts_crypter {
+  const alts_crypter_vtable* vtable;
+};
+
+/**
+ * This method gets the number of overhead bytes needed for sealing data that
+ * is the difference in size between the protected and raw data. The counter
+ * value used in a seal or unseal operation is locally maintained (not sent or
+ * received from the other peer) and therefore, will not be counted as part of
+ * overhead bytes.
+ *
+ * - crypter: an alts_crypter instance.
+ *
+ * On success, the method returns the number of overhead bytes. Otherwise, it
+ * returns zero.
+ *
+ */
+size_t alts_crypter_num_overhead_bytes(const alts_crypter* crypter);
+
+/**
+ * This method performs either a seal or an unseal operation depending on the
+ * alts_crypter instance - crypter passed to the method. If the crypter is
+ * an instance implementing a seal operation, the method will perform a seal
+ * operation. That is, it seals raw data and stores the result in-place, and the
+ * memory allocated for data must be at least data_length +
+ * alts_crypter_num_overhead_bytes(). If the crypter is an instance
+ * implementing an unseal operation, the method will perform an unseal
+ * operation. That is, it unseals protected data and stores the result in-place.
+ * The size of unsealed data will be data_length -
+ * alts_crypter_num_overhead_bytes(). Integrity tag will be verified during
+ * the unseal operation, and if verification fails, the data will be wiped.
+ * The counters used in both seal and unseal operations are managed internally.
+ *
+ * - crypter: an alts_crypter instance.
+ * - data: if the method performs a seal operation, the data represents raw data
+ *   that needs to be sealed. It also plays the role of buffer to hold the
+ *   protected data as a result of seal. If the method performs an unseal
+ *   operation, the data represents protected data that needs to be unsealed. It
+ *   also plays the role of buffer to hold raw data as a result of unseal.
+ * - data_allocated_size: the size of data buffer. The parameter is used to
+ *   check whether the result of either seal or unseal can be safely written to
+ *   the data buffer.
+ * - data_size: if the method performs a seal operation, data_size
+ *   represents the size of raw data that needs to be sealed, and if the method
+ *   performs an unseal operation, data_size represents the size of protected
+ *   data that needs to be unsealed.
+ * - output_size: size of data written to the data buffer after a seal or an
+ *   unseal operation.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is legal to pass nullptr into error_details and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_crypter_process_in_place(
+    alts_crypter* crypter, unsigned char* data, size_t data_allocated_size,
+    size_t data_size, size_t* output_size, char** error_details);
+
+/**
+ * This method creates an alts_crypter instance to be used to perform a seal
+ * operation, given a gsec_aead_crypter instance and a flag indicating if the
+ * created instance will be used at the client or server side. It takes
+ * ownership of gsec_aead_crypter instance.
+ *
+ * - gc: a gsec_aead_crypter instance used to perform AEAD encryption.
+ * - is_client: a flag indicating if the alts_crypter instance will be
+ *   used at the client (is_client = true) or server (is_client =
+ *   false) side.
+ * - overflow_size: overflow size of counter in bytes.
+ * - crypter: an alts_crypter instance to be returned from the method.
+ * - error_details: a buffer containing an error message if the method does
+ *   not function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success of creation, the method returns GRPC_STATUS_OK.
+ * Otherwise, it returns an error status code along with its details specified
+ * in error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_seal_crypter_create(gsec_aead_crypter* gc, bool is_client,
+                                          size_t overflow_size,
+                                          alts_crypter** crypter,
+                                          char** error_details);
+
+/**
+ * This method creates an alts_crypter instance used to perform an unseal
+ * operation, given a gsec_aead_crypter instance and a flag indicating if the
+ * created instance will be used at the client or server side. It takes
+ * ownership of gsec_aead_crypter instance.
+ *
+ * - gc: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - is_client: a flag indicating if the alts_crypter instance will be
+ *   used at the client (is_client = true) or server (is_client =
+ *   false) side.
+ * - overflow_size: overflow size of counter in bytes.
+ * - crypter: an alts_crypter instance to be returned from the method.
+ * - error_details: a buffer containing an error message if the method does
+ *   not function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success of creation, the method returns GRPC_STATUS_OK.
+ * Otherwise, it returns an error status code along with its details specified
+ * in error_details (if error_details is not nullptr).
+ */
+grpc_status_code alts_unseal_crypter_create(gsec_aead_crypter* gc,
+                                            bool is_client,
+                                            size_t overflow_size,
+                                            alts_crypter** crypter,
+                                            char** error_details);
+
+/**
+ * This method destroys an alts_crypter instance by de-allocating all of its
+ * occupied memory. A gsec_aead_crypter instance passed in at alts_crypter
+ * instance creation time will be destroyed in this method.
+ *
+ * - crypter: an alts_crypter instance.
+ */
+void alts_crypter_destroy(alts_crypter* crypter);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H */
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.cc
new file mode 100644
index 0000000..bfa0b7a
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.cc
@@ -0,0 +1,407 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+#include "src/core/tsi/alts/frame_protector/frame_handler.h"
+#include "src/core/tsi/transport_security.h"
+
+constexpr size_t kMinFrameLength = 1024;
+constexpr size_t kDefaultFrameLength = 16 * 1024;
+constexpr size_t kMaxFrameLength = 1024 * 1024;
+
+// Limit k on number of frames such that at most 2^(8 * k) frames can be sent.
+constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8;
+constexpr size_t kAltsRecordProtocolFrameLimit = 5;
+
+/* Main struct for alts_frame_protector. */
+struct alts_frame_protector {
+  tsi_frame_protector base;
+  alts_crypter* seal_crypter;
+  alts_crypter* unseal_crypter;
+  alts_frame_writer* writer;
+  alts_frame_reader* reader;
+  unsigned char* in_place_protect_buffer;
+  unsigned char* in_place_unprotect_buffer;
+  size_t in_place_protect_bytes_buffered;
+  size_t in_place_unprotect_bytes_processed;
+  size_t max_protected_frame_size;
+  size_t max_unprotected_frame_size;
+  size_t overhead_length;
+  size_t counter_overflow;
+};
+
+static tsi_result seal(alts_frame_protector* impl) {
+  char* error_details = nullptr;
+  size_t output_size = 0;
+  grpc_status_code status = alts_crypter_process_in_place(
+      impl->seal_crypter, impl->in_place_protect_buffer,
+      impl->max_protected_frame_size, impl->in_place_protect_bytes_buffered,
+      &output_size, &error_details);
+  impl->in_place_protect_bytes_buffered = output_size;
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "%s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  return TSI_OK;
+}
+
+static size_t max_encrypted_payload_bytes(alts_frame_protector* impl) {
+  return impl->max_protected_frame_size - kFrameHeaderSize;
+}
+
+static tsi_result alts_protect_flush(tsi_frame_protector* self,
+                                     unsigned char* protected_output_frames,
+                                     size_t* protected_output_frames_size,
+                                     size_t* still_pending_size) {
+  if (self == nullptr || protected_output_frames == nullptr ||
+      protected_output_frames_size == nullptr ||
+      still_pending_size == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect_flush().");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
+  /**
+   * If there's nothing to flush (i.e., in_place_protect_buffer is empty),
+   * we're done.
+   */
+  if (impl->in_place_protect_bytes_buffered == 0) {
+    *protected_output_frames_size = 0;
+    *still_pending_size = 0;
+    return TSI_OK;
+  }
+  /**
+   * If a new frame can start being processed, we encrypt the payload and reset
+   * the frame writer to point to in_place_protect_buffer that holds the newly
+   * sealed frame.
+   */
+  if (alts_is_frame_writer_done(impl->writer)) {
+    tsi_result result = seal(impl);
+    if (result != TSI_OK) {
+      return result;
+    }
+    if (!alts_reset_frame_writer(impl->writer, impl->in_place_protect_buffer,
+                                 impl->in_place_protect_bytes_buffered)) {
+      gpr_log(GPR_ERROR, "Couldn't reset frame writer.");
+      return TSI_INTERNAL_ERROR;
+    }
+  }
+  /**
+   * Write the sealed frame as much as possible to protected_output_frames. It's
+   * possible a frame will not be written out completely by a single flush
+   * (i.e., still_pending_size != 0), in which case the flush should be called
+   * iteratively until a complete frame has been written out.
+   */
+  size_t written_frame_bytes = *protected_output_frames_size;
+  if (!alts_write_frame_bytes(impl->writer, protected_output_frames,
+                              &written_frame_bytes)) {
+    gpr_log(GPR_ERROR, "Couldn't write frame bytes.");
+    return TSI_INTERNAL_ERROR;
+  }
+  *protected_output_frames_size = written_frame_bytes;
+  *still_pending_size = alts_get_num_writer_bytes_remaining(impl->writer);
+  /**
+   * If the current frame has been finished processing (i.e., sealed and written
+   * out completely), we empty in_place_protect_buffer.
+   */
+  if (alts_is_frame_writer_done(impl->writer)) {
+    impl->in_place_protect_bytes_buffered = 0;
+  }
+  return TSI_OK;
+}
+
+static tsi_result alts_protect(tsi_frame_protector* self,
+                               const unsigned char* unprotected_bytes,
+                               size_t* unprotected_bytes_size,
+                               unsigned char* protected_output_frames,
+                               size_t* protected_output_frames_size) {
+  if (self == nullptr || unprotected_bytes == nullptr ||
+      unprotected_bytes_size == nullptr || protected_output_frames == nullptr ||
+      protected_output_frames_size == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect().");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
+
+  /**
+   * If more payload can be buffered, we buffer it as much as possible to
+   * in_place_protect_buffer.
+   */
+  if (impl->in_place_protect_bytes_buffered + impl->overhead_length <
+      max_encrypted_payload_bytes(impl)) {
+    size_t bytes_to_buffer = GPR_MIN(*unprotected_bytes_size,
+                                     max_encrypted_payload_bytes(impl) -
+                                         impl->in_place_protect_bytes_buffered -
+                                         impl->overhead_length);
+    *unprotected_bytes_size = bytes_to_buffer;
+    if (bytes_to_buffer > 0) {
+      memcpy(
+          impl->in_place_protect_buffer + impl->in_place_protect_bytes_buffered,
+          unprotected_bytes, bytes_to_buffer);
+      impl->in_place_protect_bytes_buffered += bytes_to_buffer;
+    }
+  } else {
+    *unprotected_bytes_size = 0;
+  }
+  /**
+   * If a full frame has been buffered, we output it. If the first condition
+   * holds, then there exists an unencrypted full frame. If the second
+   * condition holds, then there exists a full frame that has already been
+   * encrypted.
+   */
+  if (max_encrypted_payload_bytes(impl) ==
+          impl->in_place_protect_bytes_buffered + impl->overhead_length ||
+      max_encrypted_payload_bytes(impl) ==
+          impl->in_place_protect_bytes_buffered) {
+    size_t still_pending_size = 0;
+    return alts_protect_flush(self, protected_output_frames,
+                              protected_output_frames_size,
+                              &still_pending_size);
+  } else {
+    *protected_output_frames_size = 0;
+    return TSI_OK;
+  }
+}
+
+static tsi_result unseal(alts_frame_protector* impl) {
+  char* error_details = nullptr;
+  size_t output_size = 0;
+  grpc_status_code status = alts_crypter_process_in_place(
+      impl->unseal_crypter, impl->in_place_unprotect_buffer,
+      impl->max_unprotected_frame_size,
+      alts_get_output_bytes_read(impl->reader), &output_size, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "%s", error_details);
+    gpr_free(error_details);
+    return TSI_DATA_CORRUPTED;
+  }
+  return TSI_OK;
+}
+
+static void ensure_buffer_size(alts_frame_protector* impl) {
+  if (!alts_has_read_frame_length(impl->reader)) {
+    return;
+  }
+  size_t buffer_space_remaining = impl->max_unprotected_frame_size -
+                                  alts_get_output_bytes_read(impl->reader);
+  /**
+   * Check if we need to resize in_place_unprotect_buffer in order to hold
+   * remaining bytes of a full frame.
+   */
+  if (buffer_space_remaining < alts_get_reader_bytes_remaining(impl->reader)) {
+    size_t buffer_len = alts_get_output_bytes_read(impl->reader) +
+                        alts_get_reader_bytes_remaining(impl->reader);
+    unsigned char* buffer = static_cast<unsigned char*>(gpr_malloc(buffer_len));
+    memcpy(buffer, impl->in_place_unprotect_buffer,
+           alts_get_output_bytes_read(impl->reader));
+    impl->max_unprotected_frame_size = buffer_len;
+    gpr_free(impl->in_place_unprotect_buffer);
+    impl->in_place_unprotect_buffer = buffer;
+    alts_reset_reader_output_buffer(
+        impl->reader, buffer + alts_get_output_bytes_read(impl->reader));
+  }
+}
+
+static tsi_result alts_unprotect(tsi_frame_protector* self,
+                                 const unsigned char* protected_frames_bytes,
+                                 size_t* protected_frames_bytes_size,
+                                 unsigned char* unprotected_bytes,
+                                 size_t* unprotected_bytes_size) {
+  if (self == nullptr || protected_frames_bytes == nullptr ||
+      protected_frames_bytes_size == nullptr || unprotected_bytes == nullptr ||
+      unprotected_bytes_size == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_unprotect().");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
+  /**
+   * If a new frame can start being processed, we reset the frame reader to
+   * point to in_place_unprotect_buffer that will be used to hold deframed
+   * result.
+   */
+  if (alts_is_frame_reader_done(impl->reader) &&
+      ((alts_get_output_buffer(impl->reader) == nullptr) ||
+       (alts_get_output_bytes_read(impl->reader) ==
+        impl->in_place_unprotect_bytes_processed + impl->overhead_length))) {
+    if (!alts_reset_frame_reader(impl->reader,
+                                 impl->in_place_unprotect_buffer)) {
+      gpr_log(GPR_ERROR, "Couldn't reset frame reader.");
+      return TSI_INTERNAL_ERROR;
+    }
+    impl->in_place_unprotect_bytes_processed = 0;
+  }
+  /**
+   * If a full frame has not yet been read, we read more bytes from
+   * protected_frames_bytes until a full frame has been read. We also need to
+   * make sure in_place_unprotect_buffer is large enough to hold a complete
+   * frame.
+   */
+  if (!alts_is_frame_reader_done(impl->reader)) {
+    ensure_buffer_size(impl);
+    *protected_frames_bytes_size =
+        GPR_MIN(impl->max_unprotected_frame_size -
+                    alts_get_output_bytes_read(impl->reader),
+                *protected_frames_bytes_size);
+    size_t read_frames_bytes_size = *protected_frames_bytes_size;
+    if (!alts_read_frame_bytes(impl->reader, protected_frames_bytes,
+                               &read_frames_bytes_size)) {
+      gpr_log(GPR_ERROR, "Failed to process frame.");
+      return TSI_INTERNAL_ERROR;
+    }
+    *protected_frames_bytes_size = read_frames_bytes_size;
+  } else {
+    *protected_frames_bytes_size = 0;
+  }
+  /**
+   * If a full frame has been read, we unseal it, and write out the
+   * deframed result to unprotected_bytes.
+   */
+  if (alts_is_frame_reader_done(impl->reader)) {
+    if (impl->in_place_unprotect_bytes_processed == 0) {
+      tsi_result result = unseal(impl);
+      if (result != TSI_OK) {
+        return result;
+      }
+    }
+    size_t bytes_to_write = GPR_MIN(
+        *unprotected_bytes_size, alts_get_output_bytes_read(impl->reader) -
+                                     impl->in_place_unprotect_bytes_processed -
+                                     impl->overhead_length);
+    if (bytes_to_write > 0) {
+      memcpy(unprotected_bytes,
+             impl->in_place_unprotect_buffer +
+                 impl->in_place_unprotect_bytes_processed,
+             bytes_to_write);
+    }
+    *unprotected_bytes_size = bytes_to_write;
+    impl->in_place_unprotect_bytes_processed += bytes_to_write;
+    return TSI_OK;
+  } else {
+    *unprotected_bytes_size = 0;
+    return TSI_OK;
+  }
+}
+
+static void alts_destroy(tsi_frame_protector* self) {
+  alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
+  if (impl != nullptr) {
+    alts_crypter_destroy(impl->seal_crypter);
+    alts_crypter_destroy(impl->unseal_crypter);
+    gpr_free(impl->in_place_protect_buffer);
+    gpr_free(impl->in_place_unprotect_buffer);
+    alts_destroy_frame_writer(impl->writer);
+    alts_destroy_frame_reader(impl->reader);
+    gpr_free(impl);
+  }
+}
+
+static const tsi_frame_protector_vtable alts_frame_protector_vtable = {
+    alts_protect, alts_protect_flush, alts_unprotect, alts_destroy};
+
+static grpc_status_code create_alts_crypters(const uint8_t* key,
+                                             size_t key_size, bool is_client,
+                                             bool is_rekey,
+                                             alts_frame_protector* impl,
+                                             char** error_details) {
+  grpc_status_code status;
+  gsec_aead_crypter* aead_crypter_seal = nullptr;
+  gsec_aead_crypter* aead_crypter_unseal = nullptr;
+  status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength,
+                                            kAesGcmTagLength, is_rekey,
+                                            &aead_crypter_seal, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  status = gsec_aes_gcm_aead_crypter_create(
+      key, key_size, kAesGcmNonceLength, kAesGcmTagLength, is_rekey,
+      &aead_crypter_unseal, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  size_t overflow_size = is_rekey ? kAltsRecordProtocolRekeyFrameLimit
+                                  : kAltsRecordProtocolFrameLimit;
+  status = alts_seal_crypter_create(aead_crypter_seal, is_client, overflow_size,
+                                    &impl->seal_crypter, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  status =
+      alts_unseal_crypter_create(aead_crypter_unseal, is_client, overflow_size,
+                                 &impl->unseal_crypter, error_details);
+  return status;
+}
+
+tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size,
+                                       bool is_client, bool is_rekey,
+                                       size_t* max_protected_frame_size,
+                                       tsi_frame_protector** self) {
+  if (key == nullptr || self == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_create_frame_protector().");
+    return TSI_INTERNAL_ERROR;
+  }
+  char* error_details = nullptr;
+  alts_frame_protector* impl =
+      static_cast<alts_frame_protector*>(gpr_zalloc(sizeof(*impl)));
+  grpc_status_code status = create_alts_crypters(
+      key, key_size, is_client, is_rekey, impl, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to create ALTS crypters, %s.", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  /**
+   * Set maximum frame size to be used by a frame protector. If it is nullptr, a
+   * default frame size will be used. Otherwise, the provided frame size will be
+   * adjusted (if not falling into a valid frame range) and used.
+   */
+  size_t max_protected_frame_size_to_set = kDefaultFrameLength;
+  if (max_protected_frame_size != nullptr) {
+    *max_protected_frame_size =
+        GPR_MIN(*max_protected_frame_size, kMaxFrameLength);
+    *max_protected_frame_size =
+        GPR_MAX(*max_protected_frame_size, kMinFrameLength);
+    max_protected_frame_size_to_set = *max_protected_frame_size;
+  }
+  impl->max_protected_frame_size = max_protected_frame_size_to_set;
+  impl->max_unprotected_frame_size = max_protected_frame_size_to_set;
+  impl->in_place_protect_bytes_buffered = 0;
+  impl->in_place_unprotect_bytes_processed = 0;
+  impl->in_place_protect_buffer = static_cast<unsigned char*>(
+      gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set));
+  impl->in_place_unprotect_buffer = static_cast<unsigned char*>(
+      gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set));
+  impl->overhead_length = alts_crypter_num_overhead_bytes(impl->seal_crypter);
+  impl->writer = alts_create_frame_writer();
+  impl->reader = alts_create_frame_reader();
+  impl->base.vtable = &alts_frame_protector_vtable;
+  *self = &impl->base;
+  return TSI_OK;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.h b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.h
new file mode 100644
index 0000000..321bffa
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/transport_security_interface.h"
+
+typedef struct alts_frame_protector alts_frame_protector;
+
+/**
+ * TODO: Add a parameter to the interface to support the use of
+ * different record protocols within a frame protector.
+ *
+ * This method creates a frame protector.
+ *
+ * - key: a symmetric key used to seal/unseal frames.
+ * - key_size: the size of symmetric key.
+ * - is_client: a flag indicating if the frame protector will be used at client
+ *   (is_client = true) or server (is_client = false) side.
+ * - is_rekey: a flag indicating if the frame protector will use an AEAD with
+ *   rekeying.
+ * - max_protected_frame_size: an in/out parameter indicating max frame size
+ *   to be used by the frame protector. If it is nullptr, the default frame
+ *   size will be used. Otherwise, the provided frame size will be adjusted (if
+ *   not falling into a valid frame range) and used.
+ * - self: a pointer to the frame protector returned from the method.
+ *
+ * This method returns TSI_OK on success and TSI_INTERNAL_ERROR otherwise.
+ */
+tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size,
+                                       bool is_client, bool is_rekey,
+                                       size_t* max_protected_frame_size,
+                                       tsi_frame_protector** self);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H */
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc
new file mode 100644
index 0000000..0574ed5
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h"
+
+#include <grpc/support/alloc.h>
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+grpc_status_code input_sanity_check(
+    const alts_record_protocol_crypter* rp_crypter, const unsigned char* data,
+    size_t* output_size, char** error_details) {
+  if (rp_crypter == nullptr) {
+    maybe_copy_error_msg("alts_crypter instance is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  } else if (data == nullptr) {
+    maybe_copy_error_msg("data is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  } else if (output_size == nullptr) {
+    maybe_copy_error_msg("output_size is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  return GRPC_STATUS_OK;
+}
+
+grpc_status_code increment_counter(alts_record_protocol_crypter* rp_crypter,
+                                   char** error_details) {
+  bool is_overflow = false;
+  grpc_status_code status =
+      alts_counter_increment(rp_crypter->ctr, &is_overflow, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  if (is_overflow) {
+    const char error_msg[] =
+        "crypter counter is wrapped. The connection"
+        "should be closed and the key should be deleted.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+size_t alts_record_protocol_crypter_num_overhead_bytes(const alts_crypter* c) {
+  if (c != nullptr) {
+    size_t num_overhead_bytes = 0;
+    char* error_details = nullptr;
+    const alts_record_protocol_crypter* rp_crypter =
+        reinterpret_cast<const alts_record_protocol_crypter*>(c);
+    grpc_status_code status = gsec_aead_crypter_tag_length(
+        rp_crypter->crypter, &num_overhead_bytes, &error_details);
+    if (status == GRPC_STATUS_OK) {
+      return num_overhead_bytes;
+    }
+  }
+  return 0;
+}
+
+void alts_record_protocol_crypter_destruct(alts_crypter* c) {
+  if (c != nullptr) {
+    alts_record_protocol_crypter* rp_crypter =
+        reinterpret_cast<alts_record_protocol_crypter*>(c);
+    alts_counter_destroy(rp_crypter->ctr);
+    gsec_aead_crypter_destroy(rp_crypter->crypter);
+  }
+}
+
+alts_record_protocol_crypter* alts_crypter_create_common(
+    gsec_aead_crypter* crypter, bool is_client, size_t overflow_size,
+    char** error_details) {
+  if (crypter != nullptr) {
+    auto* rp_crypter = static_cast<alts_record_protocol_crypter*>(
+        gpr_malloc(sizeof(alts_record_protocol_crypter)));
+    size_t counter_size = 0;
+    grpc_status_code status =
+        gsec_aead_crypter_nonce_length(crypter, &counter_size, error_details);
+    if (status != GRPC_STATUS_OK) {
+      return nullptr;
+    }
+    /* Create a counter. */
+    status = alts_counter_create(is_client, counter_size, overflow_size,
+                                 &rp_crypter->ctr, error_details);
+    if (status != GRPC_STATUS_OK) {
+      return nullptr;
+    }
+    rp_crypter->crypter = crypter;
+    return rp_crypter;
+  }
+  const char error_msg[] = "crypter is nullptr.";
+  maybe_copy_error_msg(error_msg, error_details);
+  return nullptr;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h
new file mode 100644
index 0000000..682a8f7
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+
+/**
+ * This file contains common implementation that will be used in both seal and
+ * unseal operations.
+ */
+
+/**
+ * Main struct for alts_record_protocol_crypter that will be used in both
+ * seal and unseal operations.
+ */
+typedef struct alts_record_protocol_crypter {
+  alts_crypter base;
+  gsec_aead_crypter* crypter;
+  alts_counter* ctr;
+} alts_record_protocol_crypter;
+
+/**
+ * This method performs input sanity checks on a subset of inputs to
+ * alts_crypter_process_in_place() for both seal and unseal operations.
+ *
+ * - rp_crypter: an alts_record_protocol_crypter instance.
+ * - data: it represents raw data that needs to be sealed in a seal operation or
+ *   protected data that needs to be unsealed in an unseal operation.
+ * - output_size: size of data written to the data buffer after a seal or
+ *   unseal operation.
+ * - error_details: a buffer containing an error message if any of checked
+ *   inputs is nullptr. It is legal to pass nullptr into error_details and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code input_sanity_check(
+    const alts_record_protocol_crypter* rp_crypter, const unsigned char* data,
+    size_t* output_size, char** error_details);
+
+/**
+ * This method increments the counter within an alts_record_protocol_crypter
+ * instance.
+ *
+ * - rp_crypter: an alts_record_protocol_crypter instance.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly or the counter is wrapped. It is legal to pass nullptr
+ *   into error_details and otherwise, the parameter should be freed with
+ *   gpr_free.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise,
+ * it returns an error status code along with its details specified in
+ * error_details (if error_details is not nullptr).
+ */
+grpc_status_code increment_counter(alts_record_protocol_crypter* rp_crypter,
+                                   char** error_details);
+
+/**
+ * This method creates an alts_crypter instance, and populates the fields
+ * that are common to both seal and unseal operations.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. The
+ *   function does not take ownership of crypter.
+ * - is_client: a flag indicating if the alts_crypter instance will be
+ *   used at the client (is_client = true) or server (is_client =
+ *   false) side.
+ * - overflow_size: overflow size of counter in bytes.
+ * - error_details: a buffer containing an error message if the method does
+ *   not function correctly. It is legal to pass nullptr into error_details, and
+ *   otherwise, the parameter should be freed with gpr_free.
+ *
+ * On success of creation, the method returns alts_record_protocol_crypter
+ * instance. Otherwise, it returns nullptr with its details specified in
+ * error_details (if error_details is not nullptr).
+ *
+ */
+alts_record_protocol_crypter* alts_crypter_create_common(
+    gsec_aead_crypter* crypter, bool is_client, size_t overflow_size,
+    char** error_details);
+
+/**
+ * For the following two methods, please refer to the corresponding API in
+ * alts_crypter.h for detailed specifications.
+ */
+size_t alts_record_protocol_crypter_num_overhead_bytes(const alts_crypter* c);
+
+void alts_record_protocol_crypter_destruct(alts_crypter* c);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H \
+        */
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc
new file mode 100644
index 0000000..f407831
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h"
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+/* Perform input santity check for a seal operation. */
+static grpc_status_code seal_check(alts_crypter* c, const unsigned char* data,
+                                   size_t data_allocated_size, size_t data_size,
+                                   size_t* output_size, char** error_details) {
+  /* Do common input sanity check. */
+  grpc_status_code status = input_sanity_check(
+      reinterpret_cast<const alts_record_protocol_crypter*>(c), data,
+      output_size, error_details);
+  if (status != GRPC_STATUS_OK) return status;
+  /* Do seal-specific check. */
+  size_t num_overhead_bytes =
+      alts_crypter_num_overhead_bytes(reinterpret_cast<const alts_crypter*>(c));
+  if (data_size == 0) {
+    const char error_msg[] = "data_size is zero.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (data_size + num_overhead_bytes > data_allocated_size) {
+    const char error_msg[] =
+        "data_allocated_size is smaller than sum of data_size and "
+        "num_overhead_bytes.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code alts_seal_crypter_process_in_place(
+    alts_crypter* c, unsigned char* data, size_t data_allocated_size,
+    size_t data_size, size_t* output_size, char** error_details) {
+  grpc_status_code status = seal_check(c, data, data_allocated_size, data_size,
+                                       output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Do AEAD encryption. */
+  alts_record_protocol_crypter* rp_crypter =
+      reinterpret_cast<alts_record_protocol_crypter*>(c);
+  status = gsec_aead_crypter_encrypt(
+      rp_crypter->crypter, alts_counter_get_counter(rp_crypter->ctr),
+      alts_counter_get_size(rp_crypter->ctr), nullptr /* aad */,
+      0 /* aad_length */, data, data_size, data, data_allocated_size,
+      output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Increment the crypter counter. */
+  return increment_counter(rp_crypter, error_details);
+}
+
+static const alts_crypter_vtable vtable = {
+    alts_record_protocol_crypter_num_overhead_bytes,
+    alts_seal_crypter_process_in_place, alts_record_protocol_crypter_destruct};
+
+grpc_status_code alts_seal_crypter_create(gsec_aead_crypter* gc, bool is_client,
+                                          size_t overflow_size,
+                                          alts_crypter** crypter,
+                                          char** error_details) {
+  if (crypter == nullptr) {
+    const char error_msg[] = "crypter is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  alts_record_protocol_crypter* rp_crypter =
+      alts_crypter_create_common(gc, !is_client, overflow_size, error_details);
+  if (rp_crypter == nullptr) {
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  rp_crypter->base.vtable = &vtable;
+  *crypter = &rp_crypter->base;
+  return GRPC_STATUS_OK;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
new file mode 100644
index 0000000..51bea24
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+#include "src/core/tsi/alts/frame_protector/alts_crypter.h"
+#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h"
+
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+/* Perform input santity check. */
+static grpc_status_code unseal_check(alts_crypter* c, const unsigned char* data,
+                                     size_t data_allocated_size,
+                                     size_t data_size, size_t* output_size,
+                                     char** error_details) {
+  /* Do common input sanity check. */
+  grpc_status_code status = input_sanity_check(
+      reinterpret_cast<const alts_record_protocol_crypter*>(c), data,
+      output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Do unseal-specific input check. */
+  size_t num_overhead_bytes =
+      alts_crypter_num_overhead_bytes(reinterpret_cast<const alts_crypter*>(c));
+  if (num_overhead_bytes > data_size) {
+    const char error_msg[] = "data_size is smaller than num_overhead_bytes.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  return GRPC_STATUS_OK;
+}
+
+static grpc_status_code alts_unseal_crypter_process_in_place(
+    alts_crypter* c, unsigned char* data, size_t data_allocated_size,
+    size_t data_size, size_t* output_size, char** error_details) {
+  grpc_status_code status = unseal_check(c, data, data_allocated_size,
+                                         data_size, output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Do AEAD decryption. */
+  alts_record_protocol_crypter* rp_crypter =
+      reinterpret_cast<alts_record_protocol_crypter*>(c);
+  status = gsec_aead_crypter_decrypt(
+      rp_crypter->crypter, alts_counter_get_counter(rp_crypter->ctr),
+      alts_counter_get_size(rp_crypter->ctr), nullptr /* aad */,
+      0 /* aad_length */, data, data_size, data, data_allocated_size,
+      output_size, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Increment the crypter counter. */
+  return increment_counter(rp_crypter, error_details);
+}
+
+static const alts_crypter_vtable vtable = {
+    alts_record_protocol_crypter_num_overhead_bytes,
+    alts_unseal_crypter_process_in_place,
+    alts_record_protocol_crypter_destruct};
+
+grpc_status_code alts_unseal_crypter_create(gsec_aead_crypter* gc,
+                                            bool is_client,
+                                            size_t overflow_size,
+                                            alts_crypter** crypter,
+                                            char** error_details) {
+  if (crypter == nullptr) {
+    const char error_msg[] = "crypter is nullptr.";
+    maybe_copy_error_msg(error_msg, error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  alts_record_protocol_crypter* rp_crypter =
+      alts_crypter_create_common(gc, is_client, overflow_size, error_details);
+  if (rp_crypter == nullptr) {
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  rp_crypter->base.vtable = &vtable;
+  *crypter = &rp_crypter->base;
+  return GRPC_STATUS_OK;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.cc
new file mode 100644
index 0000000..d3fda63
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.cc
@@ -0,0 +1,218 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/frame_protector/frame_handler.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+
+/* Use little endian to interpret a string of bytes as uint32_t. */
+static uint32_t load_32_le(const unsigned char* buffer) {
+  return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) |
+         (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]);
+}
+
+/* Store uint32_t as a string of little endian bytes. */
+static void store_32_le(uint32_t value, unsigned char* buffer) {
+  buffer[3] = (unsigned char)(value >> 24) & 0xFF;
+  buffer[2] = (unsigned char)(value >> 16) & 0xFF;
+  buffer[1] = (unsigned char)(value >> 8) & 0xFF;
+  buffer[0] = (unsigned char)(value)&0xFF;
+}
+
+/* Frame writer implementation. */
+alts_frame_writer* alts_create_frame_writer() {
+  alts_frame_writer* writer =
+      static_cast<alts_frame_writer*>(gpr_zalloc(sizeof(*writer)));
+  return writer;
+}
+
+bool alts_reset_frame_writer(alts_frame_writer* writer,
+                             const unsigned char* buffer, size_t length) {
+  if (buffer == nullptr) return false;
+  size_t max_input_size = SIZE_MAX - kFrameLengthFieldSize;
+  if (length > max_input_size) {
+    gpr_log(GPR_ERROR, "length must be at most %zu", max_input_size);
+    return false;
+  }
+  writer->input_buffer = buffer;
+  writer->input_size = length;
+  writer->input_bytes_written = 0;
+  writer->header_bytes_written = 0;
+  store_32_le(
+      static_cast<uint32_t>(writer->input_size + kFrameMessageTypeFieldSize),
+      writer->header_buffer);
+  store_32_le(kFrameMessageType, writer->header_buffer + kFrameLengthFieldSize);
+  return true;
+}
+
+bool alts_write_frame_bytes(alts_frame_writer* writer, unsigned char* output,
+                            size_t* bytes_size) {
+  if (bytes_size == nullptr || output == nullptr) return false;
+  if (alts_is_frame_writer_done(writer)) {
+    *bytes_size = 0;
+    return true;
+  }
+  size_t bytes_written = 0;
+  /* Write some header bytes, if needed. */
+  if (writer->header_bytes_written != sizeof(writer->header_buffer)) {
+    size_t bytes_to_write =
+        GPR_MIN(*bytes_size,
+                sizeof(writer->header_buffer) - writer->header_bytes_written);
+    memcpy(output, writer->header_buffer + writer->header_bytes_written,
+           bytes_to_write);
+    bytes_written += bytes_to_write;
+    *bytes_size -= bytes_to_write;
+    writer->header_bytes_written += bytes_to_write;
+    output += bytes_to_write;
+    if (writer->header_bytes_written != sizeof(writer->header_buffer)) {
+      *bytes_size = bytes_written;
+      return true;
+    }
+  }
+  /* Write some non-header bytes. */
+  size_t bytes_to_write =
+      GPR_MIN(writer->input_size - writer->input_bytes_written, *bytes_size);
+  memcpy(output, writer->input_buffer, bytes_to_write);
+  writer->input_buffer += bytes_to_write;
+  bytes_written += bytes_to_write;
+  writer->input_bytes_written += bytes_to_write;
+  *bytes_size = bytes_written;
+  return true;
+}
+
+bool alts_is_frame_writer_done(alts_frame_writer* writer) {
+  return writer->input_buffer == nullptr ||
+         writer->input_size == writer->input_bytes_written;
+}
+
+size_t alts_get_num_writer_bytes_remaining(alts_frame_writer* writer) {
+  return (sizeof(writer->header_buffer) - writer->header_bytes_written) +
+         (writer->input_size - writer->input_bytes_written);
+}
+
+void alts_destroy_frame_writer(alts_frame_writer* writer) { gpr_free(writer); }
+
+/* Frame reader implementation. */
+alts_frame_reader* alts_create_frame_reader() {
+  alts_frame_reader* reader =
+      static_cast<alts_frame_reader*>(gpr_zalloc(sizeof(*reader)));
+  return reader;
+}
+
+bool alts_is_frame_reader_done(alts_frame_reader* reader) {
+  return reader->output_buffer == nullptr ||
+         (reader->header_bytes_read == sizeof(reader->header_buffer) &&
+          reader->bytes_remaining == 0);
+}
+
+bool alts_has_read_frame_length(alts_frame_reader* reader) {
+  return sizeof(reader->header_buffer) == reader->header_bytes_read;
+}
+
+size_t alts_get_reader_bytes_remaining(alts_frame_reader* reader) {
+  return alts_has_read_frame_length(reader) ? reader->bytes_remaining : 0;
+}
+
+void alts_reset_reader_output_buffer(alts_frame_reader* reader,
+                                     unsigned char* buffer) {
+  reader->output_buffer = buffer;
+}
+
+bool alts_reset_frame_reader(alts_frame_reader* reader, unsigned char* buffer) {
+  if (buffer == nullptr) return false;
+  reader->output_buffer = buffer;
+  reader->bytes_remaining = 0;
+  reader->header_bytes_read = 0;
+  reader->output_bytes_read = 0;
+  return true;
+}
+
+bool alts_read_frame_bytes(alts_frame_reader* reader,
+                           const unsigned char* bytes, size_t* bytes_size) {
+  if (bytes_size == nullptr) return false;
+  if (bytes == nullptr) {
+    *bytes_size = 0;
+    return false;
+  }
+  if (alts_is_frame_reader_done(reader)) {
+    *bytes_size = 0;
+    return true;
+  }
+  size_t bytes_processed = 0;
+  /* Process the header, if needed. */
+  if (reader->header_bytes_read != sizeof(reader->header_buffer)) {
+    size_t bytes_to_write = GPR_MIN(
+        *bytes_size, sizeof(reader->header_buffer) - reader->header_bytes_read);
+    memcpy(reader->header_buffer + reader->header_bytes_read, bytes,
+           bytes_to_write);
+    reader->header_bytes_read += bytes_to_write;
+    bytes_processed += bytes_to_write;
+    bytes += bytes_to_write;
+    *bytes_size -= bytes_to_write;
+    if (reader->header_bytes_read != sizeof(reader->header_buffer)) {
+      *bytes_size = bytes_processed;
+      return true;
+    }
+    size_t frame_length = load_32_le(reader->header_buffer);
+    if (frame_length < kFrameMessageTypeFieldSize ||
+        frame_length > kFrameMaxSize) {
+      gpr_log(GPR_ERROR,
+              "Bad frame length (should be at least %zu, and at most %zu)",
+              kFrameMessageTypeFieldSize, kFrameMaxSize);
+      *bytes_size = 0;
+      return false;
+    }
+    size_t message_type =
+        load_32_le(reader->header_buffer + kFrameLengthFieldSize);
+    if (message_type != kFrameMessageType) {
+      gpr_log(GPR_ERROR, "Unsupported message type %zu (should be %zu)",
+              message_type, kFrameMessageType);
+      *bytes_size = 0;
+      return false;
+    }
+    reader->bytes_remaining = frame_length - kFrameMessageTypeFieldSize;
+  }
+  /* Process the non-header bytes. */
+  size_t bytes_to_write = GPR_MIN(*bytes_size, reader->bytes_remaining);
+  memcpy(reader->output_buffer, bytes, bytes_to_write);
+  reader->output_buffer += bytes_to_write;
+  bytes_processed += bytes_to_write;
+  reader->bytes_remaining -= bytes_to_write;
+  reader->output_bytes_read += bytes_to_write;
+  *bytes_size = bytes_processed;
+  return true;
+}
+
+size_t alts_get_output_bytes_read(alts_frame_reader* reader) {
+  return reader->output_bytes_read;
+}
+
+unsigned char* alts_get_output_buffer(alts_frame_reader* reader) {
+  return reader->output_buffer;
+}
+
+void alts_destroy_frame_reader(alts_frame_reader* reader) { gpr_free(reader); }
diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.h b/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.h
new file mode 100644
index 0000000..a703ff4
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.h
@@ -0,0 +1,236 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H
+#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+const size_t kFrameMessageType = 0x06;
+const size_t kFrameLengthFieldSize = 4;
+const size_t kFrameMessageTypeFieldSize = 4;
+const size_t kFrameMaxSize = 1024 * 1024;
+const size_t kFrameHeaderSize =
+    kFrameLengthFieldSize + kFrameMessageTypeFieldSize;
+
+/**
+ * Implementation of frame reader and frame writer. All APIs in the
+ * header are thread-compatible.
+ */
+
+/**
+ * Main struct for a frame writer. It reads frames from an input buffer, and
+ * writes the contents as raw bytes. It does not own the input buffer.
+ */
+typedef struct alts_frame_writer {
+  const unsigned char* input_buffer;
+  unsigned char header_buffer[kFrameHeaderSize];
+  size_t input_bytes_written;
+  size_t header_bytes_written;
+  size_t input_size;
+} alts_frame_writer;
+
+/**
+ * Main struct for a frame reader. It reads raw bytes and puts the framed
+ * result into an output buffer. It does not own the output buffer.
+ */
+typedef struct alts_frame_reader {
+  unsigned char* output_buffer;
+  unsigned char header_buffer[kFrameHeaderSize];
+  size_t header_bytes_read;
+  size_t output_bytes_read;
+  size_t bytes_remaining;
+} alts_frame_reader;
+
+/**
+ * This method creates a frame writer instance and initializes its internal
+ * states.
+ */
+alts_frame_writer* alts_create_frame_writer();
+
+/**
+ * This method resets internal states of a frame writer and prepares to write
+ * a single frame. It does not take ownership of payload_buffer.
+ * The payload_buffer must outlive the writer.
+ *
+ * - writer: a frame writer instance.
+ * - buffer: a buffer storing full payload data to be framed.
+ * - length: size of payload data.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool alts_reset_frame_writer(alts_frame_writer* writer,
+                             const unsigned char* buffer, size_t length);
+
+/**
+ * This method writes up to bytes_size bytes of a frame to output.
+ *
+ * - writer: a frame writer instance.
+ * - output: an output buffer used to store the frame.
+ * - bytes_size: an in/out parameter that stores the size of output buffer
+ *   before the call, and gets written the number of frame bytes written to the
+ *   buffer.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool alts_write_frame_bytes(alts_frame_writer* writer, unsigned char* output,
+                            size_t* bytes_size);
+
+/**
+ * This method checks if a reset can be called to write a new frame. It returns
+ * true if it's the first time to frame a payload, or the current frame has
+ * been finished processing. It returns false if it's not ready yet to start a
+ * new frame (e.g., more payload data needs to be accumulated to process the
+ * current frame).
+ *
+ * if (alts_is_frame_writer_done(writer)) {
+ *   // a new frame can be written, call reset.
+ *   alts_reset_frame_writer(writer, payload_buffer, payload_size);
+ * } else {
+ *   // accumulate more payload data until a full frame can be written.
+ * }
+ *
+ * - writer: a frame writer instance.
+ */
+bool alts_is_frame_writer_done(alts_frame_writer* writer);
+
+/**
+ * This method returns the number of bytes left to write before a complete frame
+ * is formed.
+ *
+ * - writer: a frame writer instance.
+ */
+size_t alts_get_num_writer_bytes_remaining(alts_frame_writer* writer);
+
+/**
+ * This method destroys a frame writer instance.
+ *
+ * - writer: a frame writer instance.
+ */
+void alts_destroy_frame_writer(alts_frame_writer* writer);
+
+/**
+ * This method creates a frame reader instance and initializes its internal
+ * states.
+ */
+alts_frame_reader* alts_create_frame_reader();
+
+/**
+ * This method resets internal states of a frame reader (including setting its
+ * output_buffer with buffer), and prepares to write processed bytes to
+ * an output_buffer. It does not take ownership of buffer. The buffer must
+ * outlive reader.
+ *
+ * - reader: a frame reader instance.
+ * - buffer: an output buffer used to store deframed results.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool alts_reset_frame_reader(alts_frame_reader* reader, unsigned char* buffer);
+
+/**
+ * This method processes up to the number of bytes given in bytes_size. It may
+ * choose not to process all the bytes, if, for instance, more bytes are
+ * given to the method than required to complete the current frame.
+ *
+ * - reader: a frame reader instance.
+ * - bytes: a buffer that stores data to be processed.
+ * - bytes_size: an in/out parameter that stores the size of bytes before the
+ *   call and gets written the number of bytes processed.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool alts_read_frame_bytes(alts_frame_reader* reader,
+                           const unsigned char* bytes, size_t* bytes_size);
+
+/**
+ * This method checks if a frame length has been read.
+ *
+ * - reader: a frame reader instance.
+ *
+ * The method returns true if a frame length has been read and false otherwise.
+ */
+bool alts_has_read_frame_length(alts_frame_reader* reader);
+
+/**
+ * This method returns the number of bytes the frame reader intends to write.
+ * It may only be called if alts_has_read_frame_length() returns true.
+ *
+ * - reader: a frame reader instance.
+ */
+size_t alts_get_reader_bytes_remaining(alts_frame_reader* reader);
+
+/**
+ * This method resets output_buffer but does not otherwise modify other internal
+ * states of a frame reader instance. After being set, the new output_buffer
+ * will hold the deframed payload held by the original output_buffer. It does
+ * not take ownership of buffer. The buffer must outlive the reader.
+ * To distinguish between two reset methods on a frame reader,
+ *
+ * if (alts_fh_is_frame_reader_done(reader)) {
+ *   // if buffer contains a full payload to be deframed, call reset.
+ *   alts_reset_frame_reader(reader, buffer);
+ * }
+ *
+ * // if remaining buffer space is not enough to hold a full payload
+ * if (buffer_space_remaining < alts_get_reader_bytes_remaining(reader)) {
+ *   // allocate enough space for a new buffer, copy back data processed so far,
+ *   // and call reset.
+ *   alts_reset_reader_output_buffer(reader, new_buffer).
+ * }
+ *
+ * - reader: a frame reader instance.
+ * - buffer: a buffer used to set reader's output_buffer.
+ */
+void alts_reset_reader_output_buffer(alts_frame_reader* reader,
+                                     unsigned char* buffer);
+
+/**
+ * This method checks if reset can be called to start processing a new frame.
+ * If true and reset was previously called, a full frame has been processed and
+ * the content of the frame is available in output_buffer.
+
+ * - reader: a frame reader instance.
+ */
+bool alts_is_frame_reader_done(alts_frame_reader* reader);
+
+/**
+ * This method returns output_bytes_read of a frame reader instance.
+ *
+ * - reader: a frame reader instance.
+ */
+size_t alts_get_output_bytes_read(alts_frame_reader* reader);
+
+/**
+ * This method returns output_buffer of a frame reader instance.
+ *
+ * - reader: a frame reader instance.
+ */
+unsigned char* alts_get_output_buffer(alts_frame_reader* reader);
+
+/**
+ * This method destroys a frame reader instance.
+ *
+ * - reader: a frame reader instance.
+ */
+void alts_destroy_frame_reader(alts_frame_reader* reader);
+
+#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.cc
new file mode 100644
index 0000000..43d0979
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.cc
@@ -0,0 +1,615 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h"
+#include "src/core/tsi/alts/handshaker/alts_shared_resource.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
+
+#define TSI_ALTS_INITIAL_BUFFER_SIZE 256
+
+const int kHandshakerClientOpNum = 4;
+
+struct alts_handshaker_client {
+  const alts_handshaker_client_vtable* vtable;
+};
+
+typedef struct alts_grpc_handshaker_client {
+  alts_handshaker_client base;
+  alts_tsi_handshaker* handshaker;
+  grpc_call* call;
+  /* A pointer to a function handling the interaction with handshaker service.
+   * That is, it points to grpc_call_start_batch_and_execute when the handshaker
+   * client is used in a non-testing use case and points to a custom function
+   * that validates the data to be sent to handshaker service in a testing use
+   * case. */
+  alts_grpc_caller grpc_caller;
+  /* A callback function provided by gRPC to handle the response returned from
+   * handshaker service. It also serves to bring the control safely back to
+   * application when dedicated CQ and thread are used. */
+  grpc_iomgr_cb_func grpc_cb;
+  /* A gRPC closure to be scheduled when the response from handshaker service
+   * is received. It will be initialized with grpc_cb. */
+  grpc_closure on_handshaker_service_resp_recv;
+  /* Buffers containing information to be sent (or received) to (or from) the
+   * handshaker service. */
+  grpc_byte_buffer* send_buffer;
+  grpc_byte_buffer* recv_buffer;
+  grpc_status_code status;
+  /* Initial metadata to be received from handshaker service. */
+  grpc_metadata_array recv_initial_metadata;
+  /* A callback function provided by an application to be invoked when response
+   * is received from handshaker service. */
+  tsi_handshaker_on_next_done_cb cb;
+  void* user_data;
+  /* ALTS credential options passed in from the caller. */
+  grpc_alts_credentials_options* options;
+  /* target name information to be passed to handshaker service for server
+   * authorization check. */
+  grpc_slice target_name;
+  /* boolean flag indicating if the handshaker client is used at client
+   * (is_client = true) or server (is_client = false) side. */
+  bool is_client;
+  /* a temporary store for data received from handshaker service used to extract
+   * unused data. */
+  grpc_slice recv_bytes;
+  /* a buffer containing data to be sent to the grpc client or server's peer. */
+  unsigned char* buffer;
+  size_t buffer_size;
+} alts_grpc_handshaker_client;
+
+static void handshaker_client_send_buffer_destroy(
+    alts_grpc_handshaker_client* client) {
+  GPR_ASSERT(client != nullptr);
+  grpc_byte_buffer_destroy(client->send_buffer);
+  client->send_buffer = nullptr;
+}
+
+static bool is_handshake_finished_properly(grpc_gcp_handshaker_resp* resp) {
+  GPR_ASSERT(resp != nullptr);
+  if (resp->has_result) {
+    return true;
+  }
+  return false;
+}
+
+void alts_handshaker_client_handle_response(alts_handshaker_client* c,
+                                            bool is_ok) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  grpc_byte_buffer* recv_buffer = client->recv_buffer;
+  grpc_status_code status = client->status;
+  tsi_handshaker_on_next_done_cb cb = client->cb;
+  void* user_data = client->user_data;
+  alts_tsi_handshaker* handshaker = client->handshaker;
+
+  /* Invalid input check. */
+  if (cb == nullptr) {
+    gpr_log(GPR_ERROR,
+            "cb is nullptr in alts_tsi_handshaker_handle_response()");
+    return;
+  }
+  if (handshaker == nullptr) {
+    gpr_log(GPR_ERROR,
+            "handshaker is nullptr in alts_tsi_handshaker_handle_response()");
+    cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
+    return;
+  }
+  /* TSI handshake has been shutdown. */
+  if (alts_tsi_handshaker_has_shutdown(handshaker)) {
+    gpr_log(GPR_ERROR, "TSI handshake shutdown");
+    cb(TSI_HANDSHAKE_SHUTDOWN, user_data, nullptr, 0, nullptr);
+    return;
+  }
+  /* Failed grpc call check. */
+  if (!is_ok || status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "grpc call made to handshaker service failed");
+    cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
+    return;
+  }
+  if (recv_buffer == nullptr) {
+    gpr_log(GPR_ERROR,
+            "recv_buffer is nullptr in alts_tsi_handshaker_handle_response()");
+    cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
+    return;
+  }
+  grpc_gcp_handshaker_resp* resp =
+      alts_tsi_utils_deserialize_response(recv_buffer);
+  grpc_byte_buffer_destroy(client->recv_buffer);
+  client->recv_buffer = nullptr;
+  /* Invalid handshaker response check. */
+  if (resp == nullptr) {
+    gpr_log(GPR_ERROR, "alts_tsi_utils_deserialize_response() failed");
+    cb(TSI_DATA_CORRUPTED, user_data, nullptr, 0, nullptr);
+    return;
+  }
+  grpc_slice* slice = static_cast<grpc_slice*>(resp->out_frames.arg);
+  unsigned char* bytes_to_send = nullptr;
+  size_t bytes_to_send_size = 0;
+  if (slice != nullptr) {
+    bytes_to_send_size = GRPC_SLICE_LENGTH(*slice);
+    while (bytes_to_send_size > client->buffer_size) {
+      client->buffer_size *= 2;
+      client->buffer = static_cast<unsigned char*>(
+          gpr_realloc(client->buffer, client->buffer_size));
+    }
+    memcpy(client->buffer, GRPC_SLICE_START_PTR(*slice), bytes_to_send_size);
+    bytes_to_send = client->buffer;
+  }
+  tsi_handshaker_result* result = nullptr;
+  if (is_handshake_finished_properly(resp)) {
+    alts_tsi_handshaker_result_create(resp, client->is_client, &result);
+    alts_tsi_handshaker_result_set_unused_bytes(result, &client->recv_bytes,
+                                                resp->bytes_consumed);
+  }
+  grpc_status_code code = static_cast<grpc_status_code>(resp->status.code);
+  if (code != GRPC_STATUS_OK) {
+    grpc_slice* details = static_cast<grpc_slice*>(resp->status.details.arg);
+    if (details != nullptr) {
+      char* error_details = grpc_slice_to_c_string(*details);
+      gpr_log(GPR_ERROR, "Error from handshaker service:%s", error_details);
+      gpr_free(error_details);
+    }
+  }
+  grpc_gcp_handshaker_resp_destroy(resp);
+  cb(alts_tsi_utils_convert_to_tsi_result(code), user_data, bytes_to_send,
+     bytes_to_send_size, result);
+}
+
+/**
+ * Populate grpc operation data with the fields of ALTS handshaker client and
+ * make a grpc call.
+ */
+static tsi_result make_grpc_call(alts_handshaker_client* c, bool is_start) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  grpc_op ops[kHandshakerClientOpNum];
+  memset(ops, 0, sizeof(ops));
+  grpc_op* op = ops;
+  if (is_start) {
+    op->op = GRPC_OP_SEND_INITIAL_METADATA;
+    op->data.send_initial_metadata.count = 0;
+    op++;
+    GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
+    op->op = GRPC_OP_RECV_INITIAL_METADATA;
+    op->data.recv_initial_metadata.recv_initial_metadata =
+        &client->recv_initial_metadata;
+    op++;
+    GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
+  }
+  op->op = GRPC_OP_SEND_MESSAGE;
+  op->data.send_message.send_message = client->send_buffer;
+  op++;
+  GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
+  op->op = GRPC_OP_RECV_MESSAGE;
+  op->data.recv_message.recv_message = &client->recv_buffer;
+  op++;
+  GPR_ASSERT(op - ops <= kHandshakerClientOpNum);
+  GPR_ASSERT(client->grpc_caller != nullptr);
+  if (client->grpc_caller(client->call, ops, static_cast<size_t>(op - ops),
+                          &client->on_handshaker_service_resp_recv) !=
+      GRPC_CALL_OK) {
+    gpr_log(GPR_ERROR, "Start batch operation failed");
+    return TSI_INTERNAL_ERROR;
+  }
+  return TSI_OK;
+}
+
+/* Create and populate a client_start handshaker request, then serialize it. */
+static grpc_byte_buffer* get_serialized_start_client(
+    alts_handshaker_client* c) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  bool ok = true;
+  grpc_gcp_handshaker_req* req =
+      grpc_gcp_handshaker_req_create(CLIENT_START_REQ);
+  ok &= grpc_gcp_handshaker_req_set_handshake_protocol(
+      req, grpc_gcp_HandshakeProtocol_ALTS);
+  ok &= grpc_gcp_handshaker_req_add_application_protocol(
+      req, ALTS_APPLICATION_PROTOCOL);
+  ok &= grpc_gcp_handshaker_req_add_record_protocol(req, ALTS_RECORD_PROTOCOL);
+  grpc_gcp_rpc_protocol_versions* versions = &client->options->rpc_versions;
+  ok &= grpc_gcp_handshaker_req_set_rpc_versions(
+      req, versions->max_rpc_version.major, versions->max_rpc_version.minor,
+      versions->min_rpc_version.major, versions->min_rpc_version.minor);
+  char* target_name = grpc_slice_to_c_string(client->target_name);
+  ok &= grpc_gcp_handshaker_req_set_target_name(req, target_name);
+  target_service_account* ptr =
+      (reinterpret_cast<grpc_alts_credentials_client_options*>(client->options))
+          ->target_account_list_head;
+  while (ptr != nullptr) {
+    grpc_gcp_handshaker_req_add_target_identity_service_account(req, ptr->data);
+    ptr = ptr->next;
+  }
+  grpc_slice slice;
+  ok &= grpc_gcp_handshaker_req_encode(req, &slice);
+  grpc_byte_buffer* buffer = nullptr;
+  if (ok) {
+    buffer = grpc_raw_byte_buffer_create(&slice, 1 /* number of slices */);
+  }
+  grpc_slice_unref_internal(slice);
+  gpr_free(target_name);
+  grpc_gcp_handshaker_req_destroy(req);
+  return buffer;
+}
+
+static tsi_result handshaker_client_start_client(alts_handshaker_client* c) {
+  if (c == nullptr) {
+    gpr_log(GPR_ERROR, "client is nullptr in handshaker_client_start_client()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_byte_buffer* buffer = get_serialized_start_client(c);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  if (buffer == nullptr) {
+    gpr_log(GPR_ERROR, "get_serialized_start_client() failed");
+    return TSI_INTERNAL_ERROR;
+  }
+  handshaker_client_send_buffer_destroy(client);
+  client->send_buffer = buffer;
+  tsi_result result = make_grpc_call(&client->base, true /* is_start */);
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "make_grpc_call() failed");
+  }
+  return result;
+}
+
+/* Create and populate a start_server handshaker request, then serialize it. */
+static grpc_byte_buffer* get_serialized_start_server(
+    alts_handshaker_client* c, grpc_slice* bytes_received) {
+  GPR_ASSERT(c != nullptr);
+  GPR_ASSERT(bytes_received != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  grpc_gcp_handshaker_req* req =
+      grpc_gcp_handshaker_req_create(SERVER_START_REQ);
+  bool ok = grpc_gcp_handshaker_req_add_application_protocol(
+      req, ALTS_APPLICATION_PROTOCOL);
+  ok &= grpc_gcp_handshaker_req_param_add_record_protocol(
+      req, grpc_gcp_HandshakeProtocol_ALTS, ALTS_RECORD_PROTOCOL);
+  ok &= grpc_gcp_handshaker_req_set_in_bytes(
+      req, reinterpret_cast<const char*> GRPC_SLICE_START_PTR(*bytes_received),
+      GRPC_SLICE_LENGTH(*bytes_received));
+  grpc_gcp_rpc_protocol_versions* versions = &client->options->rpc_versions;
+  ok &= grpc_gcp_handshaker_req_set_rpc_versions(
+      req, versions->max_rpc_version.major, versions->max_rpc_version.minor,
+      versions->min_rpc_version.major, versions->min_rpc_version.minor);
+  grpc_slice req_slice;
+  ok &= grpc_gcp_handshaker_req_encode(req, &req_slice);
+  grpc_byte_buffer* buffer = nullptr;
+  if (ok) {
+    buffer = grpc_raw_byte_buffer_create(&req_slice, 1 /* number of slices */);
+  }
+  grpc_slice_unref_internal(req_slice);
+  grpc_gcp_handshaker_req_destroy(req);
+  return buffer;
+}
+
+static tsi_result handshaker_client_start_server(alts_handshaker_client* c,
+                                                 grpc_slice* bytes_received) {
+  if (c == nullptr || bytes_received == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to handshaker_client_start_server()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  grpc_byte_buffer* buffer = get_serialized_start_server(c, bytes_received);
+  if (buffer == nullptr) {
+    gpr_log(GPR_ERROR, "get_serialized_start_server() failed");
+    return TSI_INTERNAL_ERROR;
+  }
+  handshaker_client_send_buffer_destroy(client);
+  client->send_buffer = buffer;
+  tsi_result result = make_grpc_call(&client->base, true /* is_start */);
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "make_grpc_call() failed");
+  }
+  return result;
+}
+
+/* Create and populate a next handshaker request, then serialize it. */
+static grpc_byte_buffer* get_serialized_next(grpc_slice* bytes_received) {
+  GPR_ASSERT(bytes_received != nullptr);
+  grpc_gcp_handshaker_req* req = grpc_gcp_handshaker_req_create(NEXT_REQ);
+  bool ok = grpc_gcp_handshaker_req_set_in_bytes(
+      req, reinterpret_cast<const char*> GRPC_SLICE_START_PTR(*bytes_received),
+      GRPC_SLICE_LENGTH(*bytes_received));
+  grpc_slice req_slice;
+  ok &= grpc_gcp_handshaker_req_encode(req, &req_slice);
+  grpc_byte_buffer* buffer = nullptr;
+  if (ok) {
+    buffer = grpc_raw_byte_buffer_create(&req_slice, 1 /* number of slices */);
+  }
+  grpc_slice_unref_internal(req_slice);
+  grpc_gcp_handshaker_req_destroy(req);
+  return buffer;
+}
+
+static tsi_result handshaker_client_next(alts_handshaker_client* c,
+                                         grpc_slice* bytes_received) {
+  if (c == nullptr || bytes_received == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to handshaker_client_next()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  grpc_slice_unref_internal(client->recv_bytes);
+  client->recv_bytes = grpc_slice_ref(*bytes_received);
+  grpc_byte_buffer* buffer = get_serialized_next(bytes_received);
+  if (buffer == nullptr) {
+    gpr_log(GPR_ERROR, "get_serialized_next() failed");
+    return TSI_INTERNAL_ERROR;
+  }
+  handshaker_client_send_buffer_destroy(client);
+  client->send_buffer = buffer;
+  tsi_result result = make_grpc_call(&client->base, false /* is_start */);
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "make_grpc_call() failed");
+  }
+  return result;
+}
+
+static void handshaker_client_shutdown(alts_handshaker_client* c) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  if (client->call != nullptr) {
+    grpc_call_cancel_internal(client->call);
+  }
+}
+
+static void handshaker_client_destruct(alts_handshaker_client* c) {
+  if (c == nullptr) {
+    return;
+  }
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  if (client->call != nullptr) {
+    grpc_call_unref(client->call);
+  }
+}
+
+static const alts_handshaker_client_vtable vtable = {
+    handshaker_client_start_client, handshaker_client_start_server,
+    handshaker_client_next, handshaker_client_shutdown,
+    handshaker_client_destruct};
+
+alts_handshaker_client* alts_grpc_handshaker_client_create(
+    alts_tsi_handshaker* handshaker, grpc_channel* channel,
+    const char* handshaker_service_url, grpc_pollset_set* interested_parties,
+    grpc_alts_credentials_options* options, grpc_slice target_name,
+    grpc_iomgr_cb_func grpc_cb, tsi_handshaker_on_next_done_cb cb,
+    void* user_data, alts_handshaker_client_vtable* vtable_for_testing,
+    bool is_client) {
+  if (channel == nullptr || handshaker_service_url == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to alts_handshaker_client_create()");
+    return nullptr;
+  }
+  alts_grpc_handshaker_client* client =
+      static_cast<alts_grpc_handshaker_client*>(gpr_zalloc(sizeof(*client)));
+  client->grpc_caller = grpc_call_start_batch_and_execute;
+  client->handshaker = handshaker;
+  client->cb = cb;
+  client->user_data = user_data;
+  client->send_buffer = nullptr;
+  client->recv_buffer = nullptr;
+  client->options = grpc_alts_credentials_options_copy(options);
+  client->target_name = grpc_slice_copy(target_name);
+  client->recv_bytes = grpc_empty_slice();
+  grpc_metadata_array_init(&client->recv_initial_metadata);
+  client->grpc_cb = grpc_cb;
+  client->is_client = is_client;
+  client->buffer_size = TSI_ALTS_INITIAL_BUFFER_SIZE;
+  client->buffer = static_cast<unsigned char*>(gpr_zalloc(client->buffer_size));
+  grpc_slice slice = grpc_slice_from_copied_string(handshaker_service_url);
+  client->call =
+      strcmp(handshaker_service_url, ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING) ==
+              0
+          ? nullptr
+          : grpc_channel_create_pollset_set_call(
+                channel, nullptr, GRPC_PROPAGATE_DEFAULTS, interested_parties,
+                grpc_slice_from_static_string(ALTS_SERVICE_METHOD), &slice,
+                GRPC_MILLIS_INF_FUTURE, nullptr);
+  client->base.vtable =
+      vtable_for_testing == nullptr ? &vtable : vtable_for_testing;
+  GRPC_CLOSURE_INIT(&client->on_handshaker_service_resp_recv, client->grpc_cb,
+                    client, grpc_schedule_on_exec_ctx);
+  grpc_slice_unref_internal(slice);
+  return &client->base;
+}
+
+namespace grpc_core {
+namespace internal {
+
+void alts_handshaker_client_set_grpc_caller_for_testing(
+    alts_handshaker_client* c, alts_grpc_caller caller) {
+  GPR_ASSERT(c != nullptr && caller != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  client->grpc_caller = caller;
+}
+
+grpc_byte_buffer* alts_handshaker_client_get_send_buffer_for_testing(
+    alts_handshaker_client* c) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  return client->send_buffer;
+}
+
+grpc_byte_buffer** alts_handshaker_client_get_recv_buffer_addr_for_testing(
+    alts_handshaker_client* c) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  return &client->recv_buffer;
+}
+
+grpc_metadata_array* alts_handshaker_client_get_initial_metadata_for_testing(
+    alts_handshaker_client* c) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  return &client->recv_initial_metadata;
+}
+
+void alts_handshaker_client_set_recv_bytes_for_testing(
+    alts_handshaker_client* c, grpc_slice* recv_bytes) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  client->recv_bytes = grpc_slice_ref(*recv_bytes);
+}
+
+void alts_handshaker_client_set_fields_for_testing(
+    alts_handshaker_client* c, alts_tsi_handshaker* handshaker,
+    tsi_handshaker_on_next_done_cb cb, void* user_data,
+    grpc_byte_buffer* recv_buffer, grpc_status_code status) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  client->handshaker = handshaker;
+  client->cb = cb;
+  client->user_data = user_data;
+  client->recv_buffer = recv_buffer;
+  client->status = status;
+}
+
+void alts_handshaker_client_check_fields_for_testing(
+    alts_handshaker_client* c, tsi_handshaker_on_next_done_cb cb,
+    void* user_data, bool has_sent_start_message, grpc_slice* recv_bytes) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  GPR_ASSERT(client->cb == cb);
+  GPR_ASSERT(client->user_data == user_data);
+  if (recv_bytes != nullptr) {
+    GPR_ASSERT(grpc_slice_cmp(client->recv_bytes, *recv_bytes) == 0);
+  }
+  GPR_ASSERT(alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+                 client->handshaker) == has_sent_start_message);
+}
+
+void alts_handshaker_client_set_vtable_for_testing(
+    alts_handshaker_client* c, alts_handshaker_client_vtable* vtable) {
+  GPR_ASSERT(c != nullptr);
+  GPR_ASSERT(vtable != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  client->base.vtable = vtable;
+}
+
+alts_tsi_handshaker* alts_handshaker_client_get_handshaker_for_testing(
+    alts_handshaker_client* c) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  return client->handshaker;
+}
+
+void alts_handshaker_client_set_cb_for_testing(
+    alts_handshaker_client* c, tsi_handshaker_on_next_done_cb cb) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  client->cb = cb;
+}
+
+grpc_closure* alts_handshaker_client_get_closure_for_testing(
+    alts_handshaker_client* c) {
+  GPR_ASSERT(c != nullptr);
+  alts_grpc_handshaker_client* client =
+      reinterpret_cast<alts_grpc_handshaker_client*>(c);
+  return &client->on_handshaker_service_resp_recv;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client) {
+  if (client != nullptr && client->vtable != nullptr &&
+      client->vtable->client_start != nullptr) {
+    return client->vtable->client_start(client);
+  }
+  gpr_log(GPR_ERROR,
+          "client or client->vtable has not been initialized properly");
+  return TSI_INVALID_ARGUMENT;
+}
+
+tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client,
+                                               grpc_slice* bytes_received) {
+  if (client != nullptr && client->vtable != nullptr &&
+      client->vtable->server_start != nullptr) {
+    return client->vtable->server_start(client, bytes_received);
+  }
+  gpr_log(GPR_ERROR,
+          "client or client->vtable has not been initialized properly");
+  return TSI_INVALID_ARGUMENT;
+}
+
+tsi_result alts_handshaker_client_next(alts_handshaker_client* client,
+                                       grpc_slice* bytes_received) {
+  if (client != nullptr && client->vtable != nullptr &&
+      client->vtable->next != nullptr) {
+    return client->vtable->next(client, bytes_received);
+  }
+  gpr_log(GPR_ERROR,
+          "client or client->vtable has not been initialized properly");
+  return TSI_INVALID_ARGUMENT;
+}
+
+void alts_handshaker_client_shutdown(alts_handshaker_client* client) {
+  if (client != nullptr && client->vtable != nullptr &&
+      client->vtable->shutdown != nullptr) {
+    client->vtable->shutdown(client);
+  }
+}
+
+void alts_handshaker_client_destroy(alts_handshaker_client* c) {
+  if (c != nullptr) {
+    if (c->vtable != nullptr && c->vtable->destruct != nullptr) {
+      c->vtable->destruct(c);
+    }
+    alts_grpc_handshaker_client* client =
+        reinterpret_cast<alts_grpc_handshaker_client*>(c);
+    grpc_byte_buffer_destroy(client->send_buffer);
+    grpc_byte_buffer_destroy(client->recv_buffer);
+    client->send_buffer = nullptr;
+    client->recv_buffer = nullptr;
+    grpc_metadata_array_destroy(&client->recv_initial_metadata);
+    grpc_slice_unref_internal(client->recv_bytes);
+    grpc_slice_unref_internal(client->target_name);
+    grpc_alts_credentials_options_destroy(client->options);
+    gpr_free(client->buffer);
+    gpr_free(client);
+  }
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.h
new file mode 100644
index 0000000..4b48987
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.h
@@ -0,0 +1,157 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/byte_buffer_reader.h>
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+#define ALTS_SERVICE_METHOD "/grpc.gcp.HandshakerService/DoHandshake"
+#define ALTS_APPLICATION_PROTOCOL "grpc"
+#define ALTS_RECORD_PROTOCOL "ALTSRP_GCM_AES128_REKEY"
+#define ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING "lame"
+
+const size_t kAltsAes128GcmRekeyKeyLength = 44;
+
+typedef struct alts_tsi_handshaker alts_tsi_handshaker;
+/**
+ * A ALTS handshaker client interface. It is used to communicate with
+ * ALTS handshaker service by scheduling a handshaker request that could be one
+ * of client_start, server_start, and next handshaker requests. All APIs in the
+ * header are thread-compatible.
+ */
+typedef struct alts_handshaker_client alts_handshaker_client;
+
+/* A function that makes the grpc call to the handshaker service. */
+typedef grpc_call_error (*alts_grpc_caller)(grpc_call* call, const grpc_op* ops,
+                                            size_t nops, grpc_closure* tag);
+
+/* V-table for ALTS handshaker client operations. */
+typedef struct alts_handshaker_client_vtable {
+  tsi_result (*client_start)(alts_handshaker_client* client);
+  tsi_result (*server_start)(alts_handshaker_client* client,
+                             grpc_slice* bytes_received);
+  tsi_result (*next)(alts_handshaker_client* client,
+                     grpc_slice* bytes_received);
+  void (*shutdown)(alts_handshaker_client* client);
+  void (*destruct)(alts_handshaker_client* client);
+} alts_handshaker_client_vtable;
+
+/**
+ * This method schedules a client_start handshaker request to ALTS handshaker
+ * service.
+ *
+ * - client: ALTS handshaker client instance.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client);
+
+/**
+ * This method schedules a server_start handshaker request to ALTS handshaker
+ * service.
+ *
+ * - client: ALTS handshaker client instance.
+ * - bytes_received: bytes in out_frames returned from the peer's handshaker
+ *   response.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client,
+                                               grpc_slice* bytes_received);
+
+/**
+ * This method schedules a next handshaker request to ALTS handshaker service.
+ *
+ * - client: ALTS handshaker client instance.
+ * - bytes_received: bytes in out_frames returned from the peer's handshaker
+ *   response.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result alts_handshaker_client_next(alts_handshaker_client* client,
+                                       grpc_slice* bytes_received);
+
+/**
+ * This method cancels previously scheduled, but yet executed handshaker
+ * requests to ALTS handshaker service. After this operation, the handshake
+ * will be shutdown, and no more handshaker requests will get scheduled.
+ *
+ * - client: ALTS handshaker client instance.
+ */
+void alts_handshaker_client_shutdown(alts_handshaker_client* client);
+
+/**
+ * This method destroys an ALTS handshaker client.
+ *
+ * - client: an ALTS handshaker client instance.
+ */
+void alts_handshaker_client_destroy(alts_handshaker_client* client);
+
+/**
+ * This method creates an ALTS handshaker client.
+ *
+ * - handshaker: ALTS TSI handshaker to which the created handshaker client
+ * belongs to.
+ * - channel: grpc channel to ALTS handshaker service.
+ * - handshaker_service_url: address of ALTS handshaker service in the format of
+ *   "host:port".
+ * - interested_parties: set of pollsets interested in this connection.
+ * - options: ALTS credentials options containing information passed from TSI
+ *   caller (e.g., rpc protocol versions)
+ * - target_name: the name of the endpoint that the channel is connecting to,
+ *   and will be used for secure naming check
+ * - grpc_cb: gRPC provided callbacks passed from TSI handshaker.
+ * - cb: callback to be executed when tsi_handshaker_next API compltes.
+ * - user_data: argument passed to cb.
+ * - vtable_for_testing: ALTS handshaker client vtable instance used for
+ *   testing purpose.
+ * - is_client: a boolean value indicating if the created handshaker client is
+ * used at the client (is_client = true) or server (is_client = false) side. It
+ * returns the created ALTS handshaker client on success, and NULL on failure.
+ */
+alts_handshaker_client* alts_grpc_handshaker_client_create(
+    alts_tsi_handshaker* handshaker, grpc_channel* channel,
+    const char* handshaker_service_url, grpc_pollset_set* interested_parties,
+    grpc_alts_credentials_options* options, grpc_slice target_name,
+    grpc_iomgr_cb_func grpc_cb, tsi_handshaker_on_next_done_cb cb,
+    void* user_data, alts_handshaker_client_vtable* vtable_for_testing,
+    bool is_client);
+
+/**
+ * This method handles handshaker response returned from ALTS handshaker
+ * service. Note that the only reason the API is exposed is that it is used in
+ * alts_shared_resources.cc.
+ *
+ * - client: an ALTS handshaker client instance.
+ * - is_ok: a boolean value indicating if the handshaker response is ok to read.
+ */
+void alts_handshaker_client_handle_response(alts_handshaker_client* client,
+                                            bool is_ok);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc
new file mode 100644
index 0000000..256e414
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc
@@ -0,0 +1,520 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+/* HandshakerReq */
+grpc_gcp_handshaker_req* grpc_gcp_handshaker_req_create(
+    grpc_gcp_handshaker_req_type type) {
+  grpc_gcp_handshaker_req* req =
+      static_cast<grpc_gcp_handshaker_req*>(gpr_zalloc(sizeof(*req)));
+  switch (type) {
+    case CLIENT_START_REQ:
+      req->has_client_start = true;
+      break;
+    case SERVER_START_REQ:
+      req->has_server_start = true;
+      break;
+    case NEXT_REQ:
+      req->has_next = true;
+      break;
+  }
+  return req;
+}
+
+void grpc_gcp_handshaker_req_destroy(grpc_gcp_handshaker_req* req) {
+  if (req == nullptr) {
+    return;
+  }
+  if (req->has_client_start) {
+    /* Destroy client_start request. */
+    destroy_repeated_field_list_identity(
+        static_cast<repeated_field*>(req->client_start.target_identities.arg));
+    destroy_repeated_field_list_string(static_cast<repeated_field*>(
+        req->client_start.application_protocols.arg));
+    destroy_repeated_field_list_string(
+        static_cast<repeated_field*>(req->client_start.record_protocols.arg));
+    if (req->client_start.has_local_identity) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->client_start.local_identity.hostname.arg));
+      destroy_slice(static_cast<grpc_slice*>(
+          req->client_start.local_identity.service_account.arg));
+    }
+    if (req->client_start.has_local_endpoint) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->client_start.local_endpoint.ip_address.arg));
+    }
+    if (req->client_start.has_remote_endpoint) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->client_start.remote_endpoint.ip_address.arg));
+    }
+    destroy_slice(static_cast<grpc_slice*>(req->client_start.target_name.arg));
+  } else if (req->has_server_start) {
+    /* Destroy server_start request. */
+    size_t i = 0;
+    for (i = 0; i < req->server_start.handshake_parameters_count; i++) {
+      destroy_repeated_field_list_identity(
+          static_cast<repeated_field*>(req->server_start.handshake_parameters[i]
+                                           .value.local_identities.arg));
+      destroy_repeated_field_list_string(
+          static_cast<repeated_field*>(req->server_start.handshake_parameters[i]
+                                           .value.record_protocols.arg));
+    }
+    destroy_repeated_field_list_string(static_cast<repeated_field*>(
+        req->server_start.application_protocols.arg));
+    if (req->server_start.has_local_endpoint) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->server_start.local_endpoint.ip_address.arg));
+    }
+    if (req->server_start.has_remote_endpoint) {
+      destroy_slice(static_cast<grpc_slice*>(
+          req->server_start.remote_endpoint.ip_address.arg));
+    }
+    destroy_slice(static_cast<grpc_slice*>(req->server_start.in_bytes.arg));
+  } else {
+    /* Destroy next request. */
+    destroy_slice(static_cast<grpc_slice*>(req->next.in_bytes.arg));
+  }
+  gpr_free(req);
+}
+
+bool grpc_gcp_handshaker_req_set_handshake_protocol(
+    grpc_gcp_handshaker_req* req,
+    grpc_gcp_handshake_protocol handshake_protocol) {
+  if (req == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_handshake_protocol().");
+    return false;
+  }
+  req->client_start.has_handshake_security_protocol = true;
+  req->client_start.handshake_security_protocol = handshake_protocol;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_target_name(grpc_gcp_handshaker_req* req,
+                                             const char* target_name) {
+  if (req == nullptr || target_name == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_target_name().");
+    return false;
+  }
+  grpc_slice* slice = create_slice(target_name, strlen(target_name));
+  req->client_start.target_name.arg = slice;
+  req->client_start.target_name.funcs.encode = encode_string_or_bytes_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_add_application_protocol(
+    grpc_gcp_handshaker_req* req, const char* application_protocol) {
+  if (req == nullptr || application_protocol == nullptr || req->has_next) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_add_application_protocol().");
+    return false;
+  }
+  grpc_slice* slice =
+      create_slice(application_protocol, strlen(application_protocol));
+  if (req->has_client_start) {
+    add_repeated_field(reinterpret_cast<repeated_field**>(
+                           &req->client_start.application_protocols.arg),
+                       slice);
+    req->client_start.application_protocols.funcs.encode =
+        encode_repeated_string_cb;
+  } else {
+    add_repeated_field(reinterpret_cast<repeated_field**>(
+                           &req->server_start.application_protocols.arg),
+                       slice);
+    req->server_start.application_protocols.funcs.encode =
+        encode_repeated_string_cb;
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_add_record_protocol(grpc_gcp_handshaker_req* req,
+                                                 const char* record_protocol) {
+  if (req == nullptr || record_protocol == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_add_record_protocol().");
+    return false;
+  }
+  grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol));
+  add_repeated_field(reinterpret_cast<repeated_field**>(
+                         &req->client_start.record_protocols.arg),
+                     slice);
+  req->client_start.record_protocols.funcs.encode = encode_repeated_string_cb;
+  return true;
+}
+
+static void set_identity_hostname(grpc_gcp_identity* identity,
+                                  const char* hostname) {
+  grpc_slice* slice = create_slice(hostname, strlen(hostname));
+  identity->hostname.arg = slice;
+  identity->hostname.funcs.encode = encode_string_or_bytes_cb;
+}
+
+static void set_identity_service_account(grpc_gcp_identity* identity,
+                                         const char* service_account) {
+  grpc_slice* slice = create_slice(service_account, strlen(service_account));
+  identity->service_account.arg = slice;
+  identity->service_account.funcs.encode = encode_string_or_bytes_cb;
+}
+
+bool grpc_gcp_handshaker_req_add_target_identity_hostname(
+    grpc_gcp_handshaker_req* req, const char* hostname) {
+  if (req == nullptr || hostname == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_req_add_target_identity_hostname().");
+    return false;
+  }
+  grpc_gcp_identity* target_identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*target_identity)));
+  set_identity_hostname(target_identity, hostname);
+  req->client_start.target_identities.funcs.encode =
+      encode_repeated_identity_cb;
+  add_repeated_field(reinterpret_cast<repeated_field**>(
+                         &req->client_start.target_identities.arg),
+                     target_identity);
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_add_target_identity_service_account(
+    grpc_gcp_handshaker_req* req, const char* service_account) {
+  if (req == nullptr || service_account == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_req_add_target_identity_service_account().");
+    return false;
+  }
+  grpc_gcp_identity* target_identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*target_identity)));
+  set_identity_service_account(target_identity, service_account);
+  req->client_start.target_identities.funcs.encode =
+      encode_repeated_identity_cb;
+  add_repeated_field(reinterpret_cast<repeated_field**>(
+                         &req->client_start.target_identities.arg),
+                     target_identity);
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_local_identity_hostname(
+    grpc_gcp_handshaker_req* req, const char* hostname) {
+  if (req == nullptr || hostname == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_req_set_local_identity_hostname().");
+    return false;
+  }
+  req->client_start.has_local_identity = true;
+  set_identity_hostname(&req->client_start.local_identity, hostname);
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_local_identity_service_account(
+    grpc_gcp_handshaker_req* req, const char* service_account) {
+  if (req == nullptr || service_account == nullptr || !req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_handshaker_req_set_local_identity_service_account().");
+    return false;
+  }
+  req->client_start.has_local_identity = true;
+  set_identity_service_account(&req->client_start.local_identity,
+                               service_account);
+  return true;
+}
+
+static void set_endpoint(grpc_gcp_endpoint* endpoint, const char* ip_address,
+                         size_t port, grpc_gcp_network_protocol protocol) {
+  grpc_slice* slice = create_slice(ip_address, strlen(ip_address));
+  endpoint->ip_address.arg = slice;
+  endpoint->ip_address.funcs.encode = encode_string_or_bytes_cb;
+  endpoint->has_port = true;
+  endpoint->port = static_cast<int32_t>(port);
+  endpoint->has_protocol = true;
+  endpoint->protocol = protocol;
+}
+
+bool grpc_gcp_handshaker_req_set_rpc_versions(grpc_gcp_handshaker_req* req,
+                                              uint32_t max_major,
+                                              uint32_t max_minor,
+                                              uint32_t min_major,
+                                              uint32_t min_minor) {
+  if (req == nullptr || req->has_next) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_rpc_versions().");
+    return false;
+  }
+  if (req->has_client_start) {
+    req->client_start.has_rpc_versions = true;
+    grpc_gcp_rpc_protocol_versions_set_max(&req->client_start.rpc_versions,
+                                           max_major, max_minor);
+    grpc_gcp_rpc_protocol_versions_set_min(&req->client_start.rpc_versions,
+                                           min_major, min_minor);
+  } else {
+    req->server_start.has_rpc_versions = true;
+    grpc_gcp_rpc_protocol_versions_set_max(&req->server_start.rpc_versions,
+                                           max_major, max_minor);
+    grpc_gcp_rpc_protocol_versions_set_min(&req->server_start.rpc_versions,
+                                           min_major, min_minor);
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_local_endpoint(
+    grpc_gcp_handshaker_req* req, const char* ip_address, size_t port,
+    grpc_gcp_network_protocol protocol) {
+  if (req == nullptr || ip_address == nullptr || port > 65535 ||
+      req->has_next) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_local_endpoint().");
+    return false;
+  }
+  if (req->has_client_start) {
+    req->client_start.has_local_endpoint = true;
+    set_endpoint(&req->client_start.local_endpoint, ip_address, port, protocol);
+  } else {
+    req->server_start.has_local_endpoint = true;
+    set_endpoint(&req->server_start.local_endpoint, ip_address, port, protocol);
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_remote_endpoint(
+    grpc_gcp_handshaker_req* req, const char* ip_address, size_t port,
+    grpc_gcp_network_protocol protocol) {
+  if (req == nullptr || ip_address == nullptr || port > 65535 ||
+      req->has_next) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_remote_endpoint().");
+    return false;
+  }
+  if (req->has_client_start) {
+    req->client_start.has_remote_endpoint = true;
+    set_endpoint(&req->client_start.remote_endpoint, ip_address, port,
+                 protocol);
+  } else {
+    req->server_start.has_remote_endpoint = true;
+    set_endpoint(&req->server_start.remote_endpoint, ip_address, port,
+                 protocol);
+  }
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_set_in_bytes(grpc_gcp_handshaker_req* req,
+                                          const char* in_bytes, size_t size) {
+  if (req == nullptr || in_bytes == nullptr || req->has_client_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_set_in_bytes().");
+    return false;
+  }
+  grpc_slice* slice = create_slice(in_bytes, size);
+  if (req->has_next) {
+    req->next.in_bytes.arg = slice;
+    req->next.in_bytes.funcs.encode = &encode_string_or_bytes_cb;
+  } else {
+    req->server_start.in_bytes.arg = slice;
+    req->server_start.in_bytes.funcs.encode = &encode_string_or_bytes_cb;
+  }
+  return true;
+}
+
+static grpc_gcp_server_handshake_parameters* server_start_find_param(
+    grpc_gcp_handshaker_req* req, int32_t key) {
+  size_t i = 0;
+  for (i = 0; i < req->server_start.handshake_parameters_count; i++) {
+    if (req->server_start.handshake_parameters[i].key == key) {
+      return &req->server_start.handshake_parameters[i].value;
+    }
+  }
+  req->server_start
+      .handshake_parameters[req->server_start.handshake_parameters_count]
+      .has_key = true;
+  req->server_start
+      .handshake_parameters[req->server_start.handshake_parameters_count]
+      .has_value = true;
+  req->server_start
+      .handshake_parameters[req->server_start.handshake_parameters_count++]
+      .key = key;
+  return &req->server_start
+              .handshake_parameters
+                  [req->server_start.handshake_parameters_count - 1]
+              .value;
+}
+
+bool grpc_gcp_handshaker_req_param_add_record_protocol(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* record_protocol) {
+  if (req == nullptr || record_protocol == nullptr || !req->has_server_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_param_add_record_protocol().");
+    return false;
+  }
+  grpc_gcp_server_handshake_parameters* param =
+      server_start_find_param(req, key);
+  grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol));
+  add_repeated_field(
+      reinterpret_cast<repeated_field**>(&param->record_protocols.arg), slice);
+  param->record_protocols.funcs.encode = &encode_repeated_string_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_param_add_local_identity_hostname(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* hostname) {
+  if (req == nullptr || hostname == nullptr || !req->has_server_start) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_handshaker_req_param_add_local_identity_hostname().");
+    return false;
+  }
+  grpc_gcp_server_handshake_parameters* param =
+      server_start_find_param(req, key);
+  grpc_gcp_identity* local_identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*local_identity)));
+  set_identity_hostname(local_identity, hostname);
+  add_repeated_field(
+      reinterpret_cast<repeated_field**>(&param->local_identities.arg),
+      local_identity);
+  param->local_identities.funcs.encode = &encode_repeated_identity_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_param_add_local_identity_service_account(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* service_account) {
+  if (req == nullptr || service_account == nullptr || !req->has_server_start) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid arguments to "
+        "grpc_gcp_handshaker_req_param_add_local_identity_service_account().");
+    return false;
+  }
+  grpc_gcp_server_handshake_parameters* param =
+      server_start_find_param(req, key);
+  grpc_gcp_identity* local_identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*local_identity)));
+  set_identity_service_account(local_identity, service_account);
+  add_repeated_field(
+      reinterpret_cast<repeated_field**>(&param->local_identities.arg),
+      local_identity);
+  param->local_identities.funcs.encode = &encode_repeated_identity_cb;
+  return true;
+}
+
+bool grpc_gcp_handshaker_req_encode(grpc_gcp_handshaker_req* req,
+                                    grpc_slice* slice) {
+  if (req == nullptr || slice == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to grpc_gcp_handshaker_req_encode().");
+    return false;
+  }
+  pb_ostream_t size_stream;
+  memset(&size_stream, 0, sizeof(pb_ostream_t));
+  if (!pb_encode(&size_stream, grpc_gcp_HandshakerReq_fields, req)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream));
+    return false;
+  }
+  size_t encoded_length = size_stream.bytes_written;
+  *slice = grpc_slice_malloc(encoded_length);
+  pb_ostream_t output_stream =
+      pb_ostream_from_buffer(GRPC_SLICE_START_PTR(*slice), encoded_length);
+  if (!pb_encode(&output_stream, grpc_gcp_HandshakerReq_fields, req) != 0) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&output_stream));
+    return false;
+  }
+  return true;
+}
+
+/* HandshakerResp. */
+grpc_gcp_handshaker_resp* grpc_gcp_handshaker_resp_create(void) {
+  grpc_gcp_handshaker_resp* resp =
+      static_cast<grpc_gcp_handshaker_resp*>(gpr_zalloc(sizeof(*resp)));
+  return resp;
+}
+
+void grpc_gcp_handshaker_resp_destroy(grpc_gcp_handshaker_resp* resp) {
+  if (resp != nullptr) {
+    destroy_slice(static_cast<grpc_slice*>(resp->out_frames.arg));
+    if (resp->has_status) {
+      destroy_slice(static_cast<grpc_slice*>(resp->status.details.arg));
+    }
+    if (resp->has_result) {
+      destroy_slice(
+          static_cast<grpc_slice*>(resp->result.application_protocol.arg));
+      destroy_slice(static_cast<grpc_slice*>(resp->result.record_protocol.arg));
+      destroy_slice(static_cast<grpc_slice*>(resp->result.key_data.arg));
+      if (resp->result.has_local_identity) {
+        destroy_slice(
+            static_cast<grpc_slice*>(resp->result.local_identity.hostname.arg));
+        destroy_slice(static_cast<grpc_slice*>(
+            resp->result.local_identity.service_account.arg));
+      }
+      if (resp->result.has_peer_identity) {
+        destroy_slice(
+            static_cast<grpc_slice*>(resp->result.peer_identity.hostname.arg));
+        destroy_slice(static_cast<grpc_slice*>(
+            resp->result.peer_identity.service_account.arg));
+      }
+    }
+    gpr_free(resp);
+  }
+}
+
+bool grpc_gcp_handshaker_resp_decode(grpc_slice encoded_handshaker_resp,
+                                     grpc_gcp_handshaker_resp* resp) {
+  if (resp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr argument to grpc_gcp_handshaker_resp_decode().");
+    return false;
+  }
+  pb_istream_t stream =
+      pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_handshaker_resp),
+                             GRPC_SLICE_LENGTH(encoded_handshaker_resp));
+  resp->out_frames.funcs.decode = decode_string_or_bytes_cb;
+  resp->status.details.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.application_protocol.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.record_protocol.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.key_data.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.peer_identity.hostname.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.peer_identity.service_account.funcs.decode =
+      decode_string_or_bytes_cb;
+  resp->result.local_identity.hostname.funcs.decode = decode_string_or_bytes_cb;
+  resp->result.local_identity.service_account.funcs.decode =
+      decode_string_or_bytes_cb;
+  if (!pb_decode(&stream, grpc_gcp_HandshakerResp_fields, resp)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return false;
+  }
+  return true;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h
new file mode 100644
index 0000000..5df56a8
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h
@@ -0,0 +1,323 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h"
+
+/**
+ * An implementation of nanopb thin wrapper used to set/get and
+ * serialize/de-serialize of ALTS handshake requests and responses.
+ *
+ * All APIs in the header are thread-compatible. A typical usage of this API at
+ * the client side is as follows:
+ *
+ * -----------------------------------------------------------------------------
+ * // Create, populate, and serialize an ALTS client_start handshake request to
+ * // send to the server.
+ * grpc_gcp_handshaker_req* req =
+ *     grpc_gcp_handshaker_req_create(CLIENT_START_REQ);
+ * grpc_gcp_handshaker_req_set_handshake_protocol(
+       req, grpc_gcp_HandshakeProtocol_ALTS);
+ * grpc_gcp_handshaker_req_add_application_protocol(req, "grpc");
+ * grpc_gcp_handshaker_req_add_record_protocol(req, "ALTSRP_GCM_AES128");
+ * grpc_slice client_slice;
+ * if (!grpc_gcp_handshaker_req_encode(req, &client_slice)) {
+ *   fprintf(stderr, "ALTS handshake request encoding failed.";
+ * }
+ *
+ * // De-serialize a data stream received from the server, and store the result
+ * // at ALTS handshake response.
+ * grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create();
+ * if (!grpc_gcp_handshaker_resp_decode(server_slice, resp)) {
+ *    fprintf(stderr, "ALTS handshake response decoding failed.");
+ * }
+ * // To access a variable-length datatype field (i.e., pb_callback_t),
+ * // access its "arg" subfield (if it has been set).
+ * if (resp->out_frames.arg != nullptr) {
+ *   grpc_slice* slice = resp->out_frames.arg;
+ * }
+ * // To access a fixed-length datatype field (i.e., not pb_calback_t),
+ * // access the field directly (if it has been set).
+ * if (resp->has_status && resp->status->has_code) {
+ *   uint32_t code = resp->status->code;
+ * }
+ *------------------------------------------------------------------------------
+ */
+
+/**
+ * This method creates an ALTS handshake request.
+ *
+ * - type: an enum type value that can be either CLIENT_START_REQ,
+ *   SERVER_START_REQ, or NEXT_REQ to indicate the created instance will be
+ *   client_start, server_start, and next handshake request message
+ * respectively.
+ *
+ * The method returns a pointer to the created instance.
+ */
+grpc_gcp_handshaker_req* grpc_gcp_handshaker_req_create(
+    grpc_gcp_handshaker_req_type type);
+
+/**
+ * This method sets the value for handshake_security_protocol field of ALTS
+ * client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - handshake_protocol: a enum type value representing the handshake security
+ *   protocol.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_handshake_protocol(
+    grpc_gcp_handshaker_req* req,
+    grpc_gcp_handshake_protocol handshake_protocol);
+
+/**
+ * This method sets the value for target_name field of ALTS client_start
+ * handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - target_name: a target name to be set.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_target_name(grpc_gcp_handshaker_req* req,
+                                             const char* target_name);
+
+/**
+ * This method adds an application protocol supported by the server (or
+ * client) to ALTS server_start (or client_start) handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - application_protocol: an application protocol (e.g., grpc) to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_add_application_protocol(
+    grpc_gcp_handshaker_req* req, const char* application_protocol);
+
+/**
+ * This method adds a record protocol supported by the client to ALTS
+ * client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - record_protocol: a record protocol (e.g., ALTSRP_GCM_AES128) to be
+ *   added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_add_record_protocol(grpc_gcp_handshaker_req* req,
+                                                 const char* record_protocol);
+
+/**
+ * This method adds a target server identity represented as hostname and
+ * acceptable by a client to ALTS client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - hostname: a string representation of hostname at the connection
+ *   endpoint to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_add_target_identity_hostname(
+    grpc_gcp_handshaker_req* req, const char* hostname);
+
+/**
+ * This method adds a target server identity represented as service account and
+ * acceptable by a client to ALTS client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - service_account: a string representation of service account at the
+ *   connection endpoint to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_add_target_identity_service_account(
+    grpc_gcp_handshaker_req* req, const char* service_account);
+
+/**
+ * This method sets the hostname for local_identity field of ALTS client_start
+ * handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - hostname: a string representation of hostname.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_local_identity_hostname(
+    grpc_gcp_handshaker_req* req, const char* hostname);
+
+/**
+ * This method sets the service account for local_identity field of ALTS
+ * client_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - service_account: a string representation of service account.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_local_identity_service_account(
+    grpc_gcp_handshaker_req* req, const char* service_account);
+
+/**
+ * This method sets the value for local_endpoint field of either ALTS
+ * client_start or server_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - ip_address: a string representation of ip address associated with the
+ *   local endpoint, that could be either IPv4 or IPv6.
+ * - port: a port number associated with the local endpoint.
+ * - protocol: a network protocol (e.g., TCP or UDP) associated with the
+ *   local endpoint.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_local_endpoint(
+    grpc_gcp_handshaker_req* req, const char* ip_address, size_t port,
+    grpc_gcp_network_protocol protocol);
+
+/**
+ * This method sets the value for remote_endpoint field of either ALTS
+ * client_start or server_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - ip_address: a string representation of ip address associated with the
+ *   remote endpoint, that could be either IPv4 or IPv6.
+ * - port: a port number associated with the remote endpoint.
+ * - protocol: a network protocol (e.g., TCP or UDP) associated with the
+ *   remote endpoint.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_remote_endpoint(
+    grpc_gcp_handshaker_req* req, const char* ip_address, size_t port,
+    grpc_gcp_network_protocol protocol);
+
+/**
+ * This method sets the value for in_bytes field of either ALTS server_start or
+ * next handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - in_bytes: a buffer containing bytes taken from out_frames of the peer's
+ *   ALTS handshake response. It is possible that the peer's out_frames are
+ * split into multiple handshake request messages.
+ * - size: size of in_bytes buffer.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_in_bytes(grpc_gcp_handshaker_req* req,
+                                          const char* in_bytes, size_t size);
+
+/**
+ * This method adds a record protocol to handshake parameters mapped by the
+ * handshake protocol for ALTS server_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - key: an enum type value representing a handshake security protocol.
+ * - record_protocol: a record protocol to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_param_add_record_protocol(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* record_protocol);
+
+/**
+ * This method adds a local identity represented as hostname to handshake
+ * parameters mapped by the handshake protocol for ALTS server_start handshake
+ * request.
+ *
+ * - req: an ALTS handshake request.
+ * - key: an enum type value representing a handshake security protocol.
+ * - hostname: a string representation of hostname to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_param_add_local_identity_hostname(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* hostname);
+
+/**
+ * This method adds a local identity represented as service account to handshake
+ * parameters mapped by the handshake protocol for ALTS server_start handshake
+ * request.
+ *
+ * - req: an ALTS handshake request.
+ * - key: an enum type value representing a handshake security protocol.
+ * - service_account: a string representation of service account to be added.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_param_add_local_identity_service_account(
+    grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key,
+    const char* service_account);
+
+/**
+ * This method sets the value for rpc_versions field of either ALTS
+ * client_start or server_start handshake request.
+ *
+ * - req: an ALTS handshake request.
+ * - max_major: a major version of maximum supported RPC version.
+ * - max_minor: a minor version of maximum supported RPC version.
+ * - min_major: a major version of minimum supported RPC version.
+ * - min_minor: a minor version of minimum supported RPC version.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_set_rpc_versions(grpc_gcp_handshaker_req* req,
+                                              uint32_t max_major,
+                                              uint32_t max_minor,
+                                              uint32_t min_major,
+                                              uint32_t min_minor);
+
+/**
+ * This method serializes an ALTS handshake request and returns a data stream.
+ *
+ * - req: an ALTS handshake request.
+ * - slice: a data stream where the serialized result will be written.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_req_encode(grpc_gcp_handshaker_req* req,
+                                    grpc_slice* slice);
+
+/* This method destroys an ALTS handshake request. */
+void grpc_gcp_handshaker_req_destroy(grpc_gcp_handshaker_req* req);
+
+/* This method creates an ALTS handshake response. */
+grpc_gcp_handshaker_resp* grpc_gcp_handshaker_resp_create(void);
+
+/**
+ * This method de-serializes a data stream and stores the result
+ * in an ALTS handshake response.
+ *
+ * - slice: a data stream containing a serialized ALTS handshake response.
+ * - resp: an ALTS handshake response used to hold de-serialized result.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_handshaker_resp_decode(grpc_slice slice,
+                                     grpc_gcp_handshaker_resp* resp);
+
+/* This method destroys an ALTS handshake response. */
+void grpc_gcp_handshaker_resp_destroy(grpc_gcp_handshaker_resp* resp);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc
new file mode 100644
index 0000000..d63d353
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc
@@ -0,0 +1,145 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h"
+
+#include "src/core/lib/slice/slice_internal.h"
+
+void add_repeated_field(repeated_field** head, const void* data) {
+  repeated_field* field =
+      static_cast<repeated_field*>(gpr_zalloc(sizeof(*field)));
+  field->data = data;
+  if (*head == nullptr) {
+    *head = field;
+    (*head)->next = nullptr;
+  } else {
+    field->next = *head;
+    *head = field;
+  }
+}
+
+void destroy_repeated_field_list_identity(repeated_field* head) {
+  repeated_field* field = head;
+  while (field != nullptr) {
+    repeated_field* next_field = field->next;
+    const grpc_gcp_identity* identity =
+        static_cast<const grpc_gcp_identity*>(field->data);
+    destroy_slice(static_cast<grpc_slice*>(identity->hostname.arg));
+    destroy_slice(static_cast<grpc_slice*>(identity->service_account.arg));
+    gpr_free((void*)identity);
+    gpr_free(field);
+    field = next_field;
+  }
+}
+
+void destroy_repeated_field_list_string(repeated_field* head) {
+  repeated_field* field = head;
+  while (field != nullptr) {
+    repeated_field* next_field = field->next;
+    destroy_slice((grpc_slice*)field->data);
+    gpr_free(field);
+    field = next_field;
+  }
+}
+
+grpc_slice* create_slice(const char* data, size_t size) {
+  grpc_slice slice = grpc_slice_from_copied_buffer(data, size);
+  grpc_slice* cb_slice =
+      static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice)));
+  memcpy(cb_slice, &slice, sizeof(*cb_slice));
+  return cb_slice;
+}
+
+void destroy_slice(grpc_slice* slice) {
+  if (slice != nullptr) {
+    grpc_slice_unref_internal(*slice);
+    gpr_free(slice);
+  }
+}
+
+bool encode_string_or_bytes_cb(pb_ostream_t* stream, const pb_field_t* field,
+                               void* const* arg) {
+  grpc_slice* slice = static_cast<grpc_slice*>(*arg);
+  if (!pb_encode_tag_for_field(stream, field)) return false;
+  return pb_encode_string(stream, GRPC_SLICE_START_PTR(*slice),
+                          GRPC_SLICE_LENGTH(*slice));
+}
+
+bool encode_repeated_identity_cb(pb_ostream_t* stream, const pb_field_t* field,
+                                 void* const* arg) {
+  repeated_field* var = static_cast<repeated_field*>(*arg);
+  while (var != nullptr) {
+    if (!pb_encode_tag_for_field(stream, field)) return false;
+    if (!pb_encode_submessage(stream, grpc_gcp_Identity_fields,
+                              (grpc_gcp_identity*)var->data))
+      return false;
+    var = var->next;
+  }
+  return true;
+}
+
+bool encode_repeated_string_cb(pb_ostream_t* stream, const pb_field_t* field,
+                               void* const* arg) {
+  repeated_field* var = static_cast<repeated_field*>(*arg);
+  while (var != nullptr) {
+    if (!pb_encode_tag_for_field(stream, field)) return false;
+    const grpc_slice* slice = static_cast<const grpc_slice*>(var->data);
+    if (!pb_encode_string(stream, GRPC_SLICE_START_PTR(*slice),
+                          GRPC_SLICE_LENGTH(*slice)))
+      return false;
+    var = var->next;
+  }
+  return true;
+}
+
+bool decode_string_or_bytes_cb(pb_istream_t* stream, const pb_field_t* field,
+                               void** arg) {
+  grpc_slice slice = grpc_slice_malloc(stream->bytes_left);
+  grpc_slice* cb_slice =
+      static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice)));
+  memcpy(cb_slice, &slice, sizeof(*cb_slice));
+  if (!pb_read(stream, GRPC_SLICE_START_PTR(*cb_slice), stream->bytes_left))
+    return false;
+  *arg = cb_slice;
+  return true;
+}
+
+bool decode_repeated_identity_cb(pb_istream_t* stream, const pb_field_t* field,
+                                 void** arg) {
+  grpc_gcp_identity* identity =
+      static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*identity)));
+  identity->hostname.funcs.decode = decode_string_or_bytes_cb;
+  identity->service_account.funcs.decode = decode_string_or_bytes_cb;
+  add_repeated_field(reinterpret_cast<repeated_field**>(arg), identity);
+  if (!pb_decode(stream, grpc_gcp_Identity_fields, identity)) return false;
+  return true;
+}
+
+bool decode_repeated_string_cb(pb_istream_t* stream, const pb_field_t* field,
+                               void** arg) {
+  grpc_slice slice = grpc_slice_malloc(stream->bytes_left);
+  grpc_slice* cb_slice =
+      static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice)));
+  memcpy(cb_slice, &slice, sizeof(grpc_slice));
+  if (!pb_read(stream, GRPC_SLICE_START_PTR(*cb_slice), stream->bytes_left))
+    return false;
+  add_repeated_field(reinterpret_cast<repeated_field**>(arg), cb_slice);
+  return true;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h
new file mode 100644
index 0000000..966ea45
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H
+
+#include <grpc/support/port_platform.h>
+
+#include "pb_decode.h"
+#include "pb_encode.h"
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/handshaker/handshaker.pb.h"
+
+/**
+ * An implementation of utility functions used to serialize/
+ * de-serialize ALTS handshake requests/responses. All APIs in the header
+ * are thread-compatible.
+ */
+
+/* Renaming of message/field structs generated by nanopb compiler. */
+typedef grpc_gcp_HandshakeProtocol grpc_gcp_handshake_protocol;
+typedef grpc_gcp_NetworkProtocol grpc_gcp_network_protocol;
+typedef grpc_gcp_Identity grpc_gcp_identity;
+typedef grpc_gcp_NextHandshakeMessageReq grpc_gcp_next_handshake_message_req;
+typedef grpc_gcp_ServerHandshakeParameters grpc_gcp_server_handshake_parameters;
+typedef grpc_gcp_Endpoint grpc_gcp_endpoint;
+typedef grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry
+    grpc_gcp_handshake_parameters_entry;
+typedef grpc_gcp_StartClientHandshakeReq grpc_gcp_start_client_handshake_req;
+typedef grpc_gcp_StartServerHandshakeReq grpc_gcp_start_server_handshake_req;
+typedef grpc_gcp_HandshakerReq grpc_gcp_handshaker_req;
+typedef grpc_gcp_HandshakerResult grpc_gcp_handshaker_result;
+typedef grpc_gcp_HandshakerStatus grpc_gcp_handshaker_status;
+typedef grpc_gcp_HandshakerResp grpc_gcp_handshaker_resp;
+
+typedef enum {
+  CLIENT_START_REQ = 0, /* StartClientHandshakeReq. */
+  SERVER_START_REQ = 1, /* StartServerHandshakeReq. */
+  NEXT_REQ = 2,         /* NextHandshakeMessageReq. */
+} grpc_gcp_handshaker_req_type;
+
+/**
+ *  A struct representing a repeated field. The struct is used to organize all
+ *  instances of a specific repeated field into a linked list, which then will
+ *  be used at encode/decode phase. For instance at the encode phase, the encode
+ *  function will iterate through the list, encode each field, and then output
+ *  the result to the stream.
+ */
+typedef struct repeated_field_ {
+  struct repeated_field_* next;
+  const void* data;
+} repeated_field;
+
+/**
+ * This method adds a repeated field to the head of repeated field list.
+ *
+ * - head: a head of repeated field list.
+ * - field: a repeated field to be added to the list.
+ */
+void add_repeated_field(repeated_field** head, const void* field);
+
+/**
+ * This method destroys a repeated field list that consists of string type
+ * fields.
+ *
+ * - head: a head of repeated field list.
+ */
+void destroy_repeated_field_list_string(repeated_field* head);
+
+/**
+ * This method destroys a repeated field list that consists of
+ * grpc_gcp_identity type fields.
+ *
+ * - head: a head of repeated field list.
+ */
+void destroy_repeated_field_list_identity(repeated_field* head);
+
+/**
+ * This method creates a grpc_slice instance by copying a data buffer. It is
+ * similar to grpc_slice_from_copied_buffer() except that it returns an instance
+ * allocated from the heap.
+ *
+ * - data: a data buffer to be copied to grpc_slice instance.
+ * - size: size of data buffer.
+ */
+grpc_slice* create_slice(const char* data, size_t size);
+
+/* This method destroys a grpc_slice instance. */
+void destroy_slice(grpc_slice* slice);
+
+/**
+ * The following encode/decode functions will be assigned to encode/decode
+ * function pointers of pb_callback_t struct (defined in
+ * //third_party/nanopb/pb.h), that represent a repeated field with a dynamic
+ * length (e.g., a string type or repeated field).
+ */
+
+/* This method is an encode callback function for a string or byte array. */
+bool encode_string_or_bytes_cb(pb_ostream_t* stream, const pb_field_t* field,
+                               void* const* arg);
+
+/**
+ * This method is an encode callback function for a repeated grpc_gcp_identity
+ * field.
+ */
+bool encode_repeated_identity_cb(pb_ostream_t* stream, const pb_field_t* field,
+                                 void* const* arg);
+
+/* This method is an encode callback function for a repeated string field. */
+bool encode_repeated_string_cb(pb_ostream_t* stream, const pb_field_t* field,
+                               void* const* arg);
+
+/**
+ * This method is a decode callback function for a string or byte array field.
+ */
+bool decode_string_or_bytes_cb(pb_istream_t* stream, const pb_field_t* field,
+                               void** arg);
+/**
+ * This method is a decode callback function for a repeated grpc_gcp_identity
+ * field.
+ */
+bool decode_repeated_identity_cb(pb_istream_t* stream, const pb_field_t* field,
+                                 void** arg);
+
+/* This method is a decode callback function for a repeated string field. */
+bool decode_repeated_string_cb(pb_istream_t* stream, const pb_field_t* field,
+                               void** arg);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.cc
new file mode 100644
index 0000000..3501257
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.cc
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_shared_resource.h"
+
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+
+static alts_shared_resource_dedicated g_alts_resource_dedicated;
+
+alts_shared_resource_dedicated* grpc_alts_get_shared_resource_dedicated(void) {
+  return &g_alts_resource_dedicated;
+}
+
+static void thread_worker(void* arg) {
+  while (true) {
+    grpc_event event =
+        grpc_completion_queue_next(g_alts_resource_dedicated.cq,
+                                   gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
+    GPR_ASSERT(event.type != GRPC_QUEUE_TIMEOUT);
+    if (event.type == GRPC_QUEUE_SHUTDOWN) {
+      break;
+    }
+    GPR_ASSERT(event.type == GRPC_OP_COMPLETE);
+    alts_handshaker_client* client =
+        static_cast<alts_handshaker_client*>(event.tag);
+    alts_handshaker_client_handle_response(client, event.success);
+  }
+}
+
+void grpc_alts_shared_resource_dedicated_init() {
+  g_alts_resource_dedicated.cq = nullptr;
+  gpr_mu_init(&g_alts_resource_dedicated.mu);
+}
+
+void grpc_alts_shared_resource_dedicated_start(
+    const char* handshaker_service_url) {
+  gpr_mu_lock(&g_alts_resource_dedicated.mu);
+  if (g_alts_resource_dedicated.cq == nullptr) {
+    g_alts_resource_dedicated.channel =
+        grpc_insecure_channel_create(handshaker_service_url, nullptr, nullptr);
+    g_alts_resource_dedicated.cq =
+        grpc_completion_queue_create_for_next(nullptr);
+    g_alts_resource_dedicated.thread =
+        grpc_core::Thread("alts_tsi_handshaker", &thread_worker, nullptr);
+    g_alts_resource_dedicated.interested_parties = grpc_pollset_set_create();
+    grpc_pollset_set_add_pollset(g_alts_resource_dedicated.interested_parties,
+                                 grpc_cq_pollset(g_alts_resource_dedicated.cq));
+    g_alts_resource_dedicated.thread.Start();
+  }
+  gpr_mu_unlock(&g_alts_resource_dedicated.mu);
+}
+
+void grpc_alts_shared_resource_dedicated_shutdown() {
+  if (g_alts_resource_dedicated.cq != nullptr) {
+    grpc_pollset_set_del_pollset(g_alts_resource_dedicated.interested_parties,
+                                 grpc_cq_pollset(g_alts_resource_dedicated.cq));
+    grpc_completion_queue_shutdown(g_alts_resource_dedicated.cq);
+    g_alts_resource_dedicated.thread.Join();
+    grpc_pollset_set_destroy(g_alts_resource_dedicated.interested_parties);
+    grpc_completion_queue_destroy(g_alts_resource_dedicated.cq);
+    grpc_channel_destroy(g_alts_resource_dedicated.channel);
+  }
+  gpr_mu_destroy(&g_alts_resource_dedicated.mu);
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.h
new file mode 100644
index 0000000..8ae0089
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/surface/completion_queue.h"
+
+/**
+ * Main struct containing ALTS shared resources used when
+ * employing the dedicated completion queue and thread.
+ */
+typedef struct alts_shared_resource_dedicated {
+  grpc_core::Thread thread;
+  grpc_completion_queue* cq;
+  grpc_pollset_set* interested_parties;
+  grpc_cq_completion storage;
+  gpr_mu mu;
+  grpc_channel* channel;
+} alts_shared_resource_dedicated;
+
+/* This method returns the address of alts_shared_resource_dedicated
+ * object shared by all TSI handshakes.
+ */
+alts_shared_resource_dedicated* grpc_alts_get_shared_resource_dedicated(void);
+
+/**
+ * This method destroys the alts_shared_resource_dedicated object
+ * shared by all TSI handshakes. The applicaiton is responsible for
+ * invoking the API before calling grpc_shutdown().
+ */
+void grpc_alts_shared_resource_dedicated_shutdown();
+
+/**
+ * This method initializes the alts_shared_resource_dedicated object
+ * shared by all TSI handshakes. The application is responsible for
+ * invoking the API after calling grpc_init();
+ */
+void grpc_alts_shared_resource_dedicated_init();
+
+/**
+ * This method populates various fields of the alts_shared_resource_dedicated
+ * object shared by all TSI handshakes and start the dedicated thread.
+ * The API will be invoked by the caller in a lazy manner. That is,
+ * it will get invoked when ALTS TSI handshake occurs for the first time.
+ */
+void grpc_alts_shared_resource_dedicated_start(
+    const char* handshaker_service_url);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H \
+        */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
new file mode 100644
index 0000000..1b7e58d
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc
@@ -0,0 +1,466 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd_id.h>
+
+#include "src/core/lib/gpr/host_port.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+#include "src/core/tsi/alts/handshaker/alts_shared_resource.h"
+#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
+
+/* Main struct for ALTS TSI handshaker. */
+struct alts_tsi_handshaker {
+  tsi_handshaker base;
+  alts_handshaker_client* client;
+  grpc_slice target_name;
+  bool is_client;
+  bool has_sent_start_message;
+  bool has_created_handshaker_client;
+  char* handshaker_service_url;
+  grpc_pollset_set* interested_parties;
+  grpc_alts_credentials_options* options;
+  alts_handshaker_client_vtable* client_vtable_for_testing;
+  grpc_channel* channel;
+};
+
+/* Main struct for ALTS TSI handshaker result. */
+typedef struct alts_tsi_handshaker_result {
+  tsi_handshaker_result base;
+  char* peer_identity;
+  char* key_data;
+  unsigned char* unused_bytes;
+  size_t unused_bytes_size;
+  grpc_slice rpc_versions;
+  bool is_client;
+} alts_tsi_handshaker_result;
+
+static tsi_result handshaker_result_extract_peer(
+    const tsi_handshaker_result* self, tsi_peer* peer) {
+  if (self == nullptr || peer == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid argument to handshaker_result_extract_peer()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  GPR_ASSERT(kTsiAltsNumOfPeerProperties == 3);
+  tsi_result ok = tsi_construct_peer(kTsiAltsNumOfPeerProperties, peer);
+  int index = 0;
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to construct tsi peer");
+    return ok;
+  }
+  GPR_ASSERT(&peer->properties[index] != nullptr);
+  ok = tsi_construct_string_peer_property_from_cstring(
+      TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE,
+      &peer->properties[index]);
+  if (ok != TSI_OK) {
+    tsi_peer_destruct(peer);
+    gpr_log(GPR_ERROR, "Failed to set tsi peer property");
+    return ok;
+  }
+  index++;
+  GPR_ASSERT(&peer->properties[index] != nullptr);
+  ok = tsi_construct_string_peer_property_from_cstring(
+      TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, result->peer_identity,
+      &peer->properties[index]);
+  if (ok != TSI_OK) {
+    tsi_peer_destruct(peer);
+    gpr_log(GPR_ERROR, "Failed to set tsi peer property");
+  }
+  index++;
+  GPR_ASSERT(&peer->properties[index] != nullptr);
+  ok = tsi_construct_string_peer_property(
+      TSI_ALTS_RPC_VERSIONS,
+      reinterpret_cast<char*>(GRPC_SLICE_START_PTR(result->rpc_versions)),
+      GRPC_SLICE_LENGTH(result->rpc_versions), &peer->properties[2]);
+  if (ok != TSI_OK) {
+    tsi_peer_destruct(peer);
+    gpr_log(GPR_ERROR, "Failed to set tsi peer property");
+  }
+  GPR_ASSERT(++index == kTsiAltsNumOfPeerProperties);
+  return ok;
+}
+
+static tsi_result handshaker_result_create_zero_copy_grpc_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector) {
+  if (self == nullptr || protector == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to create_zero_copy_grpc_protector()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  tsi_result ok = alts_zero_copy_grpc_protector_create(
+      reinterpret_cast<const uint8_t*>(result->key_data),
+      kAltsAes128GcmRekeyKeyLength, /*is_rekey=*/true, result->is_client,
+      /*is_integrity_only=*/false, /*enable_extra_copy=*/false,
+      max_output_protected_frame_size, protector);
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to create zero-copy grpc protector");
+  }
+  return ok;
+}
+
+static tsi_result handshaker_result_create_frame_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_frame_protector** protector) {
+  if (self == nullptr || protector == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to handshaker_result_create_frame_protector()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  tsi_result ok = alts_create_frame_protector(
+      reinterpret_cast<const uint8_t*>(result->key_data),
+      kAltsAes128GcmRekeyKeyLength, result->is_client, /*is_rekey=*/true,
+      max_output_protected_frame_size, protector);
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to create frame protector");
+  }
+  return ok;
+}
+
+static tsi_result handshaker_result_get_unused_bytes(
+    const tsi_handshaker_result* self, const unsigned char** bytes,
+    size_t* bytes_size) {
+  if (self == nullptr || bytes == nullptr || bytes_size == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to handshaker_result_get_unused_bytes()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  *bytes = result->unused_bytes;
+  *bytes_size = result->unused_bytes_size;
+  return TSI_OK;
+}
+
+static void handshaker_result_destroy(tsi_handshaker_result* self) {
+  if (self == nullptr) {
+    return;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  gpr_free(result->peer_identity);
+  gpr_free(result->key_data);
+  gpr_free(result->unused_bytes);
+  grpc_slice_unref_internal(result->rpc_versions);
+  gpr_free(result);
+}
+
+static const tsi_handshaker_result_vtable result_vtable = {
+    handshaker_result_extract_peer,
+    handshaker_result_create_zero_copy_grpc_protector,
+    handshaker_result_create_frame_protector,
+    handshaker_result_get_unused_bytes, handshaker_result_destroy};
+
+tsi_result alts_tsi_handshaker_result_create(grpc_gcp_handshaker_resp* resp,
+                                             bool is_client,
+                                             tsi_handshaker_result** self) {
+  if (self == nullptr || resp == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to create_handshaker_result()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_slice* key = static_cast<grpc_slice*>(resp->result.key_data.arg);
+  GPR_ASSERT(key != nullptr);
+  grpc_slice* identity =
+      static_cast<grpc_slice*>(resp->result.peer_identity.service_account.arg);
+  if (identity == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid service account");
+    return TSI_FAILED_PRECONDITION;
+  }
+  if (GRPC_SLICE_LENGTH(*key) < kAltsAes128GcmRekeyKeyLength) {
+    gpr_log(GPR_ERROR, "Bad key length");
+    return TSI_FAILED_PRECONDITION;
+  }
+  alts_tsi_handshaker_result* result =
+      static_cast<alts_tsi_handshaker_result*>(gpr_zalloc(sizeof(*result)));
+  result->key_data =
+      static_cast<char*>(gpr_zalloc(kAltsAes128GcmRekeyKeyLength));
+  memcpy(result->key_data, GRPC_SLICE_START_PTR(*key),
+         kAltsAes128GcmRekeyKeyLength);
+  result->peer_identity = grpc_slice_to_c_string(*identity);
+  if (!resp->result.has_peer_rpc_versions) {
+    gpr_log(GPR_ERROR, "Peer does not set RPC protocol versions.");
+    return TSI_FAILED_PRECONDITION;
+  }
+  if (!grpc_gcp_rpc_protocol_versions_encode(&resp->result.peer_rpc_versions,
+                                             &result->rpc_versions)) {
+    gpr_log(GPR_ERROR, "Failed to serialize peer's RPC protocol versions.");
+    return TSI_FAILED_PRECONDITION;
+  }
+  result->is_client = is_client;
+  result->base.vtable = &result_vtable;
+  *self = &result->base;
+  return TSI_OK;
+}
+
+/* gRPC provided callback used when gRPC thread model is applied. */
+static void on_handshaker_service_resp_recv(void* arg, grpc_error* error) {
+  alts_handshaker_client* client = static_cast<alts_handshaker_client*>(arg);
+  if (client == nullptr) {
+    gpr_log(GPR_ERROR, "ALTS handshaker client is nullptr");
+    return;
+  }
+  alts_handshaker_client_handle_response(client, true);
+}
+
+/* gRPC provided callback used when dedicatd CQ and thread are used.
+ * It serves to safely bring the control back to application. */
+static void on_handshaker_service_resp_recv_dedicated(void* arg,
+                                                      grpc_error* error) {
+  alts_shared_resource_dedicated* resource =
+      grpc_alts_get_shared_resource_dedicated();
+  grpc_cq_end_op(resource->cq, arg, GRPC_ERROR_NONE,
+                 [](void* done_arg, grpc_cq_completion* storage) {}, nullptr,
+                 &resource->storage);
+}
+
+static tsi_result handshaker_next(
+    tsi_handshaker* self, const unsigned char* received_bytes,
+    size_t received_bytes_size, const unsigned char** bytes_to_send,
+    size_t* bytes_to_send_size, tsi_handshaker_result** result,
+    tsi_handshaker_on_next_done_cb cb, void* user_data) {
+  if (self == nullptr || cb == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to handshaker_next()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->handshake_shutdown) {
+    gpr_log(GPR_ERROR, "TSI handshake shutdown");
+    return TSI_HANDSHAKE_SHUTDOWN;
+  }
+  alts_tsi_handshaker* handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(self);
+  tsi_result ok = TSI_OK;
+  if (!handshaker->has_created_handshaker_client) {
+    if (handshaker->channel == nullptr) {
+      grpc_alts_shared_resource_dedicated_start(
+          handshaker->handshaker_service_url);
+      handshaker->interested_parties =
+          grpc_alts_get_shared_resource_dedicated()->interested_parties;
+      GPR_ASSERT(handshaker->interested_parties != nullptr);
+    }
+    grpc_iomgr_cb_func grpc_cb = handshaker->channel == nullptr
+                                     ? on_handshaker_service_resp_recv_dedicated
+                                     : on_handshaker_service_resp_recv;
+    grpc_channel* channel =
+        handshaker->channel == nullptr
+            ? grpc_alts_get_shared_resource_dedicated()->channel
+            : handshaker->channel;
+    handshaker->client = alts_grpc_handshaker_client_create(
+        handshaker, channel, handshaker->handshaker_service_url,
+        handshaker->interested_parties, handshaker->options,
+        handshaker->target_name, grpc_cb, cb, user_data,
+        handshaker->client_vtable_for_testing, handshaker->is_client);
+    if (handshaker->client == nullptr) {
+      gpr_log(GPR_ERROR, "Failed to create ALTS handshaker client");
+      return TSI_FAILED_PRECONDITION;
+    }
+    handshaker->has_created_handshaker_client = true;
+  }
+  if (handshaker->channel == nullptr &&
+      handshaker->client_vtable_for_testing == nullptr) {
+    GPR_ASSERT(grpc_cq_begin_op(grpc_alts_get_shared_resource_dedicated()->cq,
+                                handshaker->client));
+  }
+  grpc_slice slice = (received_bytes == nullptr || received_bytes_size == 0)
+                         ? grpc_empty_slice()
+                         : grpc_slice_from_copied_buffer(
+                               reinterpret_cast<const char*>(received_bytes),
+                               received_bytes_size);
+  if (!handshaker->has_sent_start_message) {
+    ok = handshaker->is_client
+             ? alts_handshaker_client_start_client(handshaker->client)
+             : alts_handshaker_client_start_server(handshaker->client, &slice);
+    handshaker->has_sent_start_message = true;
+  } else {
+    ok = alts_handshaker_client_next(handshaker->client, &slice);
+  }
+  grpc_slice_unref_internal(slice);
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to schedule ALTS handshaker requests");
+    return ok;
+  }
+  return TSI_ASYNC;
+}
+
+/*
+ * This API will be invoked by a non-gRPC application, and an ExecCtx needs
+ * to be explicitly created in order to invoke ALTS handshaker client API's
+ * that assumes the caller is inside gRPC core.
+ */
+static tsi_result handshaker_next_dedicated(
+    tsi_handshaker* self, const unsigned char* received_bytes,
+    size_t received_bytes_size, const unsigned char** bytes_to_send,
+    size_t* bytes_to_send_size, tsi_handshaker_result** result,
+    tsi_handshaker_on_next_done_cb cb, void* user_data) {
+  grpc_core::ExecCtx exec_ctx;
+  return handshaker_next(self, received_bytes, received_bytes_size,
+                         bytes_to_send, bytes_to_send_size, result, cb,
+                         user_data);
+}
+
+static void handshaker_shutdown(tsi_handshaker* self) {
+  GPR_ASSERT(self != nullptr);
+  if (self->handshake_shutdown) {
+    return;
+  }
+  alts_tsi_handshaker* handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(self);
+  alts_handshaker_client_shutdown(handshaker->client);
+}
+
+static void handshaker_destroy(tsi_handshaker* self) {
+  if (self == nullptr) {
+    return;
+  }
+  alts_tsi_handshaker* handshaker =
+      reinterpret_cast<alts_tsi_handshaker*>(self);
+  alts_handshaker_client_destroy(handshaker->client);
+  grpc_slice_unref_internal(handshaker->target_name);
+  grpc_alts_credentials_options_destroy(handshaker->options);
+  if (handshaker->channel != nullptr) {
+    grpc_channel_destroy(handshaker->channel);
+  }
+  gpr_free(handshaker->handshaker_service_url);
+  gpr_free(handshaker);
+}
+
+static const tsi_handshaker_vtable handshaker_vtable = {
+    nullptr,         nullptr,
+    nullptr,         nullptr,
+    nullptr,         handshaker_destroy,
+    handshaker_next, handshaker_shutdown};
+
+static const tsi_handshaker_vtable handshaker_vtable_dedicated = {
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    handshaker_destroy,
+    handshaker_next_dedicated,
+    handshaker_shutdown};
+
+bool alts_tsi_handshaker_has_shutdown(alts_tsi_handshaker* handshaker) {
+  GPR_ASSERT(handshaker != nullptr);
+  return handshaker->base.handshake_shutdown;
+}
+
+tsi_result alts_tsi_handshaker_create(
+    const grpc_alts_credentials_options* options, const char* target_name,
+    const char* handshaker_service_url, bool is_client,
+    grpc_pollset_set* interested_parties, tsi_handshaker** self) {
+  if (handshaker_service_url == nullptr || self == nullptr ||
+      options == nullptr || (is_client && target_name == nullptr)) {
+    gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_handshaker_create()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_tsi_handshaker* handshaker =
+      static_cast<alts_tsi_handshaker*>(gpr_zalloc(sizeof(*handshaker)));
+  bool use_dedicated_cq = interested_parties == nullptr;
+  handshaker->client = nullptr;
+  handshaker->is_client = is_client;
+  handshaker->has_sent_start_message = false;
+  handshaker->target_name = target_name == nullptr
+                                ? grpc_empty_slice()
+                                : grpc_slice_from_static_string(target_name);
+  handshaker->interested_parties = interested_parties;
+  handshaker->has_created_handshaker_client = false;
+  handshaker->handshaker_service_url = gpr_strdup(handshaker_service_url);
+  handshaker->options = grpc_alts_credentials_options_copy(options);
+  handshaker->base.vtable =
+      use_dedicated_cq ? &handshaker_vtable_dedicated : &handshaker_vtable;
+  handshaker->channel =
+      use_dedicated_cq
+          ? nullptr
+          : grpc_insecure_channel_create(handshaker->handshaker_service_url,
+                                         nullptr, nullptr);
+  *self = &handshaker->base;
+  return TSI_OK;
+}
+
+void alts_tsi_handshaker_result_set_unused_bytes(tsi_handshaker_result* self,
+                                                 grpc_slice* recv_bytes,
+                                                 size_t bytes_consumed) {
+  GPR_ASSERT(recv_bytes != nullptr && self != nullptr);
+  if (GRPC_SLICE_LENGTH(*recv_bytes) == bytes_consumed) {
+    return;
+  }
+  alts_tsi_handshaker_result* result =
+      reinterpret_cast<alts_tsi_handshaker_result*>(self);
+  result->unused_bytes_size = GRPC_SLICE_LENGTH(*recv_bytes) - bytes_consumed;
+  result->unused_bytes =
+      static_cast<unsigned char*>(gpr_zalloc(result->unused_bytes_size));
+  memcpy(result->unused_bytes,
+         GRPC_SLICE_START_PTR(*recv_bytes) + bytes_consumed,
+         result->unused_bytes_size);
+}
+
+namespace grpc_core {
+namespace internal {
+
+bool alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+    alts_tsi_handshaker* handshaker) {
+  GPR_ASSERT(handshaker != nullptr);
+  return handshaker->has_sent_start_message;
+}
+
+void alts_tsi_handshaker_set_client_vtable_for_testing(
+    alts_tsi_handshaker* handshaker, alts_handshaker_client_vtable* vtable) {
+  GPR_ASSERT(handshaker != nullptr);
+  handshaker->client_vtable_for_testing = vtable;
+}
+
+bool alts_tsi_handshaker_get_is_client_for_testing(
+    alts_tsi_handshaker* handshaker) {
+  GPR_ASSERT(handshaker != nullptr);
+  return handshaker->is_client;
+}
+
+alts_handshaker_client* alts_tsi_handshaker_get_client_for_testing(
+    alts_tsi_handshaker* handshaker) {
+  return handshaker->client;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
new file mode 100644
index 0000000..32f94bc
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h"
+#include "src/core/tsi/transport_security.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+#define TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY "service_accont"
+#define TSI_ALTS_CERTIFICATE_TYPE "ALTS"
+#define TSI_ALTS_RPC_VERSIONS "rpc_versions"
+
+const size_t kTsiAltsNumOfPeerProperties = 3;
+
+typedef struct alts_tsi_handshaker alts_tsi_handshaker;
+
+/**
+ * This method creates a ALTS TSI handshaker instance.
+ *
+ * - options: ALTS credentials options containing information passed from TSI
+ *   caller (e.g., rpc protocol versions).
+ * - target_name: the name of the endpoint that the channel is connecting to,
+ *   and will be used for secure naming check.
+ * - handshaker_service_url: address of ALTS handshaker service in the format of
+ *   "host:port".
+ * - is_client: boolean value indicating if the handshaker is used at the client
+ *   (is_client = true) or server (is_client = false) side.
+ * - interested_parties: set of pollsets interested in this connection.
+ * - self: address of ALTS TSI handshaker instance to be returned from the
+ *   method.
+ *
+ * It returns TSI_OK on success and an error status code on failure. Note that
+ * if interested_parties is nullptr, a dedicated TSI thread will be created and
+ * used.
+ */
+tsi_result alts_tsi_handshaker_create(
+    const grpc_alts_credentials_options* options, const char* target_name,
+    const char* handshaker_service_url, bool is_client,
+    grpc_pollset_set* interested_parties, tsi_handshaker** self);
+
+/**
+ * This method creates an ALTS TSI handshaker result instance.
+ *
+ * - resp: data received from the handshaker service.
+ * - is_client: a boolean value indicating if the result belongs to a
+ *   client or not.
+ * - result: address of ALTS TSI handshaker result instance.
+ */
+tsi_result alts_tsi_handshaker_result_create(grpc_gcp_handshaker_resp* resp,
+                                             bool is_client,
+                                             tsi_handshaker_result** result);
+
+/**
+ * This method sets unused bytes of ALTS TSI handshaker result instance.
+ *
+ * - result: an ALTS TSI handshaker result instance.
+ * - recv_bytes: data received from the handshaker service.
+ * - bytes_consumed: size of data consumed by the handshaker service.
+ */
+void alts_tsi_handshaker_result_set_unused_bytes(tsi_handshaker_result* result,
+                                                 grpc_slice* recv_bytes,
+                                                 size_t bytes_consumed);
+
+/**
+ * This method returns a boolean value indicating if an ALTS TSI handshaker
+ * has been shutdown or not.
+ */
+bool alts_tsi_handshaker_has_shutdown(alts_tsi_handshaker* handshaker);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
new file mode 100644
index 0000000..ec2616e
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
+
+namespace grpc_core {
+namespace internal {
+
+/**
+ * Unsafe, use for testing only. */
+
+alts_handshaker_client* alts_tsi_handshaker_get_client_for_testing(
+    alts_tsi_handshaker* handshaker);
+
+bool alts_tsi_handshaker_get_has_sent_start_message_for_testing(
+    alts_tsi_handshaker* handshaker);
+
+void alts_tsi_handshaker_set_client_vtable_for_testing(
+    alts_tsi_handshaker* handshaker, alts_handshaker_client_vtable* vtable);
+
+bool alts_tsi_handshaker_get_is_client_for_testing(
+    alts_tsi_handshaker* handshaker);
+
+void alts_handshaker_client_set_grpc_caller_for_testing(
+    alts_handshaker_client* client, alts_grpc_caller caller);
+
+grpc_byte_buffer* alts_handshaker_client_get_send_buffer_for_testing(
+    alts_handshaker_client* client);
+
+grpc_byte_buffer** alts_handshaker_client_get_recv_buffer_addr_for_testing(
+    alts_handshaker_client* client);
+
+grpc_metadata_array* alts_handshaker_client_get_initial_metadata_for_testing(
+    alts_handshaker_client* client);
+
+void alts_handshaker_client_set_recv_bytes_for_testing(
+    alts_handshaker_client* client, grpc_slice* recv_bytes);
+
+void alts_handshaker_client_check_fields_for_testing(
+    alts_handshaker_client* client, tsi_handshaker_on_next_done_cb cb,
+    void* user_data, bool has_sent_start_message, grpc_slice* recv_bytes);
+
+void alts_handshaker_client_set_fields_for_testing(
+    alts_handshaker_client* client, alts_tsi_handshaker* handshaker,
+    tsi_handshaker_on_next_done_cb cb, void* user_data,
+    grpc_byte_buffer* recv_buffer, grpc_status_code status);
+
+void alts_handshaker_client_set_vtable_for_testing(
+    alts_handshaker_client* client, alts_handshaker_client_vtable* vtable);
+
+alts_tsi_handshaker* alts_handshaker_client_get_handshaker_for_testing(
+    alts_handshaker_client* client);
+
+void alts_handshaker_client_set_cb_for_testing(
+    alts_handshaker_client* client, tsi_handshaker_on_next_done_cb cb);
+
+grpc_closure* alts_handshaker_client_get_closure_for_testing(
+    alts_handshaker_client* client);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.cc
new file mode 100644
index 0000000..1747f1a
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.cc
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
+
+#include <grpc/byte_buffer_reader.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+
+tsi_result alts_tsi_utils_convert_to_tsi_result(grpc_status_code code) {
+  switch (code) {
+    case GRPC_STATUS_OK:
+      return TSI_OK;
+    case GRPC_STATUS_UNKNOWN:
+      return TSI_UNKNOWN_ERROR;
+    case GRPC_STATUS_INVALID_ARGUMENT:
+      return TSI_INVALID_ARGUMENT;
+    case GRPC_STATUS_NOT_FOUND:
+      return TSI_NOT_FOUND;
+    case GRPC_STATUS_INTERNAL:
+      return TSI_INTERNAL_ERROR;
+    default:
+      return TSI_UNKNOWN_ERROR;
+  }
+}
+
+grpc_gcp_handshaker_resp* alts_tsi_utils_deserialize_response(
+    grpc_byte_buffer* resp_buffer) {
+  GPR_ASSERT(resp_buffer != nullptr);
+  grpc_byte_buffer_reader bbr;
+  grpc_byte_buffer_reader_init(&bbr, resp_buffer);
+  grpc_slice slice = grpc_byte_buffer_reader_readall(&bbr);
+  grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create();
+  bool ok = grpc_gcp_handshaker_resp_decode(slice, resp);
+  grpc_slice_unref_internal(slice);
+  grpc_byte_buffer_reader_destroy(&bbr);
+  if (!ok) {
+    grpc_gcp_handshaker_resp_destroy(resp);
+    gpr_log(GPR_ERROR, "grpc_gcp_handshaker_resp_decode() failed");
+    return nullptr;
+  }
+  return resp;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.h
new file mode 100644
index 0000000..9ef649d
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+/**
+ * This method converts grpc_status_code code to the corresponding tsi_result
+ * code.
+ *
+ * - code: grpc_status_code code.
+ *
+ * It returns the converted tsi_result code.
+ */
+tsi_result alts_tsi_utils_convert_to_tsi_result(grpc_status_code code);
+
+/**
+ * This method deserializes a handshaker response returned from ALTS handshaker
+ * service.
+ *
+ * - bytes_received: data returned from ALTS handshaker service.
+ *
+ * It returns a deserialized handshaker response on success and nullptr on
+ * failure.
+ */
+grpc_gcp_handshaker_resp* alts_tsi_utils_deserialize_response(
+    grpc_byte_buffer* resp_buffer);
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.c b/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.c
new file mode 100644
index 0000000..5fb152a
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.c
@@ -0,0 +1,47 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/tsi/alts/handshaker/altscontext.pb.h"
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_gcp_AltsContext_fields[7] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_gcp_AltsContext, application_protocol, application_protocol, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, record_protocol, application_protocol, 0),
+    PB_FIELD(  3, UENUM   , OPTIONAL, STATIC  , OTHER, grpc_gcp_AltsContext, security_level, record_protocol, 0),
+    PB_FIELD(  4, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, peer_service_account, security_level, 0),
+    PB_FIELD(  5, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, local_service_account, peer_service_account, 0),
+    PB_FIELD(  6, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_AltsContext, peer_rpc_versions, local_service_account, &grpc_gcp_RpcProtocolVersions_fields),
+    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(grpc_gcp_AltsContext, peer_rpc_versions) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_AltsContext)
+#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(grpc_gcp_AltsContext, peer_rpc_versions) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_AltsContext)
+#endif
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.h b/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.h
new file mode 100644
index 0000000..632b20c
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.h
@@ -0,0 +1,63 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GRPC_GCP_ALTSCONTEXT_PB_H_INCLUDED
+#define PB_GRPC_GCP_ALTSCONTEXT_PB_H_INCLUDED
+#include "pb.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+/* @@protoc_insertion_point(includes) */
+#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 _grpc_gcp_AltsContext {
+    pb_callback_t application_protocol;
+    pb_callback_t record_protocol;
+    bool has_security_level;
+    grpc_gcp_SecurityLevel security_level;
+    pb_callback_t peer_service_account;
+    pb_callback_t local_service_account;
+    bool has_peer_rpc_versions;
+    grpc_gcp_RpcProtocolVersions peer_rpc_versions;
+/* @@protoc_insertion_point(struct:grpc_gcp_AltsContext) */
+} grpc_gcp_AltsContext;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_gcp_AltsContext_init_default        {{{NULL}, NULL}, {{NULL}, NULL}, false, (grpc_gcp_SecurityLevel)0, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_default}
+#define grpc_gcp_AltsContext_init_zero           {{{NULL}, NULL}, {{NULL}, NULL}, false, (grpc_gcp_SecurityLevel)0, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_zero}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_gcp_AltsContext_application_protocol_tag 1
+#define grpc_gcp_AltsContext_record_protocol_tag 2
+#define grpc_gcp_AltsContext_security_level_tag  3
+#define grpc_gcp_AltsContext_peer_service_account_tag 4
+#define grpc_gcp_AltsContext_local_service_account_tag 5
+#define grpc_gcp_AltsContext_peer_rpc_versions_tag 6
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_gcp_AltsContext_fields[7];
+
+/* Maximum encoded size of messages (where known) */
+/* grpc_gcp_AltsContext_size depends on runtime parameters */
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define ALTSCONTEXT_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.c b/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.c
new file mode 100644
index 0000000..5450b16
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.c
@@ -0,0 +1,122 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/tsi/alts/handshaker/handshaker.pb.h"
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_gcp_Endpoint_fields[4] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_gcp_Endpoint, ip_address, ip_address, 0),
+    PB_FIELD(  2, INT32   , OPTIONAL, STATIC  , OTHER, grpc_gcp_Endpoint, port, ip_address, 0),
+    PB_FIELD(  3, UENUM   , OPTIONAL, STATIC  , OTHER, grpc_gcp_Endpoint, protocol, port, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_Identity_fields[3] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_gcp_Identity, service_account, service_account, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_Identity, hostname, service_account, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_StartClientHandshakeReq_fields[10] = {
+    PB_FIELD(  1, UENUM   , OPTIONAL, STATIC  , FIRST, grpc_gcp_StartClientHandshakeReq, handshake_security_protocol, handshake_security_protocol, 0),
+    PB_FIELD(  2, STRING  , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, application_protocols, handshake_security_protocol, 0),
+    PB_FIELD(  3, STRING  , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, record_protocols, application_protocols, 0),
+    PB_FIELD(  4, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, target_identities, record_protocols, &grpc_gcp_Identity_fields),
+    PB_FIELD(  5, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartClientHandshakeReq, local_identity, target_identities, &grpc_gcp_Identity_fields),
+    PB_FIELD(  6, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartClientHandshakeReq, local_endpoint, local_identity, &grpc_gcp_Endpoint_fields),
+    PB_FIELD(  7, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartClientHandshakeReq, remote_endpoint, local_endpoint, &grpc_gcp_Endpoint_fields),
+    PB_FIELD(  8, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, target_name, remote_endpoint, 0),
+    PB_FIELD(  9, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartClientHandshakeReq, rpc_versions, target_name, &grpc_gcp_RpcProtocolVersions_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_ServerHandshakeParameters_fields[3] = {
+    PB_FIELD(  1, STRING  , REPEATED, CALLBACK, FIRST, grpc_gcp_ServerHandshakeParameters, record_protocols, record_protocols, 0),
+    PB_FIELD(  2, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_gcp_ServerHandshakeParameters, local_identities, record_protocols, &grpc_gcp_Identity_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_StartServerHandshakeReq_fields[7] = {
+    PB_FIELD(  1, STRING  , REPEATED, CALLBACK, FIRST, grpc_gcp_StartServerHandshakeReq, application_protocols, application_protocols, 0),
+    PB_FIELD(  2, MESSAGE , REPEATED, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq, handshake_parameters, application_protocols, &grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields),
+    PB_FIELD(  3, BYTES   , OPTIONAL, CALLBACK, OTHER, grpc_gcp_StartServerHandshakeReq, in_bytes, handshake_parameters, 0),
+    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq, local_endpoint, in_bytes, &grpc_gcp_Endpoint_fields),
+    PB_FIELD(  5, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq, remote_endpoint, local_endpoint, &grpc_gcp_Endpoint_fields),
+    PB_FIELD(  6, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq, rpc_versions, remote_endpoint, &grpc_gcp_RpcProtocolVersions_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields[3] = {
+    PB_FIELD(  1, INT32   , OPTIONAL, STATIC  , FIRST, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, key, key, 0),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value, key, &grpc_gcp_ServerHandshakeParameters_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_NextHandshakeMessageReq_fields[2] = {
+    PB_FIELD(  1, BYTES   , OPTIONAL, CALLBACK, FIRST, grpc_gcp_NextHandshakeMessageReq, in_bytes, in_bytes, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_HandshakerReq_fields[4] = {
+    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, grpc_gcp_HandshakerReq, client_start, client_start, &grpc_gcp_StartClientHandshakeReq_fields),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerReq, server_start, client_start, &grpc_gcp_StartServerHandshakeReq_fields),
+    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerReq, next, server_start, &grpc_gcp_NextHandshakeMessageReq_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_HandshakerResult_fields[8] = {
+    PB_FIELD(  1, STRING  , OPTIONAL, CALLBACK, FIRST, grpc_gcp_HandshakerResult, application_protocol, application_protocol, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerResult, record_protocol, application_protocol, 0),
+    PB_FIELD(  3, BYTES   , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerResult, key_data, record_protocol, 0),
+    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResult, peer_identity, key_data, &grpc_gcp_Identity_fields),
+    PB_FIELD(  5, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResult, local_identity, peer_identity, &grpc_gcp_Identity_fields),
+    PB_FIELD(  6, BOOL    , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResult, keep_channel_open, local_identity, 0),
+    PB_FIELD(  7, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResult, peer_rpc_versions, keep_channel_open, &grpc_gcp_RpcProtocolVersions_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_HandshakerStatus_fields[3] = {
+    PB_FIELD(  1, UINT32  , OPTIONAL, STATIC  , FIRST, grpc_gcp_HandshakerStatus, code, code, 0),
+    PB_FIELD(  2, STRING  , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerStatus, details, code, 0),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_HandshakerResp_fields[5] = {
+    PB_FIELD(  1, BYTES   , OPTIONAL, CALLBACK, FIRST, grpc_gcp_HandshakerResp, out_frames, out_frames, 0),
+    PB_FIELD(  2, UINT32  , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResp, bytes_consumed, out_frames, 0),
+    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResp, result, bytes_consumed, &grpc_gcp_HandshakerResult_fields),
+    PB_FIELD(  4, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_HandshakerResp, status, result, &grpc_gcp_HandshakerStatus_fields),
+    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(grpc_gcp_StartClientHandshakeReq, target_identities) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_identity) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_endpoint) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, remote_endpoint) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, rpc_versions) < 65536 && pb_membersize(grpc_gcp_ServerHandshakeParameters, local_identities) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, handshake_parameters[0]) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, local_endpoint) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, remote_endpoint) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, rpc_versions) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, client_start) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, server_start) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, next) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, peer_identity) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, local_identity) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, peer_rpc_versions) < 65536 && pb_membersize(grpc_gcp_HandshakerResp, result) < 65536 && pb_membersize(grpc_gcp_HandshakerResp, status) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_Endpoint_grpc_gcp_Identity_grpc_gcp_StartClientHandshakeReq_grpc_gcp_ServerHandshakeParameters_grpc_gcp_StartServerHandshakeReq_grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_grpc_gcp_NextHandshakeMessageReq_grpc_gcp_HandshakerReq_grpc_gcp_HandshakerResult_grpc_gcp_HandshakerStatus_grpc_gcp_HandshakerResp)
+#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(grpc_gcp_StartClientHandshakeReq, target_identities) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_identity) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_endpoint) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, remote_endpoint) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, rpc_versions) < 256 && pb_membersize(grpc_gcp_ServerHandshakeParameters, local_identities) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, handshake_parameters[0]) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, local_endpoint) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, remote_endpoint) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, rpc_versions) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value) < 256 && pb_membersize(grpc_gcp_HandshakerReq, client_start) < 256 && pb_membersize(grpc_gcp_HandshakerReq, server_start) < 256 && pb_membersize(grpc_gcp_HandshakerReq, next) < 256 && pb_membersize(grpc_gcp_HandshakerResult, peer_identity) < 256 && pb_membersize(grpc_gcp_HandshakerResult, local_identity) < 256 && pb_membersize(grpc_gcp_HandshakerResult, peer_rpc_versions) < 256 && pb_membersize(grpc_gcp_HandshakerResp, result) < 256 && pb_membersize(grpc_gcp_HandshakerResp, status) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_Endpoint_grpc_gcp_Identity_grpc_gcp_StartClientHandshakeReq_grpc_gcp_ServerHandshakeParameters_grpc_gcp_StartServerHandshakeReq_grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_grpc_gcp_NextHandshakeMessageReq_grpc_gcp_HandshakerReq_grpc_gcp_HandshakerResult_grpc_gcp_HandshakerStatus_grpc_gcp_HandshakerResp)
+#endif
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.h b/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.h
new file mode 100644
index 0000000..5ee42a3
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.h
@@ -0,0 +1,254 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GRPC_GCP_HANDSHAKER_PB_H_INCLUDED
+#define PB_GRPC_GCP_HANDSHAKER_PB_H_INCLUDED
+#include "pb.h"
+#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+/* @@protoc_insertion_point(includes) */
+#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 _grpc_gcp_HandshakeProtocol {
+    grpc_gcp_HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED = 0,
+    grpc_gcp_HandshakeProtocol_TLS = 1,
+    grpc_gcp_HandshakeProtocol_ALTS = 2
+} grpc_gcp_HandshakeProtocol;
+#define _grpc_gcp_HandshakeProtocol_MIN grpc_gcp_HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED
+#define _grpc_gcp_HandshakeProtocol_MAX grpc_gcp_HandshakeProtocol_ALTS
+#define _grpc_gcp_HandshakeProtocol_ARRAYSIZE ((grpc_gcp_HandshakeProtocol)(grpc_gcp_HandshakeProtocol_ALTS+1))
+
+typedef enum _grpc_gcp_NetworkProtocol {
+    grpc_gcp_NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED = 0,
+    grpc_gcp_NetworkProtocol_TCP = 1,
+    grpc_gcp_NetworkProtocol_UDP = 2
+} grpc_gcp_NetworkProtocol;
+#define _grpc_gcp_NetworkProtocol_MIN grpc_gcp_NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED
+#define _grpc_gcp_NetworkProtocol_MAX grpc_gcp_NetworkProtocol_UDP
+#define _grpc_gcp_NetworkProtocol_ARRAYSIZE ((grpc_gcp_NetworkProtocol)(grpc_gcp_NetworkProtocol_UDP+1))
+
+/* Struct definitions */
+typedef struct _grpc_gcp_Identity {
+    pb_callback_t service_account;
+    pb_callback_t hostname;
+/* @@protoc_insertion_point(struct:grpc_gcp_Identity) */
+} grpc_gcp_Identity;
+
+typedef struct _grpc_gcp_NextHandshakeMessageReq {
+    pb_callback_t in_bytes;
+/* @@protoc_insertion_point(struct:grpc_gcp_NextHandshakeMessageReq) */
+} grpc_gcp_NextHandshakeMessageReq;
+
+typedef struct _grpc_gcp_ServerHandshakeParameters {
+    pb_callback_t record_protocols;
+    pb_callback_t local_identities;
+/* @@protoc_insertion_point(struct:grpc_gcp_ServerHandshakeParameters) */
+} grpc_gcp_ServerHandshakeParameters;
+
+typedef struct _grpc_gcp_Endpoint {
+    pb_callback_t ip_address;
+    bool has_port;
+    int32_t port;
+    bool has_protocol;
+    grpc_gcp_NetworkProtocol protocol;
+/* @@protoc_insertion_point(struct:grpc_gcp_Endpoint) */
+} grpc_gcp_Endpoint;
+
+typedef struct _grpc_gcp_HandshakerResult {
+    pb_callback_t application_protocol;
+    pb_callback_t record_protocol;
+    pb_callback_t key_data;
+    bool has_peer_identity;
+    grpc_gcp_Identity peer_identity;
+    bool has_local_identity;
+    grpc_gcp_Identity local_identity;
+    bool has_keep_channel_open;
+    bool keep_channel_open;
+    bool has_peer_rpc_versions;
+    grpc_gcp_RpcProtocolVersions peer_rpc_versions;
+/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerResult) */
+} grpc_gcp_HandshakerResult;
+
+typedef struct _grpc_gcp_HandshakerStatus {
+    bool has_code;
+    uint32_t code;
+    pb_callback_t details;
+/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerStatus) */
+} grpc_gcp_HandshakerStatus;
+
+typedef struct _grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry {
+    bool has_key;
+    int32_t key;
+    bool has_value;
+    grpc_gcp_ServerHandshakeParameters value;
+/* @@protoc_insertion_point(struct:grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry) */
+} grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry;
+
+typedef struct _grpc_gcp_HandshakerResp {
+    pb_callback_t out_frames;
+    bool has_bytes_consumed;
+    uint32_t bytes_consumed;
+    bool has_result;
+    grpc_gcp_HandshakerResult result;
+    bool has_status;
+    grpc_gcp_HandshakerStatus status;
+/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerResp) */
+} grpc_gcp_HandshakerResp;
+
+typedef struct _grpc_gcp_StartClientHandshakeReq {
+    bool has_handshake_security_protocol;
+    grpc_gcp_HandshakeProtocol handshake_security_protocol;
+    pb_callback_t application_protocols;
+    pb_callback_t record_protocols;
+    pb_callback_t target_identities;
+    bool has_local_identity;
+    grpc_gcp_Identity local_identity;
+    bool has_local_endpoint;
+    grpc_gcp_Endpoint local_endpoint;
+    bool has_remote_endpoint;
+    grpc_gcp_Endpoint remote_endpoint;
+    pb_callback_t target_name;
+    bool has_rpc_versions;
+    grpc_gcp_RpcProtocolVersions rpc_versions;
+/* @@protoc_insertion_point(struct:grpc_gcp_StartClientHandshakeReq) */
+} grpc_gcp_StartClientHandshakeReq;
+
+typedef struct _grpc_gcp_StartServerHandshakeReq {
+    pb_callback_t application_protocols;
+    pb_size_t handshake_parameters_count;
+    grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry handshake_parameters[3];
+    pb_callback_t in_bytes;
+    bool has_local_endpoint;
+    grpc_gcp_Endpoint local_endpoint;
+    bool has_remote_endpoint;
+    grpc_gcp_Endpoint remote_endpoint;
+    bool has_rpc_versions;
+    grpc_gcp_RpcProtocolVersions rpc_versions;
+/* @@protoc_insertion_point(struct:grpc_gcp_StartServerHandshakeReq) */
+} grpc_gcp_StartServerHandshakeReq;
+
+typedef struct _grpc_gcp_HandshakerReq {
+    bool has_client_start;
+    grpc_gcp_StartClientHandshakeReq client_start;
+    bool has_server_start;
+    grpc_gcp_StartServerHandshakeReq server_start;
+    bool has_next;
+    grpc_gcp_NextHandshakeMessageReq next;
+/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerReq) */
+} grpc_gcp_HandshakerReq;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_gcp_Endpoint_init_default           {{{NULL}, NULL}, false, 0, false, (grpc_gcp_NetworkProtocol)0}
+#define grpc_gcp_Identity_init_default           {{{NULL}, NULL}, {{NULL}, NULL}}
+#define grpc_gcp_StartClientHandshakeReq_init_default {false, (grpc_gcp_HandshakeProtocol)0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_default, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_Endpoint_init_default, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_default}
+#define grpc_gcp_ServerHandshakeParameters_init_default {{{NULL}, NULL}, {{NULL}, NULL}}
+#define grpc_gcp_StartServerHandshakeReq_init_default {{{NULL}, NULL}, 0, {grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default}, {{NULL}, NULL}, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_RpcProtocolVersions_init_default}
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default {false, 0, false, grpc_gcp_ServerHandshakeParameters_init_default}
+#define grpc_gcp_NextHandshakeMessageReq_init_default {{{NULL}, NULL}}
+#define grpc_gcp_HandshakerReq_init_default      {false, grpc_gcp_StartClientHandshakeReq_init_default, false, grpc_gcp_StartServerHandshakeReq_init_default, false, grpc_gcp_NextHandshakeMessageReq_init_default}
+#define grpc_gcp_HandshakerResult_init_default   {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_default, false, grpc_gcp_Identity_init_default, false, 0, false, grpc_gcp_RpcProtocolVersions_init_default}
+#define grpc_gcp_HandshakerStatus_init_default   {false, 0, {{NULL}, NULL}}
+#define grpc_gcp_HandshakerResp_init_default     {{{NULL}, NULL}, false, 0, false, grpc_gcp_HandshakerResult_init_default, false, grpc_gcp_HandshakerStatus_init_default}
+#define grpc_gcp_Endpoint_init_zero              {{{NULL}, NULL}, false, 0, false, (grpc_gcp_NetworkProtocol)0}
+#define grpc_gcp_Identity_init_zero              {{{NULL}, NULL}, {{NULL}, NULL}}
+#define grpc_gcp_StartClientHandshakeReq_init_zero {false, (grpc_gcp_HandshakeProtocol)0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_zero, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_Endpoint_init_zero, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_zero}
+#define grpc_gcp_ServerHandshakeParameters_init_zero {{{NULL}, NULL}, {{NULL}, NULL}}
+#define grpc_gcp_StartServerHandshakeReq_init_zero {{{NULL}, NULL}, 0, {grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero}, {{NULL}, NULL}, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_RpcProtocolVersions_init_zero}
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero {false, 0, false, grpc_gcp_ServerHandshakeParameters_init_zero}
+#define grpc_gcp_NextHandshakeMessageReq_init_zero {{{NULL}, NULL}}
+#define grpc_gcp_HandshakerReq_init_zero         {false, grpc_gcp_StartClientHandshakeReq_init_zero, false, grpc_gcp_StartServerHandshakeReq_init_zero, false, grpc_gcp_NextHandshakeMessageReq_init_zero}
+#define grpc_gcp_HandshakerResult_init_zero      {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_zero, false, grpc_gcp_Identity_init_zero, false, 0, false, grpc_gcp_RpcProtocolVersions_init_zero}
+#define grpc_gcp_HandshakerStatus_init_zero      {false, 0, {{NULL}, NULL}}
+#define grpc_gcp_HandshakerResp_init_zero        {{{NULL}, NULL}, false, 0, false, grpc_gcp_HandshakerResult_init_zero, false, grpc_gcp_HandshakerStatus_init_zero}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_gcp_Identity_service_account_tag    1
+#define grpc_gcp_Identity_hostname_tag           2
+#define grpc_gcp_NextHandshakeMessageReq_in_bytes_tag 1
+#define grpc_gcp_ServerHandshakeParameters_record_protocols_tag 1
+#define grpc_gcp_ServerHandshakeParameters_local_identities_tag 2
+#define grpc_gcp_Endpoint_ip_address_tag         1
+#define grpc_gcp_Endpoint_port_tag               2
+#define grpc_gcp_Endpoint_protocol_tag           3
+#define grpc_gcp_HandshakerResult_application_protocol_tag 1
+#define grpc_gcp_HandshakerResult_record_protocol_tag 2
+#define grpc_gcp_HandshakerResult_key_data_tag   3
+#define grpc_gcp_HandshakerResult_peer_identity_tag 4
+#define grpc_gcp_HandshakerResult_local_identity_tag 5
+#define grpc_gcp_HandshakerResult_keep_channel_open_tag 6
+#define grpc_gcp_HandshakerResult_peer_rpc_versions_tag 7
+#define grpc_gcp_HandshakerStatus_code_tag       1
+#define grpc_gcp_HandshakerStatus_details_tag    2
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_key_tag 1
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_value_tag 2
+#define grpc_gcp_HandshakerResp_out_frames_tag   1
+#define grpc_gcp_HandshakerResp_bytes_consumed_tag 2
+#define grpc_gcp_HandshakerResp_result_tag       3
+#define grpc_gcp_HandshakerResp_status_tag       4
+#define grpc_gcp_StartClientHandshakeReq_handshake_security_protocol_tag 1
+#define grpc_gcp_StartClientHandshakeReq_application_protocols_tag 2
+#define grpc_gcp_StartClientHandshakeReq_record_protocols_tag 3
+#define grpc_gcp_StartClientHandshakeReq_target_identities_tag 4
+#define grpc_gcp_StartClientHandshakeReq_local_identity_tag 5
+#define grpc_gcp_StartClientHandshakeReq_local_endpoint_tag 6
+#define grpc_gcp_StartClientHandshakeReq_remote_endpoint_tag 7
+#define grpc_gcp_StartClientHandshakeReq_target_name_tag 8
+#define grpc_gcp_StartClientHandshakeReq_rpc_versions_tag 9
+#define grpc_gcp_StartServerHandshakeReq_application_protocols_tag 1
+#define grpc_gcp_StartServerHandshakeReq_handshake_parameters_tag 2
+#define grpc_gcp_StartServerHandshakeReq_in_bytes_tag 3
+#define grpc_gcp_StartServerHandshakeReq_local_endpoint_tag 4
+#define grpc_gcp_StartServerHandshakeReq_remote_endpoint_tag 5
+#define grpc_gcp_StartServerHandshakeReq_rpc_versions_tag 6
+#define grpc_gcp_HandshakerReq_client_start_tag  1
+#define grpc_gcp_HandshakerReq_server_start_tag  2
+#define grpc_gcp_HandshakerReq_next_tag          3
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_gcp_Endpoint_fields[4];
+extern const pb_field_t grpc_gcp_Identity_fields[3];
+extern const pb_field_t grpc_gcp_StartClientHandshakeReq_fields[10];
+extern const pb_field_t grpc_gcp_ServerHandshakeParameters_fields[3];
+extern const pb_field_t grpc_gcp_StartServerHandshakeReq_fields[7];
+extern const pb_field_t grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields[3];
+extern const pb_field_t grpc_gcp_NextHandshakeMessageReq_fields[2];
+extern const pb_field_t grpc_gcp_HandshakerReq_fields[4];
+extern const pb_field_t grpc_gcp_HandshakerResult_fields[8];
+extern const pb_field_t grpc_gcp_HandshakerStatus_fields[3];
+extern const pb_field_t grpc_gcp_HandshakerResp_fields[5];
+
+/* Maximum encoded size of messages (where known) */
+/* grpc_gcp_Endpoint_size depends on runtime parameters */
+/* grpc_gcp_Identity_size depends on runtime parameters */
+/* grpc_gcp_StartClientHandshakeReq_size depends on runtime parameters */
+/* grpc_gcp_ServerHandshakeParameters_size depends on runtime parameters */
+/* grpc_gcp_StartServerHandshakeReq_size depends on runtime parameters */
+#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_size (17 + grpc_gcp_ServerHandshakeParameters_size)
+/* grpc_gcp_NextHandshakeMessageReq_size depends on runtime parameters */
+#define grpc_gcp_HandshakerReq_size              (18 + grpc_gcp_StartClientHandshakeReq_size + grpc_gcp_StartServerHandshakeReq_size + grpc_gcp_NextHandshakeMessageReq_size)
+/* grpc_gcp_HandshakerResult_size depends on runtime parameters */
+/* grpc_gcp_HandshakerStatus_size depends on runtime parameters */
+/* grpc_gcp_HandshakerResp_size depends on runtime parameters */
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define HANDSHAKER_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/proto/altscontext.proto b/third_party/grpc/src/core/tsi/alts/handshaker/proto/altscontext.proto
new file mode 100644
index 0000000..9a1dad5
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/proto/altscontext.proto
@@ -0,0 +1,41 @@
+// Copyright 2018 gRPC authors.
+//
+// Licensed 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.
+
+syntax = "proto3";
+
+import "transport_security_common.proto";
+
+package grpc.gcp;
+
+option java_package = "io.grpc.alts.internal";
+
+message AltsContext {
+  // The application protocol negotiated for this connection.
+  string application_protocol = 1;
+
+  // The record protocol negotiated for this connection.
+  string record_protocol = 2;
+
+  // The security level of the created secure channel.
+  SecurityLevel security_level = 3;
+
+  // The peer service account.
+  string peer_service_account = 4;
+
+  // The local service account.
+  string local_service_account = 5;
+
+  // The RPC protocol versions supported by the peer.
+  RpcProtocolVersions peer_rpc_versions = 6;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.options b/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.options
new file mode 100644
index 0000000..702ba38
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.options
@@ -0,0 +1,2 @@
+handshaker.proto no_unions:true
+grpc.gcp.StartServerHandshakeReq.handshake_parameters max_count:3
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.proto b/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.proto
new file mode 100644
index 0000000..84a4153
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.proto
@@ -0,0 +1,224 @@
+// Copyright 2018 gRPC authors.
+//
+// Licensed 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.
+
+syntax = "proto3";
+
+import "transport_security_common.proto";
+
+package grpc.gcp;
+
+option java_package = "io.grpc.alts.internal";
+
+enum HandshakeProtocol {
+  // Default value.
+  HANDSHAKE_PROTOCOL_UNSPECIFIED = 0;
+
+  // TLS handshake protocol.
+  TLS = 1;
+
+  // Application Layer Transport Security handshake protocol.
+  ALTS = 2;
+}
+
+enum NetworkProtocol {
+  NETWORK_PROTOCOL_UNSPECIFIED = 0;
+  TCP = 1;
+  UDP = 2;
+}
+
+message Endpoint {
+  // IP address. It should contain an IPv4 or IPv6 string literal, e.g.
+  // "192.168.0.1" or "2001:db8::1".
+  string ip_address = 1;
+
+  // Port number.
+  int32 port = 2;
+
+  // Network protocol (e.g., TCP, UDP) associated with this endpoint.
+  NetworkProtocol protocol = 3;
+}
+
+message Identity {
+  oneof identity_oneof {
+    // Service account of a connection endpoint.
+    string service_account = 1;
+
+    // Hostname of a connection endpoint.
+    string hostname = 2;
+  }
+}
+
+message StartClientHandshakeReq {
+  // Handshake security protocol requested by the client.
+  HandshakeProtocol handshake_security_protocol = 1;
+
+  // The application protocols supported by the client, e.g., "h2" (for http2),
+  // "grpc".
+  repeated string application_protocols = 2;
+
+  // The record protocols supported by the client, e.g.,
+  // "ALTSRP_GCM_AES128".
+  repeated string record_protocols = 3;
+
+  // (Optional) Describes which server identities are acceptable by the client.
+  // If target identities are provided and none of them matches the peer
+  // identity of the server, handshake will fail.
+  repeated Identity target_identities = 4;
+
+  // (Optional) Application may specify a local identity. Otherwise, the
+  // handshaker chooses a default local identity.
+  Identity local_identity = 5;
+
+  // (Optional) Local endpoint information of the connection to the server,
+  // such as local IP address, port number, and network protocol.
+  Endpoint local_endpoint = 6;
+
+  // (Optional) Endpoint information of the remote server, such as IP address,
+  // port number, and network protocol.
+  Endpoint remote_endpoint = 7;
+
+  // (Optional) If target name is provided, a secure naming check is performed
+  // to verify that the peer authenticated identity is indeed authorized to run
+  // the target name.
+  string target_name = 8;
+
+  // (Optional) RPC protocol versions supported by the client.
+  RpcProtocolVersions rpc_versions = 9;
+}
+
+message ServerHandshakeParameters {
+  // The record protocols supported by the server, e.g.,
+  // "ALTSRP_GCM_AES128".
+  repeated string record_protocols = 1;
+
+  // (Optional) A list of local identities supported by the server, if
+  // specified. Otherwise, the handshaker chooses a default local identity.
+  repeated Identity local_identities = 2;
+}
+
+message StartServerHandshakeReq {
+  // The application protocols supported by the server, e.g., "h2" (for http2),
+  // "grpc".
+  repeated string application_protocols = 1;
+
+  // Handshake parameters (record protocols and local identities supported by
+  // the server) mapped by the handshake protocol. Each handshake security
+  // protocol (e.g., TLS or ALTS) has its own set of record protocols and local
+  // identities. Since protobuf does not support enum as key to the map, the key
+  // to handshake_parameters is the integer value of HandshakeProtocol enum.
+  map<int32, ServerHandshakeParameters> handshake_parameters = 2;
+
+  // Bytes in out_frames returned from the peer's HandshakerResp. It is possible
+  // that the peer's out_frames are split into multiple HandshakReq messages.
+  bytes in_bytes = 3;
+
+  // (Optional) Local endpoint information of the connection to the client,
+  // such as local IP address, port number, and network protocol.
+  Endpoint local_endpoint = 4;
+
+  // (Optional) Endpoint information of the remote client, such as IP address,
+  // port number, and network protocol.
+  Endpoint remote_endpoint = 5;
+
+  // (Optional) RPC protocol versions supported by the server.
+  RpcProtocolVersions rpc_versions = 6;
+}
+
+message NextHandshakeMessageReq {
+  // Bytes in out_frames returned from the peer's HandshakerResp. It is possible
+  // that the peer's out_frames are split into multiple NextHandshakerMessageReq
+  // messages.
+  bytes in_bytes = 1;
+}
+
+message HandshakerReq {
+  oneof req_oneof {
+    // The start client handshake request message.
+    StartClientHandshakeReq client_start = 1;
+
+    // The start server handshake request message.
+    StartServerHandshakeReq server_start = 2;
+
+    // The next handshake request message.
+    NextHandshakeMessageReq next = 3;
+  }
+}
+
+message HandshakerResult {
+  // The application protocol negotiated for this connection.
+  string application_protocol = 1;
+
+  // The record protocol negotiated for this connection.
+  string record_protocol = 2;
+
+  // Cryptographic key data. The key data may be more than the key length
+  // required for the record protocol, thus the client of the handshaker
+  // service needs to truncate the key data into the right key length.
+  bytes key_data = 3;
+
+  // The authenticated identity of the peer.
+  Identity peer_identity = 4;
+
+  // The local identity used in the handshake.
+  Identity local_identity = 5;
+
+  // Indicate whether the handshaker service client should keep the channel
+  // between the handshaker service open, e.g., in order to handle
+  // post-handshake messages in the future.
+  bool keep_channel_open = 6;
+
+  // The RPC protocol versions supported by the peer.
+  RpcProtocolVersions peer_rpc_versions = 7;
+}
+
+message HandshakerStatus {
+  // The status code. This could be the gRPC status code.
+  uint32 code = 1;
+
+  // The status details.
+  string details = 2;
+}
+
+message HandshakerResp {
+  // Frames to be given to the peer for the NextHandshakeMessageReq. May be
+  // empty if no out_frames have to be sent to the peer or if in_bytes in the
+  // HandshakerReq are incomplete. All the non-empty out frames must be sent to
+  // the peer even if the handshaker status is not OK as these frames may
+  // contain the alert frames.
+  bytes out_frames = 1;
+
+  // Number of bytes in the in_bytes consumed by the handshaker. It is possible
+  // that part of in_bytes in HandshakerReq was unrelated to the handshake
+  // process.
+  uint32 bytes_consumed = 2;
+
+  // This is set iff the handshake was successful. out_frames may still be set
+  // to frames that needs to be forwarded to the peer.
+  HandshakerResult result = 3;
+
+  // Status of the handshaker.
+  HandshakerStatus status = 4;
+}
+
+service HandshakerService {
+  // Handshaker service accepts a stream of handshaker request, returning a
+  // stream of handshaker response. Client is expected to send exactly one
+  // message with either client_start or server_start followed by one or more
+  // messages with next. Each time client sends a request, the handshaker
+  // service expects to respond. Client does not have to wait for service's
+  // response before sending next request.
+  rpc DoHandshake(stream HandshakerReq)
+      returns (stream HandshakerResp) {
+  }
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/proto/transport_security_common.proto b/third_party/grpc/src/core/tsi/alts/handshaker/proto/transport_security_common.proto
new file mode 100644
index 0000000..d0f861e
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/proto/transport_security_common.proto
@@ -0,0 +1,40 @@
+// Copyright 2018 gRPC authors.
+//
+// Licensed 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.
+
+syntax = "proto3";
+
+package grpc.gcp;
+
+option java_package = "io.grpc.alts.internal";
+
+// The security level of the created channel. The list is sorted in increasing
+// level of security. This order must always be maintained.
+enum SecurityLevel {
+  SECURITY_NONE = 0;
+  INTEGRITY_ONLY = 1;
+  INTEGRITY_AND_PRIVACY = 2;
+}
+
+// Max and min supported RPC protocol versions.
+message RpcProtocolVersions {
+  // RPC version contains a major version and a minor version.
+  message Version {
+    uint32 major = 1;
+    uint32 minor = 2;
+  }
+  // Maximum supported RPC version.
+  Version max_rpc_version = 1;
+  // Minimum supported RPC version.
+  Version min_rpc_version = 2;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.c b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.c
new file mode 100644
index 0000000..326b1b1
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.c
@@ -0,0 +1,49 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.7-dev */
+
+#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_gcp_RpcProtocolVersions_fields[3] = {
+    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, grpc_gcp_RpcProtocolVersions, max_rpc_version, max_rpc_version, &grpc_gcp_RpcProtocolVersions_Version_fields),
+    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, grpc_gcp_RpcProtocolVersions, min_rpc_version, max_rpc_version, &grpc_gcp_RpcProtocolVersions_Version_fields),
+    PB_LAST_FIELD
+};
+
+const pb_field_t grpc_gcp_RpcProtocolVersions_Version_fields[3] = {
+    PB_FIELD(  1, UINT32  , OPTIONAL, STATIC  , FIRST, grpc_gcp_RpcProtocolVersions_Version, major, major, 0),
+    PB_FIELD(  2, UINT32  , OPTIONAL, STATIC  , OTHER, grpc_gcp_RpcProtocolVersions_Version, minor, major, 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(grpc_gcp_RpcProtocolVersions, max_rpc_version) < 65536 && pb_membersize(grpc_gcp_RpcProtocolVersions, min_rpc_version) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_RpcProtocolVersions_grpc_gcp_RpcProtocolVersions_Version)
+#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(grpc_gcp_RpcProtocolVersions, max_rpc_version) < 256 && pb_membersize(grpc_gcp_RpcProtocolVersions, min_rpc_version) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_RpcProtocolVersions_grpc_gcp_RpcProtocolVersions_Version)
+#endif
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.h b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.h
new file mode 100644
index 0000000..87d9abf
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.h
@@ -0,0 +1,78 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.7-dev */
+
+#ifndef PB_GRPC_GCP_TRANSPORT_SECURITY_COMMON_PB_H_INCLUDED
+#define PB_GRPC_GCP_TRANSPORT_SECURITY_COMMON_PB_H_INCLUDED
+#include "pb.h"
+/* @@protoc_insertion_point(includes) */
+#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 _grpc_gcp_SecurityLevel {
+    grpc_gcp_SecurityLevel_SECURITY_NONE = 0,
+    grpc_gcp_SecurityLevel_INTEGRITY_ONLY = 1,
+    grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY = 2
+} grpc_gcp_SecurityLevel;
+#define _grpc_gcp_SecurityLevel_MIN grpc_gcp_SecurityLevel_SECURITY_NONE
+#define _grpc_gcp_SecurityLevel_MAX grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY
+#define _grpc_gcp_SecurityLevel_ARRAYSIZE ((grpc_gcp_SecurityLevel)(grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY+1))
+
+/* Struct definitions */
+typedef struct _grpc_gcp_RpcProtocolVersions_Version {
+    bool has_major;
+    uint32_t major;
+    bool has_minor;
+    uint32_t minor;
+/* @@protoc_insertion_point(struct:grpc_gcp_RpcProtocolVersions_Version) */
+} grpc_gcp_RpcProtocolVersions_Version;
+
+typedef struct _grpc_gcp_RpcProtocolVersions {
+    bool has_max_rpc_version;
+    grpc_gcp_RpcProtocolVersions_Version max_rpc_version;
+    bool has_min_rpc_version;
+    grpc_gcp_RpcProtocolVersions_Version min_rpc_version;
+/* @@protoc_insertion_point(struct:grpc_gcp_RpcProtocolVersions) */
+} grpc_gcp_RpcProtocolVersions;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_gcp_RpcProtocolVersions_init_default {false, grpc_gcp_RpcProtocolVersions_Version_init_default, false, grpc_gcp_RpcProtocolVersions_Version_init_default}
+#define grpc_gcp_RpcProtocolVersions_Version_init_default {false, 0, false, 0}
+#define grpc_gcp_RpcProtocolVersions_init_zero   {false, grpc_gcp_RpcProtocolVersions_Version_init_zero, false, grpc_gcp_RpcProtocolVersions_Version_init_zero}
+#define grpc_gcp_RpcProtocolVersions_Version_init_zero {false, 0, false, 0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_gcp_RpcProtocolVersions_Version_major_tag 1
+#define grpc_gcp_RpcProtocolVersions_Version_minor_tag 2
+#define grpc_gcp_RpcProtocolVersions_max_rpc_version_tag 1
+#define grpc_gcp_RpcProtocolVersions_min_rpc_version_tag 2
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_gcp_RpcProtocolVersions_fields[3];
+extern const pb_field_t grpc_gcp_RpcProtocolVersions_Version_fields[3];
+
+/* Maximum encoded size of messages (where known) */
+#define grpc_gcp_RpcProtocolVersions_size        28
+#define grpc_gcp_RpcProtocolVersions_Version_size 12
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define TRANSPORT_SECURITY_COMMON_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.cc b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.cc
new file mode 100644
index 0000000..8a7edb5
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.cc
@@ -0,0 +1,196 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common_api.h"
+
+bool grpc_gcp_rpc_protocol_versions_set_max(
+    grpc_gcp_rpc_protocol_versions* versions, uint32_t max_major,
+    uint32_t max_minor) {
+  if (versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "versions is nullptr in "
+            "grpc_gcp_rpc_protocol_versions_set_max().");
+    return false;
+  }
+  versions->has_max_rpc_version = true;
+  versions->max_rpc_version.has_major = true;
+  versions->max_rpc_version.has_minor = true;
+  versions->max_rpc_version.major = max_major;
+  versions->max_rpc_version.minor = max_minor;
+  return true;
+}
+
+bool grpc_gcp_rpc_protocol_versions_set_min(
+    grpc_gcp_rpc_protocol_versions* versions, uint32_t min_major,
+    uint32_t min_minor) {
+  if (versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "versions is nullptr in "
+            "grpc_gcp_rpc_protocol_versions_set_min().");
+    return false;
+  }
+  versions->has_min_rpc_version = true;
+  versions->min_rpc_version.has_major = true;
+  versions->min_rpc_version.has_minor = true;
+  versions->min_rpc_version.major = min_major;
+  versions->min_rpc_version.minor = min_minor;
+  return true;
+}
+
+size_t grpc_gcp_rpc_protocol_versions_encode_length(
+    const grpc_gcp_rpc_protocol_versions* versions) {
+  if (versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_rpc_protocol_versions_encode_length().");
+    return 0;
+  }
+  pb_ostream_t size_stream;
+  memset(&size_stream, 0, sizeof(pb_ostream_t));
+  if (!pb_encode(&size_stream, grpc_gcp_RpcProtocolVersions_fields, versions)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream));
+    return 0;
+  }
+  return size_stream.bytes_written;
+}
+
+bool grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+    const grpc_gcp_rpc_protocol_versions* versions, uint8_t* bytes,
+    size_t bytes_length) {
+  if (versions == nullptr || bytes == nullptr || bytes_length == 0) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes().");
+    return false;
+  }
+  pb_ostream_t output_stream = pb_ostream_from_buffer(bytes, bytes_length);
+  if (!pb_encode(&output_stream, grpc_gcp_RpcProtocolVersions_fields,
+                 versions)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&output_stream));
+    return false;
+  }
+  return true;
+}
+
+bool grpc_gcp_rpc_protocol_versions_encode(
+    const grpc_gcp_rpc_protocol_versions* versions, grpc_slice* slice) {
+  if (versions == nullptr || slice == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to "
+            "grpc_gcp_rpc_protocol_versions_encode().");
+    return false;
+  }
+  size_t encoded_length =
+      grpc_gcp_rpc_protocol_versions_encode_length(versions);
+  if (encoded_length == 0) return false;
+  *slice = grpc_slice_malloc(encoded_length);
+  return grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+      versions, GRPC_SLICE_START_PTR(*slice), encoded_length);
+}
+
+bool grpc_gcp_rpc_protocol_versions_decode(
+    grpc_slice slice, grpc_gcp_rpc_protocol_versions* versions) {
+  if (versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "version is nullptr in "
+            "grpc_gcp_rpc_protocol_versions_decode().");
+    return false;
+  }
+  pb_istream_t stream = pb_istream_from_buffer(GRPC_SLICE_START_PTR(slice),
+                                               GRPC_SLICE_LENGTH(slice));
+  if (!pb_decode(&stream, grpc_gcp_RpcProtocolVersions_fields, versions)) {
+    gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream));
+    return false;
+  }
+  return true;
+}
+
+bool grpc_gcp_rpc_protocol_versions_copy(
+    const grpc_gcp_rpc_protocol_versions* src,
+    grpc_gcp_rpc_protocol_versions* dst) {
+  if ((src == nullptr && dst != nullptr) ||
+      (src != nullptr && dst == nullptr)) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_rpc_protocol_versions_copy().");
+    return false;
+  }
+  if (src == nullptr) {
+    return true;
+  }
+  grpc_gcp_rpc_protocol_versions_set_max(dst, src->max_rpc_version.major,
+                                         src->max_rpc_version.minor);
+  grpc_gcp_rpc_protocol_versions_set_min(dst, src->min_rpc_version.major,
+                                         src->min_rpc_version.minor);
+  return true;
+}
+
+namespace grpc_core {
+namespace internal {
+
+int grpc_gcp_rpc_protocol_version_compare(
+    const grpc_gcp_rpc_protocol_versions_version* v1,
+    const grpc_gcp_rpc_protocol_versions_version* v2) {
+  if ((v1->major > v2->major) ||
+      (v1->major == v2->major && v1->minor > v2->minor)) {
+    return 1;
+  }
+  if ((v1->major < v2->major) ||
+      (v1->major == v2->major && v1->minor < v2->minor)) {
+    return -1;
+  }
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace grpc_core
+
+bool grpc_gcp_rpc_protocol_versions_check(
+    const grpc_gcp_rpc_protocol_versions* local_versions,
+    const grpc_gcp_rpc_protocol_versions* peer_versions,
+    grpc_gcp_rpc_protocol_versions_version* highest_common_version) {
+  if (local_versions == nullptr || peer_versions == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to "
+            "grpc_gcp_rpc_protocol_versions_check().");
+    return false;
+  }
+  /* max_common_version is MIN(local.max, peer.max) */
+  const grpc_gcp_rpc_protocol_versions_version* max_common_version =
+      grpc_core::internal::grpc_gcp_rpc_protocol_version_compare(
+          &local_versions->max_rpc_version, &peer_versions->max_rpc_version) > 0
+          ? &peer_versions->max_rpc_version
+          : &local_versions->max_rpc_version;
+  /* min_common_version is MAX(local.min, peer.min) */
+  const grpc_gcp_rpc_protocol_versions_version* min_common_version =
+      grpc_core::internal::grpc_gcp_rpc_protocol_version_compare(
+          &local_versions->min_rpc_version, &peer_versions->min_rpc_version) > 0
+          ? &local_versions->min_rpc_version
+          : &peer_versions->min_rpc_version;
+  bool result = grpc_core::internal::grpc_gcp_rpc_protocol_version_compare(
+                    max_common_version, min_common_version) >= 0
+                    ? true
+                    : false;
+  if (result && highest_common_version != nullptr) {
+    memcpy(highest_common_version, max_common_version,
+           sizeof(grpc_gcp_rpc_protocol_versions_version));
+  }
+  return result;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.h b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.h
new file mode 100644
index 0000000..ec2a0b4
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.h
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H
+#define GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H
+
+#include <grpc/support/port_platform.h>
+
+#include "pb_decode.h"
+#include "pb_encode.h"
+
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h"
+
+typedef grpc_gcp_RpcProtocolVersions grpc_gcp_rpc_protocol_versions;
+
+typedef grpc_gcp_RpcProtocolVersions_Version
+    grpc_gcp_rpc_protocol_versions_version;
+
+/**
+ * This method sets the value for max_rpc_versions field of rpc protocol
+ * versions.
+ *
+ * - versions: an rpc protocol version instance.
+ * - max_major: a major version of maximum supported RPC version.
+ * - max_minor: a minor version of maximum supported RPC version.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_set_max(
+    grpc_gcp_rpc_protocol_versions* versions, uint32_t max_major,
+    uint32_t max_minor);
+
+/**
+ * This method sets the value for min_rpc_versions field of rpc protocol
+ * versions.
+ *
+ * - versions: an rpc protocol version instance.
+ * - min_major: a major version of minimum supported RPC version.
+ * - min_minor: a minor version of minimum supported RPC version.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_set_min(
+    grpc_gcp_rpc_protocol_versions* versions, uint32_t min_major,
+    uint32_t min_minor);
+
+/**
+ * This method computes serialized byte length of rpc protocol versions.
+ *
+ * - versions: an rpc protocol versions instance.
+ *
+ * The method returns serialized byte length. It returns 0 on failure.
+ */
+size_t grpc_gcp_rpc_protocol_versions_encode_length(
+    const grpc_gcp_rpc_protocol_versions* versions);
+
+/**
+ * This method serializes rpc protocol versions and writes the result to
+ * the memory buffer provided by the caller. Caller is responsible for
+ * allocating sufficient memory to store the serialized data.
+ *
+ * - versions: an rpc protocol versions instance.
+ * - bytes: bytes buffer where the result will be written to.
+ * - bytes_length: length of the bytes buffer.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes(
+    const grpc_gcp_rpc_protocol_versions* versions, uint8_t* bytes,
+    size_t bytes_length);
+
+/**
+ * This method serializes an rpc protocol version and returns serialized rpc
+ * versions in grpc slice.
+ *
+ * - versions: an rpc protocol versions instance.
+ * - slice: grpc slice where the serialized result will be written.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_encode(
+    const grpc_gcp_rpc_protocol_versions* versions, grpc_slice* slice);
+
+/**
+ * This method de-serializes input in grpc slice form and stores the result
+ * in rpc protocol versions.
+ *
+ * - slice: a data stream containing a serialized rpc protocol version.
+ * - versions: an rpc protocol version instance used to hold de-serialized
+ *   result.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_decode(
+    grpc_slice slice, grpc_gcp_rpc_protocol_versions* versions);
+
+/**
+ * This method performs a deep copy operation on rpc protocol versions
+ * instance.
+ *
+ * - src: rpc protocol versions instance that needs to be copied.
+ * - dst: rpc protocol versions instance that stores the copied result.
+ *
+ * The method returns true on success and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_copy(
+    const grpc_gcp_rpc_protocol_versions* src,
+    grpc_gcp_rpc_protocol_versions* dst);
+
+/**
+ * This method performs a version check between local and peer rpc protocol
+ * versions.
+ *
+ * - local_versions: local rpc protocol versions instance.
+ * - peer_versions: peer rpc protocol versions instance.
+ * - highest_common_version: an output parameter that will store the highest
+ *   common rpc protocol version both parties agreed on.
+ *
+ * The method returns true if the check passes which means both parties agreed
+ * on a common rpc protocol to use, and false otherwise.
+ */
+bool grpc_gcp_rpc_protocol_versions_check(
+    const grpc_gcp_rpc_protocol_versions* local_versions,
+    const grpc_gcp_rpc_protocol_versions* peer_versions,
+    grpc_gcp_rpc_protocol_versions_version* highest_common_version);
+
+namespace grpc_core {
+namespace internal {
+
+/**
+ * Exposed for testing only.
+ * The method returns 0 if v1 = v2,
+ *            returns 1 if v1 > v2,
+ *            returns -1 if v1 < v2.
+ */
+int grpc_gcp_rpc_protocol_version_compare(
+    const grpc_gcp_rpc_protocol_versions_version* v1,
+    const grpc_gcp_rpc_protocol_versions_version* v2);
+
+}  // namespace internal
+}  // namespace grpc_core
+
+#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H */
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
new file mode 100644
index 0000000..352561d
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc
@@ -0,0 +1,226 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include <string.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* Main struct for alts_grpc_integrity_only_record_protocol.  */
+typedef struct alts_grpc_integrity_only_record_protocol {
+  alts_grpc_record_protocol base;
+  bool enable_extra_copy;
+  grpc_slice_buffer data_sb;
+  unsigned char* tag_buf;
+} alts_grpc_integrity_only_record_protocol;
+
+/* --- alts_grpc_record_protocol methods implementation. --- */
+
+static tsi_result alts_grpc_integrity_only_extra_copy_protect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  /* Allocates memory for protected frame and copies data.  */
+  size_t data_length = unprotected_slices->length;
+  size_t protected_frame_size =
+      unprotected_slices->length + rp->header_length + rp->tag_length;
+  grpc_slice protected_slice = GRPC_SLICE_MALLOC(protected_frame_size);
+  uint8_t* data = GRPC_SLICE_START_PTR(protected_slice) + rp->header_length;
+  for (size_t i = 0; i < unprotected_slices->count; i++) {
+    memcpy(data, GRPC_SLICE_START_PTR(unprotected_slices->slices[i]),
+           GRPC_SLICE_LENGTH(unprotected_slices->slices[i]));
+    data += GRPC_SLICE_LENGTH(unprotected_slices->slices[i]);
+  }
+  /* Calls alts_iovec_record_protocol protect.  */
+  char* error_details = nullptr;
+  iovec_t header_iovec = {GRPC_SLICE_START_PTR(protected_slice),
+                          rp->header_length};
+  iovec_t tag_iovec = {
+      GRPC_SLICE_START_PTR(protected_slice) + rp->header_length + data_length,
+      rp->tag_length};
+  rp->iovec_buf[0].iov_base =
+      GRPC_SLICE_START_PTR(protected_slice) + rp->header_length;
+  rp->iovec_buf[0].iov_len = data_length;
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+      rp->iovec_rp, rp->iovec_buf, 1, header_iovec, tag_iovec, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  grpc_slice_buffer_add(protected_slices, protected_slice);
+  grpc_slice_buffer_reset_and_unref_internal(unprotected_slices);
+  return TSI_OK;
+}
+
+static tsi_result alts_grpc_integrity_only_protect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  /* Input sanity check.  */
+  if (rp == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol protect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol =
+      reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
+  if (integrity_only_record_protocol->enable_extra_copy) {
+    return alts_grpc_integrity_only_extra_copy_protect(rp, unprotected_slices,
+                                                       protected_slices);
+  }
+  /* Allocates memory for header and tag slices.  */
+  grpc_slice header_slice = GRPC_SLICE_MALLOC(rp->header_length);
+  grpc_slice tag_slice = GRPC_SLICE_MALLOC(rp->tag_length);
+  /* Calls alts_iovec_record_protocol protect.  */
+  char* error_details = nullptr;
+  iovec_t header_iovec = {GRPC_SLICE_START_PTR(header_slice),
+                          GRPC_SLICE_LENGTH(header_slice)};
+  iovec_t tag_iovec = {GRPC_SLICE_START_PTR(tag_slice),
+                       GRPC_SLICE_LENGTH(tag_slice)};
+  alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp,
+                                                          unprotected_slices);
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect(
+      rp->iovec_rp, rp->iovec_buf, unprotected_slices->count, header_iovec,
+      tag_iovec, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  /* Appends result to protected_slices.  */
+  grpc_slice_buffer_add(protected_slices, header_slice);
+  grpc_slice_buffer_move_into(unprotected_slices, protected_slices);
+  grpc_slice_buffer_add(protected_slices, tag_slice);
+  return TSI_OK;
+}
+
+static tsi_result alts_grpc_integrity_only_unprotect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  /* Input sanity check.  */
+  if (rp == nullptr || protected_slices == nullptr ||
+      unprotected_slices == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to alts_grpc_record_protocol unprotect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (protected_slices->length < rp->header_length + rp->tag_length) {
+    gpr_log(GPR_ERROR, "Protected slices do not have sufficient data.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* In this method, rp points to alts_grpc_record_protocol struct
+   * and integrity_only_record_protocol points to
+   * alts_grpc_integrity_only_record_protocol struct.  */
+  alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol =
+      reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
+  /* Strips frame header from protected slices.  */
+  grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+  grpc_slice_buffer_move_first(protected_slices, rp->header_length,
+                               &rp->header_sb);
+  GPR_ASSERT(rp->header_sb.length == rp->header_length);
+  iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp);
+  /* Moves protected slices data to data_sb and leaves the remaining tag.  */
+  grpc_slice_buffer_reset_and_unref_internal(
+      &integrity_only_record_protocol->data_sb);
+  grpc_slice_buffer_move_first(protected_slices,
+                               protected_slices->length - rp->tag_length,
+                               &integrity_only_record_protocol->data_sb);
+  GPR_ASSERT(protected_slices->length == rp->tag_length);
+  iovec_t tag_iovec = {nullptr, rp->tag_length};
+  if (protected_slices->count == 1) {
+    tag_iovec.iov_base = GRPC_SLICE_START_PTR(protected_slices->slices[0]);
+  } else {
+    /* Frame tag is in multiple slices, copies the tag bytes from slice
+     * buffer to a single flat buffer.  */
+    alts_grpc_record_protocol_copy_slice_buffer(
+        protected_slices, integrity_only_record_protocol->tag_buf);
+    tag_iovec.iov_base = integrity_only_record_protocol->tag_buf;
+  }
+  /* Calls alts_iovec_record_protocol unprotect.  */
+  char* error_details = nullptr;
+  alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+      rp, &integrity_only_record_protocol->data_sb);
+  grpc_status_code status = alts_iovec_record_protocol_integrity_only_unprotect(
+      rp->iovec_rp, rp->iovec_buf,
+      integrity_only_record_protocol->data_sb.count, header_iovec, tag_iovec,
+      &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+  grpc_slice_buffer_reset_and_unref_internal(protected_slices);
+  grpc_slice_buffer_move_into(&integrity_only_record_protocol->data_sb,
+                              unprotected_slices);
+  return TSI_OK;
+}
+
+static void alts_grpc_integrity_only_destruct(alts_grpc_record_protocol* rp) {
+  if (rp == nullptr) {
+    return;
+  }
+  alts_grpc_integrity_only_record_protocol* integrity_only_rp =
+      reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp);
+  grpc_slice_buffer_destroy_internal(&integrity_only_rp->data_sb);
+  gpr_free(integrity_only_rp->tag_buf);
+}
+
+static const alts_grpc_record_protocol_vtable
+    alts_grpc_integrity_only_record_protocol_vtable = {
+        alts_grpc_integrity_only_protect, alts_grpc_integrity_only_unprotect,
+        alts_grpc_integrity_only_destruct};
+
+tsi_result alts_grpc_integrity_only_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_protect, bool enable_extra_copy, alts_grpc_record_protocol** rp) {
+  if (crypter == nullptr || rp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol create.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_grpc_integrity_only_record_protocol* impl =
+      static_cast<alts_grpc_integrity_only_record_protocol*>(
+          gpr_zalloc(sizeof(alts_grpc_integrity_only_record_protocol)));
+  /* Calls alts_grpc_record_protocol init.  */
+  tsi_result result = alts_grpc_record_protocol_init(
+      &impl->base, crypter, overflow_size, is_client,
+      /*is_integrity_only=*/true, is_protect);
+  if (result != TSI_OK) {
+    gpr_free(impl);
+    return result;
+  }
+  impl->enable_extra_copy = enable_extra_copy;
+  /* Initializes slice buffer for data_sb.  */
+  grpc_slice_buffer_init(&impl->data_sb);
+  /* Allocates tag buffer.  */
+  impl->tag_buf =
+      static_cast<unsigned char*>(gpr_malloc(impl->base.tag_length));
+  impl->base.vtable = &alts_grpc_integrity_only_record_protocol_vtable;
+  *rp = &impl->base;
+  return TSI_OK;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h
new file mode 100644
index 0000000..5456d34
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+
+/**
+ * This method creates an integrity-only alts_grpc_record_protocol instance,
+ * given a gsec_aead_crypter instance and a flag indicating if the created
+ * instance will be used at the client or server side. The ownership of
+ * gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - overflow_size: overflow size of counter in bytes.
+ * - is_client: a flag indicating if the alts_grpc_record_protocol instance will
+ *   be used at the client or server side.
+ * - is_protect: a flag indicating if the alts_grpc_record_protocol instance
+ *   will be used for protect or unprotect.
+ *-  enable_extra_copy: a flag indicating if the instance uses one-copy instead
+ *   of zero-copy in the protect operation.
+ * - rp: an alts_grpc_record_protocol instance to be returned from
+ *   the method.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_integrity_only_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_protect, bool enable_extra_copy, alts_grpc_record_protocol** rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H \
+        */
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
new file mode 100644
index 0000000..e789090
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* Privacy-integrity alts_grpc_record_protocol object uses the same struct
+ * defined in alts_grpc_record_protocol_common.h.  */
+
+/* --- alts_grpc_record_protocol methods implementation. --- */
+
+static tsi_result alts_grpc_privacy_integrity_protect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  /* Input sanity check.  */
+  if (rp == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol protect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Allocates memory for output frame. In privacy-integrity protect, the
+   * protected frame is stored in a newly allocated buffer.  */
+  size_t protected_frame_size =
+      unprotected_slices->length + rp->header_length +
+      alts_iovec_record_protocol_get_tag_length(rp->iovec_rp);
+  grpc_slice protected_slice = GRPC_SLICE_MALLOC(protected_frame_size);
+  iovec_t protected_iovec = {GRPC_SLICE_START_PTR(protected_slice),
+                             GRPC_SLICE_LENGTH(protected_slice)};
+  /* Calls alts_iovec_record_protocol protect.  */
+  char* error_details = nullptr;
+  alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp,
+                                                          unprotected_slices);
+  grpc_status_code status =
+      alts_iovec_record_protocol_privacy_integrity_protect(
+          rp->iovec_rp, rp->iovec_buf, unprotected_slices->count,
+          protected_iovec, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to protect, %s", error_details);
+    gpr_free(error_details);
+    grpc_slice_unref_internal(protected_slice);
+    return TSI_INTERNAL_ERROR;
+  }
+  grpc_slice_buffer_add(protected_slices, protected_slice);
+  grpc_slice_buffer_reset_and_unref_internal(unprotected_slices);
+  return TSI_OK;
+}
+
+static tsi_result alts_grpc_privacy_integrity_unprotect(
+    alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  /* Input sanity check.  */
+  if (rp == nullptr || protected_slices == nullptr ||
+      unprotected_slices == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to alts_grpc_record_protocol unprotect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Allocates memory for output frame. In privacy-integrity unprotect, the
+   * unprotected data are stored in a newly allocated buffer.  */
+  if (protected_slices->length < rp->header_length + rp->tag_length) {
+    gpr_log(GPR_ERROR, "Protected slices do not have sufficient data.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  size_t unprotected_frame_size =
+      protected_slices->length - rp->header_length - rp->tag_length;
+  grpc_slice unprotected_slice = GRPC_SLICE_MALLOC(unprotected_frame_size);
+  iovec_t unprotected_iovec = {GRPC_SLICE_START_PTR(unprotected_slice),
+                               GRPC_SLICE_LENGTH(unprotected_slice)};
+  /* Strips frame header from protected slices.  */
+  grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+  grpc_slice_buffer_move_first(protected_slices, rp->header_length,
+                               &rp->header_sb);
+  iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp);
+  /* Calls alts_iovec_record_protocol unprotect.  */
+  char* error_details = nullptr;
+  alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, protected_slices);
+  grpc_status_code status =
+      alts_iovec_record_protocol_privacy_integrity_unprotect(
+          rp->iovec_rp, header_iovec, rp->iovec_buf, protected_slices->count,
+          unprotected_iovec, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details);
+    gpr_free(error_details);
+    grpc_slice_unref_internal(unprotected_slice);
+    return TSI_INTERNAL_ERROR;
+  }
+  grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb);
+  grpc_slice_buffer_reset_and_unref_internal(protected_slices);
+  grpc_slice_buffer_add(unprotected_slices, unprotected_slice);
+  return TSI_OK;
+}
+
+static const alts_grpc_record_protocol_vtable
+    alts_grpc_privacy_integrity_record_protocol_vtable = {
+        alts_grpc_privacy_integrity_protect,
+        alts_grpc_privacy_integrity_unprotect, nullptr};
+
+tsi_result alts_grpc_privacy_integrity_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_protect, alts_grpc_record_protocol** rp) {
+  if (crypter == nullptr || rp == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol create.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  auto* impl = static_cast<alts_grpc_record_protocol*>(
+      gpr_zalloc(sizeof(alts_grpc_record_protocol)));
+  /* Calls alts_grpc_record_protocol init.  */
+  tsi_result result =
+      alts_grpc_record_protocol_init(impl, crypter, overflow_size, is_client,
+                                     /*is_integrity_only=*/false, is_protect);
+  if (result != TSI_OK) {
+    gpr_free(impl);
+    return result;
+  }
+  impl->vtable = &alts_grpc_privacy_integrity_record_protocol_vtable;
+  *rp = impl;
+  return TSI_OK;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h
new file mode 100644
index 0000000..1e34aef
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+
+/**
+ * This method creates a privacy-integrity alts_grpc_record_protocol instance,
+ * given a gsec_aead_crypter instance and a flag indicating if the created
+ * instance will be used at the client or server side. The ownership of
+ * gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - is_client: a flag indicating if the alts_grpc_record_protocol instance will
+ *   be used at the client or server side.
+ * - rp: an alts_grpc_record_protocol instance to be returned from
+ *   the method.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_privacy_integrity_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_protect, alts_grpc_record_protocol** rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H \
+        */
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h
new file mode 100644
index 0000000..d1e433d
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+
+#include "src/core/tsi/transport_security_interface.h"
+
+/**
+ * This alts_grpc_record_protocol object protects and unprotects a single frame
+ * stored in grpc slice buffer with zero or minimized memory copy.
+ * Implementations of this object must be thread compatible.
+ */
+typedef struct alts_grpc_record_protocol alts_grpc_record_protocol;
+
+/**
+ * This methods performs protect operation on unprotected data and appends the
+ * protected frame to protected_slices. The caller needs to ensure the length
+ * of unprotected data plus the frame overhead is less than or equal to the
+ * maximum frame length. The input unprotected data slice buffer will be
+ * cleared, although the actual unprotected data bytes are not modified.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - unprotected_slices: the unprotected data to be protected.
+ * - protected_slices: slice buffer where the protected frame is appended.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_record_protocol_protect(
+    alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices);
+
+/**
+ * This methods performs unprotect operation on a full frame of protected data
+ * and appends unprotected data to unprotected_slices. It is the caller's
+ * responsibility to prepare a full frame of data before calling this method.
+ * The input protected frame slice buffer will be cleared, although the actual
+ * protected data bytes are not modified.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - protected_slices: a full frame of protected data in grpc slices.
+ * - unprotected_slices: slice buffer where unprotected data is appended.
+ *
+ * This method returns TSI_OK in case of success or a specific error code in
+ * case of failure.
+ */
+tsi_result alts_grpc_record_protocol_unprotect(
+    alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices);
+
+/**
+ * This method returns maximum allowed unprotected data size, given maximum
+ * protected frame size.
+ *
+ * - self: an alts_grpc_record_protocol instance.
+ * - max_protected_frame_size: maximum protected frame size.
+ *
+ * On success, the method returns the maximum allowed unprotected data size.
+ * Otherwise, it returns zero.
+ */
+size_t alts_grpc_record_protocol_max_unprotected_data_size(
+    const alts_grpc_record_protocol* self, size_t max_protected_frame_size);
+
+/**
+ * This method destroys an alts_grpc_record_protocol instance by de-allocating
+ * all of its occupied memory.
+ */
+void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H \
+        */
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
new file mode 100644
index 0000000..1048b60
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc
@@ -0,0 +1,174 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+const size_t kInitialIovecBufferSize = 8;
+
+/* Makes sure iovec_buf in alts_grpc_record_protocol is large enough.  */
+static void ensure_iovec_buf_size(alts_grpc_record_protocol* rp,
+                                  const grpc_slice_buffer* sb) {
+  GPR_ASSERT(rp != nullptr && sb != nullptr);
+  if (sb->count <= rp->iovec_buf_length) {
+    return;
+  }
+  /* At least double the iovec buffer size.  */
+  rp->iovec_buf_length = GPR_MAX(sb->count, 2 * rp->iovec_buf_length);
+  rp->iovec_buf = static_cast<iovec_t*>(
+      gpr_realloc(rp->iovec_buf, rp->iovec_buf_length * sizeof(iovec_t)));
+}
+
+/* --- Implementation of methods defined in tsi_grpc_record_protocol_common.h.
+ * --- */
+
+void alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+    alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb) {
+  GPR_ASSERT(rp != nullptr && sb != nullptr);
+  ensure_iovec_buf_size(rp, sb);
+  for (size_t i = 0; i < sb->count; i++) {
+    rp->iovec_buf[i].iov_base = GRPC_SLICE_START_PTR(sb->slices[i]);
+    rp->iovec_buf[i].iov_len = GRPC_SLICE_LENGTH(sb->slices[i]);
+  }
+}
+
+void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src,
+                                                 unsigned char* dst) {
+  GPR_ASSERT(src != nullptr && dst != nullptr);
+  for (size_t i = 0; i < src->count; i++) {
+    size_t slice_length = GRPC_SLICE_LENGTH(src->slices[i]);
+    memcpy(dst, GRPC_SLICE_START_PTR(src->slices[i]), slice_length);
+    dst += slice_length;
+  }
+}
+
+iovec_t alts_grpc_record_protocol_get_header_iovec(
+    alts_grpc_record_protocol* rp) {
+  iovec_t header_iovec = {nullptr, 0};
+  if (rp == nullptr) {
+    return header_iovec;
+  }
+  header_iovec.iov_len = rp->header_length;
+  if (rp->header_sb.count == 1) {
+    header_iovec.iov_base = GRPC_SLICE_START_PTR(rp->header_sb.slices[0]);
+  } else {
+    /* Frame header is in multiple slices, copies the header bytes from slice
+     * buffer to a single flat buffer.  */
+    alts_grpc_record_protocol_copy_slice_buffer(&rp->header_sb, rp->header_buf);
+    header_iovec.iov_base = rp->header_buf;
+  }
+  return header_iovec;
+}
+
+tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp,
+                                          gsec_aead_crypter* crypter,
+                                          size_t overflow_size, bool is_client,
+                                          bool is_integrity_only,
+                                          bool is_protect) {
+  if (rp == nullptr || crypter == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to alts_grpc_record_protocol init.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Creates alts_iovec_record_protocol.  */
+  char* error_details = nullptr;
+  grpc_status_code status = alts_iovec_record_protocol_create(
+      crypter, overflow_size, is_client, is_integrity_only, is_protect,
+      &rp->iovec_rp, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to create alts_iovec_record_protocol, %s.",
+            error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  /* Allocates header slice buffer.  */
+  grpc_slice_buffer_init(&rp->header_sb);
+  /* Allocates header buffer.  */
+  rp->header_length = alts_iovec_record_protocol_get_header_length();
+  rp->header_buf = static_cast<unsigned char*>(gpr_malloc(rp->header_length));
+  rp->tag_length = alts_iovec_record_protocol_get_tag_length(rp->iovec_rp);
+  /* Allocates iovec buffer.  */
+  rp->iovec_buf_length = kInitialIovecBufferSize;
+  rp->iovec_buf =
+      static_cast<iovec_t*>(gpr_malloc(rp->iovec_buf_length * sizeof(iovec_t)));
+  return TSI_OK;
+}
+
+/* --- Implementation of methods defined in tsi_grpc_record_protocol.h. --- */
+tsi_result alts_grpc_record_protocol_protect(
+    alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr ||
+      self->vtable == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->protect == nullptr) {
+    return TSI_UNIMPLEMENTED;
+  }
+  return self->vtable->protect(self, unprotected_slices, protected_slices);
+}
+
+tsi_result alts_grpc_record_protocol_unprotect(
+    alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr ||
+      self->vtable == nullptr || protected_slices == nullptr ||
+      unprotected_slices == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->unprotect == nullptr) {
+    return TSI_UNIMPLEMENTED;
+  }
+  return self->vtable->unprotect(self, protected_slices, unprotected_slices);
+}
+
+void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self) {
+  if (self == nullptr) {
+    return;
+  }
+  if (self->vtable->destruct != nullptr) {
+    self->vtable->destruct(self);
+  }
+  alts_iovec_record_protocol_destroy(self->iovec_rp);
+  grpc_slice_buffer_destroy_internal(&self->header_sb);
+  gpr_free(self->header_buf);
+  gpr_free(self->iovec_buf);
+  gpr_free(self);
+}
+
+/* Integrity-only and privacy-integrity share the same implementation. No need
+ * to call vtable.  */
+size_t alts_grpc_record_protocol_max_unprotected_data_size(
+    const alts_grpc_record_protocol* self, size_t max_protected_frame_size) {
+  if (self == nullptr) {
+    return 0;
+  }
+  return alts_iovec_record_protocol_max_unprotected_data_size(
+      self->iovec_rp, max_protected_frame_size);
+}
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h
new file mode 100644
index 0000000..43b8a4a
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H
+
+/**
+ * this file contains alts_grpc_record_protocol internals and internal-only
+ * helper functions. The public functions of alts_grpc_record_protocol are
+ * defined in the alts_grpc_record_protocol.h.
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+/* V-table for alts_grpc_record_protocol implementations.  */
+typedef struct {
+  tsi_result (*protect)(alts_grpc_record_protocol* self,
+                        grpc_slice_buffer* unprotected_slices,
+                        grpc_slice_buffer* protected_slices);
+  tsi_result (*unprotect)(alts_grpc_record_protocol* self,
+                          grpc_slice_buffer* protected_slices,
+                          grpc_slice_buffer* unprotected_slices);
+  void (*destruct)(alts_grpc_record_protocol* self);
+} alts_grpc_record_protocol_vtable;
+
+/* Main struct for alts_grpc_record_protocol implementation, shared by both
+ * integrity-only record protocol and privacy-integrity record protocol.
+ * Integrity-only record protocol has additional data elements.
+ * Privacy-integrity record protocol uses this struct directly.  */
+struct alts_grpc_record_protocol {
+  const alts_grpc_record_protocol_vtable* vtable;
+  alts_iovec_record_protocol* iovec_rp;
+  grpc_slice_buffer header_sb;
+  unsigned char* header_buf;
+  size_t header_length;
+  size_t tag_length;
+  iovec_t* iovec_buf;
+  size_t iovec_buf_length;
+};
+
+/**
+ * Converts the slices of input sb into iovec_t's and puts the result into
+ * rp->iovec_buf. Note that the actual data are not copied, only
+ * pointers and lengths are copied.
+ */
+void alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
+    alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb);
+
+/**
+ * Copies bytes from slice buffer to destination buffer. Caller is responsible
+ * for allocating enough memory of destination buffer. This method is used for
+ * copying frame header and tag in case they are stored in multiple slices.
+ */
+void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src,
+                                                 unsigned char* dst);
+
+/**
+ * This method returns an iovec object pointing to the frame header stored in
+ * rp->header_sb. If the frame header is stored in multiple slices,
+ * this method will copy the bytes in rp->header_sb to
+ * rp->header_buf, and return an iovec object pointing to
+ * rp->header_buf.
+ */
+iovec_t alts_grpc_record_protocol_get_header_iovec(
+    alts_grpc_record_protocol* rp);
+
+/**
+ * Initializes an alts_grpc_record_protocol object, given a gsec_aead_crypter
+ * instance, the overflow size of the counter in bytes, a flag indicating if the
+ * object is used for client or server side, a flag indicating if it is used for
+ * integrity-only or privacy-integrity mode, and a flag indicating if it is for
+ * protect or unprotect. The ownership of gsec_aead_crypter object is
+ * transferred to the alts_grpc_record_protocol object.
+ */
+tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp,
+                                          gsec_aead_crypter* crypter,
+                                          size_t overflow_size, bool is_client,
+                                          bool is_integrity_only,
+                                          bool is_protect);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H \
+        */
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
new file mode 100644
index 0000000..6a548e5
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc
@@ -0,0 +1,476 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/tsi/alts/frame_protector/alts_counter.h"
+
+struct alts_iovec_record_protocol {
+  alts_counter* ctr;
+  gsec_aead_crypter* crypter;
+  size_t tag_length;
+  bool is_integrity_only;
+  bool is_protect;
+};
+
+/* Copies error message to destination.  */
+static void maybe_copy_error_msg(const char* src, char** dst) {
+  if (dst != nullptr && src != nullptr) {
+    *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
+    memcpy(*dst, src, strlen(src) + 1);
+  }
+}
+
+/* Appends error message to destination.  */
+static void maybe_append_error_msg(const char* appendix, char** dst) {
+  if (dst != nullptr && appendix != nullptr) {
+    int dst_len = static_cast<int>(strlen(*dst));
+    *dst = static_cast<char*>(realloc(*dst, dst_len + strlen(appendix) + 1));
+    assert(*dst != nullptr);
+    memcpy(*dst + dst_len, appendix, strlen(appendix) + 1);
+  }
+}
+
+/* Use little endian to interpret a string of bytes as uint32_t.  */
+static uint32_t load_32_le(const unsigned char* buffer) {
+  return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) |
+         (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]);
+}
+
+/* Store uint32_t as a string of little endian bytes.  */
+static void store_32_le(uint32_t value, unsigned char* buffer) {
+  buffer[3] = (unsigned char)(value >> 24) & 0xFF;
+  buffer[2] = (unsigned char)(value >> 16) & 0xFF;
+  buffer[1] = (unsigned char)(value >> 8) & 0xFF;
+  buffer[0] = (unsigned char)(value)&0xFF;
+}
+
+/* Ensures header and tag iovec have sufficient length.  */
+static grpc_status_code ensure_header_and_tag_length(
+    const alts_iovec_record_protocol* rp, iovec_t header, iovec_t tag,
+    char** error_details) {
+  if (rp == nullptr) {
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (header.iov_base == nullptr) {
+    maybe_copy_error_msg("Header is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (header.iov_len != alts_iovec_record_protocol_get_header_length()) {
+    maybe_copy_error_msg("Header length is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (tag.iov_base == nullptr) {
+    maybe_copy_error_msg("Tag is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (tag.iov_len != rp->tag_length) {
+    maybe_copy_error_msg("Tag length is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  return GRPC_STATUS_OK;
+}
+
+/* Increments crypter counter and checks overflow.  */
+static grpc_status_code increment_counter(alts_counter* counter,
+                                          char** error_details) {
+  if (counter == nullptr) {
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  bool is_overflow = false;
+  grpc_status_code status =
+      alts_counter_increment(counter, &is_overflow, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  if (is_overflow) {
+    maybe_copy_error_msg("Crypter counter is overflowed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+/* Given an array of iovec, computes the total length of buffer.  */
+static size_t get_total_length(const iovec_t* vec, size_t vec_length) {
+  size_t total_length = 0;
+  for (size_t i = 0; i < vec_length; ++i) {
+    total_length += vec[i].iov_len;
+  }
+  return total_length;
+}
+
+/* Writes frame header given data and tag length.  */
+static grpc_status_code write_frame_header(size_t data_length,
+                                           unsigned char* header,
+                                           char** error_details) {
+  if (header == nullptr) {
+    maybe_copy_error_msg("Header is nullptr.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  size_t frame_length = kZeroCopyFrameMessageTypeFieldSize + data_length;
+  store_32_le(static_cast<uint32_t>(frame_length), header);
+  store_32_le(kZeroCopyFrameMessageType,
+              header + kZeroCopyFrameLengthFieldSize);
+  return GRPC_STATUS_OK;
+}
+
+/* Verifies frame header given protected data length.  */
+static grpc_status_code verify_frame_header(size_t data_length,
+                                            unsigned char* header,
+                                            char** error_details) {
+  if (header == nullptr) {
+    maybe_copy_error_msg("Header is nullptr.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  size_t frame_length = load_32_le(header);
+  if (frame_length != kZeroCopyFrameMessageTypeFieldSize + data_length) {
+    maybe_copy_error_msg("Bad frame length.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  size_t message_type = load_32_le(header + kZeroCopyFrameLengthFieldSize);
+  if (message_type != kZeroCopyFrameMessageType) {
+    maybe_copy_error_msg("Unsupported message type.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  return GRPC_STATUS_OK;
+}
+
+/* --- alts_iovec_record_protocol methods implementation. --- */
+
+size_t alts_iovec_record_protocol_get_header_length() {
+  return kZeroCopyFrameHeaderSize;
+}
+
+size_t alts_iovec_record_protocol_get_tag_length(
+    const alts_iovec_record_protocol* rp) {
+  if (rp != nullptr) {
+    return rp->tag_length;
+  }
+  return 0;
+}
+
+size_t alts_iovec_record_protocol_max_unprotected_data_size(
+    const alts_iovec_record_protocol* rp, size_t max_protected_frame_size) {
+  if (rp == nullptr) {
+    return 0;
+  }
+  size_t overhead_bytes_size =
+      kZeroCopyFrameMessageTypeFieldSize + rp->tag_length;
+  if (max_protected_frame_size <= overhead_bytes_size) return 0;
+  return max_protected_frame_size - overhead_bytes_size;
+}
+
+grpc_status_code alts_iovec_record_protocol_integrity_only_protect(
+    alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+    size_t unprotected_vec_length, iovec_t header, iovec_t tag,
+    char** error_details) {
+  /* Input sanity checks.  */
+  if (rp == nullptr) {
+    maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+                         error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (!rp->is_integrity_only) {
+    maybe_copy_error_msg(
+        "Integrity-only operations are not allowed for this object.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (!rp->is_protect) {
+    maybe_copy_error_msg("Protect operations are not allowed for this object.",
+                         error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  grpc_status_code status =
+      ensure_header_and_tag_length(rp, header, tag, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Unprotected data should not be zero length.  */
+  size_t data_length =
+      get_total_length(unprotected_vec, unprotected_vec_length);
+  /* Sets frame header.  */
+  status = write_frame_header(data_length + rp->tag_length,
+                              static_cast<unsigned char*>(header.iov_base),
+                              error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Computes frame tag by calling AEAD crypter.  */
+  size_t bytes_written = 0;
+  status = gsec_aead_crypter_encrypt_iovec(
+      rp->crypter, alts_counter_get_counter(rp->ctr),
+      alts_counter_get_size(rp->ctr), unprotected_vec, unprotected_vec_length,
+      /* plaintext_vec = */ nullptr, /* plaintext_vec_length = */ 0, tag,
+      &bytes_written, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  if (bytes_written != rp->tag_length) {
+    maybe_copy_error_msg("Bytes written expects to be the same as tag length.",
+                         error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  /* Increments the crypter counter.  */
+  return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect(
+    alts_iovec_record_protocol* rp, const iovec_t* protected_vec,
+    size_t protected_vec_length, iovec_t header, iovec_t tag,
+    char** error_details) {
+  /* Input sanity checks.  */
+  if (rp == nullptr) {
+    maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+                         error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (!rp->is_integrity_only) {
+    maybe_copy_error_msg(
+        "Integrity-only operations are not allowed for this object.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (rp->is_protect) {
+    maybe_copy_error_msg(
+        "Unprotect operations are not allowed for this object.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  grpc_status_code status =
+      ensure_header_and_tag_length(rp, header, tag, error_details);
+  if (status != GRPC_STATUS_OK) return status;
+  /* Protected data should not be zero length.  */
+  size_t data_length = get_total_length(protected_vec, protected_vec_length);
+  /* Verifies frame header.  */
+  status = verify_frame_header(data_length + rp->tag_length,
+                               static_cast<unsigned char*>(header.iov_base),
+                               error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Verifies frame tag by calling AEAD crypter.  */
+  iovec_t plaintext = {nullptr, 0};
+  size_t bytes_written = 0;
+  status = gsec_aead_crypter_decrypt_iovec(
+      rp->crypter, alts_counter_get_counter(rp->ctr),
+      alts_counter_get_size(rp->ctr), protected_vec, protected_vec_length, &tag,
+      1, plaintext, &bytes_written, error_details);
+  if (status != GRPC_STATUS_OK || bytes_written != 0) {
+    maybe_append_error_msg(" Frame tag verification failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  /* Increments the crypter counter.  */
+  return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect(
+    alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+    size_t unprotected_vec_length, iovec_t protected_frame,
+    char** error_details) {
+  /* Input sanity checks.  */
+  if (rp == nullptr) {
+    maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+                         error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (rp->is_integrity_only) {
+    maybe_copy_error_msg(
+        "Privacy-integrity operations are not allowed for this object.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (!rp->is_protect) {
+    maybe_copy_error_msg("Protect operations are not allowed for this object.",
+                         error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  /* Unprotected data should not be zero length.  */
+  size_t data_length =
+      get_total_length(unprotected_vec, unprotected_vec_length);
+  /* Ensures protected frame iovec has sufficient size.  */
+  if (protected_frame.iov_base == nullptr) {
+    maybe_copy_error_msg("Protected frame is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (protected_frame.iov_len !=
+      alts_iovec_record_protocol_get_header_length() + data_length +
+          rp->tag_length) {
+    maybe_copy_error_msg("Protected frame size is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Writer frame header.  */
+  grpc_status_code status = write_frame_header(
+      data_length + rp->tag_length,
+      static_cast<unsigned char*>(protected_frame.iov_base), error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Encrypt unprotected data by calling AEAD crypter.  */
+  unsigned char* ciphertext_buffer =
+      static_cast<unsigned char*>(protected_frame.iov_base) +
+      alts_iovec_record_protocol_get_header_length();
+  iovec_t ciphertext = {ciphertext_buffer, data_length + rp->tag_length};
+  size_t bytes_written = 0;
+  status = gsec_aead_crypter_encrypt_iovec(
+      rp->crypter, alts_counter_get_counter(rp->ctr),
+      alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr,
+      /* aad_vec_length = */ 0, unprotected_vec, unprotected_vec_length,
+      ciphertext, &bytes_written, error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  if (bytes_written != data_length + rp->tag_length) {
+    maybe_copy_error_msg(
+        "Bytes written expects to be data length plus tag length.",
+        error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  /* Increments the crypter counter. */
+  return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect(
+    alts_iovec_record_protocol* rp, iovec_t header,
+    const iovec_t* protected_vec, size_t protected_vec_length,
+    iovec_t unprotected_data, char** error_details) {
+  /* Input sanity checks.  */
+  if (rp == nullptr) {
+    maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
+                         error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (rp->is_integrity_only) {
+    maybe_copy_error_msg(
+        "Privacy-integrity operations are not allowed for this object.",
+        error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  if (rp->is_protect) {
+    maybe_copy_error_msg(
+        "Unprotect operations are not allowed for this object.", error_details);
+    return GRPC_STATUS_FAILED_PRECONDITION;
+  }
+  /* Protected data size should be no less than tag size.  */
+  size_t protected_data_length =
+      get_total_length(protected_vec, protected_vec_length);
+  if (protected_data_length < rp->tag_length) {
+    maybe_copy_error_msg(
+        "Protected data length should be more than the tag length.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Ensures header has sufficient size.  */
+  if (header.iov_base == nullptr) {
+    maybe_copy_error_msg("Header is nullptr.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  if (header.iov_len != alts_iovec_record_protocol_get_header_length()) {
+    maybe_copy_error_msg("Header length is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Ensures unprotected data iovec has sufficient size.  */
+  if (unprotected_data.iov_len != protected_data_length - rp->tag_length) {
+    maybe_copy_error_msg("Unprotected data size is incorrect.", error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  /* Verify frame header.  */
+  grpc_status_code status = verify_frame_header(
+      protected_data_length, static_cast<unsigned char*>(header.iov_base),
+      error_details);
+  if (status != GRPC_STATUS_OK) {
+    return status;
+  }
+  /* Decrypt protected data by calling AEAD crypter.  */
+  size_t bytes_written = 0;
+  status = gsec_aead_crypter_decrypt_iovec(
+      rp->crypter, alts_counter_get_counter(rp->ctr),
+      alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr,
+      /* aad_vec_length = */ 0, protected_vec, protected_vec_length,
+      unprotected_data, &bytes_written, error_details);
+  if (status != GRPC_STATUS_OK) {
+    maybe_append_error_msg(" Frame decryption failed.", error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  if (bytes_written != protected_data_length - rp->tag_length) {
+    maybe_copy_error_msg(
+        "Bytes written expects to be protected data length minus tag length.",
+        error_details);
+    return GRPC_STATUS_INTERNAL;
+  }
+  /* Increments the crypter counter. */
+  return increment_counter(rp->ctr, error_details);
+}
+
+grpc_status_code alts_iovec_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp,
+    char** error_details) {
+  if (crypter == nullptr || rp == nullptr) {
+    maybe_copy_error_msg(
+        "Invalid nullptr arguments to alts_iovec_record_protocol create.",
+        error_details);
+    return GRPC_STATUS_INVALID_ARGUMENT;
+  }
+  alts_iovec_record_protocol* impl = static_cast<alts_iovec_record_protocol*>(
+      gpr_zalloc(sizeof(alts_iovec_record_protocol)));
+  /* Gets counter length.  */
+  size_t counter_length = 0;
+  grpc_status_code status =
+      gsec_aead_crypter_nonce_length(crypter, &counter_length, error_details);
+  if (status != GRPC_STATUS_OK) {
+    goto cleanup;
+  }
+  /* Creates counters.  */
+  status =
+      alts_counter_create(is_protect ? !is_client : is_client, counter_length,
+                          overflow_size, &impl->ctr, error_details);
+  if (status != GRPC_STATUS_OK) {
+    goto cleanup;
+  }
+  /* Gets tag length.  */
+  status =
+      gsec_aead_crypter_tag_length(crypter, &impl->tag_length, error_details);
+  if (status != GRPC_STATUS_OK) {
+    goto cleanup;
+  }
+  impl->crypter = crypter;
+  impl->is_integrity_only = is_integrity_only;
+  impl->is_protect = is_protect;
+  *rp = impl;
+  return GRPC_STATUS_OK;
+cleanup:
+  alts_counter_destroy(impl->ctr);
+  gpr_free(impl);
+  return GRPC_STATUS_FAILED_PRECONDITION;
+}
+
+void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp) {
+  if (rp != nullptr) {
+    alts_counter_destroy(rp->ctr);
+    gsec_aead_crypter_destroy(rp->crypter);
+    gpr_free(rp);
+  }
+}
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h
new file mode 100644
index 0000000..0b7d1bf
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h
@@ -0,0 +1,199 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/alts/crypt/gsec.h"
+
+constexpr size_t kZeroCopyFrameMessageType = 0x06;
+constexpr size_t kZeroCopyFrameLengthFieldSize = 4;
+constexpr size_t kZeroCopyFrameMessageTypeFieldSize = 4;
+constexpr size_t kZeroCopyFrameHeaderSize =
+    kZeroCopyFrameLengthFieldSize + kZeroCopyFrameMessageTypeFieldSize;
+
+// Limit k on number of frames such that at most 2^(8 * k) frames can be sent.
+constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8;
+constexpr size_t kAltsRecordProtocolFrameLimit = 5;
+
+/* An implementation of alts record protocol. The API is thread-compatible. */
+
+typedef struct iovec iovec_t;
+
+typedef struct alts_iovec_record_protocol alts_iovec_record_protocol;
+
+/**
+ * This method gets the length of record protocol frame header.
+ */
+size_t alts_iovec_record_protocol_get_header_length();
+
+/**
+ * This method gets the length of record protocol frame tag.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ *
+ * On success, the method returns the length of record protocol frame tag.
+ * Otherwise, it returns zero.
+ */
+size_t alts_iovec_record_protocol_get_tag_length(
+    const alts_iovec_record_protocol* rp);
+
+/**
+ * This method returns maximum allowed unprotected data size, given maximum
+ * protected frame size.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - max_protected_frame_size: maximum protected frame size.
+ *
+ * On success, the method returns the maximum allowed unprotected data size.
+ * Otherwise, it returns zero.
+ */
+size_t alts_iovec_record_protocol_max_unprotected_data_size(
+    const alts_iovec_record_protocol* rp, size_t max_protected_frame_size);
+
+/**
+ * This method performs integrity-only protect operation on a
+ * alts_iovec_record_protocol instance, i.e., compute frame header and tag. The
+ * caller needs to allocate the memory for header and tag prior to calling this
+ * method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - unprotected_vec: an iovec array containing unprotected data.
+ * - unprotected_vec_length: the array length of unprotected_vec.
+ * - header: an iovec containing the output frame header.
+ * - tag: an iovec containing the output frame tag.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_integrity_only_protect(
+    alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+    size_t unprotected_vec_length, iovec_t header, iovec_t tag,
+    char** error_details);
+
+/**
+ * This method performs integrity-only unprotect operation on a
+ * alts_iovec_record_protocol instance, i.e., verify frame header and tag.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - protected_vec: an iovec array containing protected data.
+ * - protected_vec_length: the array length of protected_vec.
+ * - header: an iovec containing the frame header.
+ * - tag: an iovec containing the frame tag.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect(
+    alts_iovec_record_protocol* rp, const iovec_t* protected_vec,
+    size_t protected_vec_length, iovec_t header, iovec_t tag,
+    char** error_details);
+
+/**
+ * This method performs privacy-integrity protect operation on a
+ * alts_iovec_record_protocol instance, i.e., compute a protected frame. The
+ * caller needs to allocate the memory for the protected frame prior to calling
+ * this method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - unprotected_vec: an iovec array containing unprotected data.
+ * - unprotected_vec_length: the array length of unprotected_vec.
+ * - protected_frame: an iovec containing the output protected frame.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect(
+    alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
+    size_t unprotected_vec_length, iovec_t protected_frame,
+    char** error_details);
+
+/**
+ * This method performs privacy-integrity unprotect operation on a
+ * alts_iovec_record_protocol instance given a full protected frame, i.e.,
+ * compute the unprotected data. The caller needs to allocated the memory for
+ * the unprotected data prior to calling this method.
+ *
+ * - rp: an alts_iovec_record_protocol instance.
+ * - header: an iovec containing the frame header.
+ * - protected_vec: an iovec array containing protected data including the tag.
+ * - protected_vec_length: the array length of protected_vec.
+ * - unprotected_data: an iovec containing the output unprotected data.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect(
+    alts_iovec_record_protocol* rp, iovec_t header,
+    const iovec_t* protected_vec, size_t protected_vec_length,
+    iovec_t unprotected_data, char** error_details);
+
+/**
+ * This method creates an alts_iovec_record_protocol instance, given a
+ * gsec_aead_crypter instance, a flag indicating if the created instance will be
+ * used at the client or server side, and a flag indicating if the created
+ * instance will be used for integrity-only mode or privacy-integrity mode. The
+ * ownership of gsec_aead_crypter instance is transferred to this new object.
+ *
+ * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption.
+ * - overflow_size: overflow size of counter in bytes.
+ * - is_client: a flag indicating if the alts_iovec_record_protocol instance
+ *   will be used at the client or server side.
+ * - is_integrity_only: a flag indicating if the alts_iovec_record_protocol
+ *   instance will be used for integrity-only or privacy-integrity mode.
+ * - is_protect: a flag indicating if the alts_grpc_record_protocol instance
+ *   will be used for protect or unprotect.
+ * - rp: an alts_iovec_record_protocol instance to be returned from
+ *   the method.
+ * - error_details: a buffer containing an error message if the method does not
+ *   function correctly. It is OK to pass nullptr into error_details.
+ *
+ * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an
+ * error status code along with its details specified in error_details (if
+ * error_details is not nullptr).
+ */
+grpc_status_code alts_iovec_record_protocol_create(
+    gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
+    bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp,
+    char** error_details);
+
+/**
+ * This method destroys an alts_iovec_record_protocol instance by de-allocating
+ * all of its occupied memory. A gsec_aead_crypter instance passed in at
+ * gsec_alts_crypter instance creation time will be destroyed in this method.
+ */
+void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H \
+        */
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
new file mode 100644
index 0000000..58aba9b
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc
@@ -0,0 +1,297 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/alts/crypt/gsec.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
+#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
+#include "src/core/tsi/transport_security_grpc.h"
+
+constexpr size_t kMinFrameLength = 1024;
+constexpr size_t kDefaultFrameLength = 16 * 1024;
+constexpr size_t kMaxFrameLength = 1024 * 1024;
+
+/**
+ * Main struct for alts_zero_copy_grpc_protector.
+ * We choose to have two alts_grpc_record_protocol objects and two sets of slice
+ * buffers: one for protect and the other for unprotect, so that protect and
+ * unprotect can be executed in parallel. Implementations of this object must be
+ * thread compatible.
+ */
+typedef struct alts_zero_copy_grpc_protector {
+  tsi_zero_copy_grpc_protector base;
+  alts_grpc_record_protocol* record_protocol;
+  alts_grpc_record_protocol* unrecord_protocol;
+  size_t max_protected_frame_size;
+  size_t max_unprotected_data_size;
+  grpc_slice_buffer unprotected_staging_sb;
+  grpc_slice_buffer protected_sb;
+  grpc_slice_buffer protected_staging_sb;
+  uint32_t parsed_frame_size;
+} alts_zero_copy_grpc_protector;
+
+/**
+ * Given a slice buffer, parses the first 4 bytes little-endian unsigned frame
+ * size and returns the total frame size including the frame field. Caller
+ * needs to make sure the input slice buffer has at least 4 bytes. Returns true
+ * on success and false on failure.
+ */
+static bool read_frame_size(const grpc_slice_buffer* sb,
+                            uint32_t* total_frame_size) {
+  if (sb == nullptr || sb->length < kZeroCopyFrameLengthFieldSize) {
+    return false;
+  }
+  uint8_t frame_size_buffer[kZeroCopyFrameLengthFieldSize];
+  uint8_t* buf = frame_size_buffer;
+  /* Copies the first 4 bytes to a temporary buffer.  */
+  size_t remaining = kZeroCopyFrameLengthFieldSize;
+  for (size_t i = 0; i < sb->count; i++) {
+    size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]);
+    if (remaining <= slice_length) {
+      memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining);
+      remaining = 0;
+      break;
+    } else {
+      memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length);
+      buf += slice_length;
+      remaining -= slice_length;
+    }
+  }
+  GPR_ASSERT(remaining == 0);
+  /* Gets little-endian frame size.  */
+  uint32_t frame_size = (((uint32_t)frame_size_buffer[3]) << 24) |
+                        (((uint32_t)frame_size_buffer[2]) << 16) |
+                        (((uint32_t)frame_size_buffer[1]) << 8) |
+                        ((uint32_t)frame_size_buffer[0]);
+  if (frame_size > kMaxFrameLength) {
+    gpr_log(GPR_ERROR, "Frame size is larger than maximum frame size");
+    return false;
+  }
+  /* Returns frame size including frame length field.  */
+  *total_frame_size =
+      static_cast<uint32_t>(frame_size + kZeroCopyFrameLengthFieldSize);
+  return true;
+}
+
+/**
+ * Creates an alts_grpc_record_protocol object, given key, key size, and flags
+ * to indicate whether the record_protocol object uses the rekeying AEAD,
+ * whether the object is for client or server, whether the object is for
+ * integrity-only or privacy-integrity mode, and whether the object is is used
+ * for protect or unprotect.
+ */
+static tsi_result create_alts_grpc_record_protocol(
+    const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+    bool is_integrity_only, bool is_protect, bool enable_extra_copy,
+    alts_grpc_record_protocol** record_protocol) {
+  if (key == nullptr || record_protocol == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_status_code status;
+  gsec_aead_crypter* crypter = nullptr;
+  char* error_details = nullptr;
+  status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength,
+                                            kAesGcmTagLength, is_rekey,
+                                            &crypter, &error_details);
+  if (status != GRPC_STATUS_OK) {
+    gpr_log(GPR_ERROR, "Failed to create AEAD crypter, %s", error_details);
+    gpr_free(error_details);
+    return TSI_INTERNAL_ERROR;
+  }
+  size_t overflow_limit = is_rekey ? kAltsRecordProtocolRekeyFrameLimit
+                                   : kAltsRecordProtocolFrameLimit;
+  /* Creates alts_grpc_record_protocol with AEAD crypter ownership transferred.
+   */
+  tsi_result result = is_integrity_only
+                          ? alts_grpc_integrity_only_record_protocol_create(
+                                crypter, overflow_limit, is_client, is_protect,
+                                enable_extra_copy, record_protocol)
+                          : alts_grpc_privacy_integrity_record_protocol_create(
+                                crypter, overflow_limit, is_client, is_protect,
+                                record_protocol);
+  if (result != TSI_OK) {
+    gsec_aead_crypter_destroy(crypter);
+    return result;
+  }
+  return TSI_OK;
+}
+
+/* --- tsi_zero_copy_grpc_protector methods implementation. --- */
+
+static tsi_result alts_zero_copy_grpc_protector_protect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  if (self == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to zero-copy grpc protect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_zero_copy_grpc_protector* protector =
+      reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+  /* Calls alts_grpc_record_protocol protect repeatly.  */
+  while (unprotected_slices->length > protector->max_unprotected_data_size) {
+    grpc_slice_buffer_move_first(unprotected_slices,
+                                 protector->max_unprotected_data_size,
+                                 &protector->unprotected_staging_sb);
+    tsi_result status = alts_grpc_record_protocol_protect(
+        protector->record_protocol, &protector->unprotected_staging_sb,
+        protected_slices);
+    if (status != TSI_OK) {
+      return status;
+    }
+  }
+  return alts_grpc_record_protocol_protect(
+      protector->record_protocol, unprotected_slices, protected_slices);
+}
+
+static tsi_result alts_zero_copy_grpc_protector_unprotect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  if (self == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to zero-copy grpc unprotect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  alts_zero_copy_grpc_protector* protector =
+      reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+  grpc_slice_buffer_move_into(protected_slices, &protector->protected_sb);
+  /* Keep unprotecting each frame if possible.  */
+  while (protector->protected_sb.length >= kZeroCopyFrameLengthFieldSize) {
+    if (protector->parsed_frame_size == 0) {
+      /* We have not parsed frame size yet. Parses frame size.  */
+      if (!read_frame_size(&protector->protected_sb,
+                           &protector->parsed_frame_size)) {
+        grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb);
+        return TSI_DATA_CORRUPTED;
+      }
+    }
+    if (protector->protected_sb.length < protector->parsed_frame_size) break;
+    /* At this point, protected_sb contains at least one frame of data.  */
+    tsi_result status;
+    if (protector->protected_sb.length == protector->parsed_frame_size) {
+      status = alts_grpc_record_protocol_unprotect(protector->unrecord_protocol,
+                                                   &protector->protected_sb,
+                                                   unprotected_slices);
+    } else {
+      grpc_slice_buffer_move_first(&protector->protected_sb,
+                                   protector->parsed_frame_size,
+                                   &protector->protected_staging_sb);
+      status = alts_grpc_record_protocol_unprotect(
+          protector->unrecord_protocol, &protector->protected_staging_sb,
+          unprotected_slices);
+    }
+    protector->parsed_frame_size = 0;
+    if (status != TSI_OK) {
+      grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb);
+      return status;
+    }
+  }
+  return TSI_OK;
+}
+
+static void alts_zero_copy_grpc_protector_destroy(
+    tsi_zero_copy_grpc_protector* self) {
+  if (self == nullptr) {
+    return;
+  }
+  alts_zero_copy_grpc_protector* protector =
+      reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
+  alts_grpc_record_protocol_destroy(protector->record_protocol);
+  alts_grpc_record_protocol_destroy(protector->unrecord_protocol);
+  grpc_slice_buffer_destroy_internal(&protector->unprotected_staging_sb);
+  grpc_slice_buffer_destroy_internal(&protector->protected_sb);
+  grpc_slice_buffer_destroy_internal(&protector->protected_staging_sb);
+  gpr_free(protector);
+}
+
+static const tsi_zero_copy_grpc_protector_vtable
+    alts_zero_copy_grpc_protector_vtable = {
+        alts_zero_copy_grpc_protector_protect,
+        alts_zero_copy_grpc_protector_unprotect,
+        alts_zero_copy_grpc_protector_destroy};
+
+tsi_result alts_zero_copy_grpc_protector_create(
+    const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+    bool is_integrity_only, bool enable_extra_copy,
+    size_t* max_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector) {
+  if (grpc_core::ExecCtx::Get() == nullptr || key == nullptr ||
+      protector == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to alts_zero_copy_grpc_protector create.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Creates alts_zero_copy_protector.  */
+  alts_zero_copy_grpc_protector* impl =
+      static_cast<alts_zero_copy_grpc_protector*>(
+          gpr_zalloc(sizeof(alts_zero_copy_grpc_protector)));
+  /* Creates alts_grpc_record_protocol objects.  */
+  tsi_result status = create_alts_grpc_record_protocol(
+      key, key_size, is_rekey, is_client, is_integrity_only,
+      /*is_protect=*/true, enable_extra_copy, &impl->record_protocol);
+  if (status == TSI_OK) {
+    status = create_alts_grpc_record_protocol(
+        key, key_size, is_rekey, is_client, is_integrity_only,
+        /*is_protect=*/false, enable_extra_copy, &impl->unrecord_protocol);
+    if (status == TSI_OK) {
+      /* Sets maximum frame size.  */
+      size_t max_protected_frame_size_to_set = kDefaultFrameLength;
+      if (max_protected_frame_size != nullptr) {
+        *max_protected_frame_size =
+            GPR_MIN(*max_protected_frame_size, kMaxFrameLength);
+        *max_protected_frame_size =
+            GPR_MAX(*max_protected_frame_size, kMinFrameLength);
+        max_protected_frame_size_to_set = *max_protected_frame_size;
+      }
+      impl->max_protected_frame_size = max_protected_frame_size_to_set;
+      impl->max_unprotected_data_size =
+          alts_grpc_record_protocol_max_unprotected_data_size(
+              impl->record_protocol, max_protected_frame_size_to_set);
+      GPR_ASSERT(impl->max_unprotected_data_size > 0);
+      /* Allocates internal slice buffers.  */
+      grpc_slice_buffer_init(&impl->unprotected_staging_sb);
+      grpc_slice_buffer_init(&impl->protected_sb);
+      grpc_slice_buffer_init(&impl->protected_staging_sb);
+      impl->parsed_frame_size = 0;
+      impl->base.vtable = &alts_zero_copy_grpc_protector_vtable;
+      *protector = &impl->base;
+      return TSI_OK;
+    }
+  }
+
+  /* Cleanup if create failed.  */
+  alts_grpc_record_protocol_destroy(impl->record_protocol);
+  alts_grpc_record_protocol_destroy(impl->unrecord_protocol);
+  gpr_free(impl);
+  return TSI_INTERNAL_ERROR;
+}
diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h
new file mode 100644
index 0000000..515c27e
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H
+#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/tsi/transport_security_grpc.h"
+
+/**
+ * This method creates an ALTS zero-copy grpc protector.
+ *
+ * - key: a symmetric key used to seal/unseal frames.
+ * - key_size: the size of symmetric key.
+ * - is_rekey: use rekeying AEAD crypter.
+ * - is_client: a flag indicating if the protector will be used at client or
+ *   server side.
+ * - is_integrity_only: a flag indicating if the protector instance will be
+ *   used for integrity-only or privacy-integrity mode.
+ * - enable_extra_copy: a flag indicating if the protector instance does one
+ *   extra memory copy during the protect operation for integrity_only mode.
+ *   For the unprotect operation, it is still zero-copy. If application intends
+ *   to modify the data buffer after the protect operation, we can turn on this
+ *   mode to avoid integrity check failure.
+ * - max_protected_frame_size: an in/out parameter indicating max frame size
+ *   to be used by the protector. If it is nullptr, the default frame size will
+ *   be used. Otherwise, the provided frame size will be adjusted (if not
+ *   falling into a valid frame range) and used.
+ * - protector: a pointer to the zero-copy protector returned from the method.
+ *
+ * This method returns TSI_OK on success or a specific error code otherwise.
+ */
+tsi_result alts_zero_copy_grpc_protector_create(
+    const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
+    bool is_integrity_only, bool enable_extra_copy,
+    size_t* max_protected_frame_size, tsi_zero_copy_grpc_protector** protector);
+
+#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H \
+        */
diff --git a/third_party/grpc/src/core/tsi/fake_transport_security.cc b/third_party/grpc/src/core/tsi/fake_transport_security.cc
new file mode 100644
index 0000000..4d4c495
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/fake_transport_security.cc
@@ -0,0 +1,788 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/fake_transport_security.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/transport_security_grpc.h"
+
+/* --- Constants. ---*/
+#define TSI_FAKE_FRAME_HEADER_SIZE 4
+#define TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE 64
+#define TSI_FAKE_DEFAULT_FRAME_SIZE 16384
+#define TSI_FAKE_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE 256
+
+/* --- Structure definitions. ---*/
+
+/* a frame is encoded like this:
+   | size |     data    |
+   where the size field value is the size of the size field plus the size of
+   the data encoded in little endian on 4 bytes.  */
+typedef struct {
+  unsigned char* data;
+  size_t size;
+  size_t allocated_size;
+  size_t offset;
+  int needs_draining;
+} tsi_fake_frame;
+
+typedef enum {
+  TSI_FAKE_CLIENT_INIT = 0,
+  TSI_FAKE_SERVER_INIT = 1,
+  TSI_FAKE_CLIENT_FINISHED = 2,
+  TSI_FAKE_SERVER_FINISHED = 3,
+  TSI_FAKE_HANDSHAKE_MESSAGE_MAX = 4
+} tsi_fake_handshake_message;
+
+typedef struct {
+  tsi_handshaker base;
+  int is_client;
+  tsi_fake_handshake_message next_message_to_send;
+  int needs_incoming_message;
+  tsi_fake_frame incoming_frame;
+  tsi_fake_frame outgoing_frame;
+  unsigned char* outgoing_bytes_buffer;
+  size_t outgoing_bytes_buffer_size;
+  tsi_result result;
+} tsi_fake_handshaker;
+
+typedef struct {
+  tsi_frame_protector base;
+  tsi_fake_frame protect_frame;
+  tsi_fake_frame unprotect_frame;
+  size_t max_frame_size;
+} tsi_fake_frame_protector;
+
+typedef struct {
+  tsi_zero_copy_grpc_protector base;
+  grpc_slice_buffer header_sb;
+  grpc_slice_buffer protected_sb;
+  size_t max_frame_size;
+  size_t parsed_frame_size;
+} tsi_fake_zero_copy_grpc_protector;
+
+/* --- Utils. ---*/
+
+static const char* tsi_fake_handshake_message_strings[] = {
+    "CLIENT_INIT", "SERVER_INIT", "CLIENT_FINISHED", "SERVER_FINISHED"};
+
+static const char* tsi_fake_handshake_message_to_string(int msg) {
+  if (msg < 0 || msg >= TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
+    gpr_log(GPR_ERROR, "Invalid message %d", msg);
+    return "UNKNOWN";
+  }
+  return tsi_fake_handshake_message_strings[msg];
+}
+
+static tsi_result tsi_fake_handshake_message_from_string(
+    const char* msg_string, tsi_fake_handshake_message* msg) {
+  for (int i = 0; i < TSI_FAKE_HANDSHAKE_MESSAGE_MAX; i++) {
+    if (strncmp(msg_string, tsi_fake_handshake_message_strings[i],
+                strlen(tsi_fake_handshake_message_strings[i])) == 0) {
+      *msg = static_cast<tsi_fake_handshake_message>(i);
+      return TSI_OK;
+    }
+  }
+  gpr_log(GPR_ERROR, "Invalid handshake message.");
+  return TSI_DATA_CORRUPTED;
+}
+
+static uint32_t load32_little_endian(const unsigned char* buf) {
+  return (static_cast<uint32_t>(buf[0]) | static_cast<uint32_t>(buf[1] << 8) |
+          static_cast<uint32_t>(buf[2] << 16) |
+          static_cast<uint32_t>(buf[3] << 24));
+}
+
+static void store32_little_endian(uint32_t value, unsigned char* buf) {
+  buf[3] = static_cast<unsigned char>((value >> 24) & 0xFF);
+  buf[2] = static_cast<unsigned char>((value >> 16) & 0xFF);
+  buf[1] = static_cast<unsigned char>((value >> 8) & 0xFF);
+  buf[0] = static_cast<unsigned char>((value)&0xFF);
+}
+
+static uint32_t read_frame_size(const grpc_slice_buffer* sb) {
+  GPR_ASSERT(sb != nullptr && sb->length >= TSI_FAKE_FRAME_HEADER_SIZE);
+  uint8_t frame_size_buffer[TSI_FAKE_FRAME_HEADER_SIZE];
+  uint8_t* buf = frame_size_buffer;
+  /* Copies the first 4 bytes to a temporary buffer.  */
+  size_t remaining = TSI_FAKE_FRAME_HEADER_SIZE;
+  for (size_t i = 0; i < sb->count; i++) {
+    size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]);
+    if (remaining <= slice_length) {
+      memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining);
+      remaining = 0;
+      break;
+    } else {
+      memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length);
+      buf += slice_length;
+      remaining -= slice_length;
+    }
+  }
+  GPR_ASSERT(remaining == 0);
+  return load32_little_endian(frame_size_buffer);
+}
+
+static void tsi_fake_frame_reset(tsi_fake_frame* frame, int needs_draining) {
+  frame->offset = 0;
+  frame->needs_draining = needs_draining;
+  if (!needs_draining) frame->size = 0;
+}
+
+/* Checks if the frame's allocated size is at least frame->size, and reallocs
+ * more memory if necessary. */
+static void tsi_fake_frame_ensure_size(tsi_fake_frame* frame) {
+  if (frame->data == nullptr) {
+    frame->allocated_size = frame->size;
+    frame->data =
+        static_cast<unsigned char*>(gpr_malloc(frame->allocated_size));
+  } else if (frame->size > frame->allocated_size) {
+    unsigned char* new_data =
+        static_cast<unsigned char*>(gpr_realloc(frame->data, frame->size));
+    frame->data = new_data;
+    frame->allocated_size = frame->size;
+  }
+}
+
+/* Decodes the serialized fake frame contained in incoming_bytes, and fills
+ * frame with the contents of the decoded frame.
+ * This method should not be called if frame->needs_framing is not 0.  */
+static tsi_result tsi_fake_frame_decode(const unsigned char* incoming_bytes,
+                                        size_t* incoming_bytes_size,
+                                        tsi_fake_frame* frame) {
+  size_t available_size = *incoming_bytes_size;
+  size_t to_read_size = 0;
+  const unsigned char* bytes_cursor = incoming_bytes;
+
+  if (frame->needs_draining) return TSI_INTERNAL_ERROR;
+  if (frame->data == nullptr) {
+    frame->allocated_size = TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE;
+    frame->data =
+        static_cast<unsigned char*>(gpr_malloc(frame->allocated_size));
+  }
+
+  if (frame->offset < TSI_FAKE_FRAME_HEADER_SIZE) {
+    to_read_size = TSI_FAKE_FRAME_HEADER_SIZE - frame->offset;
+    if (to_read_size > available_size) {
+      /* Just fill what we can and exit. */
+      memcpy(frame->data + frame->offset, bytes_cursor, available_size);
+      bytes_cursor += available_size;
+      frame->offset += available_size;
+      *incoming_bytes_size = static_cast<size_t>(bytes_cursor - incoming_bytes);
+      return TSI_INCOMPLETE_DATA;
+    }
+    memcpy(frame->data + frame->offset, bytes_cursor, to_read_size);
+    bytes_cursor += to_read_size;
+    frame->offset += to_read_size;
+    available_size -= to_read_size;
+    frame->size = load32_little_endian(frame->data);
+    tsi_fake_frame_ensure_size(frame);
+  }
+
+  to_read_size = frame->size - frame->offset;
+  if (to_read_size > available_size) {
+    memcpy(frame->data + frame->offset, bytes_cursor, available_size);
+    frame->offset += available_size;
+    bytes_cursor += available_size;
+    *incoming_bytes_size = static_cast<size_t>(bytes_cursor - incoming_bytes);
+    return TSI_INCOMPLETE_DATA;
+  }
+  memcpy(frame->data + frame->offset, bytes_cursor, to_read_size);
+  bytes_cursor += to_read_size;
+  *incoming_bytes_size = static_cast<size_t>(bytes_cursor - incoming_bytes);
+  tsi_fake_frame_reset(frame, 1 /* needs_draining */);
+  return TSI_OK;
+}
+
+/* Encodes a fake frame into its wire format and places the result in
+ * outgoing_bytes. outgoing_bytes_size indicates the size of the encoded frame.
+ * This method should not be called if frame->needs_framing is 0.  */
+static tsi_result tsi_fake_frame_encode(unsigned char* outgoing_bytes,
+                                        size_t* outgoing_bytes_size,
+                                        tsi_fake_frame* frame) {
+  size_t to_write_size = frame->size - frame->offset;
+  if (!frame->needs_draining) return TSI_INTERNAL_ERROR;
+  if (*outgoing_bytes_size < to_write_size) {
+    memcpy(outgoing_bytes, frame->data + frame->offset, *outgoing_bytes_size);
+    frame->offset += *outgoing_bytes_size;
+    return TSI_INCOMPLETE_DATA;
+  }
+  memcpy(outgoing_bytes, frame->data + frame->offset, to_write_size);
+  *outgoing_bytes_size = to_write_size;
+  tsi_fake_frame_reset(frame, 0 /* needs_draining */);
+  return TSI_OK;
+}
+
+/* Sets the payload of a fake frame to contain the given data blob, where
+ * data_size indicates the size of data. */
+static tsi_result tsi_fake_frame_set_data(unsigned char* data, size_t data_size,
+                                          tsi_fake_frame* frame) {
+  frame->offset = 0;
+  frame->size = data_size + TSI_FAKE_FRAME_HEADER_SIZE;
+  tsi_fake_frame_ensure_size(frame);
+  store32_little_endian(static_cast<uint32_t>(frame->size), frame->data);
+  memcpy(frame->data + TSI_FAKE_FRAME_HEADER_SIZE, data, data_size);
+  tsi_fake_frame_reset(frame, 1 /* needs draining */);
+  return TSI_OK;
+}
+
+/* Destroys the contents of a fake frame. */
+static void tsi_fake_frame_destruct(tsi_fake_frame* frame) {
+  if (frame->data != nullptr) gpr_free(frame->data);
+}
+
+/* --- tsi_frame_protector methods implementation. ---*/
+
+static tsi_result fake_protector_protect(tsi_frame_protector* self,
+                                         const unsigned char* unprotected_bytes,
+                                         size_t* unprotected_bytes_size,
+                                         unsigned char* protected_output_frames,
+                                         size_t* protected_output_frames_size) {
+  tsi_result result = TSI_OK;
+  tsi_fake_frame_protector* impl =
+      reinterpret_cast<tsi_fake_frame_protector*>(self);
+  unsigned char frame_header[TSI_FAKE_FRAME_HEADER_SIZE];
+  tsi_fake_frame* frame = &impl->protect_frame;
+  size_t saved_output_size = *protected_output_frames_size;
+  size_t drained_size = 0;
+  size_t* num_bytes_written = protected_output_frames_size;
+  *num_bytes_written = 0;
+
+  /* Try to drain first. */
+  if (frame->needs_draining) {
+    drained_size = saved_output_size - *num_bytes_written;
+    result =
+        tsi_fake_frame_encode(protected_output_frames, &drained_size, frame);
+    *num_bytes_written += drained_size;
+    protected_output_frames += drained_size;
+    if (result != TSI_OK) {
+      if (result == TSI_INCOMPLETE_DATA) {
+        *unprotected_bytes_size = 0;
+        result = TSI_OK;
+      }
+      return result;
+    }
+  }
+
+  /* Now process the unprotected_bytes. */
+  if (frame->needs_draining) return TSI_INTERNAL_ERROR;
+  if (frame->size == 0) {
+    /* New frame, create a header. */
+    size_t written_in_frame_size = 0;
+    store32_little_endian(static_cast<uint32_t>(impl->max_frame_size),
+                          frame_header);
+    written_in_frame_size = TSI_FAKE_FRAME_HEADER_SIZE;
+    result = tsi_fake_frame_decode(frame_header, &written_in_frame_size, frame);
+    if (result != TSI_INCOMPLETE_DATA) {
+      gpr_log(GPR_ERROR, "tsi_fake_frame_decode returned %s",
+              tsi_result_to_string(result));
+      return result;
+    }
+  }
+  result =
+      tsi_fake_frame_decode(unprotected_bytes, unprotected_bytes_size, frame);
+  if (result != TSI_OK) {
+    if (result == TSI_INCOMPLETE_DATA) result = TSI_OK;
+    return result;
+  }
+
+  /* Try to drain again. */
+  if (!frame->needs_draining) return TSI_INTERNAL_ERROR;
+  if (frame->offset != 0) return TSI_INTERNAL_ERROR;
+  drained_size = saved_output_size - *num_bytes_written;
+  result = tsi_fake_frame_encode(protected_output_frames, &drained_size, frame);
+  *num_bytes_written += drained_size;
+  if (result == TSI_INCOMPLETE_DATA) result = TSI_OK;
+  return result;
+}
+
+static tsi_result fake_protector_protect_flush(
+    tsi_frame_protector* self, unsigned char* protected_output_frames,
+    size_t* protected_output_frames_size, size_t* still_pending_size) {
+  tsi_result result = TSI_OK;
+  tsi_fake_frame_protector* impl =
+      reinterpret_cast<tsi_fake_frame_protector*>(self);
+  tsi_fake_frame* frame = &impl->protect_frame;
+  if (!frame->needs_draining) {
+    /* Create a short frame. */
+    frame->size = frame->offset;
+    frame->offset = 0;
+    frame->needs_draining = 1;
+    store32_little_endian(static_cast<uint32_t>(frame->size),
+                          frame->data); /* Overwrite header. */
+  }
+  result = tsi_fake_frame_encode(protected_output_frames,
+                                 protected_output_frames_size, frame);
+  if (result == TSI_INCOMPLETE_DATA) result = TSI_OK;
+  *still_pending_size = frame->size - frame->offset;
+  return result;
+}
+
+static tsi_result fake_protector_unprotect(
+    tsi_frame_protector* self, const unsigned char* protected_frames_bytes,
+    size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes,
+    size_t* unprotected_bytes_size) {
+  tsi_result result = TSI_OK;
+  tsi_fake_frame_protector* impl =
+      reinterpret_cast<tsi_fake_frame_protector*>(self);
+  tsi_fake_frame* frame = &impl->unprotect_frame;
+  size_t saved_output_size = *unprotected_bytes_size;
+  size_t drained_size = 0;
+  size_t* num_bytes_written = unprotected_bytes_size;
+  *num_bytes_written = 0;
+
+  /* Try to drain first. */
+  if (frame->needs_draining) {
+    /* Go past the header if needed. */
+    if (frame->offset == 0) frame->offset = TSI_FAKE_FRAME_HEADER_SIZE;
+    drained_size = saved_output_size - *num_bytes_written;
+    result = tsi_fake_frame_encode(unprotected_bytes, &drained_size, frame);
+    unprotected_bytes += drained_size;
+    *num_bytes_written += drained_size;
+    if (result != TSI_OK) {
+      if (result == TSI_INCOMPLETE_DATA) {
+        *protected_frames_bytes_size = 0;
+        result = TSI_OK;
+      }
+      return result;
+    }
+  }
+
+  /* Now process the protected_bytes. */
+  if (frame->needs_draining) return TSI_INTERNAL_ERROR;
+  result = tsi_fake_frame_decode(protected_frames_bytes,
+                                 protected_frames_bytes_size, frame);
+  if (result != TSI_OK) {
+    if (result == TSI_INCOMPLETE_DATA) result = TSI_OK;
+    return result;
+  }
+
+  /* Try to drain again. */
+  if (!frame->needs_draining) return TSI_INTERNAL_ERROR;
+  if (frame->offset != 0) return TSI_INTERNAL_ERROR;
+  frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; /* Go past the header. */
+  drained_size = saved_output_size - *num_bytes_written;
+  result = tsi_fake_frame_encode(unprotected_bytes, &drained_size, frame);
+  *num_bytes_written += drained_size;
+  if (result == TSI_INCOMPLETE_DATA) result = TSI_OK;
+  return result;
+}
+
+static void fake_protector_destroy(tsi_frame_protector* self) {
+  tsi_fake_frame_protector* impl =
+      reinterpret_cast<tsi_fake_frame_protector*>(self);
+  tsi_fake_frame_destruct(&impl->protect_frame);
+  tsi_fake_frame_destruct(&impl->unprotect_frame);
+  gpr_free(self);
+}
+
+static const tsi_frame_protector_vtable frame_protector_vtable = {
+    fake_protector_protect,
+    fake_protector_protect_flush,
+    fake_protector_unprotect,
+    fake_protector_destroy,
+};
+
+/* --- tsi_zero_copy_grpc_protector methods implementation. ---*/
+
+static tsi_result fake_zero_copy_grpc_protector_protect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  if (self == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  tsi_fake_zero_copy_grpc_protector* impl =
+      reinterpret_cast<tsi_fake_zero_copy_grpc_protector*>(self);
+  /* Protects each frame. */
+  while (unprotected_slices->length > 0) {
+    size_t frame_length =
+        GPR_MIN(impl->max_frame_size,
+                unprotected_slices->length + TSI_FAKE_FRAME_HEADER_SIZE);
+    grpc_slice slice = GRPC_SLICE_MALLOC(TSI_FAKE_FRAME_HEADER_SIZE);
+    store32_little_endian(static_cast<uint32_t>(frame_length),
+                          GRPC_SLICE_START_PTR(slice));
+    grpc_slice_buffer_add(protected_slices, slice);
+    size_t data_length = frame_length - TSI_FAKE_FRAME_HEADER_SIZE;
+    grpc_slice_buffer_move_first(unprotected_slices, data_length,
+                                 protected_slices);
+  }
+  return TSI_OK;
+}
+
+static tsi_result fake_zero_copy_grpc_protector_unprotect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  if (self == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  tsi_fake_zero_copy_grpc_protector* impl =
+      reinterpret_cast<tsi_fake_zero_copy_grpc_protector*>(self);
+  grpc_slice_buffer_move_into(protected_slices, &impl->protected_sb);
+  /* Unprotect each frame, if we get a full frame. */
+  while (impl->protected_sb.length >= TSI_FAKE_FRAME_HEADER_SIZE) {
+    if (impl->parsed_frame_size == 0) {
+      impl->parsed_frame_size = read_frame_size(&impl->protected_sb);
+      if (impl->parsed_frame_size <= 4) {
+        gpr_log(GPR_ERROR, "Invalid frame size.");
+        return TSI_DATA_CORRUPTED;
+      }
+    }
+    /* If we do not have a full frame, return with OK status. */
+    if (impl->protected_sb.length < impl->parsed_frame_size) break;
+    /* Strips header bytes. */
+    grpc_slice_buffer_move_first(&impl->protected_sb,
+                                 TSI_FAKE_FRAME_HEADER_SIZE, &impl->header_sb);
+    /* Moves data to unprotected slices. */
+    grpc_slice_buffer_move_first(
+        &impl->protected_sb,
+        impl->parsed_frame_size - TSI_FAKE_FRAME_HEADER_SIZE,
+        unprotected_slices);
+    impl->parsed_frame_size = 0;
+    grpc_slice_buffer_reset_and_unref_internal(&impl->header_sb);
+  }
+  return TSI_OK;
+}
+
+static void fake_zero_copy_grpc_protector_destroy(
+    tsi_zero_copy_grpc_protector* self) {
+  if (self == nullptr) return;
+  tsi_fake_zero_copy_grpc_protector* impl =
+      reinterpret_cast<tsi_fake_zero_copy_grpc_protector*>(self);
+  grpc_slice_buffer_destroy_internal(&impl->header_sb);
+  grpc_slice_buffer_destroy_internal(&impl->protected_sb);
+  gpr_free(impl);
+}
+
+static const tsi_zero_copy_grpc_protector_vtable
+    zero_copy_grpc_protector_vtable = {
+        fake_zero_copy_grpc_protector_protect,
+        fake_zero_copy_grpc_protector_unprotect,
+        fake_zero_copy_grpc_protector_destroy,
+};
+
+/* --- tsi_handshaker_result methods implementation. ---*/
+
+typedef struct {
+  tsi_handshaker_result base;
+  unsigned char* unused_bytes;
+  size_t unused_bytes_size;
+} fake_handshaker_result;
+
+static tsi_result fake_handshaker_result_extract_peer(
+    const tsi_handshaker_result* self, tsi_peer* peer) {
+  /* Construct a tsi_peer with 1 property: certificate type. */
+  tsi_result result = tsi_construct_peer(1, peer);
+  if (result != TSI_OK) return result;
+  result = tsi_construct_string_peer_property_from_cstring(
+      TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_FAKE_CERTIFICATE_TYPE,
+      &peer->properties[0]);
+  if (result != TSI_OK) tsi_peer_destruct(peer);
+  return result;
+}
+
+static tsi_result fake_handshaker_result_create_zero_copy_grpc_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector) {
+  *protector =
+      tsi_create_fake_zero_copy_grpc_protector(max_output_protected_frame_size);
+  return TSI_OK;
+}
+
+static tsi_result fake_handshaker_result_create_frame_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_frame_protector** protector) {
+  *protector = tsi_create_fake_frame_protector(max_output_protected_frame_size);
+  return TSI_OK;
+}
+
+static tsi_result fake_handshaker_result_get_unused_bytes(
+    const tsi_handshaker_result* self, const unsigned char** bytes,
+    size_t* bytes_size) {
+  fake_handshaker_result* result = (fake_handshaker_result*)self;
+  *bytes_size = result->unused_bytes_size;
+  *bytes = result->unused_bytes;
+  return TSI_OK;
+}
+
+static void fake_handshaker_result_destroy(tsi_handshaker_result* self) {
+  fake_handshaker_result* result =
+      reinterpret_cast<fake_handshaker_result*>(self);
+  gpr_free(result->unused_bytes);
+  gpr_free(self);
+}
+
+static const tsi_handshaker_result_vtable handshaker_result_vtable = {
+    fake_handshaker_result_extract_peer,
+    fake_handshaker_result_create_zero_copy_grpc_protector,
+    fake_handshaker_result_create_frame_protector,
+    fake_handshaker_result_get_unused_bytes,
+    fake_handshaker_result_destroy,
+};
+
+static tsi_result fake_handshaker_result_create(
+    const unsigned char* unused_bytes, size_t unused_bytes_size,
+    tsi_handshaker_result** handshaker_result) {
+  if ((unused_bytes_size > 0 && unused_bytes == nullptr) ||
+      handshaker_result == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  fake_handshaker_result* result =
+      static_cast<fake_handshaker_result*>(gpr_zalloc(sizeof(*result)));
+  result->base.vtable = &handshaker_result_vtable;
+  if (unused_bytes_size > 0) {
+    result->unused_bytes =
+        static_cast<unsigned char*>(gpr_malloc(unused_bytes_size));
+    memcpy(result->unused_bytes, unused_bytes, unused_bytes_size);
+  }
+  result->unused_bytes_size = unused_bytes_size;
+  *handshaker_result = &result->base;
+  return TSI_OK;
+}
+
+/* --- tsi_handshaker methods implementation. ---*/
+
+static tsi_result fake_handshaker_get_bytes_to_send_to_peer(
+    tsi_handshaker* self, unsigned char* bytes, size_t* bytes_size) {
+  tsi_fake_handshaker* impl = reinterpret_cast<tsi_fake_handshaker*>(self);
+  tsi_result result = TSI_OK;
+  if (impl->needs_incoming_message || impl->result == TSI_OK) {
+    *bytes_size = 0;
+    return TSI_OK;
+  }
+  if (!impl->outgoing_frame.needs_draining) {
+    tsi_fake_handshake_message next_message_to_send =
+        static_cast<tsi_fake_handshake_message>(impl->next_message_to_send + 2);
+    const char* msg_string =
+        tsi_fake_handshake_message_to_string(impl->next_message_to_send);
+    result = tsi_fake_frame_set_data((unsigned char*)msg_string,
+                                     strlen(msg_string), &impl->outgoing_frame);
+    if (result != TSI_OK) return result;
+    if (next_message_to_send > TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
+      next_message_to_send = TSI_FAKE_HANDSHAKE_MESSAGE_MAX;
+    }
+    if (tsi_tracing_enabled.enabled()) {
+      gpr_log(GPR_INFO, "%s prepared %s.",
+              impl->is_client ? "Client" : "Server",
+              tsi_fake_handshake_message_to_string(impl->next_message_to_send));
+    }
+    impl->next_message_to_send = next_message_to_send;
+  }
+  result = tsi_fake_frame_encode(bytes, bytes_size, &impl->outgoing_frame);
+  if (result != TSI_OK) return result;
+  if (!impl->is_client &&
+      impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
+    /* We're done. */
+    if (tsi_tracing_enabled.enabled()) {
+      gpr_log(GPR_INFO, "Server is done.");
+    }
+    impl->result = TSI_OK;
+  } else {
+    impl->needs_incoming_message = 1;
+  }
+  return TSI_OK;
+}
+
+static tsi_result fake_handshaker_process_bytes_from_peer(
+    tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) {
+  tsi_result result = TSI_OK;
+  tsi_fake_handshaker* impl = reinterpret_cast<tsi_fake_handshaker*>(self);
+  tsi_fake_handshake_message expected_msg =
+      static_cast<tsi_fake_handshake_message>(impl->next_message_to_send - 1);
+  tsi_fake_handshake_message received_msg;
+
+  if (!impl->needs_incoming_message || impl->result == TSI_OK) {
+    *bytes_size = 0;
+    return TSI_OK;
+  }
+  result = tsi_fake_frame_decode(bytes, bytes_size, &impl->incoming_frame);
+  if (result != TSI_OK) return result;
+
+  /* We now have a complete frame. */
+  result = tsi_fake_handshake_message_from_string(
+      reinterpret_cast<const char*>(impl->incoming_frame.data) +
+          TSI_FAKE_FRAME_HEADER_SIZE,
+      &received_msg);
+  if (result != TSI_OK) {
+    impl->result = result;
+    return result;
+  }
+  if (received_msg != expected_msg) {
+    gpr_log(GPR_ERROR, "Invalid received message (%s instead of %s)",
+            tsi_fake_handshake_message_to_string(received_msg),
+            tsi_fake_handshake_message_to_string(expected_msg));
+  }
+  if (tsi_tracing_enabled.enabled()) {
+    gpr_log(GPR_INFO, "%s received %s.", impl->is_client ? "Client" : "Server",
+            tsi_fake_handshake_message_to_string(received_msg));
+  }
+  tsi_fake_frame_reset(&impl->incoming_frame, 0 /* needs_draining */);
+  impl->needs_incoming_message = 0;
+  if (impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
+    /* We're done. */
+    if (tsi_tracing_enabled.enabled()) {
+      gpr_log(GPR_INFO, "%s is done.", impl->is_client ? "Client" : "Server");
+    }
+    impl->result = TSI_OK;
+  }
+  return TSI_OK;
+}
+
+static tsi_result fake_handshaker_get_result(tsi_handshaker* self) {
+  tsi_fake_handshaker* impl = reinterpret_cast<tsi_fake_handshaker*>(self);
+  return impl->result;
+}
+
+static void fake_handshaker_destroy(tsi_handshaker* self) {
+  tsi_fake_handshaker* impl = reinterpret_cast<tsi_fake_handshaker*>(self);
+  tsi_fake_frame_destruct(&impl->incoming_frame);
+  tsi_fake_frame_destruct(&impl->outgoing_frame);
+  gpr_free(impl->outgoing_bytes_buffer);
+  gpr_free(self);
+}
+
+static tsi_result fake_handshaker_next(
+    tsi_handshaker* self, const unsigned char* received_bytes,
+    size_t received_bytes_size, const unsigned char** bytes_to_send,
+    size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result,
+    tsi_handshaker_on_next_done_cb cb, void* user_data) {
+  /* Sanity check the arguments. */
+  if ((received_bytes_size > 0 && received_bytes == nullptr) ||
+      bytes_to_send == nullptr || bytes_to_send_size == nullptr ||
+      handshaker_result == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  tsi_fake_handshaker* handshaker =
+      reinterpret_cast<tsi_fake_handshaker*>(self);
+  tsi_result result = TSI_OK;
+
+  /* Decode and process a handshake frame from the peer. */
+  size_t consumed_bytes_size = received_bytes_size;
+  if (received_bytes_size > 0) {
+    result = fake_handshaker_process_bytes_from_peer(self, received_bytes,
+                                                     &consumed_bytes_size);
+    if (result != TSI_OK) return result;
+  }
+
+  /* Create a handshake message to send to the peer and encode it as a fake
+   * frame. */
+  size_t offset = 0;
+  do {
+    size_t sent_bytes_size = handshaker->outgoing_bytes_buffer_size - offset;
+    result = fake_handshaker_get_bytes_to_send_to_peer(
+        self, handshaker->outgoing_bytes_buffer + offset, &sent_bytes_size);
+    offset += sent_bytes_size;
+    if (result == TSI_INCOMPLETE_DATA) {
+      handshaker->outgoing_bytes_buffer_size *= 2;
+      handshaker->outgoing_bytes_buffer = static_cast<unsigned char*>(
+          gpr_realloc(handshaker->outgoing_bytes_buffer,
+                      handshaker->outgoing_bytes_buffer_size));
+    }
+  } while (result == TSI_INCOMPLETE_DATA);
+  if (result != TSI_OK) return result;
+  *bytes_to_send = handshaker->outgoing_bytes_buffer;
+  *bytes_to_send_size = offset;
+
+  /* Check if the handshake was completed. */
+  if (fake_handshaker_get_result(self) == TSI_HANDSHAKE_IN_PROGRESS) {
+    *handshaker_result = nullptr;
+  } else {
+    /* Calculate the unused bytes. */
+    const unsigned char* unused_bytes = nullptr;
+    size_t unused_bytes_size = received_bytes_size - consumed_bytes_size;
+    if (unused_bytes_size > 0) {
+      unused_bytes = received_bytes + consumed_bytes_size;
+    }
+
+    /* Create a handshaker_result containing the unused bytes. */
+    result = fake_handshaker_result_create(unused_bytes, unused_bytes_size,
+                                           handshaker_result);
+    if (result == TSI_OK) {
+      /* Indicate that the handshake has completed and that a handshaker_result
+       * has been created. */
+      self->handshaker_result_created = true;
+    }
+  }
+  return result;
+}
+
+static const tsi_handshaker_vtable handshaker_vtable = {
+    nullptr, /* get_bytes_to_send_to_peer -- deprecated */
+    nullptr, /* process_bytes_from_peer   -- deprecated */
+    nullptr, /* get_result                -- deprecated */
+    nullptr, /* extract_peer              -- deprecated */
+    nullptr, /* create_frame_protector    -- deprecated */
+    fake_handshaker_destroy,
+    fake_handshaker_next,
+    nullptr, /* shutdown */
+};
+
+tsi_handshaker* tsi_create_fake_handshaker(int is_client) {
+  tsi_fake_handshaker* impl =
+      static_cast<tsi_fake_handshaker*>(gpr_zalloc(sizeof(*impl)));
+  impl->base.vtable = &handshaker_vtable;
+  impl->is_client = is_client;
+  impl->result = TSI_HANDSHAKE_IN_PROGRESS;
+  impl->outgoing_bytes_buffer_size =
+      TSI_FAKE_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE;
+  impl->outgoing_bytes_buffer =
+      static_cast<unsigned char*>(gpr_malloc(impl->outgoing_bytes_buffer_size));
+  if (is_client) {
+    impl->needs_incoming_message = 0;
+    impl->next_message_to_send = TSI_FAKE_CLIENT_INIT;
+  } else {
+    impl->needs_incoming_message = 1;
+    impl->next_message_to_send = TSI_FAKE_SERVER_INIT;
+  }
+  return &impl->base;
+}
+
+tsi_frame_protector* tsi_create_fake_frame_protector(
+    size_t* max_protected_frame_size) {
+  tsi_fake_frame_protector* impl =
+      static_cast<tsi_fake_frame_protector*>(gpr_zalloc(sizeof(*impl)));
+  impl->max_frame_size = (max_protected_frame_size == nullptr)
+                             ? TSI_FAKE_DEFAULT_FRAME_SIZE
+                             : *max_protected_frame_size;
+  impl->base.vtable = &frame_protector_vtable;
+  return &impl->base;
+}
+
+tsi_zero_copy_grpc_protector* tsi_create_fake_zero_copy_grpc_protector(
+    size_t* max_protected_frame_size) {
+  tsi_fake_zero_copy_grpc_protector* impl =
+      static_cast<tsi_fake_zero_copy_grpc_protector*>(
+          gpr_zalloc(sizeof(*impl)));
+  grpc_slice_buffer_init(&impl->header_sb);
+  grpc_slice_buffer_init(&impl->protected_sb);
+  impl->max_frame_size = (max_protected_frame_size == nullptr)
+                             ? TSI_FAKE_DEFAULT_FRAME_SIZE
+                             : *max_protected_frame_size;
+  impl->parsed_frame_size = 0;
+  impl->base.vtable = &zero_copy_grpc_protector_vtable;
+  return &impl->base;
+}
diff --git a/third_party/grpc/src/core/tsi/fake_transport_security.h b/third_party/grpc/src/core/tsi/fake_transport_security.h
new file mode 100644
index 0000000..3779182
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/fake_transport_security.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H
+#define GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/transport_security_interface.h"
+
+/* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for FAKE certs. */
+#define TSI_FAKE_CERTIFICATE_TYPE "FAKE"
+
+/* Creates a fake handshaker that will create a fake frame protector.
+
+   No cryptography is performed in these objects. They just simulate handshake
+   messages going back and forth for the handshaker and do some framing on
+   cleartext data for the protector.  */
+tsi_handshaker* tsi_create_fake_handshaker(int is_client);
+
+/* Creates a protector directly without going through the handshake phase. */
+tsi_frame_protector* tsi_create_fake_frame_protector(
+    size_t* max_protected_frame_size);
+
+/* Creates a zero-copy protector directly without going through the handshake
+ * phase. */
+tsi_zero_copy_grpc_protector* tsi_create_fake_zero_copy_grpc_protector(
+    size_t* max_protected_frame_size);
+
+#endif /* GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H */
diff --git a/third_party/grpc/src/core/tsi/grpc_shadow_boringssl.h b/third_party/grpc/src/core/tsi/grpc_shadow_boringssl.h
new file mode 100644
index 0000000..074be6d
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/grpc_shadow_boringssl.h
@@ -0,0 +1,3006 @@
+
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+// This file is autogenerated from a template file. Please make
+// modifications to
+// `templates/src/objective-c/tsi/grpc_shadow_boringssl.h.template`
+// instead. This file can be regenerated from the template by running
+// `tools/buildgen/generate_projects.sh`.
+
+#ifndef GRPC_CORE_TSI_GRPC_SHADOW_BORINGSSL_H
+#define GRPC_CORE_TSI_GRPC_SHADOW_BORINGSSL_H
+
+#ifdef GRPC_SHADOW_BORINGSSL_SYMBOLS
+
+#define BIO_f_ssl GRPC_SHADOW_BIO_f_ssl
+#define BIO_set_ssl GRPC_SHADOW_BIO_set_ssl
+#define SSL_CTX_add_client_custom_ext GRPC_SHADOW_SSL_CTX_add_client_custom_ext
+#define SSL_CTX_add_server_custom_ext GRPC_SHADOW_SSL_CTX_add_server_custom_ext
+#define DTLSv1_get_timeout GRPC_SHADOW_DTLSv1_get_timeout
+#define DTLSv1_handle_timeout GRPC_SHADOW_DTLSv1_handle_timeout
+#define DTLSv1_set_initial_timeout_duration GRPC_SHADOW_DTLSv1_set_initial_timeout_duration
+#define SSL_CTX_set_srtp_profiles GRPC_SHADOW_SSL_CTX_set_srtp_profiles
+#define SSL_CTX_set_tlsext_use_srtp GRPC_SHADOW_SSL_CTX_set_tlsext_use_srtp
+#define SSL_get_selected_srtp_profile GRPC_SHADOW_SSL_get_selected_srtp_profile
+#define SSL_get_srtp_profiles GRPC_SHADOW_SSL_get_srtp_profiles
+#define SSL_set_srtp_profiles GRPC_SHADOW_SSL_set_srtp_profiles
+#define SSL_set_tlsext_use_srtp GRPC_SHADOW_SSL_set_tlsext_use_srtp
+#define DTLS_client_method GRPC_SHADOW_DTLS_client_method
+#define DTLS_method GRPC_SHADOW_DTLS_method
+#define DTLS_server_method GRPC_SHADOW_DTLS_server_method
+#define DTLS_with_buffers_method GRPC_SHADOW_DTLS_with_buffers_method
+#define DTLSv1_2_client_method GRPC_SHADOW_DTLSv1_2_client_method
+#define DTLSv1_2_method GRPC_SHADOW_DTLSv1_2_method
+#define DTLSv1_2_server_method GRPC_SHADOW_DTLSv1_2_server_method
+#define DTLSv1_client_method GRPC_SHADOW_DTLSv1_client_method
+#define DTLSv1_method GRPC_SHADOW_DTLSv1_method
+#define DTLSv1_server_method GRPC_SHADOW_DTLSv1_server_method
+#define SSL_SESSION_from_bytes GRPC_SHADOW_SSL_SESSION_from_bytes
+#define SSL_SESSION_to_bytes GRPC_SHADOW_SSL_SESSION_to_bytes
+#define SSL_SESSION_to_bytes_for_ticket GRPC_SHADOW_SSL_SESSION_to_bytes_for_ticket
+#define i2d_SSL_SESSION GRPC_SHADOW_i2d_SSL_SESSION
+#define SSL_CTX_set0_client_CAs GRPC_SHADOW_SSL_CTX_set0_client_CAs
+#define SSL_CTX_set_cert_cb GRPC_SHADOW_SSL_CTX_set_cert_cb
+#define SSL_CTX_set_chain_and_key GRPC_SHADOW_SSL_CTX_set_chain_and_key
+#define SSL_CTX_set_ocsp_response GRPC_SHADOW_SSL_CTX_set_ocsp_response
+#define SSL_CTX_set_signed_cert_timestamp_list GRPC_SHADOW_SSL_CTX_set_signed_cert_timestamp_list
+#define SSL_CTX_use_certificate_ASN1 GRPC_SHADOW_SSL_CTX_use_certificate_ASN1
+#define SSL_get0_peer_certificates GRPC_SHADOW_SSL_get0_peer_certificates
+#define SSL_get0_server_requested_CAs GRPC_SHADOW_SSL_get0_server_requested_CAs
+#define SSL_set0_client_CAs GRPC_SHADOW_SSL_set0_client_CAs
+#define SSL_set_cert_cb GRPC_SHADOW_SSL_set_cert_cb
+#define SSL_set_chain_and_key GRPC_SHADOW_SSL_set_chain_and_key
+#define SSL_set_ocsp_response GRPC_SHADOW_SSL_set_ocsp_response
+#define SSL_set_signed_cert_timestamp_list GRPC_SHADOW_SSL_set_signed_cert_timestamp_list
+#define SSL_use_certificate_ASN1 GRPC_SHADOW_SSL_use_certificate_ASN1
+#define SSL_CIPHER_description GRPC_SHADOW_SSL_CIPHER_description
+#define SSL_CIPHER_get_auth_nid GRPC_SHADOW_SSL_CIPHER_get_auth_nid
+#define SSL_CIPHER_get_bits GRPC_SHADOW_SSL_CIPHER_get_bits
+#define SSL_CIPHER_get_cipher_nid GRPC_SHADOW_SSL_CIPHER_get_cipher_nid
+#define SSL_CIPHER_get_digest_nid GRPC_SHADOW_SSL_CIPHER_get_digest_nid
+#define SSL_CIPHER_get_id GRPC_SHADOW_SSL_CIPHER_get_id
+#define SSL_CIPHER_get_kx_name GRPC_SHADOW_SSL_CIPHER_get_kx_name
+#define SSL_CIPHER_get_kx_nid GRPC_SHADOW_SSL_CIPHER_get_kx_nid
+#define SSL_CIPHER_get_max_version GRPC_SHADOW_SSL_CIPHER_get_max_version
+#define SSL_CIPHER_get_min_version GRPC_SHADOW_SSL_CIPHER_get_min_version
+#define SSL_CIPHER_get_name GRPC_SHADOW_SSL_CIPHER_get_name
+#define SSL_CIPHER_get_prf_nid GRPC_SHADOW_SSL_CIPHER_get_prf_nid
+#define SSL_CIPHER_get_rfc_name GRPC_SHADOW_SSL_CIPHER_get_rfc_name
+#define SSL_CIPHER_get_version GRPC_SHADOW_SSL_CIPHER_get_version
+#define SSL_CIPHER_is_aead GRPC_SHADOW_SSL_CIPHER_is_aead
+#define SSL_CIPHER_is_block_cipher GRPC_SHADOW_SSL_CIPHER_is_block_cipher
+#define SSL_CIPHER_standard_name GRPC_SHADOW_SSL_CIPHER_standard_name
+#define SSL_COMP_add_compression_method GRPC_SHADOW_SSL_COMP_add_compression_method
+#define SSL_COMP_free_compression_methods GRPC_SHADOW_SSL_COMP_free_compression_methods
+#define SSL_COMP_get0_name GRPC_SHADOW_SSL_COMP_get0_name
+#define SSL_COMP_get_compression_methods GRPC_SHADOW_SSL_COMP_get_compression_methods
+#define SSL_COMP_get_id GRPC_SHADOW_SSL_COMP_get_id
+#define SSL_COMP_get_name GRPC_SHADOW_SSL_COMP_get_name
+#define SSL_get_cipher_by_value GRPC_SHADOW_SSL_get_cipher_by_value
+#define SSL_CTX_get_default_passwd_cb GRPC_SHADOW_SSL_CTX_get_default_passwd_cb
+#define SSL_CTX_get_default_passwd_cb_userdata GRPC_SHADOW_SSL_CTX_get_default_passwd_cb_userdata
+#define SSL_CTX_set_default_passwd_cb GRPC_SHADOW_SSL_CTX_set_default_passwd_cb
+#define SSL_CTX_set_default_passwd_cb_userdata GRPC_SHADOW_SSL_CTX_set_default_passwd_cb_userdata
+#define SSL_CTX_use_PrivateKey_file GRPC_SHADOW_SSL_CTX_use_PrivateKey_file
+#define SSL_CTX_use_RSAPrivateKey_file GRPC_SHADOW_SSL_CTX_use_RSAPrivateKey_file
+#define SSL_CTX_use_certificate_chain_file GRPC_SHADOW_SSL_CTX_use_certificate_chain_file
+#define SSL_CTX_use_certificate_file GRPC_SHADOW_SSL_CTX_use_certificate_file
+#define SSL_add_file_cert_subjects_to_stack GRPC_SHADOW_SSL_add_file_cert_subjects_to_stack
+#define SSL_load_client_CA_file GRPC_SHADOW_SSL_load_client_CA_file
+#define SSL_use_PrivateKey_file GRPC_SHADOW_SSL_use_PrivateKey_file
+#define SSL_use_RSAPrivateKey_file GRPC_SHADOW_SSL_use_RSAPrivateKey_file
+#define SSL_use_certificate_file GRPC_SHADOW_SSL_use_certificate_file
+#define SSL_get_curve_name GRPC_SHADOW_SSL_get_curve_name
+#define ERR_load_SSL_strings GRPC_SHADOW_ERR_load_SSL_strings
+#define OPENSSL_init_ssl GRPC_SHADOW_OPENSSL_init_ssl
+#define SSL_CTX_check_private_key GRPC_SHADOW_SSL_CTX_check_private_key
+#define SSL_CTX_cipher_in_group GRPC_SHADOW_SSL_CTX_cipher_in_group
+#define SSL_CTX_clear_mode GRPC_SHADOW_SSL_CTX_clear_mode
+#define SSL_CTX_clear_options GRPC_SHADOW_SSL_CTX_clear_options
+#define SSL_CTX_enable_ocsp_stapling GRPC_SHADOW_SSL_CTX_enable_ocsp_stapling
+#define SSL_CTX_enable_signed_cert_timestamps GRPC_SHADOW_SSL_CTX_enable_signed_cert_timestamps
+#define SSL_CTX_enable_tls_channel_id GRPC_SHADOW_SSL_CTX_enable_tls_channel_id
+#define SSL_CTX_free GRPC_SHADOW_SSL_CTX_free
+#define SSL_CTX_get0_privatekey GRPC_SHADOW_SSL_CTX_get0_privatekey
+#define SSL_CTX_get_ciphers GRPC_SHADOW_SSL_CTX_get_ciphers
+#define SSL_CTX_get_ex_data GRPC_SHADOW_SSL_CTX_get_ex_data
+#define SSL_CTX_get_ex_new_index GRPC_SHADOW_SSL_CTX_get_ex_new_index
+#define SSL_CTX_get_keylog_callback GRPC_SHADOW_SSL_CTX_get_keylog_callback
+#define SSL_CTX_get_max_cert_list GRPC_SHADOW_SSL_CTX_get_max_cert_list
+#define SSL_CTX_get_mode GRPC_SHADOW_SSL_CTX_get_mode
+#define SSL_CTX_get_options GRPC_SHADOW_SSL_CTX_get_options
+#define SSL_CTX_get_quiet_shutdown GRPC_SHADOW_SSL_CTX_get_quiet_shutdown
+#define SSL_CTX_get_read_ahead GRPC_SHADOW_SSL_CTX_get_read_ahead
+#define SSL_CTX_get_session_cache_mode GRPC_SHADOW_SSL_CTX_get_session_cache_mode
+#define SSL_CTX_get_tlsext_ticket_keys GRPC_SHADOW_SSL_CTX_get_tlsext_ticket_keys
+#define SSL_CTX_need_tmp_RSA GRPC_SHADOW_SSL_CTX_need_tmp_RSA
+#define SSL_CTX_new GRPC_SHADOW_SSL_CTX_new
+#define SSL_CTX_sess_accept GRPC_SHADOW_SSL_CTX_sess_accept
+#define SSL_CTX_sess_accept_good GRPC_SHADOW_SSL_CTX_sess_accept_good
+#define SSL_CTX_sess_accept_renegotiate GRPC_SHADOW_SSL_CTX_sess_accept_renegotiate
+#define SSL_CTX_sess_cache_full GRPC_SHADOW_SSL_CTX_sess_cache_full
+#define SSL_CTX_sess_cb_hits GRPC_SHADOW_SSL_CTX_sess_cb_hits
+#define SSL_CTX_sess_connect GRPC_SHADOW_SSL_CTX_sess_connect
+#define SSL_CTX_sess_connect_good GRPC_SHADOW_SSL_CTX_sess_connect_good
+#define SSL_CTX_sess_connect_renegotiate GRPC_SHADOW_SSL_CTX_sess_connect_renegotiate
+#define SSL_CTX_sess_get_cache_size GRPC_SHADOW_SSL_CTX_sess_get_cache_size
+#define SSL_CTX_sess_hits GRPC_SHADOW_SSL_CTX_sess_hits
+#define SSL_CTX_sess_misses GRPC_SHADOW_SSL_CTX_sess_misses
+#define SSL_CTX_sess_number GRPC_SHADOW_SSL_CTX_sess_number
+#define SSL_CTX_sess_set_cache_size GRPC_SHADOW_SSL_CTX_sess_set_cache_size
+#define SSL_CTX_sess_timeouts GRPC_SHADOW_SSL_CTX_sess_timeouts
+#define SSL_CTX_set0_buffer_pool GRPC_SHADOW_SSL_CTX_set0_buffer_pool
+#define SSL_CTX_set1_curves GRPC_SHADOW_SSL_CTX_set1_curves
+#define SSL_CTX_set1_curves_list GRPC_SHADOW_SSL_CTX_set1_curves_list
+#define SSL_CTX_set1_tls_channel_id GRPC_SHADOW_SSL_CTX_set1_tls_channel_id
+#define SSL_CTX_set_allow_unknown_alpn_protos GRPC_SHADOW_SSL_CTX_set_allow_unknown_alpn_protos
+#define SSL_CTX_set_alpn_protos GRPC_SHADOW_SSL_CTX_set_alpn_protos
+#define SSL_CTX_set_alpn_select_cb GRPC_SHADOW_SSL_CTX_set_alpn_select_cb
+#define SSL_CTX_set_cipher_list GRPC_SHADOW_SSL_CTX_set_cipher_list
+#define SSL_CTX_set_current_time_cb GRPC_SHADOW_SSL_CTX_set_current_time_cb
+#define SSL_CTX_set_custom_verify GRPC_SHADOW_SSL_CTX_set_custom_verify
+#define SSL_CTX_set_dos_protection_cb GRPC_SHADOW_SSL_CTX_set_dos_protection_cb
+#define SSL_CTX_set_early_data_enabled GRPC_SHADOW_SSL_CTX_set_early_data_enabled
+#define SSL_CTX_set_ex_data GRPC_SHADOW_SSL_CTX_set_ex_data
+#define SSL_CTX_set_false_start_allowed_without_alpn GRPC_SHADOW_SSL_CTX_set_false_start_allowed_without_alpn
+#define SSL_CTX_set_grease_enabled GRPC_SHADOW_SSL_CTX_set_grease_enabled
+#define SSL_CTX_set_keylog_callback GRPC_SHADOW_SSL_CTX_set_keylog_callback
+#define SSL_CTX_set_max_cert_list GRPC_SHADOW_SSL_CTX_set_max_cert_list
+#define SSL_CTX_set_max_send_fragment GRPC_SHADOW_SSL_CTX_set_max_send_fragment
+#define SSL_CTX_set_mode GRPC_SHADOW_SSL_CTX_set_mode
+#define SSL_CTX_set_msg_callback GRPC_SHADOW_SSL_CTX_set_msg_callback
+#define SSL_CTX_set_msg_callback_arg GRPC_SHADOW_SSL_CTX_set_msg_callback_arg
+#define SSL_CTX_set_next_proto_select_cb GRPC_SHADOW_SSL_CTX_set_next_proto_select_cb
+#define SSL_CTX_set_next_protos_advertised_cb GRPC_SHADOW_SSL_CTX_set_next_protos_advertised_cb
+#define SSL_CTX_set_options GRPC_SHADOW_SSL_CTX_set_options
+#define SSL_CTX_set_psk_client_callback GRPC_SHADOW_SSL_CTX_set_psk_client_callback
+#define SSL_CTX_set_psk_server_callback GRPC_SHADOW_SSL_CTX_set_psk_server_callback
+#define SSL_CTX_set_quiet_shutdown GRPC_SHADOW_SSL_CTX_set_quiet_shutdown
+#define SSL_CTX_set_read_ahead GRPC_SHADOW_SSL_CTX_set_read_ahead
+#define SSL_CTX_set_retain_only_sha256_of_client_certs GRPC_SHADOW_SSL_CTX_set_retain_only_sha256_of_client_certs
+#define SSL_CTX_set_select_certificate_cb GRPC_SHADOW_SSL_CTX_set_select_certificate_cb
+#define SSL_CTX_set_session_cache_mode GRPC_SHADOW_SSL_CTX_set_session_cache_mode
+#define SSL_CTX_set_session_id_context GRPC_SHADOW_SSL_CTX_set_session_id_context
+#define SSL_CTX_set_strict_cipher_list GRPC_SHADOW_SSL_CTX_set_strict_cipher_list
+#define SSL_CTX_set_ticket_aead_method GRPC_SHADOW_SSL_CTX_set_ticket_aead_method
+#define SSL_CTX_set_tls13_variant GRPC_SHADOW_SSL_CTX_set_tls13_variant
+#define SSL_CTX_set_tls_channel_id_enabled GRPC_SHADOW_SSL_CTX_set_tls_channel_id_enabled
+#define SSL_CTX_set_tlsext_servername_arg GRPC_SHADOW_SSL_CTX_set_tlsext_servername_arg
+#define SSL_CTX_set_tlsext_servername_callback GRPC_SHADOW_SSL_CTX_set_tlsext_servername_callback
+#define SSL_CTX_set_tlsext_ticket_key_cb GRPC_SHADOW_SSL_CTX_set_tlsext_ticket_key_cb
+#define SSL_CTX_set_tlsext_ticket_keys GRPC_SHADOW_SSL_CTX_set_tlsext_ticket_keys
+#define SSL_CTX_set_tmp_dh GRPC_SHADOW_SSL_CTX_set_tmp_dh
+#define SSL_CTX_set_tmp_dh_callback GRPC_SHADOW_SSL_CTX_set_tmp_dh_callback
+#define SSL_CTX_set_tmp_ecdh GRPC_SHADOW_SSL_CTX_set_tmp_ecdh
+#define SSL_CTX_set_tmp_rsa GRPC_SHADOW_SSL_CTX_set_tmp_rsa
+#define SSL_CTX_set_tmp_rsa_callback GRPC_SHADOW_SSL_CTX_set_tmp_rsa_callback
+#define SSL_CTX_up_ref GRPC_SHADOW_SSL_CTX_up_ref
+#define SSL_CTX_use_psk_identity_hint GRPC_SHADOW_SSL_CTX_use_psk_identity_hint
+#define SSL_accept GRPC_SHADOW_SSL_accept
+#define SSL_cache_hit GRPC_SHADOW_SSL_cache_hit
+#define SSL_certs_clear GRPC_SHADOW_SSL_certs_clear
+#define SSL_check_private_key GRPC_SHADOW_SSL_check_private_key
+#define SSL_clear GRPC_SHADOW_SSL_clear
+#define SSL_clear_mode GRPC_SHADOW_SSL_clear_mode
+#define SSL_clear_options GRPC_SHADOW_SSL_clear_options
+#define SSL_connect GRPC_SHADOW_SSL_connect
+#define SSL_cutthrough_complete GRPC_SHADOW_SSL_cutthrough_complete
+#define SSL_do_handshake GRPC_SHADOW_SSL_do_handshake
+#define SSL_dummy_pq_padding_used GRPC_SHADOW_SSL_dummy_pq_padding_used
+#define SSL_early_data_accepted GRPC_SHADOW_SSL_early_data_accepted
+#define SSL_enable_ocsp_stapling GRPC_SHADOW_SSL_enable_ocsp_stapling
+#define SSL_enable_signed_cert_timestamps GRPC_SHADOW_SSL_enable_signed_cert_timestamps
+#define SSL_enable_tls_channel_id GRPC_SHADOW_SSL_enable_tls_channel_id
+#define SSL_free GRPC_SHADOW_SSL_free
+#define SSL_get0_alpn_selected GRPC_SHADOW_SSL_get0_alpn_selected
+#define SSL_get0_certificate_types GRPC_SHADOW_SSL_get0_certificate_types
+#define SSL_get0_next_proto_negotiated GRPC_SHADOW_SSL_get0_next_proto_negotiated
+#define SSL_get0_ocsp_response GRPC_SHADOW_SSL_get0_ocsp_response
+#define SSL_get0_session_id_context GRPC_SHADOW_SSL_get0_session_id_context
+#define SSL_get0_signed_cert_timestamp_list GRPC_SHADOW_SSL_get0_signed_cert_timestamp_list
+#define SSL_get_SSL_CTX GRPC_SHADOW_SSL_get_SSL_CTX
+#define SSL_get_cipher_list GRPC_SHADOW_SSL_get_cipher_list
+#define SSL_get_ciphers GRPC_SHADOW_SSL_get_ciphers
+#define SSL_get_client_random GRPC_SHADOW_SSL_get_client_random
+#define SSL_get_current_cipher GRPC_SHADOW_SSL_get_current_cipher
+#define SSL_get_current_compression GRPC_SHADOW_SSL_get_current_compression
+#define SSL_get_current_expansion GRPC_SHADOW_SSL_get_current_expansion
+#define SSL_get_curve_id GRPC_SHADOW_SSL_get_curve_id
+#define SSL_get_default_timeout GRPC_SHADOW_SSL_get_default_timeout
+#define SSL_get_error GRPC_SHADOW_SSL_get_error
+#define SSL_get_ex_data GRPC_SHADOW_SSL_get_ex_data
+#define SSL_get_ex_new_index GRPC_SHADOW_SSL_get_ex_new_index
+#define SSL_get_extms_support GRPC_SHADOW_SSL_get_extms_support
+#define SSL_get_fd GRPC_SHADOW_SSL_get_fd
+#define SSL_get_finished GRPC_SHADOW_SSL_get_finished
+#define SSL_get_info_callback GRPC_SHADOW_SSL_get_info_callback
+#define SSL_get_ivs GRPC_SHADOW_SSL_get_ivs
+#define SSL_get_max_cert_list GRPC_SHADOW_SSL_get_max_cert_list
+#define SSL_get_mode GRPC_SHADOW_SSL_get_mode
+#define SSL_get_negotiated_token_binding_param GRPC_SHADOW_SSL_get_negotiated_token_binding_param
+#define SSL_get_options GRPC_SHADOW_SSL_get_options
+#define SSL_get_peer_finished GRPC_SHADOW_SSL_get_peer_finished
+#define SSL_get_peer_quic_transport_params GRPC_SHADOW_SSL_get_peer_quic_transport_params
+#define SSL_get_peer_signature_algorithm GRPC_SHADOW_SSL_get_peer_signature_algorithm
+#define SSL_get_pending_cipher GRPC_SHADOW_SSL_get_pending_cipher
+#define SSL_get_privatekey GRPC_SHADOW_SSL_get_privatekey
+#define SSL_get_psk_identity GRPC_SHADOW_SSL_get_psk_identity
+#define SSL_get_psk_identity_hint GRPC_SHADOW_SSL_get_psk_identity_hint
+#define SSL_get_quiet_shutdown GRPC_SHADOW_SSL_get_quiet_shutdown
+#define SSL_get_rbio GRPC_SHADOW_SSL_get_rbio
+#define SSL_get_read_ahead GRPC_SHADOW_SSL_get_read_ahead
+#define SSL_get_read_sequence GRPC_SHADOW_SSL_get_read_sequence
+#define SSL_get_rfd GRPC_SHADOW_SSL_get_rfd
+#define SSL_get_secure_renegotiation_support GRPC_SHADOW_SSL_get_secure_renegotiation_support
+#define SSL_get_server_random GRPC_SHADOW_SSL_get_server_random
+#define SSL_get_server_tmp_key GRPC_SHADOW_SSL_get_server_tmp_key
+#define SSL_get_servername GRPC_SHADOW_SSL_get_servername
+#define SSL_get_servername_type GRPC_SHADOW_SSL_get_servername_type
+#define SSL_get_shared_ciphers GRPC_SHADOW_SSL_get_shared_ciphers
+#define SSL_get_shutdown GRPC_SHADOW_SSL_get_shutdown
+#define SSL_get_structure_sizes GRPC_SHADOW_SSL_get_structure_sizes
+#define SSL_get_ticket_age_skew GRPC_SHADOW_SSL_get_ticket_age_skew
+#define SSL_get_tls_channel_id GRPC_SHADOW_SSL_get_tls_channel_id
+#define SSL_get_tls_unique GRPC_SHADOW_SSL_get_tls_unique
+#define SSL_get_verify_mode GRPC_SHADOW_SSL_get_verify_mode
+#define SSL_get_wbio GRPC_SHADOW_SSL_get_wbio
+#define SSL_get_wfd GRPC_SHADOW_SSL_get_wfd
+#define SSL_get_write_sequence GRPC_SHADOW_SSL_get_write_sequence
+#define SSL_in_early_data GRPC_SHADOW_SSL_in_early_data
+#define SSL_in_false_start GRPC_SHADOW_SSL_in_false_start
+#define SSL_in_init GRPC_SHADOW_SSL_in_init
+#define SSL_is_draft_downgrade GRPC_SHADOW_SSL_is_draft_downgrade
+#define SSL_is_dtls GRPC_SHADOW_SSL_is_dtls
+#define SSL_is_init_finished GRPC_SHADOW_SSL_is_init_finished
+#define SSL_is_server GRPC_SHADOW_SSL_is_server
+#define SSL_is_token_binding_negotiated GRPC_SHADOW_SSL_is_token_binding_negotiated
+#define SSL_library_init GRPC_SHADOW_SSL_library_init
+#define SSL_load_error_strings GRPC_SHADOW_SSL_load_error_strings
+#define SSL_need_tmp_RSA GRPC_SHADOW_SSL_need_tmp_RSA
+#define SSL_new GRPC_SHADOW_SSL_new
+#define SSL_num_renegotiations GRPC_SHADOW_SSL_num_renegotiations
+#define SSL_peek GRPC_SHADOW_SSL_peek
+#define SSL_pending GRPC_SHADOW_SSL_pending
+#define SSL_read GRPC_SHADOW_SSL_read
+#define SSL_renegotiate GRPC_SHADOW_SSL_renegotiate
+#define SSL_renegotiate_pending GRPC_SHADOW_SSL_renegotiate_pending
+#define SSL_reset_early_data_reject GRPC_SHADOW_SSL_reset_early_data_reject
+#define SSL_select_next_proto GRPC_SHADOW_SSL_select_next_proto
+#define SSL_send_fatal_alert GRPC_SHADOW_SSL_send_fatal_alert
+#define SSL_session_reused GRPC_SHADOW_SSL_session_reused
+#define SSL_set0_rbio GRPC_SHADOW_SSL_set0_rbio
+#define SSL_set0_wbio GRPC_SHADOW_SSL_set0_wbio
+#define SSL_set1_curves GRPC_SHADOW_SSL_set1_curves
+#define SSL_set1_curves_list GRPC_SHADOW_SSL_set1_curves_list
+#define SSL_set1_tls_channel_id GRPC_SHADOW_SSL_set1_tls_channel_id
+#define SSL_set_SSL_CTX GRPC_SHADOW_SSL_set_SSL_CTX
+#define SSL_set_accept_state GRPC_SHADOW_SSL_set_accept_state
+#define SSL_set_alpn_protos GRPC_SHADOW_SSL_set_alpn_protos
+#define SSL_set_bio GRPC_SHADOW_SSL_set_bio
+#define SSL_set_cipher_list GRPC_SHADOW_SSL_set_cipher_list
+#define SSL_set_connect_state GRPC_SHADOW_SSL_set_connect_state
+#define SSL_set_custom_verify GRPC_SHADOW_SSL_set_custom_verify
+#define SSL_set_dummy_pq_padding_size GRPC_SHADOW_SSL_set_dummy_pq_padding_size
+#define SSL_set_early_data_enabled GRPC_SHADOW_SSL_set_early_data_enabled
+#define SSL_set_ex_data GRPC_SHADOW_SSL_set_ex_data
+#define SSL_set_fd GRPC_SHADOW_SSL_set_fd
+#define SSL_set_info_callback GRPC_SHADOW_SSL_set_info_callback
+#define SSL_set_max_cert_list GRPC_SHADOW_SSL_set_max_cert_list
+#define SSL_set_max_send_fragment GRPC_SHADOW_SSL_set_max_send_fragment
+#define SSL_set_mode GRPC_SHADOW_SSL_set_mode
+#define SSL_set_msg_callback GRPC_SHADOW_SSL_set_msg_callback
+#define SSL_set_msg_callback_arg GRPC_SHADOW_SSL_set_msg_callback_arg
+#define SSL_set_mtu GRPC_SHADOW_SSL_set_mtu
+#define SSL_set_options GRPC_SHADOW_SSL_set_options
+#define SSL_set_psk_client_callback GRPC_SHADOW_SSL_set_psk_client_callback
+#define SSL_set_psk_server_callback GRPC_SHADOW_SSL_set_psk_server_callback
+#define SSL_set_quic_transport_params GRPC_SHADOW_SSL_set_quic_transport_params
+#define SSL_set_quiet_shutdown GRPC_SHADOW_SSL_set_quiet_shutdown
+#define SSL_set_read_ahead GRPC_SHADOW_SSL_set_read_ahead
+#define SSL_set_renegotiate_mode GRPC_SHADOW_SSL_set_renegotiate_mode
+#define SSL_set_retain_only_sha256_of_client_certs GRPC_SHADOW_SSL_set_retain_only_sha256_of_client_certs
+#define SSL_set_rfd GRPC_SHADOW_SSL_set_rfd
+#define SSL_set_session_id_context GRPC_SHADOW_SSL_set_session_id_context
+#define SSL_set_shutdown GRPC_SHADOW_SSL_set_shutdown
+#define SSL_set_state GRPC_SHADOW_SSL_set_state
+#define SSL_set_strict_cipher_list GRPC_SHADOW_SSL_set_strict_cipher_list
+#define SSL_set_tls13_variant GRPC_SHADOW_SSL_set_tls13_variant
+#define SSL_set_tls_channel_id_enabled GRPC_SHADOW_SSL_set_tls_channel_id_enabled
+#define SSL_set_tlsext_host_name GRPC_SHADOW_SSL_set_tlsext_host_name
+#define SSL_set_tmp_dh GRPC_SHADOW_SSL_set_tmp_dh
+#define SSL_set_tmp_dh_callback GRPC_SHADOW_SSL_set_tmp_dh_callback
+#define SSL_set_tmp_ecdh GRPC_SHADOW_SSL_set_tmp_ecdh
+#define SSL_set_tmp_rsa GRPC_SHADOW_SSL_set_tmp_rsa
+#define SSL_set_tmp_rsa_callback GRPC_SHADOW_SSL_set_tmp_rsa_callback
+#define SSL_set_token_binding_params GRPC_SHADOW_SSL_set_token_binding_params
+#define SSL_set_wfd GRPC_SHADOW_SSL_set_wfd
+#define SSL_shutdown GRPC_SHADOW_SSL_shutdown
+#define SSL_state GRPC_SHADOW_SSL_state
+#define SSL_total_renegotiations GRPC_SHADOW_SSL_total_renegotiations
+#define SSL_use_psk_identity_hint GRPC_SHADOW_SSL_use_psk_identity_hint
+#define SSL_want GRPC_SHADOW_SSL_want
+#define SSL_write GRPC_SHADOW_SSL_write
+#define SSL_CTX_set_private_key_method GRPC_SHADOW_SSL_CTX_set_private_key_method
+#define SSL_CTX_set_signing_algorithm_prefs GRPC_SHADOW_SSL_CTX_set_signing_algorithm_prefs
+#define SSL_CTX_set_verify_algorithm_prefs GRPC_SHADOW_SSL_CTX_set_verify_algorithm_prefs
+#define SSL_CTX_use_PrivateKey GRPC_SHADOW_SSL_CTX_use_PrivateKey
+#define SSL_CTX_use_PrivateKey_ASN1 GRPC_SHADOW_SSL_CTX_use_PrivateKey_ASN1
+#define SSL_CTX_use_RSAPrivateKey GRPC_SHADOW_SSL_CTX_use_RSAPrivateKey
+#define SSL_CTX_use_RSAPrivateKey_ASN1 GRPC_SHADOW_SSL_CTX_use_RSAPrivateKey_ASN1
+#define SSL_get_signature_algorithm_digest GRPC_SHADOW_SSL_get_signature_algorithm_digest
+#define SSL_get_signature_algorithm_key_type GRPC_SHADOW_SSL_get_signature_algorithm_key_type
+#define SSL_get_signature_algorithm_name GRPC_SHADOW_SSL_get_signature_algorithm_name
+#define SSL_is_signature_algorithm_rsa_pss GRPC_SHADOW_SSL_is_signature_algorithm_rsa_pss
+#define SSL_set_private_key_method GRPC_SHADOW_SSL_set_private_key_method
+#define SSL_set_signing_algorithm_prefs GRPC_SHADOW_SSL_set_signing_algorithm_prefs
+#define SSL_use_PrivateKey GRPC_SHADOW_SSL_use_PrivateKey
+#define SSL_use_PrivateKey_ASN1 GRPC_SHADOW_SSL_use_PrivateKey_ASN1
+#define SSL_use_RSAPrivateKey GRPC_SHADOW_SSL_use_RSAPrivateKey
+#define SSL_use_RSAPrivateKey_ASN1 GRPC_SHADOW_SSL_use_RSAPrivateKey_ASN1
+#define SSL_CTX_add_session GRPC_SHADOW_SSL_CTX_add_session
+#define SSL_CTX_flush_sessions GRPC_SHADOW_SSL_CTX_flush_sessions
+#define SSL_CTX_get_channel_id_cb GRPC_SHADOW_SSL_CTX_get_channel_id_cb
+#define SSL_CTX_get_info_callback GRPC_SHADOW_SSL_CTX_get_info_callback
+#define SSL_CTX_get_timeout GRPC_SHADOW_SSL_CTX_get_timeout
+#define SSL_CTX_remove_session GRPC_SHADOW_SSL_CTX_remove_session
+#define SSL_CTX_sess_get_get_cb GRPC_SHADOW_SSL_CTX_sess_get_get_cb
+#define SSL_CTX_sess_get_new_cb GRPC_SHADOW_SSL_CTX_sess_get_new_cb
+#define SSL_CTX_sess_get_remove_cb GRPC_SHADOW_SSL_CTX_sess_get_remove_cb
+#define SSL_CTX_sess_set_get_cb GRPC_SHADOW_SSL_CTX_sess_set_get_cb
+#define SSL_CTX_sess_set_new_cb GRPC_SHADOW_SSL_CTX_sess_set_new_cb
+#define SSL_CTX_sess_set_remove_cb GRPC_SHADOW_SSL_CTX_sess_set_remove_cb
+#define SSL_CTX_set_channel_id_cb GRPC_SHADOW_SSL_CTX_set_channel_id_cb
+#define SSL_CTX_set_info_callback GRPC_SHADOW_SSL_CTX_set_info_callback
+#define SSL_CTX_set_session_psk_dhe_timeout GRPC_SHADOW_SSL_CTX_set_session_psk_dhe_timeout
+#define SSL_CTX_set_timeout GRPC_SHADOW_SSL_CTX_set_timeout
+#define SSL_SESSION_free GRPC_SHADOW_SSL_SESSION_free
+#define SSL_SESSION_get0_peer GRPC_SHADOW_SSL_SESSION_get0_peer
+#define SSL_SESSION_get0_ticket GRPC_SHADOW_SSL_SESSION_get0_ticket
+#define SSL_SESSION_get_ex_data GRPC_SHADOW_SSL_SESSION_get_ex_data
+#define SSL_SESSION_get_ex_new_index GRPC_SHADOW_SSL_SESSION_get_ex_new_index
+#define SSL_SESSION_get_id GRPC_SHADOW_SSL_SESSION_get_id
+#define SSL_SESSION_get_master_key GRPC_SHADOW_SSL_SESSION_get_master_key
+#define SSL_SESSION_get_ticket_lifetime_hint GRPC_SHADOW_SSL_SESSION_get_ticket_lifetime_hint
+#define SSL_SESSION_get_time GRPC_SHADOW_SSL_SESSION_get_time
+#define SSL_SESSION_get_timeout GRPC_SHADOW_SSL_SESSION_get_timeout
+#define SSL_SESSION_has_ticket GRPC_SHADOW_SSL_SESSION_has_ticket
+#define SSL_SESSION_is_resumable GRPC_SHADOW_SSL_SESSION_is_resumable
+#define SSL_SESSION_new GRPC_SHADOW_SSL_SESSION_new
+#define SSL_SESSION_set1_id_context GRPC_SHADOW_SSL_SESSION_set1_id_context
+#define SSL_SESSION_set_ex_data GRPC_SHADOW_SSL_SESSION_set_ex_data
+#define SSL_SESSION_set_time GRPC_SHADOW_SSL_SESSION_set_time
+#define SSL_SESSION_set_timeout GRPC_SHADOW_SSL_SESSION_set_timeout
+#define SSL_SESSION_should_be_single_use GRPC_SHADOW_SSL_SESSION_should_be_single_use
+#define SSL_SESSION_up_ref GRPC_SHADOW_SSL_SESSION_up_ref
+#define SSL_get1_session GRPC_SHADOW_SSL_get1_session
+#define SSL_get_session GRPC_SHADOW_SSL_get_session
+#define SSL_magic_pending_session_ptr GRPC_SHADOW_SSL_magic_pending_session_ptr
+#define SSL_set_session GRPC_SHADOW_SSL_set_session
+#define SSL_alert_desc_string GRPC_SHADOW_SSL_alert_desc_string
+#define SSL_alert_desc_string_long GRPC_SHADOW_SSL_alert_desc_string_long
+#define SSL_alert_type_string GRPC_SHADOW_SSL_alert_type_string
+#define SSL_alert_type_string_long GRPC_SHADOW_SSL_alert_type_string_long
+#define SSL_state_string GRPC_SHADOW_SSL_state_string
+#define SSL_state_string_long GRPC_SHADOW_SSL_state_string_long
+#define SSL_CTX_set_max_proto_version GRPC_SHADOW_SSL_CTX_set_max_proto_version
+#define SSL_CTX_set_min_proto_version GRPC_SHADOW_SSL_CTX_set_min_proto_version
+#define SSL_SESSION_get_protocol_version GRPC_SHADOW_SSL_SESSION_get_protocol_version
+#define SSL_SESSION_get_version GRPC_SHADOW_SSL_SESSION_get_version
+#define SSL_SESSION_set_protocol_version GRPC_SHADOW_SSL_SESSION_set_protocol_version
+#define SSL_get_version GRPC_SHADOW_SSL_get_version
+#define SSL_set_max_proto_version GRPC_SHADOW_SSL_set_max_proto_version
+#define SSL_set_min_proto_version GRPC_SHADOW_SSL_set_min_proto_version
+#define SSL_version GRPC_SHADOW_SSL_version
+#define PEM_read_SSL_SESSION GRPC_SHADOW_PEM_read_SSL_SESSION
+#define PEM_read_bio_SSL_SESSION GRPC_SHADOW_PEM_read_bio_SSL_SESSION
+#define PEM_write_SSL_SESSION GRPC_SHADOW_PEM_write_SSL_SESSION
+#define PEM_write_bio_SSL_SESSION GRPC_SHADOW_PEM_write_bio_SSL_SESSION
+#define SSL_CTX_add0_chain_cert GRPC_SHADOW_SSL_CTX_add0_chain_cert
+#define SSL_CTX_add1_chain_cert GRPC_SHADOW_SSL_CTX_add1_chain_cert
+#define SSL_CTX_add_client_CA GRPC_SHADOW_SSL_CTX_add_client_CA
+#define SSL_CTX_add_extra_chain_cert GRPC_SHADOW_SSL_CTX_add_extra_chain_cert
+#define SSL_CTX_clear_chain_certs GRPC_SHADOW_SSL_CTX_clear_chain_certs
+#define SSL_CTX_clear_extra_chain_certs GRPC_SHADOW_SSL_CTX_clear_extra_chain_certs
+#define SSL_CTX_get0_certificate GRPC_SHADOW_SSL_CTX_get0_certificate
+#define SSL_CTX_get0_chain_certs GRPC_SHADOW_SSL_CTX_get0_chain_certs
+#define SSL_CTX_get0_param GRPC_SHADOW_SSL_CTX_get0_param
+#define SSL_CTX_get_cert_store GRPC_SHADOW_SSL_CTX_get_cert_store
+#define SSL_CTX_get_client_CA_list GRPC_SHADOW_SSL_CTX_get_client_CA_list
+#define SSL_CTX_get_extra_chain_certs GRPC_SHADOW_SSL_CTX_get_extra_chain_certs
+#define SSL_CTX_get_verify_callback GRPC_SHADOW_SSL_CTX_get_verify_callback
+#define SSL_CTX_get_verify_depth GRPC_SHADOW_SSL_CTX_get_verify_depth
+#define SSL_CTX_get_verify_mode GRPC_SHADOW_SSL_CTX_get_verify_mode
+#define SSL_CTX_load_verify_locations GRPC_SHADOW_SSL_CTX_load_verify_locations
+#define SSL_CTX_set0_chain GRPC_SHADOW_SSL_CTX_set0_chain
+#define SSL_CTX_set0_verify_cert_store GRPC_SHADOW_SSL_CTX_set0_verify_cert_store
+#define SSL_CTX_set1_chain GRPC_SHADOW_SSL_CTX_set1_chain
+#define SSL_CTX_set1_param GRPC_SHADOW_SSL_CTX_set1_param
+#define SSL_CTX_set1_verify_cert_store GRPC_SHADOW_SSL_CTX_set1_verify_cert_store
+#define SSL_CTX_set_cert_store GRPC_SHADOW_SSL_CTX_set_cert_store
+#define SSL_CTX_set_cert_verify_callback GRPC_SHADOW_SSL_CTX_set_cert_verify_callback
+#define SSL_CTX_set_client_CA_list GRPC_SHADOW_SSL_CTX_set_client_CA_list
+#define SSL_CTX_set_client_cert_cb GRPC_SHADOW_SSL_CTX_set_client_cert_cb
+#define SSL_CTX_set_default_verify_paths GRPC_SHADOW_SSL_CTX_set_default_verify_paths
+#define SSL_CTX_set_purpose GRPC_SHADOW_SSL_CTX_set_purpose
+#define SSL_CTX_set_trust GRPC_SHADOW_SSL_CTX_set_trust
+#define SSL_CTX_set_verify GRPC_SHADOW_SSL_CTX_set_verify
+#define SSL_CTX_set_verify_depth GRPC_SHADOW_SSL_CTX_set_verify_depth
+#define SSL_CTX_use_certificate GRPC_SHADOW_SSL_CTX_use_certificate
+#define SSL_add0_chain_cert GRPC_SHADOW_SSL_add0_chain_cert
+#define SSL_add1_chain_cert GRPC_SHADOW_SSL_add1_chain_cert
+#define SSL_add_client_CA GRPC_SHADOW_SSL_add_client_CA
+#define SSL_alert_from_verify_result GRPC_SHADOW_SSL_alert_from_verify_result
+#define SSL_clear_chain_certs GRPC_SHADOW_SSL_clear_chain_certs
+#define SSL_dup_CA_list GRPC_SHADOW_SSL_dup_CA_list
+#define SSL_get0_chain_certs GRPC_SHADOW_SSL_get0_chain_certs
+#define SSL_get0_param GRPC_SHADOW_SSL_get0_param
+#define SSL_get_certificate GRPC_SHADOW_SSL_get_certificate
+#define SSL_get_client_CA_list GRPC_SHADOW_SSL_get_client_CA_list
+#define SSL_get_ex_data_X509_STORE_CTX_idx GRPC_SHADOW_SSL_get_ex_data_X509_STORE_CTX_idx
+#define SSL_get_peer_cert_chain GRPC_SHADOW_SSL_get_peer_cert_chain
+#define SSL_get_peer_certificate GRPC_SHADOW_SSL_get_peer_certificate
+#define SSL_get_peer_full_cert_chain GRPC_SHADOW_SSL_get_peer_full_cert_chain
+#define SSL_get_verify_callback GRPC_SHADOW_SSL_get_verify_callback
+#define SSL_get_verify_depth GRPC_SHADOW_SSL_get_verify_depth
+#define SSL_get_verify_result GRPC_SHADOW_SSL_get_verify_result
+#define SSL_set0_chain GRPC_SHADOW_SSL_set0_chain
+#define SSL_set0_verify_cert_store GRPC_SHADOW_SSL_set0_verify_cert_store
+#define SSL_set1_chain GRPC_SHADOW_SSL_set1_chain
+#define SSL_set1_param GRPC_SHADOW_SSL_set1_param
+#define SSL_set1_verify_cert_store GRPC_SHADOW_SSL_set1_verify_cert_store
+#define SSL_set_client_CA_list GRPC_SHADOW_SSL_set_client_CA_list
+#define SSL_set_purpose GRPC_SHADOW_SSL_set_purpose
+#define SSL_set_trust GRPC_SHADOW_SSL_set_trust
+#define SSL_set_verify GRPC_SHADOW_SSL_set_verify
+#define SSL_set_verify_depth GRPC_SHADOW_SSL_set_verify_depth
+#define SSL_set_verify_result GRPC_SHADOW_SSL_set_verify_result
+#define SSL_use_certificate GRPC_SHADOW_SSL_use_certificate
+#define d2i_SSL_SESSION GRPC_SHADOW_d2i_SSL_SESSION
+#define d2i_SSL_SESSION_bio GRPC_SHADOW_d2i_SSL_SESSION_bio
+#define i2d_SSL_SESSION_bio GRPC_SHADOW_i2d_SSL_SESSION_bio
+#define SSL_export_early_keying_material GRPC_SHADOW_SSL_export_early_keying_material
+#define SSL_export_keying_material GRPC_SHADOW_SSL_export_keying_material
+#define SSL_generate_key_block GRPC_SHADOW_SSL_generate_key_block
+#define SSL_get_key_block_len GRPC_SHADOW_SSL_get_key_block_len
+#define SSL_CTX_set_ed25519_enabled GRPC_SHADOW_SSL_CTX_set_ed25519_enabled
+#define SSL_early_callback_ctx_extension_get GRPC_SHADOW_SSL_early_callback_ctx_extension_get
+#define SSL_extension_supported GRPC_SHADOW_SSL_extension_supported
+#define SSLv23_client_method GRPC_SHADOW_SSLv23_client_method
+#define SSLv23_method GRPC_SHADOW_SSLv23_method
+#define SSLv23_server_method GRPC_SHADOW_SSLv23_server_method
+#define TLS_client_method GRPC_SHADOW_TLS_client_method
+#define TLS_method GRPC_SHADOW_TLS_method
+#define TLS_server_method GRPC_SHADOW_TLS_server_method
+#define TLS_with_buffers_method GRPC_SHADOW_TLS_with_buffers_method
+#define TLSv1_1_client_method GRPC_SHADOW_TLSv1_1_client_method
+#define TLSv1_1_method GRPC_SHADOW_TLSv1_1_method
+#define TLSv1_1_server_method GRPC_SHADOW_TLSv1_1_server_method
+#define TLSv1_2_client_method GRPC_SHADOW_TLSv1_2_client_method
+#define TLSv1_2_method GRPC_SHADOW_TLSv1_2_method
+#define TLSv1_2_server_method GRPC_SHADOW_TLSv1_2_server_method
+#define TLSv1_client_method GRPC_SHADOW_TLSv1_client_method
+#define TLSv1_method GRPC_SHADOW_TLSv1_method
+#define TLSv1_server_method GRPC_SHADOW_TLSv1_server_method
+#define SSL_max_seal_overhead GRPC_SHADOW_SSL_max_seal_overhead
+#define OPENSSL_cpuid_setup GRPC_SHADOW_OPENSSL_cpuid_setup
+#define CRYPTO_has_asm GRPC_SHADOW_CRYPTO_has_asm
+#define CRYPTO_is_confidential_build GRPC_SHADOW_CRYPTO_is_confidential_build
+#define CRYPTO_library_init GRPC_SHADOW_CRYPTO_library_init
+#define CRYPTO_malloc_init GRPC_SHADOW_CRYPTO_malloc_init
+#define ENGINE_load_builtin_engines GRPC_SHADOW_ENGINE_load_builtin_engines
+#define ENGINE_register_all_complete GRPC_SHADOW_ENGINE_register_all_complete
+#define OPENSSL_ia32cap_P GRPC_SHADOW_OPENSSL_ia32cap_P
+#define OPENSSL_init_crypto GRPC_SHADOW_OPENSSL_init_crypto
+#define OPENSSL_load_builtin_modules GRPC_SHADOW_OPENSSL_load_builtin_modules
+#define OpenSSL_version GRPC_SHADOW_OpenSSL_version
+#define OpenSSL_version_num GRPC_SHADOW_OpenSSL_version_num
+#define SSLeay GRPC_SHADOW_SSLeay
+#define SSLeay_version GRPC_SHADOW_SSLeay_version
+#define CRYPTO_cleanup_all_ex_data GRPC_SHADOW_CRYPTO_cleanup_all_ex_data
+#define CRYPTO_free_ex_data GRPC_SHADOW_CRYPTO_free_ex_data
+#define CRYPTO_get_ex_data GRPC_SHADOW_CRYPTO_get_ex_data
+#define CRYPTO_get_ex_new_index GRPC_SHADOW_CRYPTO_get_ex_new_index
+#define CRYPTO_new_ex_data GRPC_SHADOW_CRYPTO_new_ex_data
+#define CRYPTO_set_ex_data GRPC_SHADOW_CRYPTO_set_ex_data
+#define BIO_snprintf GRPC_SHADOW_BIO_snprintf
+#define BIO_vsnprintf GRPC_SHADOW_BIO_vsnprintf
+#define CRYPTO_memcmp GRPC_SHADOW_CRYPTO_memcmp
+#define OPENSSL_cleanse GRPC_SHADOW_OPENSSL_cleanse
+#define OPENSSL_free GRPC_SHADOW_OPENSSL_free
+#define OPENSSL_hash32 GRPC_SHADOW_OPENSSL_hash32
+#define OPENSSL_malloc GRPC_SHADOW_OPENSSL_malloc
+#define OPENSSL_realloc GRPC_SHADOW_OPENSSL_realloc
+#define OPENSSL_strcasecmp GRPC_SHADOW_OPENSSL_strcasecmp
+#define OPENSSL_strdup GRPC_SHADOW_OPENSSL_strdup
+#define OPENSSL_strncasecmp GRPC_SHADOW_OPENSSL_strncasecmp
+#define OPENSSL_strnlen GRPC_SHADOW_OPENSSL_strnlen
+#define OPENSSL_tolower GRPC_SHADOW_OPENSSL_tolower
+#define CRYPTO_refcount_dec_and_test_zero GRPC_SHADOW_CRYPTO_refcount_dec_and_test_zero
+#define CRYPTO_refcount_inc GRPC_SHADOW_CRYPTO_refcount_inc
+#define CRYPTO_THREADID_current GRPC_SHADOW_CRYPTO_THREADID_current
+#define CRYPTO_THREADID_set_callback GRPC_SHADOW_CRYPTO_THREADID_set_callback
+#define CRYPTO_THREADID_set_numeric GRPC_SHADOW_CRYPTO_THREADID_set_numeric
+#define CRYPTO_THREADID_set_pointer GRPC_SHADOW_CRYPTO_THREADID_set_pointer
+#define CRYPTO_get_dynlock_create_callback GRPC_SHADOW_CRYPTO_get_dynlock_create_callback
+#define CRYPTO_get_dynlock_destroy_callback GRPC_SHADOW_CRYPTO_get_dynlock_destroy_callback
+#define CRYPTO_get_dynlock_lock_callback GRPC_SHADOW_CRYPTO_get_dynlock_lock_callback
+#define CRYPTO_get_lock_name GRPC_SHADOW_CRYPTO_get_lock_name
+#define CRYPTO_get_locking_callback GRPC_SHADOW_CRYPTO_get_locking_callback
+#define CRYPTO_num_locks GRPC_SHADOW_CRYPTO_num_locks
+#define CRYPTO_set_add_lock_callback GRPC_SHADOW_CRYPTO_set_add_lock_callback
+#define CRYPTO_set_dynlock_create_callback GRPC_SHADOW_CRYPTO_set_dynlock_create_callback
+#define CRYPTO_set_dynlock_destroy_callback GRPC_SHADOW_CRYPTO_set_dynlock_destroy_callback
+#define CRYPTO_set_dynlock_lock_callback GRPC_SHADOW_CRYPTO_set_dynlock_lock_callback
+#define CRYPTO_set_id_callback GRPC_SHADOW_CRYPTO_set_id_callback
+#define CRYPTO_set_locking_callback GRPC_SHADOW_CRYPTO_set_locking_callback
+#define CRYPTO_MUTEX_cleanup GRPC_SHADOW_CRYPTO_MUTEX_cleanup
+#define CRYPTO_MUTEX_init GRPC_SHADOW_CRYPTO_MUTEX_init
+#define CRYPTO_MUTEX_lock_read GRPC_SHADOW_CRYPTO_MUTEX_lock_read
+#define CRYPTO_MUTEX_lock_write GRPC_SHADOW_CRYPTO_MUTEX_lock_write
+#define CRYPTO_MUTEX_unlock_read GRPC_SHADOW_CRYPTO_MUTEX_unlock_read
+#define CRYPTO_MUTEX_unlock_write GRPC_SHADOW_CRYPTO_MUTEX_unlock_write
+#define CRYPTO_STATIC_MUTEX_lock_read GRPC_SHADOW_CRYPTO_STATIC_MUTEX_lock_read
+#define CRYPTO_STATIC_MUTEX_lock_write GRPC_SHADOW_CRYPTO_STATIC_MUTEX_lock_write
+#define CRYPTO_STATIC_MUTEX_unlock_read GRPC_SHADOW_CRYPTO_STATIC_MUTEX_unlock_read
+#define CRYPTO_STATIC_MUTEX_unlock_write GRPC_SHADOW_CRYPTO_STATIC_MUTEX_unlock_write
+#define CRYPTO_get_thread_local GRPC_SHADOW_CRYPTO_get_thread_local
+#define CRYPTO_once GRPC_SHADOW_CRYPTO_once
+#define CRYPTO_set_thread_local GRPC_SHADOW_CRYPTO_set_thread_local
+#define sk_deep_copy GRPC_SHADOW_sk_deep_copy
+#define sk_delete GRPC_SHADOW_sk_delete
+#define sk_delete_ptr GRPC_SHADOW_sk_delete_ptr
+#define sk_dup GRPC_SHADOW_sk_dup
+#define sk_find GRPC_SHADOW_sk_find
+#define sk_free GRPC_SHADOW_sk_free
+#define sk_insert GRPC_SHADOW_sk_insert
+#define sk_is_sorted GRPC_SHADOW_sk_is_sorted
+#define sk_new GRPC_SHADOW_sk_new
+#define sk_new_null GRPC_SHADOW_sk_new_null
+#define sk_num GRPC_SHADOW_sk_num
+#define sk_pop GRPC_SHADOW_sk_pop
+#define sk_pop_free GRPC_SHADOW_sk_pop_free
+#define sk_push GRPC_SHADOW_sk_push
+#define sk_set GRPC_SHADOW_sk_set
+#define sk_set_cmp_func GRPC_SHADOW_sk_set_cmp_func
+#define sk_shift GRPC_SHADOW_sk_shift
+#define sk_sort GRPC_SHADOW_sk_sort
+#define sk_value GRPC_SHADOW_sk_value
+#define sk_zero GRPC_SHADOW_sk_zero
+#define lh_delete GRPC_SHADOW_lh_delete
+#define lh_doall GRPC_SHADOW_lh_doall
+#define lh_doall_arg GRPC_SHADOW_lh_doall_arg
+#define lh_free GRPC_SHADOW_lh_free
+#define lh_insert GRPC_SHADOW_lh_insert
+#define lh_new GRPC_SHADOW_lh_new
+#define lh_num_items GRPC_SHADOW_lh_num_items
+#define lh_retrieve GRPC_SHADOW_lh_retrieve
+#define lh_strhash GRPC_SHADOW_lh_strhash
+#define ERR_SAVE_STATE_free GRPC_SHADOW_ERR_SAVE_STATE_free
+#define ERR_add_error_data GRPC_SHADOW_ERR_add_error_data
+#define ERR_add_error_dataf GRPC_SHADOW_ERR_add_error_dataf
+#define ERR_clear_error GRPC_SHADOW_ERR_clear_error
+#define ERR_clear_system_error GRPC_SHADOW_ERR_clear_system_error
+#define ERR_error_string GRPC_SHADOW_ERR_error_string
+#define ERR_error_string_n GRPC_SHADOW_ERR_error_string_n
+#define ERR_free_strings GRPC_SHADOW_ERR_free_strings
+#define ERR_func_error_string GRPC_SHADOW_ERR_func_error_string
+#define ERR_get_error GRPC_SHADOW_ERR_get_error
+#define ERR_get_error_line GRPC_SHADOW_ERR_get_error_line
+#define ERR_get_error_line_data GRPC_SHADOW_ERR_get_error_line_data
+#define ERR_get_next_error_library GRPC_SHADOW_ERR_get_next_error_library
+#define ERR_lib_error_string GRPC_SHADOW_ERR_lib_error_string
+#define ERR_load_BIO_strings GRPC_SHADOW_ERR_load_BIO_strings
+#define ERR_load_ERR_strings GRPC_SHADOW_ERR_load_ERR_strings
+#define ERR_load_crypto_strings GRPC_SHADOW_ERR_load_crypto_strings
+#define ERR_peek_error GRPC_SHADOW_ERR_peek_error
+#define ERR_peek_error_line GRPC_SHADOW_ERR_peek_error_line
+#define ERR_peek_error_line_data GRPC_SHADOW_ERR_peek_error_line_data
+#define ERR_peek_last_error GRPC_SHADOW_ERR_peek_last_error
+#define ERR_peek_last_error_line GRPC_SHADOW_ERR_peek_last_error_line
+#define ERR_peek_last_error_line_data GRPC_SHADOW_ERR_peek_last_error_line_data
+#define ERR_pop_to_mark GRPC_SHADOW_ERR_pop_to_mark
+#define ERR_print_errors_cb GRPC_SHADOW_ERR_print_errors_cb
+#define ERR_print_errors_fp GRPC_SHADOW_ERR_print_errors_fp
+#define ERR_put_error GRPC_SHADOW_ERR_put_error
+#define ERR_reason_error_string GRPC_SHADOW_ERR_reason_error_string
+#define ERR_remove_state GRPC_SHADOW_ERR_remove_state
+#define ERR_remove_thread_state GRPC_SHADOW_ERR_remove_thread_state
+#define ERR_restore_state GRPC_SHADOW_ERR_restore_state
+#define ERR_save_state GRPC_SHADOW_ERR_save_state
+#define ERR_set_mark GRPC_SHADOW_ERR_set_mark
+#define kOpenSSLReasonStringData GRPC_SHADOW_kOpenSSLReasonStringData
+#define kOpenSSLReasonValues GRPC_SHADOW_kOpenSSLReasonValues
+#define kOpenSSLReasonValuesLen GRPC_SHADOW_kOpenSSLReasonValuesLen
+#define EVP_DecodeBase64 GRPC_SHADOW_EVP_DecodeBase64
+#define EVP_DecodeBlock GRPC_SHADOW_EVP_DecodeBlock
+#define EVP_DecodeFinal GRPC_SHADOW_EVP_DecodeFinal
+#define EVP_DecodeInit GRPC_SHADOW_EVP_DecodeInit
+#define EVP_DecodeUpdate GRPC_SHADOW_EVP_DecodeUpdate
+#define EVP_DecodedLength GRPC_SHADOW_EVP_DecodedLength
+#define EVP_EncodeBlock GRPC_SHADOW_EVP_EncodeBlock
+#define EVP_EncodeFinal GRPC_SHADOW_EVP_EncodeFinal
+#define EVP_EncodeInit GRPC_SHADOW_EVP_EncodeInit
+#define EVP_EncodeUpdate GRPC_SHADOW_EVP_EncodeUpdate
+#define EVP_EncodedLength GRPC_SHADOW_EVP_EncodedLength
+#define CBB_finish_i2d GRPC_SHADOW_CBB_finish_i2d
+#define CBS_asn1_ber_to_der GRPC_SHADOW_CBS_asn1_ber_to_der
+#define CBS_get_asn1_implicit_string GRPC_SHADOW_CBS_get_asn1_implicit_string
+#define CBS_asn1_bitstring_has_bit GRPC_SHADOW_CBS_asn1_bitstring_has_bit
+#define CBS_asn1_oid_to_text GRPC_SHADOW_CBS_asn1_oid_to_text
+#define CBS_contains_zero_byte GRPC_SHADOW_CBS_contains_zero_byte
+#define CBS_copy_bytes GRPC_SHADOW_CBS_copy_bytes
+#define CBS_data GRPC_SHADOW_CBS_data
+#define CBS_get_any_asn1 GRPC_SHADOW_CBS_get_any_asn1
+#define CBS_get_any_asn1_element GRPC_SHADOW_CBS_get_any_asn1_element
+#define CBS_get_any_ber_asn1_element GRPC_SHADOW_CBS_get_any_ber_asn1_element
+#define CBS_get_asn1 GRPC_SHADOW_CBS_get_asn1
+#define CBS_get_asn1_bool GRPC_SHADOW_CBS_get_asn1_bool
+#define CBS_get_asn1_element GRPC_SHADOW_CBS_get_asn1_element
+#define CBS_get_asn1_uint64 GRPC_SHADOW_CBS_get_asn1_uint64
+#define CBS_get_bytes GRPC_SHADOW_CBS_get_bytes
+#define CBS_get_last_u8 GRPC_SHADOW_CBS_get_last_u8
+#define CBS_get_optional_asn1 GRPC_SHADOW_CBS_get_optional_asn1
+#define CBS_get_optional_asn1_bool GRPC_SHADOW_CBS_get_optional_asn1_bool
+#define CBS_get_optional_asn1_octet_string GRPC_SHADOW_CBS_get_optional_asn1_octet_string
+#define CBS_get_optional_asn1_uint64 GRPC_SHADOW_CBS_get_optional_asn1_uint64
+#define CBS_get_u16 GRPC_SHADOW_CBS_get_u16
+#define CBS_get_u16_length_prefixed GRPC_SHADOW_CBS_get_u16_length_prefixed
+#define CBS_get_u24 GRPC_SHADOW_CBS_get_u24
+#define CBS_get_u24_length_prefixed GRPC_SHADOW_CBS_get_u24_length_prefixed
+#define CBS_get_u32 GRPC_SHADOW_CBS_get_u32
+#define CBS_get_u8 GRPC_SHADOW_CBS_get_u8
+#define CBS_get_u8_length_prefixed GRPC_SHADOW_CBS_get_u8_length_prefixed
+#define CBS_init GRPC_SHADOW_CBS_init
+#define CBS_is_valid_asn1_bitstring GRPC_SHADOW_CBS_is_valid_asn1_bitstring
+#define CBS_len GRPC_SHADOW_CBS_len
+#define CBS_mem_equal GRPC_SHADOW_CBS_mem_equal
+#define CBS_peek_asn1_tag GRPC_SHADOW_CBS_peek_asn1_tag
+#define CBS_skip GRPC_SHADOW_CBS_skip
+#define CBS_stow GRPC_SHADOW_CBS_stow
+#define CBS_strdup GRPC_SHADOW_CBS_strdup
+#define CBB_add_asn1 GRPC_SHADOW_CBB_add_asn1
+#define CBB_add_asn1_bool GRPC_SHADOW_CBB_add_asn1_bool
+#define CBB_add_asn1_octet_string GRPC_SHADOW_CBB_add_asn1_octet_string
+#define CBB_add_asn1_oid_from_text GRPC_SHADOW_CBB_add_asn1_oid_from_text
+#define CBB_add_asn1_uint64 GRPC_SHADOW_CBB_add_asn1_uint64
+#define CBB_add_bytes GRPC_SHADOW_CBB_add_bytes
+#define CBB_add_space GRPC_SHADOW_CBB_add_space
+#define CBB_add_u16 GRPC_SHADOW_CBB_add_u16
+#define CBB_add_u16_length_prefixed GRPC_SHADOW_CBB_add_u16_length_prefixed
+#define CBB_add_u24 GRPC_SHADOW_CBB_add_u24
+#define CBB_add_u24_length_prefixed GRPC_SHADOW_CBB_add_u24_length_prefixed
+#define CBB_add_u32 GRPC_SHADOW_CBB_add_u32
+#define CBB_add_u8 GRPC_SHADOW_CBB_add_u8
+#define CBB_add_u8_length_prefixed GRPC_SHADOW_CBB_add_u8_length_prefixed
+#define CBB_cleanup GRPC_SHADOW_CBB_cleanup
+#define CBB_data GRPC_SHADOW_CBB_data
+#define CBB_did_write GRPC_SHADOW_CBB_did_write
+#define CBB_discard_child GRPC_SHADOW_CBB_discard_child
+#define CBB_finish GRPC_SHADOW_CBB_finish
+#define CBB_flush GRPC_SHADOW_CBB_flush
+#define CBB_flush_asn1_set_of GRPC_SHADOW_CBB_flush_asn1_set_of
+#define CBB_init GRPC_SHADOW_CBB_init
+#define CBB_init_fixed GRPC_SHADOW_CBB_init_fixed
+#define CBB_len GRPC_SHADOW_CBB_len
+#define CBB_reserve GRPC_SHADOW_CBB_reserve
+#define CBB_zero GRPC_SHADOW_CBB_zero
+#define CRYPTO_BUFFER_POOL_free GRPC_SHADOW_CRYPTO_BUFFER_POOL_free
+#define CRYPTO_BUFFER_POOL_new GRPC_SHADOW_CRYPTO_BUFFER_POOL_new
+#define CRYPTO_BUFFER_data GRPC_SHADOW_CRYPTO_BUFFER_data
+#define CRYPTO_BUFFER_free GRPC_SHADOW_CRYPTO_BUFFER_free
+#define CRYPTO_BUFFER_init_CBS GRPC_SHADOW_CRYPTO_BUFFER_init_CBS
+#define CRYPTO_BUFFER_len GRPC_SHADOW_CRYPTO_BUFFER_len
+#define CRYPTO_BUFFER_new GRPC_SHADOW_CRYPTO_BUFFER_new
+#define CRYPTO_BUFFER_new_from_CBS GRPC_SHADOW_CRYPTO_BUFFER_new_from_CBS
+#define CRYPTO_BUFFER_up_ref GRPC_SHADOW_CRYPTO_BUFFER_up_ref
+#define AES_cbc_encrypt GRPC_SHADOW_AES_cbc_encrypt
+#define AES_cfb128_encrypt GRPC_SHADOW_AES_cfb128_encrypt
+#define AES_ctr128_encrypt GRPC_SHADOW_AES_ctr128_encrypt
+#define AES_decrypt GRPC_SHADOW_AES_decrypt
+#define AES_ecb_encrypt GRPC_SHADOW_AES_ecb_encrypt
+#define AES_encrypt GRPC_SHADOW_AES_encrypt
+#define AES_ofb128_encrypt GRPC_SHADOW_AES_ofb128_encrypt
+#define AES_set_decrypt_key GRPC_SHADOW_AES_set_decrypt_key
+#define AES_set_encrypt_key GRPC_SHADOW_AES_set_encrypt_key
+#define AES_unwrap_key GRPC_SHADOW_AES_unwrap_key
+#define AES_wrap_key GRPC_SHADOW_AES_wrap_key
+#define BN_BLINDING_convert GRPC_SHADOW_BN_BLINDING_convert
+#define BN_BLINDING_free GRPC_SHADOW_BN_BLINDING_free
+#define BN_BLINDING_invert GRPC_SHADOW_BN_BLINDING_invert
+#define BN_BLINDING_new GRPC_SHADOW_BN_BLINDING_new
+#define BN_CTX_end GRPC_SHADOW_BN_CTX_end
+#define BN_CTX_free GRPC_SHADOW_BN_CTX_free
+#define BN_CTX_get GRPC_SHADOW_BN_CTX_get
+#define BN_CTX_new GRPC_SHADOW_BN_CTX_new
+#define BN_CTX_start GRPC_SHADOW_BN_CTX_start
+#define BN_GENCB_call GRPC_SHADOW_BN_GENCB_call
+#define BN_GENCB_set GRPC_SHADOW_BN_GENCB_set
+#define BN_MONT_CTX_copy GRPC_SHADOW_BN_MONT_CTX_copy
+#define BN_MONT_CTX_free GRPC_SHADOW_BN_MONT_CTX_free
+#define BN_MONT_CTX_new GRPC_SHADOW_BN_MONT_CTX_new
+#define BN_MONT_CTX_new_for_modulus GRPC_SHADOW_BN_MONT_CTX_new_for_modulus
+#define BN_MONT_CTX_set GRPC_SHADOW_BN_MONT_CTX_set
+#define BN_MONT_CTX_set_locked GRPC_SHADOW_BN_MONT_CTX_set_locked
+#define BN_abs_is_word GRPC_SHADOW_BN_abs_is_word
+#define BN_add GRPC_SHADOW_BN_add
+#define BN_add_word GRPC_SHADOW_BN_add_word
+#define BN_bin2bn GRPC_SHADOW_BN_bin2bn
+#define BN_bn2bin GRPC_SHADOW_BN_bn2bin
+#define BN_bn2bin_padded GRPC_SHADOW_BN_bn2bin_padded
+#define BN_bn2le_padded GRPC_SHADOW_BN_bn2le_padded
+#define BN_clear GRPC_SHADOW_BN_clear
+#define BN_clear_bit GRPC_SHADOW_BN_clear_bit
+#define BN_clear_free GRPC_SHADOW_BN_clear_free
+#define BN_cmp GRPC_SHADOW_BN_cmp
+#define BN_cmp_word GRPC_SHADOW_BN_cmp_word
+#define BN_copy GRPC_SHADOW_BN_copy
+#define BN_count_low_zero_bits GRPC_SHADOW_BN_count_low_zero_bits
+#define BN_div GRPC_SHADOW_BN_div
+#define BN_div_word GRPC_SHADOW_BN_div_word
+#define BN_dup GRPC_SHADOW_BN_dup
+#define BN_enhanced_miller_rabin_primality_test GRPC_SHADOW_BN_enhanced_miller_rabin_primality_test
+#define BN_equal_consttime GRPC_SHADOW_BN_equal_consttime
+#define BN_exp GRPC_SHADOW_BN_exp
+#define BN_free GRPC_SHADOW_BN_free
+#define BN_from_montgomery GRPC_SHADOW_BN_from_montgomery
+#define BN_gcd GRPC_SHADOW_BN_gcd
+#define BN_generate_prime_ex GRPC_SHADOW_BN_generate_prime_ex
+#define BN_get_u64 GRPC_SHADOW_BN_get_u64
+#define BN_get_word GRPC_SHADOW_BN_get_word
+#define BN_init GRPC_SHADOW_BN_init
+#define BN_is_bit_set GRPC_SHADOW_BN_is_bit_set
+#define BN_is_negative GRPC_SHADOW_BN_is_negative
+#define BN_is_odd GRPC_SHADOW_BN_is_odd
+#define BN_is_one GRPC_SHADOW_BN_is_one
+#define BN_is_pow2 GRPC_SHADOW_BN_is_pow2
+#define BN_is_prime_ex GRPC_SHADOW_BN_is_prime_ex
+#define BN_is_prime_fasttest_ex GRPC_SHADOW_BN_is_prime_fasttest_ex
+#define BN_is_word GRPC_SHADOW_BN_is_word
+#define BN_is_zero GRPC_SHADOW_BN_is_zero
+#define BN_le2bn GRPC_SHADOW_BN_le2bn
+#define BN_lshift GRPC_SHADOW_BN_lshift
+#define BN_lshift1 GRPC_SHADOW_BN_lshift1
+#define BN_mask_bits GRPC_SHADOW_BN_mask_bits
+#define BN_mod_add GRPC_SHADOW_BN_mod_add
+#define BN_mod_add_quick GRPC_SHADOW_BN_mod_add_quick
+#define BN_mod_exp GRPC_SHADOW_BN_mod_exp
+#define BN_mod_exp2_mont GRPC_SHADOW_BN_mod_exp2_mont
+#define BN_mod_exp_mont GRPC_SHADOW_BN_mod_exp_mont
+#define BN_mod_exp_mont_consttime GRPC_SHADOW_BN_mod_exp_mont_consttime
+#define BN_mod_exp_mont_word GRPC_SHADOW_BN_mod_exp_mont_word
+#define BN_mod_inverse GRPC_SHADOW_BN_mod_inverse
+#define BN_mod_inverse_blinded GRPC_SHADOW_BN_mod_inverse_blinded
+#define BN_mod_inverse_odd GRPC_SHADOW_BN_mod_inverse_odd
+#define BN_mod_lshift GRPC_SHADOW_BN_mod_lshift
+#define BN_mod_lshift1 GRPC_SHADOW_BN_mod_lshift1
+#define BN_mod_lshift1_quick GRPC_SHADOW_BN_mod_lshift1_quick
+#define BN_mod_lshift_quick GRPC_SHADOW_BN_mod_lshift_quick
+#define BN_mod_mul GRPC_SHADOW_BN_mod_mul
+#define BN_mod_mul_montgomery GRPC_SHADOW_BN_mod_mul_montgomery
+#define BN_mod_pow2 GRPC_SHADOW_BN_mod_pow2
+#define BN_mod_sqr GRPC_SHADOW_BN_mod_sqr
+#define BN_mod_sqrt GRPC_SHADOW_BN_mod_sqrt
+#define BN_mod_sub GRPC_SHADOW_BN_mod_sub
+#define BN_mod_sub_quick GRPC_SHADOW_BN_mod_sub_quick
+#define BN_mod_word GRPC_SHADOW_BN_mod_word
+#define BN_mul GRPC_SHADOW_BN_mul
+#define BN_mul_word GRPC_SHADOW_BN_mul_word
+#define BN_new GRPC_SHADOW_BN_new
+#define BN_nnmod GRPC_SHADOW_BN_nnmod
+#define BN_nnmod_pow2 GRPC_SHADOW_BN_nnmod_pow2
+#define BN_num_bits GRPC_SHADOW_BN_num_bits
+#define BN_num_bits_word GRPC_SHADOW_BN_num_bits_word
+#define BN_num_bytes GRPC_SHADOW_BN_num_bytes
+#define BN_one GRPC_SHADOW_BN_one
+#define BN_primality_test GRPC_SHADOW_BN_primality_test
+#define BN_pseudo_rand GRPC_SHADOW_BN_pseudo_rand
+#define BN_pseudo_rand_range GRPC_SHADOW_BN_pseudo_rand_range
+#define BN_rand GRPC_SHADOW_BN_rand
+#define BN_rand_range GRPC_SHADOW_BN_rand_range
+#define BN_rand_range_ex GRPC_SHADOW_BN_rand_range_ex
+#define BN_rshift GRPC_SHADOW_BN_rshift
+#define BN_rshift1 GRPC_SHADOW_BN_rshift1
+#define BN_set_bit GRPC_SHADOW_BN_set_bit
+#define BN_set_negative GRPC_SHADOW_BN_set_negative
+#define BN_set_u64 GRPC_SHADOW_BN_set_u64
+#define BN_set_word GRPC_SHADOW_BN_set_word
+#define BN_sqr GRPC_SHADOW_BN_sqr
+#define BN_sqrt GRPC_SHADOW_BN_sqrt
+#define BN_sub GRPC_SHADOW_BN_sub
+#define BN_sub_word GRPC_SHADOW_BN_sub_word
+#define BN_to_montgomery GRPC_SHADOW_BN_to_montgomery
+#define BN_uadd GRPC_SHADOW_BN_uadd
+#define BN_ucmp GRPC_SHADOW_BN_ucmp
+#define BN_usub GRPC_SHADOW_BN_usub
+#define BN_value_one GRPC_SHADOW_BN_value_one
+#define BN_zero GRPC_SHADOW_BN_zero
+#define BORINGSSL_self_test GRPC_SHADOW_BORINGSSL_self_test
+#define CRYPTO_POLYVAL_finish GRPC_SHADOW_CRYPTO_POLYVAL_finish
+#define CRYPTO_POLYVAL_init GRPC_SHADOW_CRYPTO_POLYVAL_init
+#define CRYPTO_POLYVAL_update_blocks GRPC_SHADOW_CRYPTO_POLYVAL_update_blocks
+#define CRYPTO_cbc128_decrypt GRPC_SHADOW_CRYPTO_cbc128_decrypt
+#define CRYPTO_cbc128_encrypt GRPC_SHADOW_CRYPTO_cbc128_encrypt
+#define CRYPTO_ccm128_decrypt GRPC_SHADOW_CRYPTO_ccm128_decrypt
+#define CRYPTO_ccm128_encrypt GRPC_SHADOW_CRYPTO_ccm128_encrypt
+#define CRYPTO_ccm128_init GRPC_SHADOW_CRYPTO_ccm128_init
+#define CRYPTO_ccm128_max_input GRPC_SHADOW_CRYPTO_ccm128_max_input
+#define CRYPTO_cfb128_1_encrypt GRPC_SHADOW_CRYPTO_cfb128_1_encrypt
+#define CRYPTO_cfb128_8_encrypt GRPC_SHADOW_CRYPTO_cfb128_8_encrypt
+#define CRYPTO_cfb128_encrypt GRPC_SHADOW_CRYPTO_cfb128_encrypt
+#define CRYPTO_ctr128_encrypt GRPC_SHADOW_CRYPTO_ctr128_encrypt
+#define CRYPTO_ctr128_encrypt_ctr32 GRPC_SHADOW_CRYPTO_ctr128_encrypt_ctr32
+#define CRYPTO_gcm128_aad GRPC_SHADOW_CRYPTO_gcm128_aad
+#define CRYPTO_gcm128_decrypt GRPC_SHADOW_CRYPTO_gcm128_decrypt
+#define CRYPTO_gcm128_decrypt_ctr32 GRPC_SHADOW_CRYPTO_gcm128_decrypt_ctr32
+#define CRYPTO_gcm128_encrypt GRPC_SHADOW_CRYPTO_gcm128_encrypt
+#define CRYPTO_gcm128_encrypt_ctr32 GRPC_SHADOW_CRYPTO_gcm128_encrypt_ctr32
+#define CRYPTO_gcm128_finish GRPC_SHADOW_CRYPTO_gcm128_finish
+#define CRYPTO_gcm128_init GRPC_SHADOW_CRYPTO_gcm128_init
+#define CRYPTO_gcm128_setiv GRPC_SHADOW_CRYPTO_gcm128_setiv
+#define CRYPTO_gcm128_tag GRPC_SHADOW_CRYPTO_gcm128_tag
+#define CRYPTO_ghash_init GRPC_SHADOW_CRYPTO_ghash_init
+#define CRYPTO_ofb128_encrypt GRPC_SHADOW_CRYPTO_ofb128_encrypt
+#define CRYPTO_sysrand GRPC_SHADOW_CRYPTO_sysrand
+#define CRYPTO_tls1_prf GRPC_SHADOW_CRYPTO_tls1_prf
+#define CTR_DRBG_clear GRPC_SHADOW_CTR_DRBG_clear
+#define CTR_DRBG_generate GRPC_SHADOW_CTR_DRBG_generate
+#define CTR_DRBG_init GRPC_SHADOW_CTR_DRBG_init
+#define CTR_DRBG_reseed GRPC_SHADOW_CTR_DRBG_reseed
+#define DES_decrypt3 GRPC_SHADOW_DES_decrypt3
+#define DES_ecb3_encrypt GRPC_SHADOW_DES_ecb3_encrypt
+#define DES_ecb_encrypt GRPC_SHADOW_DES_ecb_encrypt
+#define DES_ede2_cbc_encrypt GRPC_SHADOW_DES_ede2_cbc_encrypt
+#define DES_ede3_cbc_encrypt GRPC_SHADOW_DES_ede3_cbc_encrypt
+#define DES_encrypt3 GRPC_SHADOW_DES_encrypt3
+#define DES_ncbc_encrypt GRPC_SHADOW_DES_ncbc_encrypt
+#define DES_set_key GRPC_SHADOW_DES_set_key
+#define DES_set_key_unchecked GRPC_SHADOW_DES_set_key_unchecked
+#define DES_set_odd_parity GRPC_SHADOW_DES_set_odd_parity
+#define ECDSA_SIG_free GRPC_SHADOW_ECDSA_SIG_free
+#define ECDSA_SIG_get0 GRPC_SHADOW_ECDSA_SIG_get0
+#define ECDSA_SIG_new GRPC_SHADOW_ECDSA_SIG_new
+#define ECDSA_SIG_set0 GRPC_SHADOW_ECDSA_SIG_set0
+#define ECDSA_do_sign GRPC_SHADOW_ECDSA_do_sign
+#define ECDSA_do_verify GRPC_SHADOW_ECDSA_do_verify
+#define EC_GFp_mont_method GRPC_SHADOW_EC_GFp_mont_method
+#define EC_GFp_nistp224_method GRPC_SHADOW_EC_GFp_nistp224_method
+#define EC_GFp_nistp256_method GRPC_SHADOW_EC_GFp_nistp256_method
+#define EC_GFp_nistz256_method GRPC_SHADOW_EC_GFp_nistz256_method
+#define EC_GROUP_cmp GRPC_SHADOW_EC_GROUP_cmp
+#define EC_GROUP_dup GRPC_SHADOW_EC_GROUP_dup
+#define EC_GROUP_free GRPC_SHADOW_EC_GROUP_free
+#define EC_GROUP_get0_generator GRPC_SHADOW_EC_GROUP_get0_generator
+#define EC_GROUP_get0_order GRPC_SHADOW_EC_GROUP_get0_order
+#define EC_GROUP_get_cofactor GRPC_SHADOW_EC_GROUP_get_cofactor
+#define EC_GROUP_get_curve_GFp GRPC_SHADOW_EC_GROUP_get_curve_GFp
+#define EC_GROUP_get_curve_name GRPC_SHADOW_EC_GROUP_get_curve_name
+#define EC_GROUP_get_degree GRPC_SHADOW_EC_GROUP_get_degree
+#define EC_GROUP_get_order GRPC_SHADOW_EC_GROUP_get_order
+#define EC_GROUP_method_of GRPC_SHADOW_EC_GROUP_method_of
+#define EC_GROUP_new_by_curve_name GRPC_SHADOW_EC_GROUP_new_by_curve_name
+#define EC_GROUP_new_curve_GFp GRPC_SHADOW_EC_GROUP_new_curve_GFp
+#define EC_GROUP_set_asn1_flag GRPC_SHADOW_EC_GROUP_set_asn1_flag
+#define EC_GROUP_set_generator GRPC_SHADOW_EC_GROUP_set_generator
+#define EC_GROUP_set_point_conversion_form GRPC_SHADOW_EC_GROUP_set_point_conversion_form
+#define EC_KEY_check_fips GRPC_SHADOW_EC_KEY_check_fips
+#define EC_KEY_check_key GRPC_SHADOW_EC_KEY_check_key
+#define EC_KEY_dup GRPC_SHADOW_EC_KEY_dup
+#define EC_KEY_free GRPC_SHADOW_EC_KEY_free
+#define EC_KEY_generate_key GRPC_SHADOW_EC_KEY_generate_key
+#define EC_KEY_generate_key_fips GRPC_SHADOW_EC_KEY_generate_key_fips
+#define EC_KEY_get0_group GRPC_SHADOW_EC_KEY_get0_group
+#define EC_KEY_get0_private_key GRPC_SHADOW_EC_KEY_get0_private_key
+#define EC_KEY_get0_public_key GRPC_SHADOW_EC_KEY_get0_public_key
+#define EC_KEY_get_conv_form GRPC_SHADOW_EC_KEY_get_conv_form
+#define EC_KEY_get_enc_flags GRPC_SHADOW_EC_KEY_get_enc_flags
+#define EC_KEY_get_ex_data GRPC_SHADOW_EC_KEY_get_ex_data
+#define EC_KEY_get_ex_new_index GRPC_SHADOW_EC_KEY_get_ex_new_index
+#define EC_KEY_is_opaque GRPC_SHADOW_EC_KEY_is_opaque
+#define EC_KEY_new GRPC_SHADOW_EC_KEY_new
+#define EC_KEY_new_by_curve_name GRPC_SHADOW_EC_KEY_new_by_curve_name
+#define EC_KEY_new_method GRPC_SHADOW_EC_KEY_new_method
+#define EC_KEY_set_asn1_flag GRPC_SHADOW_EC_KEY_set_asn1_flag
+#define EC_KEY_set_conv_form GRPC_SHADOW_EC_KEY_set_conv_form
+#define EC_KEY_set_enc_flags GRPC_SHADOW_EC_KEY_set_enc_flags
+#define EC_KEY_set_ex_data GRPC_SHADOW_EC_KEY_set_ex_data
+#define EC_KEY_set_group GRPC_SHADOW_EC_KEY_set_group
+#define EC_KEY_set_private_key GRPC_SHADOW_EC_KEY_set_private_key
+#define EC_KEY_set_public_key GRPC_SHADOW_EC_KEY_set_public_key
+#define EC_KEY_set_public_key_affine_coordinates GRPC_SHADOW_EC_KEY_set_public_key_affine_coordinates
+#define EC_KEY_up_ref GRPC_SHADOW_EC_KEY_up_ref
+#define EC_METHOD_get_field_type GRPC_SHADOW_EC_METHOD_get_field_type
+#define EC_POINT_add GRPC_SHADOW_EC_POINT_add
+#define EC_POINT_clear_free GRPC_SHADOW_EC_POINT_clear_free
+#define EC_POINT_cmp GRPC_SHADOW_EC_POINT_cmp
+#define EC_POINT_copy GRPC_SHADOW_EC_POINT_copy
+#define EC_POINT_dbl GRPC_SHADOW_EC_POINT_dbl
+#define EC_POINT_dup GRPC_SHADOW_EC_POINT_dup
+#define EC_POINT_free GRPC_SHADOW_EC_POINT_free
+#define EC_POINT_get_affine_coordinates_GFp GRPC_SHADOW_EC_POINT_get_affine_coordinates_GFp
+#define EC_POINT_invert GRPC_SHADOW_EC_POINT_invert
+#define EC_POINT_is_at_infinity GRPC_SHADOW_EC_POINT_is_at_infinity
+#define EC_POINT_is_on_curve GRPC_SHADOW_EC_POINT_is_on_curve
+#define EC_POINT_make_affine GRPC_SHADOW_EC_POINT_make_affine
+#define EC_POINT_mul GRPC_SHADOW_EC_POINT_mul
+#define EC_POINT_new GRPC_SHADOW_EC_POINT_new
+#define EC_POINT_oct2point GRPC_SHADOW_EC_POINT_oct2point
+#define EC_POINT_point2oct GRPC_SHADOW_EC_POINT_point2oct
+#define EC_POINT_set_affine_coordinates_GFp GRPC_SHADOW_EC_POINT_set_affine_coordinates_GFp
+#define EC_POINT_set_compressed_coordinates_GFp GRPC_SHADOW_EC_POINT_set_compressed_coordinates_GFp
+#define EC_POINT_set_to_infinity GRPC_SHADOW_EC_POINT_set_to_infinity
+#define EC_POINTs_make_affine GRPC_SHADOW_EC_POINTs_make_affine
+#define EC_get_builtin_curves GRPC_SHADOW_EC_get_builtin_curves
+#define EVP_AEAD_CTX_aead GRPC_SHADOW_EVP_AEAD_CTX_aead
+#define EVP_AEAD_CTX_cleanup GRPC_SHADOW_EVP_AEAD_CTX_cleanup
+#define EVP_AEAD_CTX_free GRPC_SHADOW_EVP_AEAD_CTX_free
+#define EVP_AEAD_CTX_get_iv GRPC_SHADOW_EVP_AEAD_CTX_get_iv
+#define EVP_AEAD_CTX_init GRPC_SHADOW_EVP_AEAD_CTX_init
+#define EVP_AEAD_CTX_init_with_direction GRPC_SHADOW_EVP_AEAD_CTX_init_with_direction
+#define EVP_AEAD_CTX_new GRPC_SHADOW_EVP_AEAD_CTX_new
+#define EVP_AEAD_CTX_open GRPC_SHADOW_EVP_AEAD_CTX_open
+#define EVP_AEAD_CTX_open_gather GRPC_SHADOW_EVP_AEAD_CTX_open_gather
+#define EVP_AEAD_CTX_seal GRPC_SHADOW_EVP_AEAD_CTX_seal
+#define EVP_AEAD_CTX_seal_scatter GRPC_SHADOW_EVP_AEAD_CTX_seal_scatter
+#define EVP_AEAD_CTX_tag_len GRPC_SHADOW_EVP_AEAD_CTX_tag_len
+#define EVP_AEAD_CTX_zero GRPC_SHADOW_EVP_AEAD_CTX_zero
+#define EVP_AEAD_key_length GRPC_SHADOW_EVP_AEAD_key_length
+#define EVP_AEAD_max_overhead GRPC_SHADOW_EVP_AEAD_max_overhead
+#define EVP_AEAD_max_tag_len GRPC_SHADOW_EVP_AEAD_max_tag_len
+#define EVP_AEAD_nonce_length GRPC_SHADOW_EVP_AEAD_nonce_length
+#define EVP_CIPHER_CTX_block_size GRPC_SHADOW_EVP_CIPHER_CTX_block_size
+#define EVP_CIPHER_CTX_cipher GRPC_SHADOW_EVP_CIPHER_CTX_cipher
+#define EVP_CIPHER_CTX_cleanup GRPC_SHADOW_EVP_CIPHER_CTX_cleanup
+#define EVP_CIPHER_CTX_copy GRPC_SHADOW_EVP_CIPHER_CTX_copy
+#define EVP_CIPHER_CTX_ctrl GRPC_SHADOW_EVP_CIPHER_CTX_ctrl
+#define EVP_CIPHER_CTX_flags GRPC_SHADOW_EVP_CIPHER_CTX_flags
+#define EVP_CIPHER_CTX_free GRPC_SHADOW_EVP_CIPHER_CTX_free
+#define EVP_CIPHER_CTX_get_app_data GRPC_SHADOW_EVP_CIPHER_CTX_get_app_data
+#define EVP_CIPHER_CTX_init GRPC_SHADOW_EVP_CIPHER_CTX_init
+#define EVP_CIPHER_CTX_iv_length GRPC_SHADOW_EVP_CIPHER_CTX_iv_length
+#define EVP_CIPHER_CTX_key_length GRPC_SHADOW_EVP_CIPHER_CTX_key_length
+#define EVP_CIPHER_CTX_mode GRPC_SHADOW_EVP_CIPHER_CTX_mode
+#define EVP_CIPHER_CTX_new GRPC_SHADOW_EVP_CIPHER_CTX_new
+#define EVP_CIPHER_CTX_nid GRPC_SHADOW_EVP_CIPHER_CTX_nid
+#define EVP_CIPHER_CTX_reset GRPC_SHADOW_EVP_CIPHER_CTX_reset
+#define EVP_CIPHER_CTX_set_app_data GRPC_SHADOW_EVP_CIPHER_CTX_set_app_data
+#define EVP_CIPHER_CTX_set_flags GRPC_SHADOW_EVP_CIPHER_CTX_set_flags
+#define EVP_CIPHER_CTX_set_key_length GRPC_SHADOW_EVP_CIPHER_CTX_set_key_length
+#define EVP_CIPHER_CTX_set_padding GRPC_SHADOW_EVP_CIPHER_CTX_set_padding
+#define EVP_CIPHER_block_size GRPC_SHADOW_EVP_CIPHER_block_size
+#define EVP_CIPHER_flags GRPC_SHADOW_EVP_CIPHER_flags
+#define EVP_CIPHER_iv_length GRPC_SHADOW_EVP_CIPHER_iv_length
+#define EVP_CIPHER_key_length GRPC_SHADOW_EVP_CIPHER_key_length
+#define EVP_CIPHER_mode GRPC_SHADOW_EVP_CIPHER_mode
+#define EVP_CIPHER_nid GRPC_SHADOW_EVP_CIPHER_nid
+#define EVP_Cipher GRPC_SHADOW_EVP_Cipher
+#define EVP_CipherFinal_ex GRPC_SHADOW_EVP_CipherFinal_ex
+#define EVP_CipherInit GRPC_SHADOW_EVP_CipherInit
+#define EVP_CipherInit_ex GRPC_SHADOW_EVP_CipherInit_ex
+#define EVP_CipherUpdate GRPC_SHADOW_EVP_CipherUpdate
+#define EVP_DecryptFinal_ex GRPC_SHADOW_EVP_DecryptFinal_ex
+#define EVP_DecryptInit GRPC_SHADOW_EVP_DecryptInit
+#define EVP_DecryptInit_ex GRPC_SHADOW_EVP_DecryptInit_ex
+#define EVP_DecryptUpdate GRPC_SHADOW_EVP_DecryptUpdate
+#define EVP_Digest GRPC_SHADOW_EVP_Digest
+#define EVP_DigestFinal GRPC_SHADOW_EVP_DigestFinal
+#define EVP_DigestFinal_ex GRPC_SHADOW_EVP_DigestFinal_ex
+#define EVP_DigestInit GRPC_SHADOW_EVP_DigestInit
+#define EVP_DigestInit_ex GRPC_SHADOW_EVP_DigestInit_ex
+#define EVP_DigestUpdate GRPC_SHADOW_EVP_DigestUpdate
+#define EVP_EncryptFinal_ex GRPC_SHADOW_EVP_EncryptFinal_ex
+#define EVP_EncryptInit GRPC_SHADOW_EVP_EncryptInit
+#define EVP_EncryptInit_ex GRPC_SHADOW_EVP_EncryptInit_ex
+#define EVP_EncryptUpdate GRPC_SHADOW_EVP_EncryptUpdate
+#define EVP_MD_CTX_block_size GRPC_SHADOW_EVP_MD_CTX_block_size
+#define EVP_MD_CTX_cleanup GRPC_SHADOW_EVP_MD_CTX_cleanup
+#define EVP_MD_CTX_copy GRPC_SHADOW_EVP_MD_CTX_copy
+#define EVP_MD_CTX_copy_ex GRPC_SHADOW_EVP_MD_CTX_copy_ex
+#define EVP_MD_CTX_create GRPC_SHADOW_EVP_MD_CTX_create
+#define EVP_MD_CTX_destroy GRPC_SHADOW_EVP_MD_CTX_destroy
+#define EVP_MD_CTX_free GRPC_SHADOW_EVP_MD_CTX_free
+#define EVP_MD_CTX_init GRPC_SHADOW_EVP_MD_CTX_init
+#define EVP_MD_CTX_md GRPC_SHADOW_EVP_MD_CTX_md
+#define EVP_MD_CTX_new GRPC_SHADOW_EVP_MD_CTX_new
+#define EVP_MD_CTX_reset GRPC_SHADOW_EVP_MD_CTX_reset
+#define EVP_MD_CTX_size GRPC_SHADOW_EVP_MD_CTX_size
+#define EVP_MD_CTX_type GRPC_SHADOW_EVP_MD_CTX_type
+#define EVP_MD_block_size GRPC_SHADOW_EVP_MD_block_size
+#define EVP_MD_flags GRPC_SHADOW_EVP_MD_flags
+#define EVP_MD_size GRPC_SHADOW_EVP_MD_size
+#define EVP_MD_type GRPC_SHADOW_EVP_MD_type
+#define EVP_add_cipher_alias GRPC_SHADOW_EVP_add_cipher_alias
+#define EVP_add_digest GRPC_SHADOW_EVP_add_digest
+#define EVP_aead_aes_128_gcm GRPC_SHADOW_EVP_aead_aes_128_gcm
+#define EVP_aead_aes_128_gcm_tls12 GRPC_SHADOW_EVP_aead_aes_128_gcm_tls12
+#define EVP_aead_aes_256_gcm GRPC_SHADOW_EVP_aead_aes_256_gcm
+#define EVP_aead_aes_256_gcm_tls12 GRPC_SHADOW_EVP_aead_aes_256_gcm_tls12
+#define EVP_aes_128_cbc GRPC_SHADOW_EVP_aes_128_cbc
+#define EVP_aes_128_ctr GRPC_SHADOW_EVP_aes_128_ctr
+#define EVP_aes_128_ecb GRPC_SHADOW_EVP_aes_128_ecb
+#define EVP_aes_128_gcm GRPC_SHADOW_EVP_aes_128_gcm
+#define EVP_aes_128_ofb GRPC_SHADOW_EVP_aes_128_ofb
+#define EVP_aes_192_cbc GRPC_SHADOW_EVP_aes_192_cbc
+#define EVP_aes_192_ctr GRPC_SHADOW_EVP_aes_192_ctr
+#define EVP_aes_192_ecb GRPC_SHADOW_EVP_aes_192_ecb
+#define EVP_aes_192_gcm GRPC_SHADOW_EVP_aes_192_gcm
+#define EVP_aes_256_cbc GRPC_SHADOW_EVP_aes_256_cbc
+#define EVP_aes_256_ctr GRPC_SHADOW_EVP_aes_256_ctr
+#define EVP_aes_256_ecb GRPC_SHADOW_EVP_aes_256_ecb
+#define EVP_aes_256_gcm GRPC_SHADOW_EVP_aes_256_gcm
+#define EVP_aes_256_ofb GRPC_SHADOW_EVP_aes_256_ofb
+#define EVP_des_cbc GRPC_SHADOW_EVP_des_cbc
+#define EVP_des_ecb GRPC_SHADOW_EVP_des_ecb
+#define EVP_des_ede GRPC_SHADOW_EVP_des_ede
+#define EVP_des_ede3 GRPC_SHADOW_EVP_des_ede3
+#define EVP_des_ede3_cbc GRPC_SHADOW_EVP_des_ede3_cbc
+#define EVP_des_ede_cbc GRPC_SHADOW_EVP_des_ede_cbc
+#define EVP_has_aes_hardware GRPC_SHADOW_EVP_has_aes_hardware
+#define EVP_md4 GRPC_SHADOW_EVP_md4
+#define EVP_md5 GRPC_SHADOW_EVP_md5
+#define EVP_md5_sha1 GRPC_SHADOW_EVP_md5_sha1
+#define EVP_sha1 GRPC_SHADOW_EVP_sha1
+#define EVP_sha224 GRPC_SHADOW_EVP_sha224
+#define EVP_sha256 GRPC_SHADOW_EVP_sha256
+#define EVP_sha384 GRPC_SHADOW_EVP_sha384
+#define EVP_sha512 GRPC_SHADOW_EVP_sha512
+#define HMAC GRPC_SHADOW_HMAC
+#define HMAC_CTX_cleanup GRPC_SHADOW_HMAC_CTX_cleanup
+#define HMAC_CTX_copy GRPC_SHADOW_HMAC_CTX_copy
+#define HMAC_CTX_copy_ex GRPC_SHADOW_HMAC_CTX_copy_ex
+#define HMAC_CTX_free GRPC_SHADOW_HMAC_CTX_free
+#define HMAC_CTX_init GRPC_SHADOW_HMAC_CTX_init
+#define HMAC_CTX_new GRPC_SHADOW_HMAC_CTX_new
+#define HMAC_CTX_reset GRPC_SHADOW_HMAC_CTX_reset
+#define HMAC_Final GRPC_SHADOW_HMAC_Final
+#define HMAC_Init GRPC_SHADOW_HMAC_Init
+#define HMAC_Init_ex GRPC_SHADOW_HMAC_Init_ex
+#define HMAC_Update GRPC_SHADOW_HMAC_Update
+#define HMAC_size GRPC_SHADOW_HMAC_size
+#define MD4 GRPC_SHADOW_MD4
+#define MD4_Final GRPC_SHADOW_MD4_Final
+#define MD4_Init GRPC_SHADOW_MD4_Init
+#define MD4_Transform GRPC_SHADOW_MD4_Transform
+#define MD4_Update GRPC_SHADOW_MD4_Update
+#define MD5 GRPC_SHADOW_MD5
+#define MD5_Final GRPC_SHADOW_MD5_Final
+#define MD5_Init GRPC_SHADOW_MD5_Init
+#define MD5_Transform GRPC_SHADOW_MD5_Transform
+#define MD5_Update GRPC_SHADOW_MD5_Update
+#define OPENSSL_built_in_curves GRPC_SHADOW_OPENSSL_built_in_curves
+#define RAND_bytes GRPC_SHADOW_RAND_bytes
+#define RAND_bytes_with_additional_data GRPC_SHADOW_RAND_bytes_with_additional_data
+#define RAND_pseudo_bytes GRPC_SHADOW_RAND_pseudo_bytes
+#define RAND_set_urandom_fd GRPC_SHADOW_RAND_set_urandom_fd
+#define RSAZ_1024_mod_exp_avx2 GRPC_SHADOW_RSAZ_1024_mod_exp_avx2
+#define RSA_add_pkcs1_prefix GRPC_SHADOW_RSA_add_pkcs1_prefix
+#define RSA_bits GRPC_SHADOW_RSA_bits
+#define RSA_blinding_on GRPC_SHADOW_RSA_blinding_on
+#define RSA_check_fips GRPC_SHADOW_RSA_check_fips
+#define RSA_check_key GRPC_SHADOW_RSA_check_key
+#define RSA_decrypt GRPC_SHADOW_RSA_decrypt
+#define RSA_default_method GRPC_SHADOW_RSA_default_method
+#define RSA_encrypt GRPC_SHADOW_RSA_encrypt
+#define RSA_flags GRPC_SHADOW_RSA_flags
+#define RSA_free GRPC_SHADOW_RSA_free
+#define RSA_generate_key_ex GRPC_SHADOW_RSA_generate_key_ex
+#define RSA_generate_key_fips GRPC_SHADOW_RSA_generate_key_fips
+#define RSA_get0_crt_params GRPC_SHADOW_RSA_get0_crt_params
+#define RSA_get0_factors GRPC_SHADOW_RSA_get0_factors
+#define RSA_get0_key GRPC_SHADOW_RSA_get0_key
+#define RSA_get_ex_data GRPC_SHADOW_RSA_get_ex_data
+#define RSA_get_ex_new_index GRPC_SHADOW_RSA_get_ex_new_index
+#define RSA_is_opaque GRPC_SHADOW_RSA_is_opaque
+#define RSA_new GRPC_SHADOW_RSA_new
+#define RSA_new_method GRPC_SHADOW_RSA_new_method
+#define RSA_padding_add_PKCS1_OAEP_mgf1 GRPC_SHADOW_RSA_padding_add_PKCS1_OAEP_mgf1
+#define RSA_padding_add_PKCS1_PSS_mgf1 GRPC_SHADOW_RSA_padding_add_PKCS1_PSS_mgf1
+#define RSA_padding_add_PKCS1_type_1 GRPC_SHADOW_RSA_padding_add_PKCS1_type_1
+#define RSA_padding_add_PKCS1_type_2 GRPC_SHADOW_RSA_padding_add_PKCS1_type_2
+#define RSA_padding_add_none GRPC_SHADOW_RSA_padding_add_none
+#define RSA_padding_check_PKCS1_OAEP_mgf1 GRPC_SHADOW_RSA_padding_check_PKCS1_OAEP_mgf1
+#define RSA_padding_check_PKCS1_type_1 GRPC_SHADOW_RSA_padding_check_PKCS1_type_1
+#define RSA_padding_check_PKCS1_type_2 GRPC_SHADOW_RSA_padding_check_PKCS1_type_2
+#define RSA_private_decrypt GRPC_SHADOW_RSA_private_decrypt
+#define RSA_private_encrypt GRPC_SHADOW_RSA_private_encrypt
+#define RSA_private_transform GRPC_SHADOW_RSA_private_transform
+#define RSA_public_decrypt GRPC_SHADOW_RSA_public_decrypt
+#define RSA_public_encrypt GRPC_SHADOW_RSA_public_encrypt
+#define RSA_set0_crt_params GRPC_SHADOW_RSA_set0_crt_params
+#define RSA_set0_factors GRPC_SHADOW_RSA_set0_factors
+#define RSA_set0_key GRPC_SHADOW_RSA_set0_key
+#define RSA_set_ex_data GRPC_SHADOW_RSA_set_ex_data
+#define RSA_sign GRPC_SHADOW_RSA_sign
+#define RSA_sign_pss_mgf1 GRPC_SHADOW_RSA_sign_pss_mgf1
+#define RSA_sign_raw GRPC_SHADOW_RSA_sign_raw
+#define RSA_size GRPC_SHADOW_RSA_size
+#define RSA_up_ref GRPC_SHADOW_RSA_up_ref
+#define RSA_verify GRPC_SHADOW_RSA_verify
+#define RSA_verify_PKCS1_PSS_mgf1 GRPC_SHADOW_RSA_verify_PKCS1_PSS_mgf1
+#define RSA_verify_pss_mgf1 GRPC_SHADOW_RSA_verify_pss_mgf1
+#define RSA_verify_raw GRPC_SHADOW_RSA_verify_raw
+#define SHA1 GRPC_SHADOW_SHA1
+#define SHA1_Final GRPC_SHADOW_SHA1_Final
+#define SHA1_Init GRPC_SHADOW_SHA1_Init
+#define SHA1_Transform GRPC_SHADOW_SHA1_Transform
+#define SHA1_Update GRPC_SHADOW_SHA1_Update
+#define SHA224 GRPC_SHADOW_SHA224
+#define SHA224_Final GRPC_SHADOW_SHA224_Final
+#define SHA224_Init GRPC_SHADOW_SHA224_Init
+#define SHA224_Update GRPC_SHADOW_SHA224_Update
+#define SHA256 GRPC_SHADOW_SHA256
+#define SHA256_Final GRPC_SHADOW_SHA256_Final
+#define SHA256_Init GRPC_SHADOW_SHA256_Init
+#define SHA256_Transform GRPC_SHADOW_SHA256_Transform
+#define SHA256_Update GRPC_SHADOW_SHA256_Update
+#define SHA384 GRPC_SHADOW_SHA384
+#define SHA384_Final GRPC_SHADOW_SHA384_Final
+#define SHA384_Init GRPC_SHADOW_SHA384_Init
+#define SHA384_Update GRPC_SHADOW_SHA384_Update
+#define SHA512 GRPC_SHADOW_SHA512
+#define SHA512_Final GRPC_SHADOW_SHA512_Final
+#define SHA512_Init GRPC_SHADOW_SHA512_Init
+#define SHA512_Transform GRPC_SHADOW_SHA512_Transform
+#define SHA512_Update GRPC_SHADOW_SHA512_Update
+#define aes_ctr_set_key GRPC_SHADOW_aes_ctr_set_key
+#define bn_abs_sub_consttime GRPC_SHADOW_bn_abs_sub_consttime
+#define bn_add_words GRPC_SHADOW_bn_add_words
+#define bn_copy_words GRPC_SHADOW_bn_copy_words
+#define bn_div_consttime GRPC_SHADOW_bn_div_consttime
+#define bn_expand GRPC_SHADOW_bn_expand
+#define bn_fits_in_words GRPC_SHADOW_bn_fits_in_words
+#define bn_from_montgomery_small GRPC_SHADOW_bn_from_montgomery_small
+#define bn_in_range_words GRPC_SHADOW_bn_in_range_words
+#define bn_is_bit_set_words GRPC_SHADOW_bn_is_bit_set_words
+#define bn_is_relatively_prime GRPC_SHADOW_bn_is_relatively_prime
+#define bn_jacobi GRPC_SHADOW_bn_jacobi
+#define bn_lcm_consttime GRPC_SHADOW_bn_lcm_consttime
+#define bn_less_than_montgomery_R GRPC_SHADOW_bn_less_than_montgomery_R
+#define bn_less_than_words GRPC_SHADOW_bn_less_than_words
+#define bn_minimal_width GRPC_SHADOW_bn_minimal_width
+#define bn_mod_add_consttime GRPC_SHADOW_bn_mod_add_consttime
+#define bn_mod_exp_base_2_consttime GRPC_SHADOW_bn_mod_exp_base_2_consttime
+#define bn_mod_exp_mont_small GRPC_SHADOW_bn_mod_exp_mont_small
+#define bn_mod_inverse_consttime GRPC_SHADOW_bn_mod_inverse_consttime
+#define bn_mod_inverse_prime GRPC_SHADOW_bn_mod_inverse_prime
+#define bn_mod_inverse_prime_mont_small GRPC_SHADOW_bn_mod_inverse_prime_mont_small
+#define bn_mod_inverse_secret_prime GRPC_SHADOW_bn_mod_inverse_secret_prime
+#define bn_mod_lshift1_consttime GRPC_SHADOW_bn_mod_lshift1_consttime
+#define bn_mod_lshift_consttime GRPC_SHADOW_bn_mod_lshift_consttime
+#define bn_mod_mul_montgomery_small GRPC_SHADOW_bn_mod_mul_montgomery_small
+#define bn_mod_sub_consttime GRPC_SHADOW_bn_mod_sub_consttime
+#define bn_mod_u16_consttime GRPC_SHADOW_bn_mod_u16_consttime
+#define bn_mont_n0 GRPC_SHADOW_bn_mont_n0
+#define bn_mul_add_words GRPC_SHADOW_bn_mul_add_words
+#define bn_mul_comba4 GRPC_SHADOW_bn_mul_comba4
+#define bn_mul_comba8 GRPC_SHADOW_bn_mul_comba8
+#define bn_mul_consttime GRPC_SHADOW_bn_mul_consttime
+#define bn_mul_small GRPC_SHADOW_bn_mul_small
+#define bn_mul_words GRPC_SHADOW_bn_mul_words
+#define bn_odd_number_is_obviously_composite GRPC_SHADOW_bn_odd_number_is_obviously_composite
+#define bn_one_to_montgomery GRPC_SHADOW_bn_one_to_montgomery
+#define bn_one_to_montgomery_small GRPC_SHADOW_bn_one_to_montgomery_small
+#define bn_rand_range_words GRPC_SHADOW_bn_rand_range_words
+#define bn_rand_secret_range GRPC_SHADOW_bn_rand_secret_range
+#define bn_resize_words GRPC_SHADOW_bn_resize_words
+#define bn_rshift1_words GRPC_SHADOW_bn_rshift1_words
+#define bn_rshift_secret_shift GRPC_SHADOW_bn_rshift_secret_shift
+#define bn_select_words GRPC_SHADOW_bn_select_words
+#define bn_set_minimal_width GRPC_SHADOW_bn_set_minimal_width
+#define bn_set_words GRPC_SHADOW_bn_set_words
+#define bn_sqr_comba4 GRPC_SHADOW_bn_sqr_comba4
+#define bn_sqr_comba8 GRPC_SHADOW_bn_sqr_comba8
+#define bn_sqr_consttime GRPC_SHADOW_bn_sqr_consttime
+#define bn_sqr_small GRPC_SHADOW_bn_sqr_small
+#define bn_sqr_words GRPC_SHADOW_bn_sqr_words
+#define bn_sub_words GRPC_SHADOW_bn_sub_words
+#define bn_to_montgomery_small GRPC_SHADOW_bn_to_montgomery_small
+#define bn_uadd_consttime GRPC_SHADOW_bn_uadd_consttime
+#define bn_usub_consttime GRPC_SHADOW_bn_usub_consttime
+#define bn_wexpand GRPC_SHADOW_bn_wexpand
+#define crypto_gcm_clmul_enabled GRPC_SHADOW_crypto_gcm_clmul_enabled
+#define ec_GFp_mont_field_decode GRPC_SHADOW_ec_GFp_mont_field_decode
+#define ec_GFp_mont_field_encode GRPC_SHADOW_ec_GFp_mont_field_encode
+#define ec_GFp_mont_field_mul GRPC_SHADOW_ec_GFp_mont_field_mul
+#define ec_GFp_mont_field_sqr GRPC_SHADOW_ec_GFp_mont_field_sqr
+#define ec_GFp_mont_group_finish GRPC_SHADOW_ec_GFp_mont_group_finish
+#define ec_GFp_mont_group_init GRPC_SHADOW_ec_GFp_mont_group_init
+#define ec_GFp_mont_group_set_curve GRPC_SHADOW_ec_GFp_mont_group_set_curve
+#define ec_GFp_nistp_recode_scalar_bits GRPC_SHADOW_ec_GFp_nistp_recode_scalar_bits
+#define ec_GFp_simple_add GRPC_SHADOW_ec_GFp_simple_add
+#define ec_GFp_simple_cmp GRPC_SHADOW_ec_GFp_simple_cmp
+#define ec_GFp_simple_dbl GRPC_SHADOW_ec_GFp_simple_dbl
+#define ec_GFp_simple_field_mul GRPC_SHADOW_ec_GFp_simple_field_mul
+#define ec_GFp_simple_field_sqr GRPC_SHADOW_ec_GFp_simple_field_sqr
+#define ec_GFp_simple_group_finish GRPC_SHADOW_ec_GFp_simple_group_finish
+#define ec_GFp_simple_group_get_curve GRPC_SHADOW_ec_GFp_simple_group_get_curve
+#define ec_GFp_simple_group_get_degree GRPC_SHADOW_ec_GFp_simple_group_get_degree
+#define ec_GFp_simple_group_init GRPC_SHADOW_ec_GFp_simple_group_init
+#define ec_GFp_simple_group_set_curve GRPC_SHADOW_ec_GFp_simple_group_set_curve
+#define ec_GFp_simple_invert GRPC_SHADOW_ec_GFp_simple_invert
+#define ec_GFp_simple_is_at_infinity GRPC_SHADOW_ec_GFp_simple_is_at_infinity
+#define ec_GFp_simple_is_on_curve GRPC_SHADOW_ec_GFp_simple_is_on_curve
+#define ec_GFp_simple_make_affine GRPC_SHADOW_ec_GFp_simple_make_affine
+#define ec_GFp_simple_point_copy GRPC_SHADOW_ec_GFp_simple_point_copy
+#define ec_GFp_simple_point_finish GRPC_SHADOW_ec_GFp_simple_point_finish
+#define ec_GFp_simple_point_init GRPC_SHADOW_ec_GFp_simple_point_init
+#define ec_GFp_simple_point_set_affine_coordinates GRPC_SHADOW_ec_GFp_simple_point_set_affine_coordinates
+#define ec_GFp_simple_point_set_to_infinity GRPC_SHADOW_ec_GFp_simple_point_set_to_infinity
+#define ec_GFp_simple_points_make_affine GRPC_SHADOW_ec_GFp_simple_points_make_affine
+#define ec_bignum_to_scalar GRPC_SHADOW_ec_bignum_to_scalar
+#define ec_bignum_to_scalar_unchecked GRPC_SHADOW_ec_bignum_to_scalar_unchecked
+#define ec_compute_wNAF GRPC_SHADOW_ec_compute_wNAF
+#define ec_group_new GRPC_SHADOW_ec_group_new
+#define ec_point_mul_scalar GRPC_SHADOW_ec_point_mul_scalar
+#define ec_point_mul_scalar_public GRPC_SHADOW_ec_point_mul_scalar_public
+#define ec_random_nonzero_scalar GRPC_SHADOW_ec_random_nonzero_scalar
+#define ec_wNAF_mul GRPC_SHADOW_ec_wNAF_mul
+#define kBoringSSLRSASqrtTwo GRPC_SHADOW_kBoringSSLRSASqrtTwo
+#define kBoringSSLRSASqrtTwoLen GRPC_SHADOW_kBoringSSLRSASqrtTwoLen
+#define md4_block_data_order GRPC_SHADOW_md4_block_data_order
+#define rsa_default_decrypt GRPC_SHADOW_rsa_default_decrypt
+#define rsa_default_private_transform GRPC_SHADOW_rsa_default_private_transform
+#define rsa_default_sign_raw GRPC_SHADOW_rsa_default_sign_raw
+#define rsa_default_size GRPC_SHADOW_rsa_default_size
+#define FIPS_mode GRPC_SHADOW_FIPS_mode
+#define aesni_gcm_decrypt GRPC_SHADOW_aesni_gcm_decrypt
+#define aesni_gcm_encrypt GRPC_SHADOW_aesni_gcm_encrypt
+#define aesni_cbc_encrypt GRPC_SHADOW_aesni_cbc_encrypt
+#define aesni_ccm64_decrypt_blocks GRPC_SHADOW_aesni_ccm64_decrypt_blocks
+#define aesni_ccm64_encrypt_blocks GRPC_SHADOW_aesni_ccm64_encrypt_blocks
+#define aesni_ctr32_encrypt_blocks GRPC_SHADOW_aesni_ctr32_encrypt_blocks
+#define aesni_decrypt GRPC_SHADOW_aesni_decrypt
+#define aesni_ecb_encrypt GRPC_SHADOW_aesni_ecb_encrypt
+#define aesni_encrypt GRPC_SHADOW_aesni_encrypt
+#define aesni_ocb_decrypt GRPC_SHADOW_aesni_ocb_decrypt
+#define aesni_ocb_encrypt GRPC_SHADOW_aesni_ocb_encrypt
+#define aesni_set_decrypt_key GRPC_SHADOW_aesni_set_decrypt_key
+#define aesni_set_encrypt_key GRPC_SHADOW_aesni_set_encrypt_key
+#define aesni_xts_decrypt GRPC_SHADOW_aesni_xts_decrypt
+#define aesni_xts_encrypt GRPC_SHADOW_aesni_xts_encrypt
+#define asm_AES_cbc_encrypt GRPC_SHADOW_asm_AES_cbc_encrypt
+#define asm_AES_decrypt GRPC_SHADOW_asm_AES_decrypt
+#define asm_AES_encrypt GRPC_SHADOW_asm_AES_encrypt
+#define asm_AES_set_decrypt_key GRPC_SHADOW_asm_AES_set_decrypt_key
+#define asm_AES_set_encrypt_key GRPC_SHADOW_asm_AES_set_encrypt_key
+#define bsaes_cbc_encrypt GRPC_SHADOW_bsaes_cbc_encrypt
+#define bsaes_ctr32_encrypt_blocks GRPC_SHADOW_bsaes_ctr32_encrypt_blocks
+#define bsaes_xts_decrypt GRPC_SHADOW_bsaes_xts_decrypt
+#define bsaes_xts_encrypt GRPC_SHADOW_bsaes_xts_encrypt
+#define gcm_ghash_4bit GRPC_SHADOW_gcm_ghash_4bit
+#define gcm_ghash_avx GRPC_SHADOW_gcm_ghash_avx
+#define gcm_ghash_clmul GRPC_SHADOW_gcm_ghash_clmul
+#define gcm_gmult_4bit GRPC_SHADOW_gcm_gmult_4bit
+#define gcm_gmult_avx GRPC_SHADOW_gcm_gmult_avx
+#define gcm_gmult_clmul GRPC_SHADOW_gcm_gmult_clmul
+#define gcm_init_avx GRPC_SHADOW_gcm_init_avx
+#define gcm_init_clmul GRPC_SHADOW_gcm_init_clmul
+#define md5_block_asm_data_order GRPC_SHADOW_md5_block_asm_data_order
+#define ecp_nistz256_avx2_select_w7 GRPC_SHADOW_ecp_nistz256_avx2_select_w7
+#define ecp_nistz256_mul_mont GRPC_SHADOW_ecp_nistz256_mul_mont
+#define ecp_nistz256_neg GRPC_SHADOW_ecp_nistz256_neg
+#define ecp_nistz256_point_add GRPC_SHADOW_ecp_nistz256_point_add
+#define ecp_nistz256_point_add_affine GRPC_SHADOW_ecp_nistz256_point_add_affine
+#define ecp_nistz256_point_double GRPC_SHADOW_ecp_nistz256_point_double
+#define ecp_nistz256_select_w5 GRPC_SHADOW_ecp_nistz256_select_w5
+#define ecp_nistz256_select_w7 GRPC_SHADOW_ecp_nistz256_select_w7
+#define ecp_nistz256_sqr_mont GRPC_SHADOW_ecp_nistz256_sqr_mont
+#define CRYPTO_rdrand GRPC_SHADOW_CRYPTO_rdrand
+#define CRYPTO_rdrand_multiple8_buf GRPC_SHADOW_CRYPTO_rdrand_multiple8_buf
+#define rsaz_1024_gather5_avx2 GRPC_SHADOW_rsaz_1024_gather5_avx2
+#define rsaz_1024_mul_avx2 GRPC_SHADOW_rsaz_1024_mul_avx2
+#define rsaz_1024_norm2red_avx2 GRPC_SHADOW_rsaz_1024_norm2red_avx2
+#define rsaz_1024_red2norm_avx2 GRPC_SHADOW_rsaz_1024_red2norm_avx2
+#define rsaz_1024_scatter5_avx2 GRPC_SHADOW_rsaz_1024_scatter5_avx2
+#define rsaz_1024_sqr_avx2 GRPC_SHADOW_rsaz_1024_sqr_avx2
+#define rsaz_avx2_eligible GRPC_SHADOW_rsaz_avx2_eligible
+#define sha1_block_data_order GRPC_SHADOW_sha1_block_data_order
+#define sha256_block_data_order GRPC_SHADOW_sha256_block_data_order
+#define sha512_block_data_order GRPC_SHADOW_sha512_block_data_order
+#define vpaes_cbc_encrypt GRPC_SHADOW_vpaes_cbc_encrypt
+#define vpaes_decrypt GRPC_SHADOW_vpaes_decrypt
+#define vpaes_encrypt GRPC_SHADOW_vpaes_encrypt
+#define vpaes_set_decrypt_key GRPC_SHADOW_vpaes_set_decrypt_key
+#define vpaes_set_encrypt_key GRPC_SHADOW_vpaes_set_encrypt_key
+#define bn_from_montgomery GRPC_SHADOW_bn_from_montgomery
+#define bn_gather5 GRPC_SHADOW_bn_gather5
+#define bn_mul_mont_gather5 GRPC_SHADOW_bn_mul_mont_gather5
+#define bn_power5 GRPC_SHADOW_bn_power5
+#define bn_scatter5 GRPC_SHADOW_bn_scatter5
+#define bn_sqr8x_internal GRPC_SHADOW_bn_sqr8x_internal
+#define bn_mul_mont GRPC_SHADOW_bn_mul_mont
+#define EVP_get_digestbyname GRPC_SHADOW_EVP_get_digestbyname
+#define EVP_get_digestbynid GRPC_SHADOW_EVP_get_digestbynid
+#define EVP_get_digestbyobj GRPC_SHADOW_EVP_get_digestbyobj
+#define EVP_marshal_digest_algorithm GRPC_SHADOW_EVP_marshal_digest_algorithm
+#define EVP_parse_digest_algorithm GRPC_SHADOW_EVP_parse_digest_algorithm
+#define EVP_get_cipherbyname GRPC_SHADOW_EVP_get_cipherbyname
+#define EVP_get_cipherbynid GRPC_SHADOW_EVP_get_cipherbynid
+#define EVP_BytesToKey GRPC_SHADOW_EVP_BytesToKey
+#define EVP_enc_null GRPC_SHADOW_EVP_enc_null
+#define EVP_rc2_40_cbc GRPC_SHADOW_EVP_rc2_40_cbc
+#define EVP_rc2_cbc GRPC_SHADOW_EVP_rc2_cbc
+#define EVP_rc4 GRPC_SHADOW_EVP_rc4
+#define EVP_aead_aes_128_gcm_siv GRPC_SHADOW_EVP_aead_aes_128_gcm_siv
+#define EVP_aead_aes_256_gcm_siv GRPC_SHADOW_EVP_aead_aes_256_gcm_siv
+#define EVP_aead_aes_128_ctr_hmac_sha256 GRPC_SHADOW_EVP_aead_aes_128_ctr_hmac_sha256
+#define EVP_aead_aes_256_ctr_hmac_sha256 GRPC_SHADOW_EVP_aead_aes_256_ctr_hmac_sha256
+#define EVP_aead_aes_128_ccm_bluetooth GRPC_SHADOW_EVP_aead_aes_128_ccm_bluetooth
+#define EVP_aead_aes_128_ccm_bluetooth_8 GRPC_SHADOW_EVP_aead_aes_128_ccm_bluetooth_8
+#define EVP_aead_chacha20_poly1305 GRPC_SHADOW_EVP_aead_chacha20_poly1305
+#define EVP_tls_cbc_copy_mac GRPC_SHADOW_EVP_tls_cbc_copy_mac
+#define EVP_tls_cbc_digest_record GRPC_SHADOW_EVP_tls_cbc_digest_record
+#define EVP_tls_cbc_record_digest_supported GRPC_SHADOW_EVP_tls_cbc_record_digest_supported
+#define EVP_tls_cbc_remove_padding GRPC_SHADOW_EVP_tls_cbc_remove_padding
+#define EVP_aead_aes_128_cbc_sha1_tls GRPC_SHADOW_EVP_aead_aes_128_cbc_sha1_tls
+#define EVP_aead_aes_128_cbc_sha1_tls_implicit_iv GRPC_SHADOW_EVP_aead_aes_128_cbc_sha1_tls_implicit_iv
+#define EVP_aead_aes_128_cbc_sha256_tls GRPC_SHADOW_EVP_aead_aes_128_cbc_sha256_tls
+#define EVP_aead_aes_256_cbc_sha1_tls GRPC_SHADOW_EVP_aead_aes_256_cbc_sha1_tls
+#define EVP_aead_aes_256_cbc_sha1_tls_implicit_iv GRPC_SHADOW_EVP_aead_aes_256_cbc_sha1_tls_implicit_iv
+#define EVP_aead_aes_256_cbc_sha256_tls GRPC_SHADOW_EVP_aead_aes_256_cbc_sha256_tls
+#define EVP_aead_aes_256_cbc_sha384_tls GRPC_SHADOW_EVP_aead_aes_256_cbc_sha384_tls
+#define EVP_aead_des_ede3_cbc_sha1_tls GRPC_SHADOW_EVP_aead_des_ede3_cbc_sha1_tls
+#define EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv GRPC_SHADOW_EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv
+#define EVP_aead_null_sha1_tls GRPC_SHADOW_EVP_aead_null_sha1_tls
+#define EVP_aead_aes_128_cbc_sha1_ssl3 GRPC_SHADOW_EVP_aead_aes_128_cbc_sha1_ssl3
+#define EVP_aead_aes_256_cbc_sha1_ssl3 GRPC_SHADOW_EVP_aead_aes_256_cbc_sha1_ssl3
+#define EVP_aead_des_ede3_cbc_sha1_ssl3 GRPC_SHADOW_EVP_aead_des_ede3_cbc_sha1_ssl3
+#define EVP_aead_null_sha1_ssl3 GRPC_SHADOW_EVP_aead_null_sha1_ssl3
+#define aes128gcmsiv_aes_ks GRPC_SHADOW_aes128gcmsiv_aes_ks
+#define aes128gcmsiv_aes_ks_enc_x1 GRPC_SHADOW_aes128gcmsiv_aes_ks_enc_x1
+#define aes128gcmsiv_dec GRPC_SHADOW_aes128gcmsiv_dec
+#define aes128gcmsiv_ecb_enc_block GRPC_SHADOW_aes128gcmsiv_ecb_enc_block
+#define aes128gcmsiv_enc_msg_x4 GRPC_SHADOW_aes128gcmsiv_enc_msg_x4
+#define aes128gcmsiv_enc_msg_x8 GRPC_SHADOW_aes128gcmsiv_enc_msg_x8
+#define aes128gcmsiv_kdf GRPC_SHADOW_aes128gcmsiv_kdf
+#define aes256gcmsiv_aes_ks GRPC_SHADOW_aes256gcmsiv_aes_ks
+#define aes256gcmsiv_aes_ks_enc_x1 GRPC_SHADOW_aes256gcmsiv_aes_ks_enc_x1
+#define aes256gcmsiv_dec GRPC_SHADOW_aes256gcmsiv_dec
+#define aes256gcmsiv_ecb_enc_block GRPC_SHADOW_aes256gcmsiv_ecb_enc_block
+#define aes256gcmsiv_enc_msg_x4 GRPC_SHADOW_aes256gcmsiv_enc_msg_x4
+#define aes256gcmsiv_enc_msg_x8 GRPC_SHADOW_aes256gcmsiv_enc_msg_x8
+#define aes256gcmsiv_kdf GRPC_SHADOW_aes256gcmsiv_kdf
+#define aesgcmsiv_htable6_init GRPC_SHADOW_aesgcmsiv_htable6_init
+#define aesgcmsiv_htable_init GRPC_SHADOW_aesgcmsiv_htable_init
+#define aesgcmsiv_htable_polyval GRPC_SHADOW_aesgcmsiv_htable_polyval
+#define aesgcmsiv_polyval_horner GRPC_SHADOW_aesgcmsiv_polyval_horner
+#define chacha20_poly1305_open GRPC_SHADOW_chacha20_poly1305_open
+#define chacha20_poly1305_seal GRPC_SHADOW_chacha20_poly1305_seal
+#define RC4 GRPC_SHADOW_RC4
+#define RC4_set_key GRPC_SHADOW_RC4_set_key
+#define CONF_VALUE_new GRPC_SHADOW_CONF_VALUE_new
+#define CONF_modules_free GRPC_SHADOW_CONF_modules_free
+#define CONF_modules_load_file GRPC_SHADOW_CONF_modules_load_file
+#define CONF_parse_list GRPC_SHADOW_CONF_parse_list
+#define NCONF_free GRPC_SHADOW_NCONF_free
+#define NCONF_get_section GRPC_SHADOW_NCONF_get_section
+#define NCONF_get_string GRPC_SHADOW_NCONF_get_string
+#define NCONF_load GRPC_SHADOW_NCONF_load
+#define NCONF_load_bio GRPC_SHADOW_NCONF_load_bio
+#define NCONF_new GRPC_SHADOW_NCONF_new
+#define OPENSSL_config GRPC_SHADOW_OPENSSL_config
+#define OPENSSL_no_config GRPC_SHADOW_OPENSSL_no_config
+#define CRYPTO_chacha_20 GRPC_SHADOW_CRYPTO_chacha_20
+#define ChaCha20_ctr32 GRPC_SHADOW_ChaCha20_ctr32
+#define CRYPTO_poly1305_finish GRPC_SHADOW_CRYPTO_poly1305_finish
+#define CRYPTO_poly1305_init GRPC_SHADOW_CRYPTO_poly1305_init
+#define CRYPTO_poly1305_update GRPC_SHADOW_CRYPTO_poly1305_update
+#define SPAKE2_CTX_free GRPC_SHADOW_SPAKE2_CTX_free
+#define SPAKE2_CTX_new GRPC_SHADOW_SPAKE2_CTX_new
+#define SPAKE2_generate_msg GRPC_SHADOW_SPAKE2_generate_msg
+#define SPAKE2_process_msg GRPC_SHADOW_SPAKE2_process_msg
+#define ED25519_keypair GRPC_SHADOW_ED25519_keypair
+#define ED25519_keypair_from_seed GRPC_SHADOW_ED25519_keypair_from_seed
+#define ED25519_sign GRPC_SHADOW_ED25519_sign
+#define ED25519_verify GRPC_SHADOW_ED25519_verify
+#define X25519 GRPC_SHADOW_X25519
+#define X25519_keypair GRPC_SHADOW_X25519_keypair
+#define X25519_public_from_private GRPC_SHADOW_X25519_public_from_private
+#define x25519_ge_add GRPC_SHADOW_x25519_ge_add
+#define x25519_ge_frombytes_vartime GRPC_SHADOW_x25519_ge_frombytes_vartime
+#define x25519_ge_p1p1_to_p2 GRPC_SHADOW_x25519_ge_p1p1_to_p2
+#define x25519_ge_p1p1_to_p3 GRPC_SHADOW_x25519_ge_p1p1_to_p3
+#define x25519_ge_p3_to_cached GRPC_SHADOW_x25519_ge_p3_to_cached
+#define x25519_ge_scalarmult GRPC_SHADOW_x25519_ge_scalarmult
+#define x25519_ge_scalarmult_base GRPC_SHADOW_x25519_ge_scalarmult_base
+#define x25519_ge_scalarmult_small_precomp GRPC_SHADOW_x25519_ge_scalarmult_small_precomp
+#define x25519_ge_sub GRPC_SHADOW_x25519_ge_sub
+#define x25519_ge_tobytes GRPC_SHADOW_x25519_ge_tobytes
+#define x25519_sc_reduce GRPC_SHADOW_x25519_sc_reduce
+#define BUF_MEM_append GRPC_SHADOW_BUF_MEM_append
+#define BUF_MEM_free GRPC_SHADOW_BUF_MEM_free
+#define BUF_MEM_grow GRPC_SHADOW_BUF_MEM_grow
+#define BUF_MEM_grow_clean GRPC_SHADOW_BUF_MEM_grow_clean
+#define BUF_MEM_new GRPC_SHADOW_BUF_MEM_new
+#define BUF_MEM_reserve GRPC_SHADOW_BUF_MEM_reserve
+#define BUF_memdup GRPC_SHADOW_BUF_memdup
+#define BUF_strdup GRPC_SHADOW_BUF_strdup
+#define BUF_strlcat GRPC_SHADOW_BUF_strlcat
+#define BUF_strlcpy GRPC_SHADOW_BUF_strlcpy
+#define BUF_strndup GRPC_SHADOW_BUF_strndup
+#define BUF_strnlen GRPC_SHADOW_BUF_strnlen
+#define BN_marshal_asn1 GRPC_SHADOW_BN_marshal_asn1
+#define BN_parse_asn1_unsigned GRPC_SHADOW_BN_parse_asn1_unsigned
+#define BN_asc2bn GRPC_SHADOW_BN_asc2bn
+#define BN_bn2cbb_padded GRPC_SHADOW_BN_bn2cbb_padded
+#define BN_bn2dec GRPC_SHADOW_BN_bn2dec
+#define BN_bn2hex GRPC_SHADOW_BN_bn2hex
+#define BN_bn2mpi GRPC_SHADOW_BN_bn2mpi
+#define BN_dec2bn GRPC_SHADOW_BN_dec2bn
+#define BN_hex2bn GRPC_SHADOW_BN_hex2bn
+#define BN_mpi2bn GRPC_SHADOW_BN_mpi2bn
+#define BN_print GRPC_SHADOW_BN_print
+#define BN_print_fp GRPC_SHADOW_BN_print_fp
+#define BIO_callback_ctrl GRPC_SHADOW_BIO_callback_ctrl
+#define BIO_clear_flags GRPC_SHADOW_BIO_clear_flags
+#define BIO_clear_retry_flags GRPC_SHADOW_BIO_clear_retry_flags
+#define BIO_copy_next_retry GRPC_SHADOW_BIO_copy_next_retry
+#define BIO_ctrl GRPC_SHADOW_BIO_ctrl
+#define BIO_ctrl_pending GRPC_SHADOW_BIO_ctrl_pending
+#define BIO_eof GRPC_SHADOW_BIO_eof
+#define BIO_find_type GRPC_SHADOW_BIO_find_type
+#define BIO_flush GRPC_SHADOW_BIO_flush
+#define BIO_free GRPC_SHADOW_BIO_free
+#define BIO_free_all GRPC_SHADOW_BIO_free_all
+#define BIO_get_data GRPC_SHADOW_BIO_get_data
+#define BIO_get_init GRPC_SHADOW_BIO_get_init
+#define BIO_get_new_index GRPC_SHADOW_BIO_get_new_index
+#define BIO_get_retry_flags GRPC_SHADOW_BIO_get_retry_flags
+#define BIO_get_retry_reason GRPC_SHADOW_BIO_get_retry_reason
+#define BIO_get_shutdown GRPC_SHADOW_BIO_get_shutdown
+#define BIO_gets GRPC_SHADOW_BIO_gets
+#define BIO_indent GRPC_SHADOW_BIO_indent
+#define BIO_int_ctrl GRPC_SHADOW_BIO_int_ctrl
+#define BIO_meth_free GRPC_SHADOW_BIO_meth_free
+#define BIO_meth_new GRPC_SHADOW_BIO_meth_new
+#define BIO_meth_set_create GRPC_SHADOW_BIO_meth_set_create
+#define BIO_meth_set_ctrl GRPC_SHADOW_BIO_meth_set_ctrl
+#define BIO_meth_set_destroy GRPC_SHADOW_BIO_meth_set_destroy
+#define BIO_meth_set_gets GRPC_SHADOW_BIO_meth_set_gets
+#define BIO_meth_set_puts GRPC_SHADOW_BIO_meth_set_puts
+#define BIO_meth_set_read GRPC_SHADOW_BIO_meth_set_read
+#define BIO_meth_set_write GRPC_SHADOW_BIO_meth_set_write
+#define BIO_method_type GRPC_SHADOW_BIO_method_type
+#define BIO_new GRPC_SHADOW_BIO_new
+#define BIO_next GRPC_SHADOW_BIO_next
+#define BIO_number_read GRPC_SHADOW_BIO_number_read
+#define BIO_number_written GRPC_SHADOW_BIO_number_written
+#define BIO_pending GRPC_SHADOW_BIO_pending
+#define BIO_pop GRPC_SHADOW_BIO_pop
+#define BIO_ptr_ctrl GRPC_SHADOW_BIO_ptr_ctrl
+#define BIO_push GRPC_SHADOW_BIO_push
+#define BIO_puts GRPC_SHADOW_BIO_puts
+#define BIO_read GRPC_SHADOW_BIO_read
+#define BIO_read_asn1 GRPC_SHADOW_BIO_read_asn1
+#define BIO_reset GRPC_SHADOW_BIO_reset
+#define BIO_set_close GRPC_SHADOW_BIO_set_close
+#define BIO_set_data GRPC_SHADOW_BIO_set_data
+#define BIO_set_flags GRPC_SHADOW_BIO_set_flags
+#define BIO_set_init GRPC_SHADOW_BIO_set_init
+#define BIO_set_retry_read GRPC_SHADOW_BIO_set_retry_read
+#define BIO_set_retry_special GRPC_SHADOW_BIO_set_retry_special
+#define BIO_set_retry_write GRPC_SHADOW_BIO_set_retry_write
+#define BIO_set_shutdown GRPC_SHADOW_BIO_set_shutdown
+#define BIO_set_write_buffer_size GRPC_SHADOW_BIO_set_write_buffer_size
+#define BIO_should_io_special GRPC_SHADOW_BIO_should_io_special
+#define BIO_should_read GRPC_SHADOW_BIO_should_read
+#define BIO_should_retry GRPC_SHADOW_BIO_should_retry
+#define BIO_should_write GRPC_SHADOW_BIO_should_write
+#define BIO_test_flags GRPC_SHADOW_BIO_test_flags
+#define BIO_up_ref GRPC_SHADOW_BIO_up_ref
+#define BIO_vfree GRPC_SHADOW_BIO_vfree
+#define BIO_wpending GRPC_SHADOW_BIO_wpending
+#define BIO_write GRPC_SHADOW_BIO_write
+#define ERR_print_errors GRPC_SHADOW_ERR_print_errors
+#define BIO_get_mem_data GRPC_SHADOW_BIO_get_mem_data
+#define BIO_get_mem_ptr GRPC_SHADOW_BIO_get_mem_ptr
+#define BIO_mem_contents GRPC_SHADOW_BIO_mem_contents
+#define BIO_new_mem_buf GRPC_SHADOW_BIO_new_mem_buf
+#define BIO_s_mem GRPC_SHADOW_BIO_s_mem
+#define BIO_set_mem_buf GRPC_SHADOW_BIO_set_mem_buf
+#define BIO_set_mem_eof_return GRPC_SHADOW_BIO_set_mem_eof_return
+#define BIO_do_connect GRPC_SHADOW_BIO_do_connect
+#define BIO_new_connect GRPC_SHADOW_BIO_new_connect
+#define BIO_s_connect GRPC_SHADOW_BIO_s_connect
+#define BIO_set_conn_hostname GRPC_SHADOW_BIO_set_conn_hostname
+#define BIO_set_conn_int_port GRPC_SHADOW_BIO_set_conn_int_port
+#define BIO_set_conn_port GRPC_SHADOW_BIO_set_conn_port
+#define BIO_set_nbio GRPC_SHADOW_BIO_set_nbio
+#define BIO_get_fd GRPC_SHADOW_BIO_get_fd
+#define BIO_new_fd GRPC_SHADOW_BIO_new_fd
+#define BIO_s_fd GRPC_SHADOW_BIO_s_fd
+#define BIO_set_fd GRPC_SHADOW_BIO_set_fd
+#define bio_fd_should_retry GRPC_SHADOW_bio_fd_should_retry
+#define BIO_append_filename GRPC_SHADOW_BIO_append_filename
+#define BIO_get_fp GRPC_SHADOW_BIO_get_fp
+#define BIO_new_file GRPC_SHADOW_BIO_new_file
+#define BIO_new_fp GRPC_SHADOW_BIO_new_fp
+#define BIO_read_filename GRPC_SHADOW_BIO_read_filename
+#define BIO_rw_filename GRPC_SHADOW_BIO_rw_filename
+#define BIO_s_file GRPC_SHADOW_BIO_s_file
+#define BIO_set_fp GRPC_SHADOW_BIO_set_fp
+#define BIO_write_filename GRPC_SHADOW_BIO_write_filename
+#define BIO_hexdump GRPC_SHADOW_BIO_hexdump
+#define BIO_ctrl_get_read_request GRPC_SHADOW_BIO_ctrl_get_read_request
+#define BIO_ctrl_get_write_guarantee GRPC_SHADOW_BIO_ctrl_get_write_guarantee
+#define BIO_new_bio_pair GRPC_SHADOW_BIO_new_bio_pair
+#define BIO_shutdown_wr GRPC_SHADOW_BIO_shutdown_wr
+#define BIO_printf GRPC_SHADOW_BIO_printf
+#define BIO_new_socket GRPC_SHADOW_BIO_new_socket
+#define BIO_s_socket GRPC_SHADOW_BIO_s_socket
+#define bio_clear_socket_error GRPC_SHADOW_bio_clear_socket_error
+#define bio_ip_and_port_to_socket_and_addr GRPC_SHADOW_bio_ip_and_port_to_socket_and_addr
+#define bio_sock_error GRPC_SHADOW_bio_sock_error
+#define bio_socket_nbio GRPC_SHADOW_bio_socket_nbio
+#define RAND_enable_fork_unsafe_buffering GRPC_SHADOW_RAND_enable_fork_unsafe_buffering
+#define rand_fork_unsafe_buffering_enabled GRPC_SHADOW_rand_fork_unsafe_buffering_enabled
+#define RAND_SSLeay GRPC_SHADOW_RAND_SSLeay
+#define RAND_add GRPC_SHADOW_RAND_add
+#define RAND_cleanup GRPC_SHADOW_RAND_cleanup
+#define RAND_egd GRPC_SHADOW_RAND_egd
+#define RAND_file_name GRPC_SHADOW_RAND_file_name
+#define RAND_get_rand_method GRPC_SHADOW_RAND_get_rand_method
+#define RAND_load_file GRPC_SHADOW_RAND_load_file
+#define RAND_poll GRPC_SHADOW_RAND_poll
+#define RAND_seed GRPC_SHADOW_RAND_seed
+#define RAND_set_rand_method GRPC_SHADOW_RAND_set_rand_method
+#define RAND_status GRPC_SHADOW_RAND_status
+#define OBJ_cbs2nid GRPC_SHADOW_OBJ_cbs2nid
+#define OBJ_cmp GRPC_SHADOW_OBJ_cmp
+#define OBJ_create GRPC_SHADOW_OBJ_create
+#define OBJ_dup GRPC_SHADOW_OBJ_dup
+#define OBJ_get0_data GRPC_SHADOW_OBJ_get0_data
+#define OBJ_length GRPC_SHADOW_OBJ_length
+#define OBJ_ln2nid GRPC_SHADOW_OBJ_ln2nid
+#define OBJ_nid2cbb GRPC_SHADOW_OBJ_nid2cbb
+#define OBJ_nid2ln GRPC_SHADOW_OBJ_nid2ln
+#define OBJ_nid2obj GRPC_SHADOW_OBJ_nid2obj
+#define OBJ_nid2sn GRPC_SHADOW_OBJ_nid2sn
+#define OBJ_obj2nid GRPC_SHADOW_OBJ_obj2nid
+#define OBJ_obj2txt GRPC_SHADOW_OBJ_obj2txt
+#define OBJ_sn2nid GRPC_SHADOW_OBJ_sn2nid
+#define OBJ_txt2nid GRPC_SHADOW_OBJ_txt2nid
+#define OBJ_txt2obj GRPC_SHADOW_OBJ_txt2obj
+#define OBJ_find_sigid_algs GRPC_SHADOW_OBJ_find_sigid_algs
+#define OBJ_find_sigid_by_algs GRPC_SHADOW_OBJ_find_sigid_by_algs
+#define ASN1_BIT_STRING_check GRPC_SHADOW_ASN1_BIT_STRING_check
+#define ASN1_BIT_STRING_get_bit GRPC_SHADOW_ASN1_BIT_STRING_get_bit
+#define ASN1_BIT_STRING_set GRPC_SHADOW_ASN1_BIT_STRING_set
+#define ASN1_BIT_STRING_set_bit GRPC_SHADOW_ASN1_BIT_STRING_set_bit
+#define c2i_ASN1_BIT_STRING GRPC_SHADOW_c2i_ASN1_BIT_STRING
+#define i2c_ASN1_BIT_STRING GRPC_SHADOW_i2c_ASN1_BIT_STRING
+#define d2i_ASN1_BOOLEAN GRPC_SHADOW_d2i_ASN1_BOOLEAN
+#define i2d_ASN1_BOOLEAN GRPC_SHADOW_i2d_ASN1_BOOLEAN
+#define ASN1_d2i_bio GRPC_SHADOW_ASN1_d2i_bio
+#define ASN1_d2i_fp GRPC_SHADOW_ASN1_d2i_fp
+#define ASN1_item_d2i_bio GRPC_SHADOW_ASN1_item_d2i_bio
+#define ASN1_item_d2i_fp GRPC_SHADOW_ASN1_item_d2i_fp
+#define ASN1_dup GRPC_SHADOW_ASN1_dup
+#define ASN1_item_dup GRPC_SHADOW_ASN1_item_dup
+#define ASN1_ENUMERATED_get GRPC_SHADOW_ASN1_ENUMERATED_get
+#define ASN1_ENUMERATED_set GRPC_SHADOW_ASN1_ENUMERATED_set
+#define ASN1_ENUMERATED_to_BN GRPC_SHADOW_ASN1_ENUMERATED_to_BN
+#define BN_to_ASN1_ENUMERATED GRPC_SHADOW_BN_to_ASN1_ENUMERATED
+#define ASN1_GENERALIZEDTIME_adj GRPC_SHADOW_ASN1_GENERALIZEDTIME_adj
+#define ASN1_GENERALIZEDTIME_check GRPC_SHADOW_ASN1_GENERALIZEDTIME_check
+#define ASN1_GENERALIZEDTIME_set GRPC_SHADOW_ASN1_GENERALIZEDTIME_set
+#define ASN1_GENERALIZEDTIME_set_string GRPC_SHADOW_ASN1_GENERALIZEDTIME_set_string
+#define asn1_generalizedtime_to_tm GRPC_SHADOW_asn1_generalizedtime_to_tm
+#define ASN1_i2d_bio GRPC_SHADOW_ASN1_i2d_bio
+#define ASN1_i2d_fp GRPC_SHADOW_ASN1_i2d_fp
+#define ASN1_item_i2d_bio GRPC_SHADOW_ASN1_item_i2d_bio
+#define ASN1_item_i2d_fp GRPC_SHADOW_ASN1_item_i2d_fp
+#define ASN1_INTEGER_cmp GRPC_SHADOW_ASN1_INTEGER_cmp
+#define ASN1_INTEGER_dup GRPC_SHADOW_ASN1_INTEGER_dup
+#define ASN1_INTEGER_get GRPC_SHADOW_ASN1_INTEGER_get
+#define ASN1_INTEGER_set GRPC_SHADOW_ASN1_INTEGER_set
+#define ASN1_INTEGER_set_uint64 GRPC_SHADOW_ASN1_INTEGER_set_uint64
+#define ASN1_INTEGER_to_BN GRPC_SHADOW_ASN1_INTEGER_to_BN
+#define BN_to_ASN1_INTEGER GRPC_SHADOW_BN_to_ASN1_INTEGER
+#define c2i_ASN1_INTEGER GRPC_SHADOW_c2i_ASN1_INTEGER
+#define d2i_ASN1_UINTEGER GRPC_SHADOW_d2i_ASN1_UINTEGER
+#define i2c_ASN1_INTEGER GRPC_SHADOW_i2c_ASN1_INTEGER
+#define ASN1_mbstring_copy GRPC_SHADOW_ASN1_mbstring_copy
+#define ASN1_mbstring_ncopy GRPC_SHADOW_ASN1_mbstring_ncopy
+#define ASN1_OBJECT_create GRPC_SHADOW_ASN1_OBJECT_create
+#define ASN1_OBJECT_free GRPC_SHADOW_ASN1_OBJECT_free
+#define ASN1_OBJECT_new GRPC_SHADOW_ASN1_OBJECT_new
+#define c2i_ASN1_OBJECT GRPC_SHADOW_c2i_ASN1_OBJECT
+#define d2i_ASN1_OBJECT GRPC_SHADOW_d2i_ASN1_OBJECT
+#define i2a_ASN1_OBJECT GRPC_SHADOW_i2a_ASN1_OBJECT
+#define i2d_ASN1_OBJECT GRPC_SHADOW_i2d_ASN1_OBJECT
+#define i2t_ASN1_OBJECT GRPC_SHADOW_i2t_ASN1_OBJECT
+#define ASN1_OCTET_STRING_cmp GRPC_SHADOW_ASN1_OCTET_STRING_cmp
+#define ASN1_OCTET_STRING_dup GRPC_SHADOW_ASN1_OCTET_STRING_dup
+#define ASN1_OCTET_STRING_set GRPC_SHADOW_ASN1_OCTET_STRING_set
+#define ASN1_PRINTABLE_type GRPC_SHADOW_ASN1_PRINTABLE_type
+#define ASN1_STRING_TABLE_add GRPC_SHADOW_ASN1_STRING_TABLE_add
+#define ASN1_STRING_TABLE_cleanup GRPC_SHADOW_ASN1_STRING_TABLE_cleanup
+#define ASN1_STRING_TABLE_get GRPC_SHADOW_ASN1_STRING_TABLE_get
+#define ASN1_STRING_get_default_mask GRPC_SHADOW_ASN1_STRING_get_default_mask
+#define ASN1_STRING_set_by_NID GRPC_SHADOW_ASN1_STRING_set_by_NID
+#define ASN1_STRING_set_default_mask GRPC_SHADOW_ASN1_STRING_set_default_mask
+#define ASN1_STRING_set_default_mask_asc GRPC_SHADOW_ASN1_STRING_set_default_mask_asc
+#define ASN1_TIME_adj GRPC_SHADOW_ASN1_TIME_adj
+#define ASN1_TIME_check GRPC_SHADOW_ASN1_TIME_check
+#define ASN1_TIME_diff GRPC_SHADOW_ASN1_TIME_diff
+#define ASN1_TIME_free GRPC_SHADOW_ASN1_TIME_free
+#define ASN1_TIME_it GRPC_SHADOW_ASN1_TIME_it
+#define ASN1_TIME_new GRPC_SHADOW_ASN1_TIME_new
+#define ASN1_TIME_set GRPC_SHADOW_ASN1_TIME_set
+#define ASN1_TIME_set_string GRPC_SHADOW_ASN1_TIME_set_string
+#define ASN1_TIME_to_generalizedtime GRPC_SHADOW_ASN1_TIME_to_generalizedtime
+#define d2i_ASN1_TIME GRPC_SHADOW_d2i_ASN1_TIME
+#define i2d_ASN1_TIME GRPC_SHADOW_i2d_ASN1_TIME
+#define ASN1_TYPE_cmp GRPC_SHADOW_ASN1_TYPE_cmp
+#define ASN1_TYPE_get GRPC_SHADOW_ASN1_TYPE_get
+#define ASN1_TYPE_set GRPC_SHADOW_ASN1_TYPE_set
+#define ASN1_TYPE_set1 GRPC_SHADOW_ASN1_TYPE_set1
+#define ASN1_UTCTIME_adj GRPC_SHADOW_ASN1_UTCTIME_adj
+#define ASN1_UTCTIME_check GRPC_SHADOW_ASN1_UTCTIME_check
+#define ASN1_UTCTIME_cmp_time_t GRPC_SHADOW_ASN1_UTCTIME_cmp_time_t
+#define ASN1_UTCTIME_set GRPC_SHADOW_ASN1_UTCTIME_set
+#define ASN1_UTCTIME_set_string GRPC_SHADOW_ASN1_UTCTIME_set_string
+#define asn1_utctime_to_tm GRPC_SHADOW_asn1_utctime_to_tm
+#define UTF8_getc GRPC_SHADOW_UTF8_getc
+#define UTF8_putc GRPC_SHADOW_UTF8_putc
+#define ASN1_STRING_cmp GRPC_SHADOW_ASN1_STRING_cmp
+#define ASN1_STRING_copy GRPC_SHADOW_ASN1_STRING_copy
+#define ASN1_STRING_data GRPC_SHADOW_ASN1_STRING_data
+#define ASN1_STRING_dup GRPC_SHADOW_ASN1_STRING_dup
+#define ASN1_STRING_free GRPC_SHADOW_ASN1_STRING_free
+#define ASN1_STRING_get0_data GRPC_SHADOW_ASN1_STRING_get0_data
+#define ASN1_STRING_length GRPC_SHADOW_ASN1_STRING_length
+#define ASN1_STRING_length_set GRPC_SHADOW_ASN1_STRING_length_set
+#define ASN1_STRING_new GRPC_SHADOW_ASN1_STRING_new
+#define ASN1_STRING_set GRPC_SHADOW_ASN1_STRING_set
+#define ASN1_STRING_set0 GRPC_SHADOW_ASN1_STRING_set0
+#define ASN1_STRING_type GRPC_SHADOW_ASN1_STRING_type
+#define ASN1_STRING_type_new GRPC_SHADOW_ASN1_STRING_type_new
+#define ASN1_get_object GRPC_SHADOW_ASN1_get_object
+#define ASN1_object_size GRPC_SHADOW_ASN1_object_size
+#define ASN1_put_eoc GRPC_SHADOW_ASN1_put_eoc
+#define ASN1_put_object GRPC_SHADOW_ASN1_put_object
+#define ASN1_tag2str GRPC_SHADOW_ASN1_tag2str
+#define ASN1_item_pack GRPC_SHADOW_ASN1_item_pack
+#define ASN1_item_unpack GRPC_SHADOW_ASN1_item_unpack
+#define i2a_ASN1_ENUMERATED GRPC_SHADOW_i2a_ASN1_ENUMERATED
+#define i2a_ASN1_INTEGER GRPC_SHADOW_i2a_ASN1_INTEGER
+#define i2a_ASN1_STRING GRPC_SHADOW_i2a_ASN1_STRING
+#define ASN1_item_d2i GRPC_SHADOW_ASN1_item_d2i
+#define ASN1_item_ex_d2i GRPC_SHADOW_ASN1_item_ex_d2i
+#define ASN1_tag2bit GRPC_SHADOW_ASN1_tag2bit
+#define asn1_ex_c2i GRPC_SHADOW_asn1_ex_c2i
+#define ASN1_item_ex_i2d GRPC_SHADOW_ASN1_item_ex_i2d
+#define ASN1_item_i2d GRPC_SHADOW_ASN1_item_i2d
+#define ASN1_item_ndef_i2d GRPC_SHADOW_ASN1_item_ndef_i2d
+#define asn1_ex_i2c GRPC_SHADOW_asn1_ex_i2c
+#define ASN1_item_ex_free GRPC_SHADOW_ASN1_item_ex_free
+#define ASN1_item_free GRPC_SHADOW_ASN1_item_free
+#define ASN1_primitive_free GRPC_SHADOW_ASN1_primitive_free
+#define ASN1_template_free GRPC_SHADOW_ASN1_template_free
+#define asn1_item_combine_free GRPC_SHADOW_asn1_item_combine_free
+#define ASN1_item_ex_new GRPC_SHADOW_ASN1_item_ex_new
+#define ASN1_item_new GRPC_SHADOW_ASN1_item_new
+#define ASN1_primitive_new GRPC_SHADOW_ASN1_primitive_new
+#define ASN1_template_new GRPC_SHADOW_ASN1_template_new
+#define ASN1_ANY_it GRPC_SHADOW_ASN1_ANY_it
+#define ASN1_BIT_STRING_free GRPC_SHADOW_ASN1_BIT_STRING_free
+#define ASN1_BIT_STRING_it GRPC_SHADOW_ASN1_BIT_STRING_it
+#define ASN1_BIT_STRING_new GRPC_SHADOW_ASN1_BIT_STRING_new
+#define ASN1_BMPSTRING_free GRPC_SHADOW_ASN1_BMPSTRING_free
+#define ASN1_BMPSTRING_it GRPC_SHADOW_ASN1_BMPSTRING_it
+#define ASN1_BMPSTRING_new GRPC_SHADOW_ASN1_BMPSTRING_new
+#define ASN1_BOOLEAN_it GRPC_SHADOW_ASN1_BOOLEAN_it
+#define ASN1_ENUMERATED_free GRPC_SHADOW_ASN1_ENUMERATED_free
+#define ASN1_ENUMERATED_it GRPC_SHADOW_ASN1_ENUMERATED_it
+#define ASN1_ENUMERATED_new GRPC_SHADOW_ASN1_ENUMERATED_new
+#define ASN1_FBOOLEAN_it GRPC_SHADOW_ASN1_FBOOLEAN_it
+#define ASN1_GENERALIZEDTIME_free GRPC_SHADOW_ASN1_GENERALIZEDTIME_free
+#define ASN1_GENERALIZEDTIME_it GRPC_SHADOW_ASN1_GENERALIZEDTIME_it
+#define ASN1_GENERALIZEDTIME_new GRPC_SHADOW_ASN1_GENERALIZEDTIME_new
+#define ASN1_GENERALSTRING_free GRPC_SHADOW_ASN1_GENERALSTRING_free
+#define ASN1_GENERALSTRING_it GRPC_SHADOW_ASN1_GENERALSTRING_it
+#define ASN1_GENERALSTRING_new GRPC_SHADOW_ASN1_GENERALSTRING_new
+#define ASN1_IA5STRING_free GRPC_SHADOW_ASN1_IA5STRING_free
+#define ASN1_IA5STRING_it GRPC_SHADOW_ASN1_IA5STRING_it
+#define ASN1_IA5STRING_new GRPC_SHADOW_ASN1_IA5STRING_new
+#define ASN1_INTEGER_free GRPC_SHADOW_ASN1_INTEGER_free
+#define ASN1_INTEGER_it GRPC_SHADOW_ASN1_INTEGER_it
+#define ASN1_INTEGER_new GRPC_SHADOW_ASN1_INTEGER_new
+#define ASN1_NULL_free GRPC_SHADOW_ASN1_NULL_free
+#define ASN1_NULL_it GRPC_SHADOW_ASN1_NULL_it
+#define ASN1_NULL_new GRPC_SHADOW_ASN1_NULL_new
+#define ASN1_OBJECT_it GRPC_SHADOW_ASN1_OBJECT_it
+#define ASN1_OCTET_STRING_NDEF_it GRPC_SHADOW_ASN1_OCTET_STRING_NDEF_it
+#define ASN1_OCTET_STRING_free GRPC_SHADOW_ASN1_OCTET_STRING_free
+#define ASN1_OCTET_STRING_it GRPC_SHADOW_ASN1_OCTET_STRING_it
+#define ASN1_OCTET_STRING_new GRPC_SHADOW_ASN1_OCTET_STRING_new
+#define ASN1_PRINTABLESTRING_free GRPC_SHADOW_ASN1_PRINTABLESTRING_free
+#define ASN1_PRINTABLESTRING_it GRPC_SHADOW_ASN1_PRINTABLESTRING_it
+#define ASN1_PRINTABLESTRING_new GRPC_SHADOW_ASN1_PRINTABLESTRING_new
+#define ASN1_PRINTABLE_free GRPC_SHADOW_ASN1_PRINTABLE_free
+#define ASN1_PRINTABLE_it GRPC_SHADOW_ASN1_PRINTABLE_it
+#define ASN1_PRINTABLE_new GRPC_SHADOW_ASN1_PRINTABLE_new
+#define ASN1_SEQUENCE_ANY_it GRPC_SHADOW_ASN1_SEQUENCE_ANY_it
+#define ASN1_SEQUENCE_it GRPC_SHADOW_ASN1_SEQUENCE_it
+#define ASN1_SET_ANY_it GRPC_SHADOW_ASN1_SET_ANY_it
+#define ASN1_T61STRING_free GRPC_SHADOW_ASN1_T61STRING_free
+#define ASN1_T61STRING_it GRPC_SHADOW_ASN1_T61STRING_it
+#define ASN1_T61STRING_new GRPC_SHADOW_ASN1_T61STRING_new
+#define ASN1_TBOOLEAN_it GRPC_SHADOW_ASN1_TBOOLEAN_it
+#define ASN1_TYPE_free GRPC_SHADOW_ASN1_TYPE_free
+#define ASN1_TYPE_new GRPC_SHADOW_ASN1_TYPE_new
+#define ASN1_UNIVERSALSTRING_free GRPC_SHADOW_ASN1_UNIVERSALSTRING_free
+#define ASN1_UNIVERSALSTRING_it GRPC_SHADOW_ASN1_UNIVERSALSTRING_it
+#define ASN1_UNIVERSALSTRING_new GRPC_SHADOW_ASN1_UNIVERSALSTRING_new
+#define ASN1_UTCTIME_free GRPC_SHADOW_ASN1_UTCTIME_free
+#define ASN1_UTCTIME_it GRPC_SHADOW_ASN1_UTCTIME_it
+#define ASN1_UTCTIME_new GRPC_SHADOW_ASN1_UTCTIME_new
+#define ASN1_UTF8STRING_free GRPC_SHADOW_ASN1_UTF8STRING_free
+#define ASN1_UTF8STRING_it GRPC_SHADOW_ASN1_UTF8STRING_it
+#define ASN1_UTF8STRING_new GRPC_SHADOW_ASN1_UTF8STRING_new
+#define ASN1_VISIBLESTRING_free GRPC_SHADOW_ASN1_VISIBLESTRING_free
+#define ASN1_VISIBLESTRING_it GRPC_SHADOW_ASN1_VISIBLESTRING_it
+#define ASN1_VISIBLESTRING_new GRPC_SHADOW_ASN1_VISIBLESTRING_new
+#define DIRECTORYSTRING_free GRPC_SHADOW_DIRECTORYSTRING_free
+#define DIRECTORYSTRING_it GRPC_SHADOW_DIRECTORYSTRING_it
+#define DIRECTORYSTRING_new GRPC_SHADOW_DIRECTORYSTRING_new
+#define DISPLAYTEXT_free GRPC_SHADOW_DISPLAYTEXT_free
+#define DISPLAYTEXT_it GRPC_SHADOW_DISPLAYTEXT_it
+#define DISPLAYTEXT_new GRPC_SHADOW_DISPLAYTEXT_new
+#define d2i_ASN1_BIT_STRING GRPC_SHADOW_d2i_ASN1_BIT_STRING
+#define d2i_ASN1_BMPSTRING GRPC_SHADOW_d2i_ASN1_BMPSTRING
+#define d2i_ASN1_ENUMERATED GRPC_SHADOW_d2i_ASN1_ENUMERATED
+#define d2i_ASN1_GENERALIZEDTIME GRPC_SHADOW_d2i_ASN1_GENERALIZEDTIME
+#define d2i_ASN1_GENERALSTRING GRPC_SHADOW_d2i_ASN1_GENERALSTRING
+#define d2i_ASN1_IA5STRING GRPC_SHADOW_d2i_ASN1_IA5STRING
+#define d2i_ASN1_INTEGER GRPC_SHADOW_d2i_ASN1_INTEGER
+#define d2i_ASN1_NULL GRPC_SHADOW_d2i_ASN1_NULL
+#define d2i_ASN1_OCTET_STRING GRPC_SHADOW_d2i_ASN1_OCTET_STRING
+#define d2i_ASN1_PRINTABLE GRPC_SHADOW_d2i_ASN1_PRINTABLE
+#define d2i_ASN1_PRINTABLESTRING GRPC_SHADOW_d2i_ASN1_PRINTABLESTRING
+#define d2i_ASN1_SEQUENCE_ANY GRPC_SHADOW_d2i_ASN1_SEQUENCE_ANY
+#define d2i_ASN1_SET_ANY GRPC_SHADOW_d2i_ASN1_SET_ANY
+#define d2i_ASN1_T61STRING GRPC_SHADOW_d2i_ASN1_T61STRING
+#define d2i_ASN1_TYPE GRPC_SHADOW_d2i_ASN1_TYPE
+#define d2i_ASN1_UNIVERSALSTRING GRPC_SHADOW_d2i_ASN1_UNIVERSALSTRING
+#define d2i_ASN1_UTCTIME GRPC_SHADOW_d2i_ASN1_UTCTIME
+#define d2i_ASN1_UTF8STRING GRPC_SHADOW_d2i_ASN1_UTF8STRING
+#define d2i_ASN1_VISIBLESTRING GRPC_SHADOW_d2i_ASN1_VISIBLESTRING
+#define d2i_DIRECTORYSTRING GRPC_SHADOW_d2i_DIRECTORYSTRING
+#define d2i_DISPLAYTEXT GRPC_SHADOW_d2i_DISPLAYTEXT
+#define i2d_ASN1_BIT_STRING GRPC_SHADOW_i2d_ASN1_BIT_STRING
+#define i2d_ASN1_BMPSTRING GRPC_SHADOW_i2d_ASN1_BMPSTRING
+#define i2d_ASN1_ENUMERATED GRPC_SHADOW_i2d_ASN1_ENUMERATED
+#define i2d_ASN1_GENERALIZEDTIME GRPC_SHADOW_i2d_ASN1_GENERALIZEDTIME
+#define i2d_ASN1_GENERALSTRING GRPC_SHADOW_i2d_ASN1_GENERALSTRING
+#define i2d_ASN1_IA5STRING GRPC_SHADOW_i2d_ASN1_IA5STRING
+#define i2d_ASN1_INTEGER GRPC_SHADOW_i2d_ASN1_INTEGER
+#define i2d_ASN1_NULL GRPC_SHADOW_i2d_ASN1_NULL
+#define i2d_ASN1_OCTET_STRING GRPC_SHADOW_i2d_ASN1_OCTET_STRING
+#define i2d_ASN1_PRINTABLE GRPC_SHADOW_i2d_ASN1_PRINTABLE
+#define i2d_ASN1_PRINTABLESTRING GRPC_SHADOW_i2d_ASN1_PRINTABLESTRING
+#define i2d_ASN1_SEQUENCE_ANY GRPC_SHADOW_i2d_ASN1_SEQUENCE_ANY
+#define i2d_ASN1_SET_ANY GRPC_SHADOW_i2d_ASN1_SET_ANY
+#define i2d_ASN1_T61STRING GRPC_SHADOW_i2d_ASN1_T61STRING
+#define i2d_ASN1_TYPE GRPC_SHADOW_i2d_ASN1_TYPE
+#define i2d_ASN1_UNIVERSALSTRING GRPC_SHADOW_i2d_ASN1_UNIVERSALSTRING
+#define i2d_ASN1_UTCTIME GRPC_SHADOW_i2d_ASN1_UTCTIME
+#define i2d_ASN1_UTF8STRING GRPC_SHADOW_i2d_ASN1_UTF8STRING
+#define i2d_ASN1_VISIBLESTRING GRPC_SHADOW_i2d_ASN1_VISIBLESTRING
+#define i2d_DIRECTORYSTRING GRPC_SHADOW_i2d_DIRECTORYSTRING
+#define i2d_DISPLAYTEXT GRPC_SHADOW_i2d_DISPLAYTEXT
+#define asn1_do_adb GRPC_SHADOW_asn1_do_adb
+#define asn1_enc_free GRPC_SHADOW_asn1_enc_free
+#define asn1_enc_init GRPC_SHADOW_asn1_enc_init
+#define asn1_enc_restore GRPC_SHADOW_asn1_enc_restore
+#define asn1_enc_save GRPC_SHADOW_asn1_enc_save
+#define asn1_get_choice_selector GRPC_SHADOW_asn1_get_choice_selector
+#define asn1_get_field_ptr GRPC_SHADOW_asn1_get_field_ptr
+#define asn1_refcount_dec_and_test_zero GRPC_SHADOW_asn1_refcount_dec_and_test_zero
+#define asn1_refcount_set_one GRPC_SHADOW_asn1_refcount_set_one
+#define asn1_set_choice_selector GRPC_SHADOW_asn1_set_choice_selector
+#define OPENSSL_gmtime GRPC_SHADOW_OPENSSL_gmtime
+#define OPENSSL_gmtime_adj GRPC_SHADOW_OPENSSL_gmtime_adj
+#define OPENSSL_gmtime_diff GRPC_SHADOW_OPENSSL_gmtime_diff
+#define ENGINE_free GRPC_SHADOW_ENGINE_free
+#define ENGINE_get_ECDSA_method GRPC_SHADOW_ENGINE_get_ECDSA_method
+#define ENGINE_get_RSA_method GRPC_SHADOW_ENGINE_get_RSA_method
+#define ENGINE_new GRPC_SHADOW_ENGINE_new
+#define ENGINE_set_ECDSA_method GRPC_SHADOW_ENGINE_set_ECDSA_method
+#define ENGINE_set_RSA_method GRPC_SHADOW_ENGINE_set_RSA_method
+#define METHOD_ref GRPC_SHADOW_METHOD_ref
+#define METHOD_unref GRPC_SHADOW_METHOD_unref
+#define DH_compute_key GRPC_SHADOW_DH_compute_key
+#define DH_free GRPC_SHADOW_DH_free
+#define DH_generate_key GRPC_SHADOW_DH_generate_key
+#define DH_generate_parameters_ex GRPC_SHADOW_DH_generate_parameters_ex
+#define DH_get0_key GRPC_SHADOW_DH_get0_key
+#define DH_get0_pqg GRPC_SHADOW_DH_get0_pqg
+#define DH_get_ex_data GRPC_SHADOW_DH_get_ex_data
+#define DH_get_ex_new_index GRPC_SHADOW_DH_get_ex_new_index
+#define DH_new GRPC_SHADOW_DH_new
+#define DH_num_bits GRPC_SHADOW_DH_num_bits
+#define DH_set0_key GRPC_SHADOW_DH_set0_key
+#define DH_set0_pqg GRPC_SHADOW_DH_set0_pqg
+#define DH_set_ex_data GRPC_SHADOW_DH_set_ex_data
+#define DH_size GRPC_SHADOW_DH_size
+#define DH_up_ref GRPC_SHADOW_DH_up_ref
+#define DHparams_dup GRPC_SHADOW_DHparams_dup
+#define BN_get_rfc3526_prime_1536 GRPC_SHADOW_BN_get_rfc3526_prime_1536
+#define DH_check GRPC_SHADOW_DH_check
+#define DH_check_pub_key GRPC_SHADOW_DH_check_pub_key
+#define DH_marshal_parameters GRPC_SHADOW_DH_marshal_parameters
+#define DH_parse_parameters GRPC_SHADOW_DH_parse_parameters
+#define d2i_DHparams GRPC_SHADOW_d2i_DHparams
+#define i2d_DHparams GRPC_SHADOW_i2d_DHparams
+#define DSA_SIG_free GRPC_SHADOW_DSA_SIG_free
+#define DSA_SIG_new GRPC_SHADOW_DSA_SIG_new
+#define DSA_check_signature GRPC_SHADOW_DSA_check_signature
+#define DSA_do_check_signature GRPC_SHADOW_DSA_do_check_signature
+#define DSA_do_sign GRPC_SHADOW_DSA_do_sign
+#define DSA_do_verify GRPC_SHADOW_DSA_do_verify
+#define DSA_dup_DH GRPC_SHADOW_DSA_dup_DH
+#define DSA_free GRPC_SHADOW_DSA_free
+#define DSA_generate_key GRPC_SHADOW_DSA_generate_key
+#define DSA_generate_parameters_ex GRPC_SHADOW_DSA_generate_parameters_ex
+#define DSA_get0_key GRPC_SHADOW_DSA_get0_key
+#define DSA_get0_pqg GRPC_SHADOW_DSA_get0_pqg
+#define DSA_get_ex_data GRPC_SHADOW_DSA_get_ex_data
+#define DSA_get_ex_new_index GRPC_SHADOW_DSA_get_ex_new_index
+#define DSA_new GRPC_SHADOW_DSA_new
+#define DSA_set0_key GRPC_SHADOW_DSA_set0_key
+#define DSA_set0_pqg GRPC_SHADOW_DSA_set0_pqg
+#define DSA_set_ex_data GRPC_SHADOW_DSA_set_ex_data
+#define DSA_sign GRPC_SHADOW_DSA_sign
+#define DSA_size GRPC_SHADOW_DSA_size
+#define DSA_up_ref GRPC_SHADOW_DSA_up_ref
+#define DSA_verify GRPC_SHADOW_DSA_verify
+#define DSAparams_dup GRPC_SHADOW_DSAparams_dup
+#define DSA_SIG_marshal GRPC_SHADOW_DSA_SIG_marshal
+#define DSA_SIG_parse GRPC_SHADOW_DSA_SIG_parse
+#define DSA_marshal_parameters GRPC_SHADOW_DSA_marshal_parameters
+#define DSA_marshal_private_key GRPC_SHADOW_DSA_marshal_private_key
+#define DSA_marshal_public_key GRPC_SHADOW_DSA_marshal_public_key
+#define DSA_parse_parameters GRPC_SHADOW_DSA_parse_parameters
+#define DSA_parse_private_key GRPC_SHADOW_DSA_parse_private_key
+#define DSA_parse_public_key GRPC_SHADOW_DSA_parse_public_key
+#define d2i_DSAPrivateKey GRPC_SHADOW_d2i_DSAPrivateKey
+#define d2i_DSAPublicKey GRPC_SHADOW_d2i_DSAPublicKey
+#define d2i_DSA_SIG GRPC_SHADOW_d2i_DSA_SIG
+#define d2i_DSAparams GRPC_SHADOW_d2i_DSAparams
+#define i2d_DSAPrivateKey GRPC_SHADOW_i2d_DSAPrivateKey
+#define i2d_DSAPublicKey GRPC_SHADOW_i2d_DSAPublicKey
+#define i2d_DSA_SIG GRPC_SHADOW_i2d_DSA_SIG
+#define i2d_DSAparams GRPC_SHADOW_i2d_DSAparams
+#define RSAPrivateKey_dup GRPC_SHADOW_RSAPrivateKey_dup
+#define RSAPublicKey_dup GRPC_SHADOW_RSAPublicKey_dup
+#define RSA_marshal_private_key GRPC_SHADOW_RSA_marshal_private_key
+#define RSA_marshal_public_key GRPC_SHADOW_RSA_marshal_public_key
+#define RSA_parse_private_key GRPC_SHADOW_RSA_parse_private_key
+#define RSA_parse_public_key GRPC_SHADOW_RSA_parse_public_key
+#define RSA_private_key_from_bytes GRPC_SHADOW_RSA_private_key_from_bytes
+#define RSA_private_key_to_bytes GRPC_SHADOW_RSA_private_key_to_bytes
+#define RSA_public_key_from_bytes GRPC_SHADOW_RSA_public_key_from_bytes
+#define RSA_public_key_to_bytes GRPC_SHADOW_RSA_public_key_to_bytes
+#define d2i_RSAPrivateKey GRPC_SHADOW_d2i_RSAPrivateKey
+#define d2i_RSAPublicKey GRPC_SHADOW_d2i_RSAPublicKey
+#define i2d_RSAPrivateKey GRPC_SHADOW_i2d_RSAPrivateKey
+#define i2d_RSAPublicKey GRPC_SHADOW_i2d_RSAPublicKey
+#define EC_KEY_marshal_curve_name GRPC_SHADOW_EC_KEY_marshal_curve_name
+#define EC_KEY_marshal_private_key GRPC_SHADOW_EC_KEY_marshal_private_key
+#define EC_KEY_parse_curve_name GRPC_SHADOW_EC_KEY_parse_curve_name
+#define EC_KEY_parse_parameters GRPC_SHADOW_EC_KEY_parse_parameters
+#define EC_KEY_parse_private_key GRPC_SHADOW_EC_KEY_parse_private_key
+#define EC_POINT_point2cbb GRPC_SHADOW_EC_POINT_point2cbb
+#define d2i_ECParameters GRPC_SHADOW_d2i_ECParameters
+#define d2i_ECPrivateKey GRPC_SHADOW_d2i_ECPrivateKey
+#define i2d_ECParameters GRPC_SHADOW_i2d_ECParameters
+#define i2d_ECPrivateKey GRPC_SHADOW_i2d_ECPrivateKey
+#define i2o_ECPublicKey GRPC_SHADOW_i2o_ECPublicKey
+#define o2i_ECPublicKey GRPC_SHADOW_o2i_ECPublicKey
+#define ECDH_compute_key GRPC_SHADOW_ECDH_compute_key
+#define ECDSA_SIG_from_bytes GRPC_SHADOW_ECDSA_SIG_from_bytes
+#define ECDSA_SIG_marshal GRPC_SHADOW_ECDSA_SIG_marshal
+#define ECDSA_SIG_max_len GRPC_SHADOW_ECDSA_SIG_max_len
+#define ECDSA_SIG_parse GRPC_SHADOW_ECDSA_SIG_parse
+#define ECDSA_SIG_to_bytes GRPC_SHADOW_ECDSA_SIG_to_bytes
+#define ECDSA_sign GRPC_SHADOW_ECDSA_sign
+#define ECDSA_size GRPC_SHADOW_ECDSA_size
+#define ECDSA_verify GRPC_SHADOW_ECDSA_verify
+#define d2i_ECDSA_SIG GRPC_SHADOW_d2i_ECDSA_SIG
+#define i2d_ECDSA_SIG GRPC_SHADOW_i2d_ECDSA_SIG
+#define AES_CMAC GRPC_SHADOW_AES_CMAC
+#define CMAC_CTX_free GRPC_SHADOW_CMAC_CTX_free
+#define CMAC_CTX_new GRPC_SHADOW_CMAC_CTX_new
+#define CMAC_Final GRPC_SHADOW_CMAC_Final
+#define CMAC_Init GRPC_SHADOW_CMAC_Init
+#define CMAC_Reset GRPC_SHADOW_CMAC_Reset
+#define CMAC_Update GRPC_SHADOW_CMAC_Update
+#define EVP_DigestSign GRPC_SHADOW_EVP_DigestSign
+#define EVP_DigestSignFinal GRPC_SHADOW_EVP_DigestSignFinal
+#define EVP_DigestSignInit GRPC_SHADOW_EVP_DigestSignInit
+#define EVP_DigestSignUpdate GRPC_SHADOW_EVP_DigestSignUpdate
+#define EVP_DigestVerify GRPC_SHADOW_EVP_DigestVerify
+#define EVP_DigestVerifyFinal GRPC_SHADOW_EVP_DigestVerifyFinal
+#define EVP_DigestVerifyInit GRPC_SHADOW_EVP_DigestVerifyInit
+#define EVP_DigestVerifyUpdate GRPC_SHADOW_EVP_DigestVerifyUpdate
+#define EVP_PKEY_CTX_get_signature_md GRPC_SHADOW_EVP_PKEY_CTX_get_signature_md
+#define EVP_PKEY_CTX_set_signature_md GRPC_SHADOW_EVP_PKEY_CTX_set_signature_md
+#define EVP_PKEY_assign GRPC_SHADOW_EVP_PKEY_assign
+#define EVP_PKEY_assign_DSA GRPC_SHADOW_EVP_PKEY_assign_DSA
+#define EVP_PKEY_assign_EC_KEY GRPC_SHADOW_EVP_PKEY_assign_EC_KEY
+#define EVP_PKEY_assign_RSA GRPC_SHADOW_EVP_PKEY_assign_RSA
+#define EVP_PKEY_bits GRPC_SHADOW_EVP_PKEY_bits
+#define EVP_PKEY_cmp GRPC_SHADOW_EVP_PKEY_cmp
+#define EVP_PKEY_cmp_parameters GRPC_SHADOW_EVP_PKEY_cmp_parameters
+#define EVP_PKEY_copy_parameters GRPC_SHADOW_EVP_PKEY_copy_parameters
+#define EVP_PKEY_free GRPC_SHADOW_EVP_PKEY_free
+#define EVP_PKEY_get0_DH GRPC_SHADOW_EVP_PKEY_get0_DH
+#define EVP_PKEY_get0_DSA GRPC_SHADOW_EVP_PKEY_get0_DSA
+#define EVP_PKEY_get0_EC_KEY GRPC_SHADOW_EVP_PKEY_get0_EC_KEY
+#define EVP_PKEY_get0_RSA GRPC_SHADOW_EVP_PKEY_get0_RSA
+#define EVP_PKEY_get1_DSA GRPC_SHADOW_EVP_PKEY_get1_DSA
+#define EVP_PKEY_get1_EC_KEY GRPC_SHADOW_EVP_PKEY_get1_EC_KEY
+#define EVP_PKEY_get1_RSA GRPC_SHADOW_EVP_PKEY_get1_RSA
+#define EVP_PKEY_id GRPC_SHADOW_EVP_PKEY_id
+#define EVP_PKEY_is_opaque GRPC_SHADOW_EVP_PKEY_is_opaque
+#define EVP_PKEY_missing_parameters GRPC_SHADOW_EVP_PKEY_missing_parameters
+#define EVP_PKEY_new GRPC_SHADOW_EVP_PKEY_new
+#define EVP_PKEY_set1_DSA GRPC_SHADOW_EVP_PKEY_set1_DSA
+#define EVP_PKEY_set1_EC_KEY GRPC_SHADOW_EVP_PKEY_set1_EC_KEY
+#define EVP_PKEY_set1_RSA GRPC_SHADOW_EVP_PKEY_set1_RSA
+#define EVP_PKEY_set_type GRPC_SHADOW_EVP_PKEY_set_type
+#define EVP_PKEY_size GRPC_SHADOW_EVP_PKEY_size
+#define EVP_PKEY_type GRPC_SHADOW_EVP_PKEY_type
+#define EVP_PKEY_up_ref GRPC_SHADOW_EVP_PKEY_up_ref
+#define EVP_cleanup GRPC_SHADOW_EVP_cleanup
+#define OPENSSL_add_all_algorithms_conf GRPC_SHADOW_OPENSSL_add_all_algorithms_conf
+#define OpenSSL_add_all_algorithms GRPC_SHADOW_OpenSSL_add_all_algorithms
+#define OpenSSL_add_all_ciphers GRPC_SHADOW_OpenSSL_add_all_ciphers
+#define OpenSSL_add_all_digests GRPC_SHADOW_OpenSSL_add_all_digests
+#define EVP_marshal_private_key GRPC_SHADOW_EVP_marshal_private_key
+#define EVP_marshal_public_key GRPC_SHADOW_EVP_marshal_public_key
+#define EVP_parse_private_key GRPC_SHADOW_EVP_parse_private_key
+#define EVP_parse_public_key GRPC_SHADOW_EVP_parse_public_key
+#define d2i_AutoPrivateKey GRPC_SHADOW_d2i_AutoPrivateKey
+#define d2i_PrivateKey GRPC_SHADOW_d2i_PrivateKey
+#define i2d_PublicKey GRPC_SHADOW_i2d_PublicKey
+#define EVP_PKEY_CTX_ctrl GRPC_SHADOW_EVP_PKEY_CTX_ctrl
+#define EVP_PKEY_CTX_dup GRPC_SHADOW_EVP_PKEY_CTX_dup
+#define EVP_PKEY_CTX_free GRPC_SHADOW_EVP_PKEY_CTX_free
+#define EVP_PKEY_CTX_get0_pkey GRPC_SHADOW_EVP_PKEY_CTX_get0_pkey
+#define EVP_PKEY_CTX_new GRPC_SHADOW_EVP_PKEY_CTX_new
+#define EVP_PKEY_CTX_new_id GRPC_SHADOW_EVP_PKEY_CTX_new_id
+#define EVP_PKEY_decrypt GRPC_SHADOW_EVP_PKEY_decrypt
+#define EVP_PKEY_decrypt_init GRPC_SHADOW_EVP_PKEY_decrypt_init
+#define EVP_PKEY_derive GRPC_SHADOW_EVP_PKEY_derive
+#define EVP_PKEY_derive_init GRPC_SHADOW_EVP_PKEY_derive_init
+#define EVP_PKEY_derive_set_peer GRPC_SHADOW_EVP_PKEY_derive_set_peer
+#define EVP_PKEY_encrypt GRPC_SHADOW_EVP_PKEY_encrypt
+#define EVP_PKEY_encrypt_init GRPC_SHADOW_EVP_PKEY_encrypt_init
+#define EVP_PKEY_keygen GRPC_SHADOW_EVP_PKEY_keygen
+#define EVP_PKEY_keygen_init GRPC_SHADOW_EVP_PKEY_keygen_init
+#define EVP_PKEY_sign GRPC_SHADOW_EVP_PKEY_sign
+#define EVP_PKEY_sign_init GRPC_SHADOW_EVP_PKEY_sign_init
+#define EVP_PKEY_verify GRPC_SHADOW_EVP_PKEY_verify
+#define EVP_PKEY_verify_init GRPC_SHADOW_EVP_PKEY_verify_init
+#define EVP_PKEY_verify_recover GRPC_SHADOW_EVP_PKEY_verify_recover
+#define EVP_PKEY_verify_recover_init GRPC_SHADOW_EVP_PKEY_verify_recover_init
+#define dsa_asn1_meth GRPC_SHADOW_dsa_asn1_meth
+#define ec_pkey_meth GRPC_SHADOW_ec_pkey_meth
+#define ec_asn1_meth GRPC_SHADOW_ec_asn1_meth
+#define ed25519_pkey_meth GRPC_SHADOW_ed25519_pkey_meth
+#define EVP_PKEY_new_ed25519_private GRPC_SHADOW_EVP_PKEY_new_ed25519_private
+#define EVP_PKEY_new_ed25519_public GRPC_SHADOW_EVP_PKEY_new_ed25519_public
+#define ed25519_asn1_meth GRPC_SHADOW_ed25519_asn1_meth
+#define EVP_PKEY_CTX_get0_rsa_oaep_label GRPC_SHADOW_EVP_PKEY_CTX_get0_rsa_oaep_label
+#define EVP_PKEY_CTX_get_rsa_mgf1_md GRPC_SHADOW_EVP_PKEY_CTX_get_rsa_mgf1_md
+#define EVP_PKEY_CTX_get_rsa_oaep_md GRPC_SHADOW_EVP_PKEY_CTX_get_rsa_oaep_md
+#define EVP_PKEY_CTX_get_rsa_padding GRPC_SHADOW_EVP_PKEY_CTX_get_rsa_padding
+#define EVP_PKEY_CTX_get_rsa_pss_saltlen GRPC_SHADOW_EVP_PKEY_CTX_get_rsa_pss_saltlen
+#define EVP_PKEY_CTX_set0_rsa_oaep_label GRPC_SHADOW_EVP_PKEY_CTX_set0_rsa_oaep_label
+#define EVP_PKEY_CTX_set_rsa_keygen_bits GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_keygen_bits
+#define EVP_PKEY_CTX_set_rsa_keygen_pubexp GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_keygen_pubexp
+#define EVP_PKEY_CTX_set_rsa_mgf1_md GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_mgf1_md
+#define EVP_PKEY_CTX_set_rsa_oaep_md GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_oaep_md
+#define EVP_PKEY_CTX_set_rsa_padding GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_padding
+#define EVP_PKEY_CTX_set_rsa_pss_saltlen GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_pss_saltlen
+#define rsa_pkey_meth GRPC_SHADOW_rsa_pkey_meth
+#define rsa_asn1_meth GRPC_SHADOW_rsa_asn1_meth
+#define PKCS5_PBKDF2_HMAC GRPC_SHADOW_PKCS5_PBKDF2_HMAC
+#define PKCS5_PBKDF2_HMAC_SHA1 GRPC_SHADOW_PKCS5_PBKDF2_HMAC_SHA1
+#define EVP_PKEY_print_params GRPC_SHADOW_EVP_PKEY_print_params
+#define EVP_PKEY_print_private GRPC_SHADOW_EVP_PKEY_print_private
+#define EVP_PKEY_print_public GRPC_SHADOW_EVP_PKEY_print_public
+#define EVP_PBE_scrypt GRPC_SHADOW_EVP_PBE_scrypt
+#define EVP_SignFinal GRPC_SHADOW_EVP_SignFinal
+#define EVP_SignInit GRPC_SHADOW_EVP_SignInit
+#define EVP_SignInit_ex GRPC_SHADOW_EVP_SignInit_ex
+#define EVP_SignUpdate GRPC_SHADOW_EVP_SignUpdate
+#define EVP_VerifyFinal GRPC_SHADOW_EVP_VerifyFinal
+#define EVP_VerifyInit GRPC_SHADOW_EVP_VerifyInit
+#define EVP_VerifyInit_ex GRPC_SHADOW_EVP_VerifyInit_ex
+#define EVP_VerifyUpdate GRPC_SHADOW_EVP_VerifyUpdate
+#define HKDF GRPC_SHADOW_HKDF
+#define HKDF_expand GRPC_SHADOW_HKDF_expand
+#define HKDF_extract GRPC_SHADOW_HKDF_extract
+#define PEM_read_DSAPrivateKey GRPC_SHADOW_PEM_read_DSAPrivateKey
+#define PEM_read_DSA_PUBKEY GRPC_SHADOW_PEM_read_DSA_PUBKEY
+#define PEM_read_DSAparams GRPC_SHADOW_PEM_read_DSAparams
+#define PEM_read_ECPrivateKey GRPC_SHADOW_PEM_read_ECPrivateKey
+#define PEM_read_EC_PUBKEY GRPC_SHADOW_PEM_read_EC_PUBKEY
+#define PEM_read_PUBKEY GRPC_SHADOW_PEM_read_PUBKEY
+#define PEM_read_RSAPrivateKey GRPC_SHADOW_PEM_read_RSAPrivateKey
+#define PEM_read_RSAPublicKey GRPC_SHADOW_PEM_read_RSAPublicKey
+#define PEM_read_RSA_PUBKEY GRPC_SHADOW_PEM_read_RSA_PUBKEY
+#define PEM_read_X509_CRL GRPC_SHADOW_PEM_read_X509_CRL
+#define PEM_read_X509_REQ GRPC_SHADOW_PEM_read_X509_REQ
+#define PEM_read_bio_DSAPrivateKey GRPC_SHADOW_PEM_read_bio_DSAPrivateKey
+#define PEM_read_bio_DSA_PUBKEY GRPC_SHADOW_PEM_read_bio_DSA_PUBKEY
+#define PEM_read_bio_DSAparams GRPC_SHADOW_PEM_read_bio_DSAparams
+#define PEM_read_bio_ECPrivateKey GRPC_SHADOW_PEM_read_bio_ECPrivateKey
+#define PEM_read_bio_EC_PUBKEY GRPC_SHADOW_PEM_read_bio_EC_PUBKEY
+#define PEM_read_bio_PUBKEY GRPC_SHADOW_PEM_read_bio_PUBKEY
+#define PEM_read_bio_RSAPrivateKey GRPC_SHADOW_PEM_read_bio_RSAPrivateKey
+#define PEM_read_bio_RSAPublicKey GRPC_SHADOW_PEM_read_bio_RSAPublicKey
+#define PEM_read_bio_RSA_PUBKEY GRPC_SHADOW_PEM_read_bio_RSA_PUBKEY
+#define PEM_read_bio_X509_CRL GRPC_SHADOW_PEM_read_bio_X509_CRL
+#define PEM_read_bio_X509_REQ GRPC_SHADOW_PEM_read_bio_X509_REQ
+#define PEM_write_DHparams GRPC_SHADOW_PEM_write_DHparams
+#define PEM_write_DSAPrivateKey GRPC_SHADOW_PEM_write_DSAPrivateKey
+#define PEM_write_DSA_PUBKEY GRPC_SHADOW_PEM_write_DSA_PUBKEY
+#define PEM_write_DSAparams GRPC_SHADOW_PEM_write_DSAparams
+#define PEM_write_ECPrivateKey GRPC_SHADOW_PEM_write_ECPrivateKey
+#define PEM_write_EC_PUBKEY GRPC_SHADOW_PEM_write_EC_PUBKEY
+#define PEM_write_PUBKEY GRPC_SHADOW_PEM_write_PUBKEY
+#define PEM_write_RSAPrivateKey GRPC_SHADOW_PEM_write_RSAPrivateKey
+#define PEM_write_RSAPublicKey GRPC_SHADOW_PEM_write_RSAPublicKey
+#define PEM_write_RSA_PUBKEY GRPC_SHADOW_PEM_write_RSA_PUBKEY
+#define PEM_write_X509_CRL GRPC_SHADOW_PEM_write_X509_CRL
+#define PEM_write_X509_REQ GRPC_SHADOW_PEM_write_X509_REQ
+#define PEM_write_X509_REQ_NEW GRPC_SHADOW_PEM_write_X509_REQ_NEW
+#define PEM_write_bio_DHparams GRPC_SHADOW_PEM_write_bio_DHparams
+#define PEM_write_bio_DSAPrivateKey GRPC_SHADOW_PEM_write_bio_DSAPrivateKey
+#define PEM_write_bio_DSA_PUBKEY GRPC_SHADOW_PEM_write_bio_DSA_PUBKEY
+#define PEM_write_bio_DSAparams GRPC_SHADOW_PEM_write_bio_DSAparams
+#define PEM_write_bio_ECPrivateKey GRPC_SHADOW_PEM_write_bio_ECPrivateKey
+#define PEM_write_bio_EC_PUBKEY GRPC_SHADOW_PEM_write_bio_EC_PUBKEY
+#define PEM_write_bio_PUBKEY GRPC_SHADOW_PEM_write_bio_PUBKEY
+#define PEM_write_bio_RSAPrivateKey GRPC_SHADOW_PEM_write_bio_RSAPrivateKey
+#define PEM_write_bio_RSAPublicKey GRPC_SHADOW_PEM_write_bio_RSAPublicKey
+#define PEM_write_bio_RSA_PUBKEY GRPC_SHADOW_PEM_write_bio_RSA_PUBKEY
+#define PEM_write_bio_X509_CRL GRPC_SHADOW_PEM_write_bio_X509_CRL
+#define PEM_write_bio_X509_REQ GRPC_SHADOW_PEM_write_bio_X509_REQ
+#define PEM_write_bio_X509_REQ_NEW GRPC_SHADOW_PEM_write_bio_X509_REQ_NEW
+#define PEM_X509_INFO_read GRPC_SHADOW_PEM_X509_INFO_read
+#define PEM_X509_INFO_read_bio GRPC_SHADOW_PEM_X509_INFO_read_bio
+#define PEM_X509_INFO_write_bio GRPC_SHADOW_PEM_X509_INFO_write_bio
+#define PEM_ASN1_read GRPC_SHADOW_PEM_ASN1_read
+#define PEM_ASN1_write GRPC_SHADOW_PEM_ASN1_write
+#define PEM_ASN1_write_bio GRPC_SHADOW_PEM_ASN1_write_bio
+#define PEM_bytes_read_bio GRPC_SHADOW_PEM_bytes_read_bio
+#define PEM_def_callback GRPC_SHADOW_PEM_def_callback
+#define PEM_dek_info GRPC_SHADOW_PEM_dek_info
+#define PEM_do_header GRPC_SHADOW_PEM_do_header
+#define PEM_get_EVP_CIPHER_INFO GRPC_SHADOW_PEM_get_EVP_CIPHER_INFO
+#define PEM_proc_type GRPC_SHADOW_PEM_proc_type
+#define PEM_read GRPC_SHADOW_PEM_read
+#define PEM_read_bio GRPC_SHADOW_PEM_read_bio
+#define PEM_write GRPC_SHADOW_PEM_write
+#define PEM_write_bio GRPC_SHADOW_PEM_write_bio
+#define PEM_ASN1_read_bio GRPC_SHADOW_PEM_ASN1_read_bio
+#define PEM_read_PKCS8 GRPC_SHADOW_PEM_read_PKCS8
+#define PEM_read_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_PEM_read_PKCS8_PRIV_KEY_INFO
+#define PEM_read_bio_PKCS8 GRPC_SHADOW_PEM_read_bio_PKCS8
+#define PEM_read_bio_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_PEM_read_bio_PKCS8_PRIV_KEY_INFO
+#define PEM_write_PKCS8 GRPC_SHADOW_PEM_write_PKCS8
+#define PEM_write_PKCS8PrivateKey GRPC_SHADOW_PEM_write_PKCS8PrivateKey
+#define PEM_write_PKCS8PrivateKey_nid GRPC_SHADOW_PEM_write_PKCS8PrivateKey_nid
+#define PEM_write_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_PEM_write_PKCS8_PRIV_KEY_INFO
+#define PEM_write_bio_PKCS8 GRPC_SHADOW_PEM_write_bio_PKCS8
+#define PEM_write_bio_PKCS8PrivateKey GRPC_SHADOW_PEM_write_bio_PKCS8PrivateKey
+#define PEM_write_bio_PKCS8PrivateKey_nid GRPC_SHADOW_PEM_write_bio_PKCS8PrivateKey_nid
+#define PEM_write_bio_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_PEM_write_bio_PKCS8_PRIV_KEY_INFO
+#define d2i_PKCS8PrivateKey_bio GRPC_SHADOW_d2i_PKCS8PrivateKey_bio
+#define d2i_PKCS8PrivateKey_fp GRPC_SHADOW_d2i_PKCS8PrivateKey_fp
+#define i2d_PKCS8PrivateKey_bio GRPC_SHADOW_i2d_PKCS8PrivateKey_bio
+#define i2d_PKCS8PrivateKey_fp GRPC_SHADOW_i2d_PKCS8PrivateKey_fp
+#define i2d_PKCS8PrivateKey_nid_bio GRPC_SHADOW_i2d_PKCS8PrivateKey_nid_bio
+#define i2d_PKCS8PrivateKey_nid_fp GRPC_SHADOW_i2d_PKCS8PrivateKey_nid_fp
+#define PEM_read_DHparams GRPC_SHADOW_PEM_read_DHparams
+#define PEM_read_PrivateKey GRPC_SHADOW_PEM_read_PrivateKey
+#define PEM_read_bio_DHparams GRPC_SHADOW_PEM_read_bio_DHparams
+#define PEM_read_bio_PrivateKey GRPC_SHADOW_PEM_read_bio_PrivateKey
+#define PEM_write_PrivateKey GRPC_SHADOW_PEM_write_PrivateKey
+#define PEM_write_bio_PrivateKey GRPC_SHADOW_PEM_write_bio_PrivateKey
+#define PEM_read_X509 GRPC_SHADOW_PEM_read_X509
+#define PEM_read_bio_X509 GRPC_SHADOW_PEM_read_bio_X509
+#define PEM_write_X509 GRPC_SHADOW_PEM_write_X509
+#define PEM_write_bio_X509 GRPC_SHADOW_PEM_write_bio_X509
+#define PEM_read_X509_AUX GRPC_SHADOW_PEM_read_X509_AUX
+#define PEM_read_bio_X509_AUX GRPC_SHADOW_PEM_read_bio_X509_AUX
+#define PEM_write_X509_AUX GRPC_SHADOW_PEM_write_X509_AUX
+#define PEM_write_bio_X509_AUX GRPC_SHADOW_PEM_write_bio_X509_AUX
+#define ASN1_digest GRPC_SHADOW_ASN1_digest
+#define ASN1_item_digest GRPC_SHADOW_ASN1_item_digest
+#define ASN1_item_sign GRPC_SHADOW_ASN1_item_sign
+#define ASN1_item_sign_ctx GRPC_SHADOW_ASN1_item_sign_ctx
+#define ASN1_STRING_print_ex GRPC_SHADOW_ASN1_STRING_print_ex
+#define ASN1_STRING_print_ex_fp GRPC_SHADOW_ASN1_STRING_print_ex_fp
+#define ASN1_STRING_to_UTF8 GRPC_SHADOW_ASN1_STRING_to_UTF8
+#define X509_NAME_print_ex GRPC_SHADOW_X509_NAME_print_ex
+#define X509_NAME_print_ex_fp GRPC_SHADOW_X509_NAME_print_ex_fp
+#define ASN1_item_verify GRPC_SHADOW_ASN1_item_verify
+#define x509_digest_sign_algorithm GRPC_SHADOW_x509_digest_sign_algorithm
+#define x509_digest_verify_init GRPC_SHADOW_x509_digest_verify_init
+#define ASN1_generate_nconf GRPC_SHADOW_ASN1_generate_nconf
+#define ASN1_generate_v3 GRPC_SHADOW_ASN1_generate_v3
+#define X509_LOOKUP_hash_dir GRPC_SHADOW_X509_LOOKUP_hash_dir
+#define X509_LOOKUP_file GRPC_SHADOW_X509_LOOKUP_file
+#define X509_load_cert_crl_file GRPC_SHADOW_X509_load_cert_crl_file
+#define X509_load_cert_file GRPC_SHADOW_X509_load_cert_file
+#define X509_load_crl_file GRPC_SHADOW_X509_load_crl_file
+#define i2d_PrivateKey GRPC_SHADOW_i2d_PrivateKey
+#define RSA_PSS_PARAMS_free GRPC_SHADOW_RSA_PSS_PARAMS_free
+#define RSA_PSS_PARAMS_it GRPC_SHADOW_RSA_PSS_PARAMS_it
+#define RSA_PSS_PARAMS_new GRPC_SHADOW_RSA_PSS_PARAMS_new
+#define d2i_RSA_PSS_PARAMS GRPC_SHADOW_d2i_RSA_PSS_PARAMS
+#define i2d_RSA_PSS_PARAMS GRPC_SHADOW_i2d_RSA_PSS_PARAMS
+#define x509_print_rsa_pss_params GRPC_SHADOW_x509_print_rsa_pss_params
+#define x509_rsa_ctx_to_pss GRPC_SHADOW_x509_rsa_ctx_to_pss
+#define x509_rsa_pss_to_ctx GRPC_SHADOW_x509_rsa_pss_to_ctx
+#define X509_CRL_print GRPC_SHADOW_X509_CRL_print
+#define X509_CRL_print_fp GRPC_SHADOW_X509_CRL_print_fp
+#define X509_REQ_print GRPC_SHADOW_X509_REQ_print
+#define X509_REQ_print_ex GRPC_SHADOW_X509_REQ_print_ex
+#define X509_REQ_print_fp GRPC_SHADOW_X509_REQ_print_fp
+#define ASN1_GENERALIZEDTIME_print GRPC_SHADOW_ASN1_GENERALIZEDTIME_print
+#define ASN1_STRING_print GRPC_SHADOW_ASN1_STRING_print
+#define ASN1_TIME_print GRPC_SHADOW_ASN1_TIME_print
+#define ASN1_UTCTIME_print GRPC_SHADOW_ASN1_UTCTIME_print
+#define X509_NAME_print GRPC_SHADOW_X509_NAME_print
+#define X509_ocspid_print GRPC_SHADOW_X509_ocspid_print
+#define X509_print GRPC_SHADOW_X509_print
+#define X509_print_ex GRPC_SHADOW_X509_print_ex
+#define X509_print_ex_fp GRPC_SHADOW_X509_print_ex_fp
+#define X509_print_fp GRPC_SHADOW_X509_print_fp
+#define X509_signature_print GRPC_SHADOW_X509_signature_print
+#define X509_CERT_AUX_print GRPC_SHADOW_X509_CERT_AUX_print
+#define PKCS8_pkey_get0 GRPC_SHADOW_PKCS8_pkey_get0
+#define PKCS8_pkey_set0 GRPC_SHADOW_PKCS8_pkey_set0
+#define X509_signature_dump GRPC_SHADOW_X509_signature_dump
+#define X509_ATTRIBUTE_count GRPC_SHADOW_X509_ATTRIBUTE_count
+#define X509_ATTRIBUTE_create_by_NID GRPC_SHADOW_X509_ATTRIBUTE_create_by_NID
+#define X509_ATTRIBUTE_create_by_OBJ GRPC_SHADOW_X509_ATTRIBUTE_create_by_OBJ
+#define X509_ATTRIBUTE_create_by_txt GRPC_SHADOW_X509_ATTRIBUTE_create_by_txt
+#define X509_ATTRIBUTE_get0_data GRPC_SHADOW_X509_ATTRIBUTE_get0_data
+#define X509_ATTRIBUTE_get0_object GRPC_SHADOW_X509_ATTRIBUTE_get0_object
+#define X509_ATTRIBUTE_get0_type GRPC_SHADOW_X509_ATTRIBUTE_get0_type
+#define X509_ATTRIBUTE_set1_data GRPC_SHADOW_X509_ATTRIBUTE_set1_data
+#define X509_ATTRIBUTE_set1_object GRPC_SHADOW_X509_ATTRIBUTE_set1_object
+#define X509at_add1_attr GRPC_SHADOW_X509at_add1_attr
+#define X509at_add1_attr_by_NID GRPC_SHADOW_X509at_add1_attr_by_NID
+#define X509at_add1_attr_by_OBJ GRPC_SHADOW_X509at_add1_attr_by_OBJ
+#define X509at_add1_attr_by_txt GRPC_SHADOW_X509at_add1_attr_by_txt
+#define X509at_delete_attr GRPC_SHADOW_X509at_delete_attr
+#define X509at_get0_data_by_OBJ GRPC_SHADOW_X509at_get0_data_by_OBJ
+#define X509at_get_attr GRPC_SHADOW_X509at_get_attr
+#define X509at_get_attr_by_NID GRPC_SHADOW_X509at_get_attr_by_NID
+#define X509at_get_attr_by_OBJ GRPC_SHADOW_X509at_get_attr_by_OBJ
+#define X509at_get_attr_count GRPC_SHADOW_X509at_get_attr_count
+#define X509_CRL_check_suiteb GRPC_SHADOW_X509_CRL_check_suiteb
+#define X509_CRL_cmp GRPC_SHADOW_X509_CRL_cmp
+#define X509_CRL_match GRPC_SHADOW_X509_CRL_match
+#define X509_NAME_cmp GRPC_SHADOW_X509_NAME_cmp
+#define X509_NAME_hash GRPC_SHADOW_X509_NAME_hash
+#define X509_NAME_hash_old GRPC_SHADOW_X509_NAME_hash_old
+#define X509_chain_check_suiteb GRPC_SHADOW_X509_chain_check_suiteb
+#define X509_chain_up_ref GRPC_SHADOW_X509_chain_up_ref
+#define X509_check_private_key GRPC_SHADOW_X509_check_private_key
+#define X509_cmp GRPC_SHADOW_X509_cmp
+#define X509_find_by_issuer_and_serial GRPC_SHADOW_X509_find_by_issuer_and_serial
+#define X509_find_by_subject GRPC_SHADOW_X509_find_by_subject
+#define X509_get0_pubkey_bitstr GRPC_SHADOW_X509_get0_pubkey_bitstr
+#define X509_get_issuer_name GRPC_SHADOW_X509_get_issuer_name
+#define X509_get_pubkey GRPC_SHADOW_X509_get_pubkey
+#define X509_get_serialNumber GRPC_SHADOW_X509_get_serialNumber
+#define X509_get_subject_name GRPC_SHADOW_X509_get_subject_name
+#define X509_issuer_and_serial_cmp GRPC_SHADOW_X509_issuer_and_serial_cmp
+#define X509_issuer_and_serial_hash GRPC_SHADOW_X509_issuer_and_serial_hash
+#define X509_issuer_name_cmp GRPC_SHADOW_X509_issuer_name_cmp
+#define X509_issuer_name_hash GRPC_SHADOW_X509_issuer_name_hash
+#define X509_issuer_name_hash_old GRPC_SHADOW_X509_issuer_name_hash_old
+#define X509_subject_name_cmp GRPC_SHADOW_X509_subject_name_cmp
+#define X509_subject_name_hash GRPC_SHADOW_X509_subject_name_hash
+#define X509_subject_name_hash_old GRPC_SHADOW_X509_subject_name_hash_old
+#define X509_STORE_load_locations GRPC_SHADOW_X509_STORE_load_locations
+#define X509_STORE_set_default_paths GRPC_SHADOW_X509_STORE_set_default_paths
+#define X509_get_default_cert_area GRPC_SHADOW_X509_get_default_cert_area
+#define X509_get_default_cert_dir GRPC_SHADOW_X509_get_default_cert_dir
+#define X509_get_default_cert_dir_env GRPC_SHADOW_X509_get_default_cert_dir_env
+#define X509_get_default_cert_file GRPC_SHADOW_X509_get_default_cert_file
+#define X509_get_default_cert_file_env GRPC_SHADOW_X509_get_default_cert_file_env
+#define X509_get_default_private_dir GRPC_SHADOW_X509_get_default_private_dir
+#define X509_CRL_add1_ext_i2d GRPC_SHADOW_X509_CRL_add1_ext_i2d
+#define X509_CRL_add_ext GRPC_SHADOW_X509_CRL_add_ext
+#define X509_CRL_delete_ext GRPC_SHADOW_X509_CRL_delete_ext
+#define X509_CRL_get_ext GRPC_SHADOW_X509_CRL_get_ext
+#define X509_CRL_get_ext_by_NID GRPC_SHADOW_X509_CRL_get_ext_by_NID
+#define X509_CRL_get_ext_by_OBJ GRPC_SHADOW_X509_CRL_get_ext_by_OBJ
+#define X509_CRL_get_ext_by_critical GRPC_SHADOW_X509_CRL_get_ext_by_critical
+#define X509_CRL_get_ext_count GRPC_SHADOW_X509_CRL_get_ext_count
+#define X509_CRL_get_ext_d2i GRPC_SHADOW_X509_CRL_get_ext_d2i
+#define X509_REVOKED_add1_ext_i2d GRPC_SHADOW_X509_REVOKED_add1_ext_i2d
+#define X509_REVOKED_add_ext GRPC_SHADOW_X509_REVOKED_add_ext
+#define X509_REVOKED_delete_ext GRPC_SHADOW_X509_REVOKED_delete_ext
+#define X509_REVOKED_get_ext GRPC_SHADOW_X509_REVOKED_get_ext
+#define X509_REVOKED_get_ext_by_NID GRPC_SHADOW_X509_REVOKED_get_ext_by_NID
+#define X509_REVOKED_get_ext_by_OBJ GRPC_SHADOW_X509_REVOKED_get_ext_by_OBJ
+#define X509_REVOKED_get_ext_by_critical GRPC_SHADOW_X509_REVOKED_get_ext_by_critical
+#define X509_REVOKED_get_ext_count GRPC_SHADOW_X509_REVOKED_get_ext_count
+#define X509_REVOKED_get_ext_d2i GRPC_SHADOW_X509_REVOKED_get_ext_d2i
+#define X509_add1_ext_i2d GRPC_SHADOW_X509_add1_ext_i2d
+#define X509_add_ext GRPC_SHADOW_X509_add_ext
+#define X509_delete_ext GRPC_SHADOW_X509_delete_ext
+#define X509_get_ext GRPC_SHADOW_X509_get_ext
+#define X509_get_ext_by_NID GRPC_SHADOW_X509_get_ext_by_NID
+#define X509_get_ext_by_OBJ GRPC_SHADOW_X509_get_ext_by_OBJ
+#define X509_get_ext_by_critical GRPC_SHADOW_X509_get_ext_by_critical
+#define X509_get_ext_count GRPC_SHADOW_X509_get_ext_count
+#define X509_get_ext_d2i GRPC_SHADOW_X509_get_ext_d2i
+#define X509_LOOKUP_by_alias GRPC_SHADOW_X509_LOOKUP_by_alias
+#define X509_LOOKUP_by_fingerprint GRPC_SHADOW_X509_LOOKUP_by_fingerprint
+#define X509_LOOKUP_by_issuer_serial GRPC_SHADOW_X509_LOOKUP_by_issuer_serial
+#define X509_LOOKUP_by_subject GRPC_SHADOW_X509_LOOKUP_by_subject
+#define X509_LOOKUP_ctrl GRPC_SHADOW_X509_LOOKUP_ctrl
+#define X509_LOOKUP_free GRPC_SHADOW_X509_LOOKUP_free
+#define X509_LOOKUP_init GRPC_SHADOW_X509_LOOKUP_init
+#define X509_LOOKUP_new GRPC_SHADOW_X509_LOOKUP_new
+#define X509_LOOKUP_shutdown GRPC_SHADOW_X509_LOOKUP_shutdown
+#define X509_OBJECT_free_contents GRPC_SHADOW_X509_OBJECT_free_contents
+#define X509_OBJECT_get0_X509 GRPC_SHADOW_X509_OBJECT_get0_X509
+#define X509_OBJECT_get_type GRPC_SHADOW_X509_OBJECT_get_type
+#define X509_OBJECT_idx_by_subject GRPC_SHADOW_X509_OBJECT_idx_by_subject
+#define X509_OBJECT_retrieve_by_subject GRPC_SHADOW_X509_OBJECT_retrieve_by_subject
+#define X509_OBJECT_retrieve_match GRPC_SHADOW_X509_OBJECT_retrieve_match
+#define X509_OBJECT_up_ref_count GRPC_SHADOW_X509_OBJECT_up_ref_count
+#define X509_STORE_CTX_get0_store GRPC_SHADOW_X509_STORE_CTX_get0_store
+#define X509_STORE_CTX_get1_issuer GRPC_SHADOW_X509_STORE_CTX_get1_issuer
+#define X509_STORE_add_cert GRPC_SHADOW_X509_STORE_add_cert
+#define X509_STORE_add_crl GRPC_SHADOW_X509_STORE_add_crl
+#define X509_STORE_add_lookup GRPC_SHADOW_X509_STORE_add_lookup
+#define X509_STORE_free GRPC_SHADOW_X509_STORE_free
+#define X509_STORE_get0_objects GRPC_SHADOW_X509_STORE_get0_objects
+#define X509_STORE_get0_param GRPC_SHADOW_X509_STORE_get0_param
+#define X509_STORE_get1_certs GRPC_SHADOW_X509_STORE_get1_certs
+#define X509_STORE_get1_crls GRPC_SHADOW_X509_STORE_get1_crls
+#define X509_STORE_get_by_subject GRPC_SHADOW_X509_STORE_get_by_subject
+#define X509_STORE_new GRPC_SHADOW_X509_STORE_new
+#define X509_STORE_set0_additional_untrusted GRPC_SHADOW_X509_STORE_set0_additional_untrusted
+#define X509_STORE_set1_param GRPC_SHADOW_X509_STORE_set1_param
+#define X509_STORE_set_depth GRPC_SHADOW_X509_STORE_set_depth
+#define X509_STORE_set_flags GRPC_SHADOW_X509_STORE_set_flags
+#define X509_STORE_set_lookup_crls_cb GRPC_SHADOW_X509_STORE_set_lookup_crls_cb
+#define X509_STORE_set_purpose GRPC_SHADOW_X509_STORE_set_purpose
+#define X509_STORE_set_trust GRPC_SHADOW_X509_STORE_set_trust
+#define X509_STORE_set_verify_cb GRPC_SHADOW_X509_STORE_set_verify_cb
+#define X509_STORE_up_ref GRPC_SHADOW_X509_STORE_up_ref
+#define X509_NAME_oneline GRPC_SHADOW_X509_NAME_oneline
+#define X509_REQ_to_X509 GRPC_SHADOW_X509_REQ_to_X509
+#define X509_REQ_add1_attr GRPC_SHADOW_X509_REQ_add1_attr
+#define X509_REQ_add1_attr_by_NID GRPC_SHADOW_X509_REQ_add1_attr_by_NID
+#define X509_REQ_add1_attr_by_OBJ GRPC_SHADOW_X509_REQ_add1_attr_by_OBJ
+#define X509_REQ_add1_attr_by_txt GRPC_SHADOW_X509_REQ_add1_attr_by_txt
+#define X509_REQ_add_extensions GRPC_SHADOW_X509_REQ_add_extensions
+#define X509_REQ_add_extensions_nid GRPC_SHADOW_X509_REQ_add_extensions_nid
+#define X509_REQ_check_private_key GRPC_SHADOW_X509_REQ_check_private_key
+#define X509_REQ_delete_attr GRPC_SHADOW_X509_REQ_delete_attr
+#define X509_REQ_extension_nid GRPC_SHADOW_X509_REQ_extension_nid
+#define X509_REQ_get_attr GRPC_SHADOW_X509_REQ_get_attr
+#define X509_REQ_get_attr_by_NID GRPC_SHADOW_X509_REQ_get_attr_by_NID
+#define X509_REQ_get_attr_by_OBJ GRPC_SHADOW_X509_REQ_get_attr_by_OBJ
+#define X509_REQ_get_attr_count GRPC_SHADOW_X509_REQ_get_attr_count
+#define X509_REQ_get_extension_nids GRPC_SHADOW_X509_REQ_get_extension_nids
+#define X509_REQ_get_extensions GRPC_SHADOW_X509_REQ_get_extensions
+#define X509_REQ_get_pubkey GRPC_SHADOW_X509_REQ_get_pubkey
+#define X509_REQ_set_extension_nids GRPC_SHADOW_X509_REQ_set_extension_nids
+#define X509_to_X509_REQ GRPC_SHADOW_X509_to_X509_REQ
+#define X509_get0_extensions GRPC_SHADOW_X509_get0_extensions
+#define X509_get0_notAfter GRPC_SHADOW_X509_get0_notAfter
+#define X509_get0_notBefore GRPC_SHADOW_X509_get0_notBefore
+#define X509_set_issuer_name GRPC_SHADOW_X509_set_issuer_name
+#define X509_set_notAfter GRPC_SHADOW_X509_set_notAfter
+#define X509_set_notBefore GRPC_SHADOW_X509_set_notBefore
+#define X509_set_pubkey GRPC_SHADOW_X509_set_pubkey
+#define X509_set_serialNumber GRPC_SHADOW_X509_set_serialNumber
+#define X509_set_subject_name GRPC_SHADOW_X509_set_subject_name
+#define X509_set_version GRPC_SHADOW_X509_set_version
+#define X509_TRUST_add GRPC_SHADOW_X509_TRUST_add
+#define X509_TRUST_cleanup GRPC_SHADOW_X509_TRUST_cleanup
+#define X509_TRUST_get0 GRPC_SHADOW_X509_TRUST_get0
+#define X509_TRUST_get0_name GRPC_SHADOW_X509_TRUST_get0_name
+#define X509_TRUST_get_by_id GRPC_SHADOW_X509_TRUST_get_by_id
+#define X509_TRUST_get_count GRPC_SHADOW_X509_TRUST_get_count
+#define X509_TRUST_get_flags GRPC_SHADOW_X509_TRUST_get_flags
+#define X509_TRUST_get_trust GRPC_SHADOW_X509_TRUST_get_trust
+#define X509_TRUST_set GRPC_SHADOW_X509_TRUST_set
+#define X509_TRUST_set_default GRPC_SHADOW_X509_TRUST_set_default
+#define X509_check_trust GRPC_SHADOW_X509_check_trust
+#define X509_verify_cert_error_string GRPC_SHADOW_X509_verify_cert_error_string
+#define X509_EXTENSION_create_by_NID GRPC_SHADOW_X509_EXTENSION_create_by_NID
+#define X509_EXTENSION_create_by_OBJ GRPC_SHADOW_X509_EXTENSION_create_by_OBJ
+#define X509_EXTENSION_get_critical GRPC_SHADOW_X509_EXTENSION_get_critical
+#define X509_EXTENSION_get_data GRPC_SHADOW_X509_EXTENSION_get_data
+#define X509_EXTENSION_get_object GRPC_SHADOW_X509_EXTENSION_get_object
+#define X509_EXTENSION_set_critical GRPC_SHADOW_X509_EXTENSION_set_critical
+#define X509_EXTENSION_set_data GRPC_SHADOW_X509_EXTENSION_set_data
+#define X509_EXTENSION_set_object GRPC_SHADOW_X509_EXTENSION_set_object
+#define X509v3_add_ext GRPC_SHADOW_X509v3_add_ext
+#define X509v3_delete_ext GRPC_SHADOW_X509v3_delete_ext
+#define X509v3_get_ext GRPC_SHADOW_X509v3_get_ext
+#define X509v3_get_ext_by_NID GRPC_SHADOW_X509v3_get_ext_by_NID
+#define X509v3_get_ext_by_OBJ GRPC_SHADOW_X509v3_get_ext_by_OBJ
+#define X509v3_get_ext_by_critical GRPC_SHADOW_X509v3_get_ext_by_critical
+#define X509v3_get_ext_count GRPC_SHADOW_X509v3_get_ext_count
+#define X509_CRL_diff GRPC_SHADOW_X509_CRL_diff
+#define X509_STORE_CTX_cleanup GRPC_SHADOW_X509_STORE_CTX_cleanup
+#define X509_STORE_CTX_free GRPC_SHADOW_X509_STORE_CTX_free
+#define X509_STORE_CTX_get0_current_crl GRPC_SHADOW_X509_STORE_CTX_get0_current_crl
+#define X509_STORE_CTX_get0_current_issuer GRPC_SHADOW_X509_STORE_CTX_get0_current_issuer
+#define X509_STORE_CTX_get0_param GRPC_SHADOW_X509_STORE_CTX_get0_param
+#define X509_STORE_CTX_get0_parent_ctx GRPC_SHADOW_X509_STORE_CTX_get0_parent_ctx
+#define X509_STORE_CTX_get0_policy_tree GRPC_SHADOW_X509_STORE_CTX_get0_policy_tree
+#define X509_STORE_CTX_get0_untrusted GRPC_SHADOW_X509_STORE_CTX_get0_untrusted
+#define X509_STORE_CTX_get1_chain GRPC_SHADOW_X509_STORE_CTX_get1_chain
+#define X509_STORE_CTX_get_chain GRPC_SHADOW_X509_STORE_CTX_get_chain
+#define X509_STORE_CTX_get_current_cert GRPC_SHADOW_X509_STORE_CTX_get_current_cert
+#define X509_STORE_CTX_get_error GRPC_SHADOW_X509_STORE_CTX_get_error
+#define X509_STORE_CTX_get_error_depth GRPC_SHADOW_X509_STORE_CTX_get_error_depth
+#define X509_STORE_CTX_get_ex_data GRPC_SHADOW_X509_STORE_CTX_get_ex_data
+#define X509_STORE_CTX_get_ex_new_index GRPC_SHADOW_X509_STORE_CTX_get_ex_new_index
+#define X509_STORE_CTX_get_explicit_policy GRPC_SHADOW_X509_STORE_CTX_get_explicit_policy
+#define X509_STORE_CTX_init GRPC_SHADOW_X509_STORE_CTX_init
+#define X509_STORE_CTX_new GRPC_SHADOW_X509_STORE_CTX_new
+#define X509_STORE_CTX_purpose_inherit GRPC_SHADOW_X509_STORE_CTX_purpose_inherit
+#define X509_STORE_CTX_set0_crls GRPC_SHADOW_X509_STORE_CTX_set0_crls
+#define X509_STORE_CTX_set0_param GRPC_SHADOW_X509_STORE_CTX_set0_param
+#define X509_STORE_CTX_set_cert GRPC_SHADOW_X509_STORE_CTX_set_cert
+#define X509_STORE_CTX_set_chain GRPC_SHADOW_X509_STORE_CTX_set_chain
+#define X509_STORE_CTX_set_default GRPC_SHADOW_X509_STORE_CTX_set_default
+#define X509_STORE_CTX_set_depth GRPC_SHADOW_X509_STORE_CTX_set_depth
+#define X509_STORE_CTX_set_error GRPC_SHADOW_X509_STORE_CTX_set_error
+#define X509_STORE_CTX_set_ex_data GRPC_SHADOW_X509_STORE_CTX_set_ex_data
+#define X509_STORE_CTX_set_flags GRPC_SHADOW_X509_STORE_CTX_set_flags
+#define X509_STORE_CTX_set_purpose GRPC_SHADOW_X509_STORE_CTX_set_purpose
+#define X509_STORE_CTX_set_time GRPC_SHADOW_X509_STORE_CTX_set_time
+#define X509_STORE_CTX_set_trust GRPC_SHADOW_X509_STORE_CTX_set_trust
+#define X509_STORE_CTX_set_verify_cb GRPC_SHADOW_X509_STORE_CTX_set_verify_cb
+#define X509_STORE_CTX_trusted_stack GRPC_SHADOW_X509_STORE_CTX_trusted_stack
+#define X509_STORE_CTX_zero GRPC_SHADOW_X509_STORE_CTX_zero
+#define X509_cmp_current_time GRPC_SHADOW_X509_cmp_current_time
+#define X509_cmp_time GRPC_SHADOW_X509_cmp_time
+#define X509_gmtime_adj GRPC_SHADOW_X509_gmtime_adj
+#define X509_time_adj GRPC_SHADOW_X509_time_adj
+#define X509_time_adj_ex GRPC_SHADOW_X509_time_adj_ex
+#define X509_verify_cert GRPC_SHADOW_X509_verify_cert
+#define X509_VERIFY_PARAM_add0_policy GRPC_SHADOW_X509_VERIFY_PARAM_add0_policy
+#define X509_VERIFY_PARAM_add0_table GRPC_SHADOW_X509_VERIFY_PARAM_add0_table
+#define X509_VERIFY_PARAM_add1_host GRPC_SHADOW_X509_VERIFY_PARAM_add1_host
+#define X509_VERIFY_PARAM_clear_flags GRPC_SHADOW_X509_VERIFY_PARAM_clear_flags
+#define X509_VERIFY_PARAM_free GRPC_SHADOW_X509_VERIFY_PARAM_free
+#define X509_VERIFY_PARAM_get0 GRPC_SHADOW_X509_VERIFY_PARAM_get0
+#define X509_VERIFY_PARAM_get0_name GRPC_SHADOW_X509_VERIFY_PARAM_get0_name
+#define X509_VERIFY_PARAM_get0_peername GRPC_SHADOW_X509_VERIFY_PARAM_get0_peername
+#define X509_VERIFY_PARAM_get_count GRPC_SHADOW_X509_VERIFY_PARAM_get_count
+#define X509_VERIFY_PARAM_get_depth GRPC_SHADOW_X509_VERIFY_PARAM_get_depth
+#define X509_VERIFY_PARAM_get_flags GRPC_SHADOW_X509_VERIFY_PARAM_get_flags
+#define X509_VERIFY_PARAM_inherit GRPC_SHADOW_X509_VERIFY_PARAM_inherit
+#define X509_VERIFY_PARAM_lookup GRPC_SHADOW_X509_VERIFY_PARAM_lookup
+#define X509_VERIFY_PARAM_new GRPC_SHADOW_X509_VERIFY_PARAM_new
+#define X509_VERIFY_PARAM_set1 GRPC_SHADOW_X509_VERIFY_PARAM_set1
+#define X509_VERIFY_PARAM_set1_email GRPC_SHADOW_X509_VERIFY_PARAM_set1_email
+#define X509_VERIFY_PARAM_set1_host GRPC_SHADOW_X509_VERIFY_PARAM_set1_host
+#define X509_VERIFY_PARAM_set1_ip GRPC_SHADOW_X509_VERIFY_PARAM_set1_ip
+#define X509_VERIFY_PARAM_set1_ip_asc GRPC_SHADOW_X509_VERIFY_PARAM_set1_ip_asc
+#define X509_VERIFY_PARAM_set1_name GRPC_SHADOW_X509_VERIFY_PARAM_set1_name
+#define X509_VERIFY_PARAM_set1_policies GRPC_SHADOW_X509_VERIFY_PARAM_set1_policies
+#define X509_VERIFY_PARAM_set_depth GRPC_SHADOW_X509_VERIFY_PARAM_set_depth
+#define X509_VERIFY_PARAM_set_flags GRPC_SHADOW_X509_VERIFY_PARAM_set_flags
+#define X509_VERIFY_PARAM_set_hostflags GRPC_SHADOW_X509_VERIFY_PARAM_set_hostflags
+#define X509_VERIFY_PARAM_set_purpose GRPC_SHADOW_X509_VERIFY_PARAM_set_purpose
+#define X509_VERIFY_PARAM_set_time GRPC_SHADOW_X509_VERIFY_PARAM_set_time
+#define X509_VERIFY_PARAM_set_trust GRPC_SHADOW_X509_VERIFY_PARAM_set_trust
+#define X509_VERIFY_PARAM_table_cleanup GRPC_SHADOW_X509_VERIFY_PARAM_table_cleanup
+#define X509_CRL_set_issuer_name GRPC_SHADOW_X509_CRL_set_issuer_name
+#define X509_CRL_set_lastUpdate GRPC_SHADOW_X509_CRL_set_lastUpdate
+#define X509_CRL_set_nextUpdate GRPC_SHADOW_X509_CRL_set_nextUpdate
+#define X509_CRL_set_version GRPC_SHADOW_X509_CRL_set_version
+#define X509_CRL_sort GRPC_SHADOW_X509_CRL_sort
+#define X509_CRL_up_ref GRPC_SHADOW_X509_CRL_up_ref
+#define X509_REVOKED_set_revocationDate GRPC_SHADOW_X509_REVOKED_set_revocationDate
+#define X509_REVOKED_set_serialNumber GRPC_SHADOW_X509_REVOKED_set_serialNumber
+#define X509_NAME_ENTRY_create_by_NID GRPC_SHADOW_X509_NAME_ENTRY_create_by_NID
+#define X509_NAME_ENTRY_create_by_OBJ GRPC_SHADOW_X509_NAME_ENTRY_create_by_OBJ
+#define X509_NAME_ENTRY_create_by_txt GRPC_SHADOW_X509_NAME_ENTRY_create_by_txt
+#define X509_NAME_ENTRY_get_data GRPC_SHADOW_X509_NAME_ENTRY_get_data
+#define X509_NAME_ENTRY_get_object GRPC_SHADOW_X509_NAME_ENTRY_get_object
+#define X509_NAME_ENTRY_set_data GRPC_SHADOW_X509_NAME_ENTRY_set_data
+#define X509_NAME_ENTRY_set_object GRPC_SHADOW_X509_NAME_ENTRY_set_object
+#define X509_NAME_add_entry GRPC_SHADOW_X509_NAME_add_entry
+#define X509_NAME_add_entry_by_NID GRPC_SHADOW_X509_NAME_add_entry_by_NID
+#define X509_NAME_add_entry_by_OBJ GRPC_SHADOW_X509_NAME_add_entry_by_OBJ
+#define X509_NAME_add_entry_by_txt GRPC_SHADOW_X509_NAME_add_entry_by_txt
+#define X509_NAME_delete_entry GRPC_SHADOW_X509_NAME_delete_entry
+#define X509_NAME_entry_count GRPC_SHADOW_X509_NAME_entry_count
+#define X509_NAME_get_entry GRPC_SHADOW_X509_NAME_get_entry
+#define X509_NAME_get_index_by_NID GRPC_SHADOW_X509_NAME_get_index_by_NID
+#define X509_NAME_get_index_by_OBJ GRPC_SHADOW_X509_NAME_get_index_by_OBJ
+#define X509_NAME_get_text_by_NID GRPC_SHADOW_X509_NAME_get_text_by_NID
+#define X509_NAME_get_text_by_OBJ GRPC_SHADOW_X509_NAME_get_text_by_OBJ
+#define X509_REQ_set_pubkey GRPC_SHADOW_X509_REQ_set_pubkey
+#define X509_REQ_set_subject_name GRPC_SHADOW_X509_REQ_set_subject_name
+#define X509_REQ_set_version GRPC_SHADOW_X509_REQ_set_version
+#define NETSCAPE_SPKI_b64_decode GRPC_SHADOW_NETSCAPE_SPKI_b64_decode
+#define NETSCAPE_SPKI_b64_encode GRPC_SHADOW_NETSCAPE_SPKI_b64_encode
+#define NETSCAPE_SPKI_get_pubkey GRPC_SHADOW_NETSCAPE_SPKI_get_pubkey
+#define NETSCAPE_SPKI_set_pubkey GRPC_SHADOW_NETSCAPE_SPKI_set_pubkey
+#define X509_ALGORS_it GRPC_SHADOW_X509_ALGORS_it
+#define X509_ALGOR_cmp GRPC_SHADOW_X509_ALGOR_cmp
+#define X509_ALGOR_dup GRPC_SHADOW_X509_ALGOR_dup
+#define X509_ALGOR_free GRPC_SHADOW_X509_ALGOR_free
+#define X509_ALGOR_get0 GRPC_SHADOW_X509_ALGOR_get0
+#define X509_ALGOR_it GRPC_SHADOW_X509_ALGOR_it
+#define X509_ALGOR_new GRPC_SHADOW_X509_ALGOR_new
+#define X509_ALGOR_set0 GRPC_SHADOW_X509_ALGOR_set0
+#define X509_ALGOR_set_md GRPC_SHADOW_X509_ALGOR_set_md
+#define d2i_X509_ALGOR GRPC_SHADOW_d2i_X509_ALGOR
+#define d2i_X509_ALGORS GRPC_SHADOW_d2i_X509_ALGORS
+#define i2d_X509_ALGOR GRPC_SHADOW_i2d_X509_ALGOR
+#define i2d_X509_ALGORS GRPC_SHADOW_i2d_X509_ALGORS
+#define NETSCAPE_SPKI_sign GRPC_SHADOW_NETSCAPE_SPKI_sign
+#define NETSCAPE_SPKI_verify GRPC_SHADOW_NETSCAPE_SPKI_verify
+#define X509_CRL_digest GRPC_SHADOW_X509_CRL_digest
+#define X509_CRL_sign GRPC_SHADOW_X509_CRL_sign
+#define X509_CRL_sign_ctx GRPC_SHADOW_X509_CRL_sign_ctx
+#define X509_NAME_digest GRPC_SHADOW_X509_NAME_digest
+#define X509_REQ_digest GRPC_SHADOW_X509_REQ_digest
+#define X509_REQ_sign GRPC_SHADOW_X509_REQ_sign
+#define X509_REQ_sign_ctx GRPC_SHADOW_X509_REQ_sign_ctx
+#define X509_REQ_verify GRPC_SHADOW_X509_REQ_verify
+#define X509_digest GRPC_SHADOW_X509_digest
+#define X509_pubkey_digest GRPC_SHADOW_X509_pubkey_digest
+#define X509_sign GRPC_SHADOW_X509_sign
+#define X509_sign_ctx GRPC_SHADOW_X509_sign_ctx
+#define X509_verify GRPC_SHADOW_X509_verify
+#define d2i_DSAPrivateKey_bio GRPC_SHADOW_d2i_DSAPrivateKey_bio
+#define d2i_DSAPrivateKey_fp GRPC_SHADOW_d2i_DSAPrivateKey_fp
+#define d2i_DSA_PUBKEY_bio GRPC_SHADOW_d2i_DSA_PUBKEY_bio
+#define d2i_DSA_PUBKEY_fp GRPC_SHADOW_d2i_DSA_PUBKEY_fp
+#define d2i_ECPrivateKey_bio GRPC_SHADOW_d2i_ECPrivateKey_bio
+#define d2i_ECPrivateKey_fp GRPC_SHADOW_d2i_ECPrivateKey_fp
+#define d2i_EC_PUBKEY_bio GRPC_SHADOW_d2i_EC_PUBKEY_bio
+#define d2i_EC_PUBKEY_fp GRPC_SHADOW_d2i_EC_PUBKEY_fp
+#define d2i_PKCS8_PRIV_KEY_INFO_bio GRPC_SHADOW_d2i_PKCS8_PRIV_KEY_INFO_bio
+#define d2i_PKCS8_PRIV_KEY_INFO_fp GRPC_SHADOW_d2i_PKCS8_PRIV_KEY_INFO_fp
+#define d2i_PKCS8_bio GRPC_SHADOW_d2i_PKCS8_bio
+#define d2i_PKCS8_fp GRPC_SHADOW_d2i_PKCS8_fp
+#define d2i_PUBKEY_bio GRPC_SHADOW_d2i_PUBKEY_bio
+#define d2i_PUBKEY_fp GRPC_SHADOW_d2i_PUBKEY_fp
+#define d2i_PrivateKey_bio GRPC_SHADOW_d2i_PrivateKey_bio
+#define d2i_PrivateKey_fp GRPC_SHADOW_d2i_PrivateKey_fp
+#define d2i_RSAPrivateKey_bio GRPC_SHADOW_d2i_RSAPrivateKey_bio
+#define d2i_RSAPrivateKey_fp GRPC_SHADOW_d2i_RSAPrivateKey_fp
+#define d2i_RSAPublicKey_bio GRPC_SHADOW_d2i_RSAPublicKey_bio
+#define d2i_RSAPublicKey_fp GRPC_SHADOW_d2i_RSAPublicKey_fp
+#define d2i_RSA_PUBKEY_bio GRPC_SHADOW_d2i_RSA_PUBKEY_bio
+#define d2i_RSA_PUBKEY_fp GRPC_SHADOW_d2i_RSA_PUBKEY_fp
+#define d2i_X509_CRL_bio GRPC_SHADOW_d2i_X509_CRL_bio
+#define d2i_X509_CRL_fp GRPC_SHADOW_d2i_X509_CRL_fp
+#define d2i_X509_REQ_bio GRPC_SHADOW_d2i_X509_REQ_bio
+#define d2i_X509_REQ_fp GRPC_SHADOW_d2i_X509_REQ_fp
+#define d2i_X509_bio GRPC_SHADOW_d2i_X509_bio
+#define d2i_X509_fp GRPC_SHADOW_d2i_X509_fp
+#define i2d_DSAPrivateKey_bio GRPC_SHADOW_i2d_DSAPrivateKey_bio
+#define i2d_DSAPrivateKey_fp GRPC_SHADOW_i2d_DSAPrivateKey_fp
+#define i2d_DSA_PUBKEY_bio GRPC_SHADOW_i2d_DSA_PUBKEY_bio
+#define i2d_DSA_PUBKEY_fp GRPC_SHADOW_i2d_DSA_PUBKEY_fp
+#define i2d_ECPrivateKey_bio GRPC_SHADOW_i2d_ECPrivateKey_bio
+#define i2d_ECPrivateKey_fp GRPC_SHADOW_i2d_ECPrivateKey_fp
+#define i2d_EC_PUBKEY_bio GRPC_SHADOW_i2d_EC_PUBKEY_bio
+#define i2d_EC_PUBKEY_fp GRPC_SHADOW_i2d_EC_PUBKEY_fp
+#define i2d_PKCS8PrivateKeyInfo_bio GRPC_SHADOW_i2d_PKCS8PrivateKeyInfo_bio
+#define i2d_PKCS8PrivateKeyInfo_fp GRPC_SHADOW_i2d_PKCS8PrivateKeyInfo_fp
+#define i2d_PKCS8_PRIV_KEY_INFO_bio GRPC_SHADOW_i2d_PKCS8_PRIV_KEY_INFO_bio
+#define i2d_PKCS8_PRIV_KEY_INFO_fp GRPC_SHADOW_i2d_PKCS8_PRIV_KEY_INFO_fp
+#define i2d_PKCS8_bio GRPC_SHADOW_i2d_PKCS8_bio
+#define i2d_PKCS8_fp GRPC_SHADOW_i2d_PKCS8_fp
+#define i2d_PUBKEY_bio GRPC_SHADOW_i2d_PUBKEY_bio
+#define i2d_PUBKEY_fp GRPC_SHADOW_i2d_PUBKEY_fp
+#define i2d_PrivateKey_bio GRPC_SHADOW_i2d_PrivateKey_bio
+#define i2d_PrivateKey_fp GRPC_SHADOW_i2d_PrivateKey_fp
+#define i2d_RSAPrivateKey_bio GRPC_SHADOW_i2d_RSAPrivateKey_bio
+#define i2d_RSAPrivateKey_fp GRPC_SHADOW_i2d_RSAPrivateKey_fp
+#define i2d_RSAPublicKey_bio GRPC_SHADOW_i2d_RSAPublicKey_bio
+#define i2d_RSAPublicKey_fp GRPC_SHADOW_i2d_RSAPublicKey_fp
+#define i2d_RSA_PUBKEY_bio GRPC_SHADOW_i2d_RSA_PUBKEY_bio
+#define i2d_RSA_PUBKEY_fp GRPC_SHADOW_i2d_RSA_PUBKEY_fp
+#define i2d_X509_CRL_bio GRPC_SHADOW_i2d_X509_CRL_bio
+#define i2d_X509_CRL_fp GRPC_SHADOW_i2d_X509_CRL_fp
+#define i2d_X509_REQ_bio GRPC_SHADOW_i2d_X509_REQ_bio
+#define i2d_X509_REQ_fp GRPC_SHADOW_i2d_X509_REQ_fp
+#define i2d_X509_bio GRPC_SHADOW_i2d_X509_bio
+#define i2d_X509_fp GRPC_SHADOW_i2d_X509_fp
+#define X509_ATTRIBUTE_SET_it GRPC_SHADOW_X509_ATTRIBUTE_SET_it
+#define X509_ATTRIBUTE_create GRPC_SHADOW_X509_ATTRIBUTE_create
+#define X509_ATTRIBUTE_dup GRPC_SHADOW_X509_ATTRIBUTE_dup
+#define X509_ATTRIBUTE_free GRPC_SHADOW_X509_ATTRIBUTE_free
+#define X509_ATTRIBUTE_it GRPC_SHADOW_X509_ATTRIBUTE_it
+#define X509_ATTRIBUTE_new GRPC_SHADOW_X509_ATTRIBUTE_new
+#define d2i_X509_ATTRIBUTE GRPC_SHADOW_d2i_X509_ATTRIBUTE
+#define i2d_X509_ATTRIBUTE GRPC_SHADOW_i2d_X509_ATTRIBUTE
+#define X509_CRL_INFO_free GRPC_SHADOW_X509_CRL_INFO_free
+#define X509_CRL_INFO_it GRPC_SHADOW_X509_CRL_INFO_it
+#define X509_CRL_INFO_new GRPC_SHADOW_X509_CRL_INFO_new
+#define X509_CRL_METHOD_free GRPC_SHADOW_X509_CRL_METHOD_free
+#define X509_CRL_METHOD_new GRPC_SHADOW_X509_CRL_METHOD_new
+#define X509_CRL_add0_revoked GRPC_SHADOW_X509_CRL_add0_revoked
+#define X509_CRL_dup GRPC_SHADOW_X509_CRL_dup
+#define X509_CRL_free GRPC_SHADOW_X509_CRL_free
+#define X509_CRL_get0_by_cert GRPC_SHADOW_X509_CRL_get0_by_cert
+#define X509_CRL_get0_by_serial GRPC_SHADOW_X509_CRL_get0_by_serial
+#define X509_CRL_get_meth_data GRPC_SHADOW_X509_CRL_get_meth_data
+#define X509_CRL_it GRPC_SHADOW_X509_CRL_it
+#define X509_CRL_new GRPC_SHADOW_X509_CRL_new
+#define X509_CRL_set_default_method GRPC_SHADOW_X509_CRL_set_default_method
+#define X509_CRL_set_meth_data GRPC_SHADOW_X509_CRL_set_meth_data
+#define X509_CRL_verify GRPC_SHADOW_X509_CRL_verify
+#define X509_REVOKED_dup GRPC_SHADOW_X509_REVOKED_dup
+#define X509_REVOKED_free GRPC_SHADOW_X509_REVOKED_free
+#define X509_REVOKED_it GRPC_SHADOW_X509_REVOKED_it
+#define X509_REVOKED_new GRPC_SHADOW_X509_REVOKED_new
+#define d2i_X509_CRL GRPC_SHADOW_d2i_X509_CRL
+#define d2i_X509_CRL_INFO GRPC_SHADOW_d2i_X509_CRL_INFO
+#define d2i_X509_REVOKED GRPC_SHADOW_d2i_X509_REVOKED
+#define i2d_X509_CRL GRPC_SHADOW_i2d_X509_CRL
+#define i2d_X509_CRL_INFO GRPC_SHADOW_i2d_X509_CRL_INFO
+#define i2d_X509_REVOKED GRPC_SHADOW_i2d_X509_REVOKED
+#define X509_EXTENSIONS_it GRPC_SHADOW_X509_EXTENSIONS_it
+#define X509_EXTENSION_dup GRPC_SHADOW_X509_EXTENSION_dup
+#define X509_EXTENSION_free GRPC_SHADOW_X509_EXTENSION_free
+#define X509_EXTENSION_it GRPC_SHADOW_X509_EXTENSION_it
+#define X509_EXTENSION_new GRPC_SHADOW_X509_EXTENSION_new
+#define d2i_X509_EXTENSION GRPC_SHADOW_d2i_X509_EXTENSION
+#define d2i_X509_EXTENSIONS GRPC_SHADOW_d2i_X509_EXTENSIONS
+#define i2d_X509_EXTENSION GRPC_SHADOW_i2d_X509_EXTENSION
+#define i2d_X509_EXTENSIONS GRPC_SHADOW_i2d_X509_EXTENSIONS
+#define X509_INFO_free GRPC_SHADOW_X509_INFO_free
+#define X509_INFO_new GRPC_SHADOW_X509_INFO_new
+#define X509_NAME_ENTRIES_it GRPC_SHADOW_X509_NAME_ENTRIES_it
+#define X509_NAME_ENTRY_dup GRPC_SHADOW_X509_NAME_ENTRY_dup
+#define X509_NAME_ENTRY_free GRPC_SHADOW_X509_NAME_ENTRY_free
+#define X509_NAME_ENTRY_it GRPC_SHADOW_X509_NAME_ENTRY_it
+#define X509_NAME_ENTRY_new GRPC_SHADOW_X509_NAME_ENTRY_new
+#define X509_NAME_ENTRY_set GRPC_SHADOW_X509_NAME_ENTRY_set
+#define X509_NAME_INTERNAL_it GRPC_SHADOW_X509_NAME_INTERNAL_it
+#define X509_NAME_dup GRPC_SHADOW_X509_NAME_dup
+#define X509_NAME_free GRPC_SHADOW_X509_NAME_free
+#define X509_NAME_get0_der GRPC_SHADOW_X509_NAME_get0_der
+#define X509_NAME_it GRPC_SHADOW_X509_NAME_it
+#define X509_NAME_new GRPC_SHADOW_X509_NAME_new
+#define X509_NAME_set GRPC_SHADOW_X509_NAME_set
+#define d2i_X509_NAME GRPC_SHADOW_d2i_X509_NAME
+#define d2i_X509_NAME_ENTRY GRPC_SHADOW_d2i_X509_NAME_ENTRY
+#define i2d_X509_NAME GRPC_SHADOW_i2d_X509_NAME
+#define i2d_X509_NAME_ENTRY GRPC_SHADOW_i2d_X509_NAME_ENTRY
+#define X509_PKEY_free GRPC_SHADOW_X509_PKEY_free
+#define X509_PKEY_new GRPC_SHADOW_X509_PKEY_new
+#define X509_PUBKEY_free GRPC_SHADOW_X509_PUBKEY_free
+#define X509_PUBKEY_get GRPC_SHADOW_X509_PUBKEY_get
+#define X509_PUBKEY_get0_param GRPC_SHADOW_X509_PUBKEY_get0_param
+#define X509_PUBKEY_it GRPC_SHADOW_X509_PUBKEY_it
+#define X509_PUBKEY_new GRPC_SHADOW_X509_PUBKEY_new
+#define X509_PUBKEY_set GRPC_SHADOW_X509_PUBKEY_set
+#define X509_PUBKEY_set0_param GRPC_SHADOW_X509_PUBKEY_set0_param
+#define d2i_DSA_PUBKEY GRPC_SHADOW_d2i_DSA_PUBKEY
+#define d2i_EC_PUBKEY GRPC_SHADOW_d2i_EC_PUBKEY
+#define d2i_PUBKEY GRPC_SHADOW_d2i_PUBKEY
+#define d2i_RSA_PUBKEY GRPC_SHADOW_d2i_RSA_PUBKEY
+#define d2i_X509_PUBKEY GRPC_SHADOW_d2i_X509_PUBKEY
+#define i2d_DSA_PUBKEY GRPC_SHADOW_i2d_DSA_PUBKEY
+#define i2d_EC_PUBKEY GRPC_SHADOW_i2d_EC_PUBKEY
+#define i2d_PUBKEY GRPC_SHADOW_i2d_PUBKEY
+#define i2d_RSA_PUBKEY GRPC_SHADOW_i2d_RSA_PUBKEY
+#define i2d_X509_PUBKEY GRPC_SHADOW_i2d_X509_PUBKEY
+#define X509_REQ_INFO_free GRPC_SHADOW_X509_REQ_INFO_free
+#define X509_REQ_INFO_it GRPC_SHADOW_X509_REQ_INFO_it
+#define X509_REQ_INFO_new GRPC_SHADOW_X509_REQ_INFO_new
+#define X509_REQ_dup GRPC_SHADOW_X509_REQ_dup
+#define X509_REQ_free GRPC_SHADOW_X509_REQ_free
+#define X509_REQ_it GRPC_SHADOW_X509_REQ_it
+#define X509_REQ_new GRPC_SHADOW_X509_REQ_new
+#define d2i_X509_REQ GRPC_SHADOW_d2i_X509_REQ
+#define d2i_X509_REQ_INFO GRPC_SHADOW_d2i_X509_REQ_INFO
+#define i2d_X509_REQ GRPC_SHADOW_i2d_X509_REQ
+#define i2d_X509_REQ_INFO GRPC_SHADOW_i2d_X509_REQ_INFO
+#define X509_SIG_free GRPC_SHADOW_X509_SIG_free
+#define X509_SIG_it GRPC_SHADOW_X509_SIG_it
+#define X509_SIG_new GRPC_SHADOW_X509_SIG_new
+#define d2i_X509_SIG GRPC_SHADOW_d2i_X509_SIG
+#define i2d_X509_SIG GRPC_SHADOW_i2d_X509_SIG
+#define NETSCAPE_SPKAC_free GRPC_SHADOW_NETSCAPE_SPKAC_free
+#define NETSCAPE_SPKAC_it GRPC_SHADOW_NETSCAPE_SPKAC_it
+#define NETSCAPE_SPKAC_new GRPC_SHADOW_NETSCAPE_SPKAC_new
+#define NETSCAPE_SPKI_free GRPC_SHADOW_NETSCAPE_SPKI_free
+#define NETSCAPE_SPKI_it GRPC_SHADOW_NETSCAPE_SPKI_it
+#define NETSCAPE_SPKI_new GRPC_SHADOW_NETSCAPE_SPKI_new
+#define d2i_NETSCAPE_SPKAC GRPC_SHADOW_d2i_NETSCAPE_SPKAC
+#define d2i_NETSCAPE_SPKI GRPC_SHADOW_d2i_NETSCAPE_SPKI
+#define i2d_NETSCAPE_SPKAC GRPC_SHADOW_i2d_NETSCAPE_SPKAC
+#define i2d_NETSCAPE_SPKI GRPC_SHADOW_i2d_NETSCAPE_SPKI
+#define X509_VAL_free GRPC_SHADOW_X509_VAL_free
+#define X509_VAL_it GRPC_SHADOW_X509_VAL_it
+#define X509_VAL_new GRPC_SHADOW_X509_VAL_new
+#define d2i_X509_VAL GRPC_SHADOW_d2i_X509_VAL
+#define i2d_X509_VAL GRPC_SHADOW_i2d_X509_VAL
+#define X509_CINF_free GRPC_SHADOW_X509_CINF_free
+#define X509_CINF_it GRPC_SHADOW_X509_CINF_it
+#define X509_CINF_new GRPC_SHADOW_X509_CINF_new
+#define X509_dup GRPC_SHADOW_X509_dup
+#define X509_free GRPC_SHADOW_X509_free
+#define X509_get0_signature GRPC_SHADOW_X509_get0_signature
+#define X509_get_ex_data GRPC_SHADOW_X509_get_ex_data
+#define X509_get_ex_new_index GRPC_SHADOW_X509_get_ex_new_index
+#define X509_get_signature_nid GRPC_SHADOW_X509_get_signature_nid
+#define X509_it GRPC_SHADOW_X509_it
+#define X509_new GRPC_SHADOW_X509_new
+#define X509_parse_from_buffer GRPC_SHADOW_X509_parse_from_buffer
+#define X509_set_ex_data GRPC_SHADOW_X509_set_ex_data
+#define X509_up_ref GRPC_SHADOW_X509_up_ref
+#define d2i_X509 GRPC_SHADOW_d2i_X509
+#define d2i_X509_AUX GRPC_SHADOW_d2i_X509_AUX
+#define d2i_X509_CINF GRPC_SHADOW_d2i_X509_CINF
+#define i2d_X509 GRPC_SHADOW_i2d_X509
+#define i2d_X509_AUX GRPC_SHADOW_i2d_X509_AUX
+#define i2d_X509_CINF GRPC_SHADOW_i2d_X509_CINF
+#define X509_CERT_AUX_free GRPC_SHADOW_X509_CERT_AUX_free
+#define X509_CERT_AUX_it GRPC_SHADOW_X509_CERT_AUX_it
+#define X509_CERT_AUX_new GRPC_SHADOW_X509_CERT_AUX_new
+#define X509_add1_reject_object GRPC_SHADOW_X509_add1_reject_object
+#define X509_add1_trust_object GRPC_SHADOW_X509_add1_trust_object
+#define X509_alias_get0 GRPC_SHADOW_X509_alias_get0
+#define X509_alias_set1 GRPC_SHADOW_X509_alias_set1
+#define X509_keyid_get0 GRPC_SHADOW_X509_keyid_get0
+#define X509_keyid_set1 GRPC_SHADOW_X509_keyid_set1
+#define X509_reject_clear GRPC_SHADOW_X509_reject_clear
+#define X509_trust_clear GRPC_SHADOW_X509_trust_clear
+#define d2i_X509_CERT_AUX GRPC_SHADOW_d2i_X509_CERT_AUX
+#define i2d_X509_CERT_AUX GRPC_SHADOW_i2d_X509_CERT_AUX
+#define policy_cache_find_data GRPC_SHADOW_policy_cache_find_data
+#define policy_cache_free GRPC_SHADOW_policy_cache_free
+#define policy_cache_set GRPC_SHADOW_policy_cache_set
+#define policy_data_free GRPC_SHADOW_policy_data_free
+#define policy_data_new GRPC_SHADOW_policy_data_new
+#define X509_policy_level_get0_node GRPC_SHADOW_X509_policy_level_get0_node
+#define X509_policy_level_node_count GRPC_SHADOW_X509_policy_level_node_count
+#define X509_policy_node_get0_parent GRPC_SHADOW_X509_policy_node_get0_parent
+#define X509_policy_node_get0_policy GRPC_SHADOW_X509_policy_node_get0_policy
+#define X509_policy_node_get0_qualifiers GRPC_SHADOW_X509_policy_node_get0_qualifiers
+#define X509_policy_tree_get0_level GRPC_SHADOW_X509_policy_tree_get0_level
+#define X509_policy_tree_get0_policies GRPC_SHADOW_X509_policy_tree_get0_policies
+#define X509_policy_tree_get0_user_policies GRPC_SHADOW_X509_policy_tree_get0_user_policies
+#define X509_policy_tree_level_count GRPC_SHADOW_X509_policy_tree_level_count
+#define policy_cache_set_mapping GRPC_SHADOW_policy_cache_set_mapping
+#define level_add_node GRPC_SHADOW_level_add_node
+#define level_find_node GRPC_SHADOW_level_find_node
+#define policy_node_cmp_new GRPC_SHADOW_policy_node_cmp_new
+#define policy_node_free GRPC_SHADOW_policy_node_free
+#define policy_node_match GRPC_SHADOW_policy_node_match
+#define tree_find_sk GRPC_SHADOW_tree_find_sk
+#define X509_policy_check GRPC_SHADOW_X509_policy_check
+#define X509_policy_tree_free GRPC_SHADOW_X509_policy_tree_free
+#define v3_akey_id GRPC_SHADOW_v3_akey_id
+#define AUTHORITY_KEYID_free GRPC_SHADOW_AUTHORITY_KEYID_free
+#define AUTHORITY_KEYID_it GRPC_SHADOW_AUTHORITY_KEYID_it
+#define AUTHORITY_KEYID_new GRPC_SHADOW_AUTHORITY_KEYID_new
+#define d2i_AUTHORITY_KEYID GRPC_SHADOW_d2i_AUTHORITY_KEYID
+#define i2d_AUTHORITY_KEYID GRPC_SHADOW_i2d_AUTHORITY_KEYID
+#define GENERAL_NAME_print GRPC_SHADOW_GENERAL_NAME_print
+#define a2i_GENERAL_NAME GRPC_SHADOW_a2i_GENERAL_NAME
+#define i2v_GENERAL_NAME GRPC_SHADOW_i2v_GENERAL_NAME
+#define i2v_GENERAL_NAMES GRPC_SHADOW_i2v_GENERAL_NAMES
+#define v2i_GENERAL_NAME GRPC_SHADOW_v2i_GENERAL_NAME
+#define v2i_GENERAL_NAMES GRPC_SHADOW_v2i_GENERAL_NAMES
+#define v2i_GENERAL_NAME_ex GRPC_SHADOW_v2i_GENERAL_NAME_ex
+#define v3_alt GRPC_SHADOW_v3_alt
+#define BASIC_CONSTRAINTS_free GRPC_SHADOW_BASIC_CONSTRAINTS_free
+#define BASIC_CONSTRAINTS_it GRPC_SHADOW_BASIC_CONSTRAINTS_it
+#define BASIC_CONSTRAINTS_new GRPC_SHADOW_BASIC_CONSTRAINTS_new
+#define d2i_BASIC_CONSTRAINTS GRPC_SHADOW_d2i_BASIC_CONSTRAINTS
+#define i2d_BASIC_CONSTRAINTS GRPC_SHADOW_i2d_BASIC_CONSTRAINTS
+#define v3_bcons GRPC_SHADOW_v3_bcons
+#define i2v_ASN1_BIT_STRING GRPC_SHADOW_i2v_ASN1_BIT_STRING
+#define v2i_ASN1_BIT_STRING GRPC_SHADOW_v2i_ASN1_BIT_STRING
+#define v3_key_usage GRPC_SHADOW_v3_key_usage
+#define v3_nscert GRPC_SHADOW_v3_nscert
+#define X509V3_EXT_CRL_add_nconf GRPC_SHADOW_X509V3_EXT_CRL_add_nconf
+#define X509V3_EXT_REQ_add_nconf GRPC_SHADOW_X509V3_EXT_REQ_add_nconf
+#define X509V3_EXT_add_nconf GRPC_SHADOW_X509V3_EXT_add_nconf
+#define X509V3_EXT_add_nconf_sk GRPC_SHADOW_X509V3_EXT_add_nconf_sk
+#define X509V3_EXT_i2d GRPC_SHADOW_X509V3_EXT_i2d
+#define X509V3_EXT_nconf GRPC_SHADOW_X509V3_EXT_nconf
+#define X509V3_EXT_nconf_nid GRPC_SHADOW_X509V3_EXT_nconf_nid
+#define X509V3_get_section GRPC_SHADOW_X509V3_get_section
+#define X509V3_get_string GRPC_SHADOW_X509V3_get_string
+#define X509V3_section_free GRPC_SHADOW_X509V3_section_free
+#define X509V3_set_ctx GRPC_SHADOW_X509V3_set_ctx
+#define X509V3_set_nconf GRPC_SHADOW_X509V3_set_nconf
+#define X509V3_string_free GRPC_SHADOW_X509V3_string_free
+#define CERTIFICATEPOLICIES_free GRPC_SHADOW_CERTIFICATEPOLICIES_free
+#define CERTIFICATEPOLICIES_it GRPC_SHADOW_CERTIFICATEPOLICIES_it
+#define CERTIFICATEPOLICIES_new GRPC_SHADOW_CERTIFICATEPOLICIES_new
+#define NOTICEREF_free GRPC_SHADOW_NOTICEREF_free
+#define NOTICEREF_it GRPC_SHADOW_NOTICEREF_it
+#define NOTICEREF_new GRPC_SHADOW_NOTICEREF_new
+#define POLICYINFO_free GRPC_SHADOW_POLICYINFO_free
+#define POLICYINFO_it GRPC_SHADOW_POLICYINFO_it
+#define POLICYINFO_new GRPC_SHADOW_POLICYINFO_new
+#define POLICYQUALINFO_free GRPC_SHADOW_POLICYQUALINFO_free
+#define POLICYQUALINFO_it GRPC_SHADOW_POLICYQUALINFO_it
+#define POLICYQUALINFO_new GRPC_SHADOW_POLICYQUALINFO_new
+#define USERNOTICE_free GRPC_SHADOW_USERNOTICE_free
+#define USERNOTICE_it GRPC_SHADOW_USERNOTICE_it
+#define USERNOTICE_new GRPC_SHADOW_USERNOTICE_new
+#define X509_POLICY_NODE_print GRPC_SHADOW_X509_POLICY_NODE_print
+#define d2i_CERTIFICATEPOLICIES GRPC_SHADOW_d2i_CERTIFICATEPOLICIES
+#define d2i_NOTICEREF GRPC_SHADOW_d2i_NOTICEREF
+#define d2i_POLICYINFO GRPC_SHADOW_d2i_POLICYINFO
+#define d2i_POLICYQUALINFO GRPC_SHADOW_d2i_POLICYQUALINFO
+#define d2i_USERNOTICE GRPC_SHADOW_d2i_USERNOTICE
+#define i2d_CERTIFICATEPOLICIES GRPC_SHADOW_i2d_CERTIFICATEPOLICIES
+#define i2d_NOTICEREF GRPC_SHADOW_i2d_NOTICEREF
+#define i2d_POLICYINFO GRPC_SHADOW_i2d_POLICYINFO
+#define i2d_POLICYQUALINFO GRPC_SHADOW_i2d_POLICYQUALINFO
+#define i2d_USERNOTICE GRPC_SHADOW_i2d_USERNOTICE
+#define v3_cpols GRPC_SHADOW_v3_cpols
+#define CRL_DIST_POINTS_free GRPC_SHADOW_CRL_DIST_POINTS_free
+#define CRL_DIST_POINTS_it GRPC_SHADOW_CRL_DIST_POINTS_it
+#define CRL_DIST_POINTS_new GRPC_SHADOW_CRL_DIST_POINTS_new
+#define DIST_POINT_NAME_free GRPC_SHADOW_DIST_POINT_NAME_free
+#define DIST_POINT_NAME_it GRPC_SHADOW_DIST_POINT_NAME_it
+#define DIST_POINT_NAME_new GRPC_SHADOW_DIST_POINT_NAME_new
+#define DIST_POINT_free GRPC_SHADOW_DIST_POINT_free
+#define DIST_POINT_it GRPC_SHADOW_DIST_POINT_it
+#define DIST_POINT_new GRPC_SHADOW_DIST_POINT_new
+#define DIST_POINT_set_dpname GRPC_SHADOW_DIST_POINT_set_dpname
+#define ISSUING_DIST_POINT_free GRPC_SHADOW_ISSUING_DIST_POINT_free
+#define ISSUING_DIST_POINT_it GRPC_SHADOW_ISSUING_DIST_POINT_it
+#define ISSUING_DIST_POINT_new GRPC_SHADOW_ISSUING_DIST_POINT_new
+#define d2i_CRL_DIST_POINTS GRPC_SHADOW_d2i_CRL_DIST_POINTS
+#define d2i_DIST_POINT GRPC_SHADOW_d2i_DIST_POINT
+#define d2i_DIST_POINT_NAME GRPC_SHADOW_d2i_DIST_POINT_NAME
+#define d2i_ISSUING_DIST_POINT GRPC_SHADOW_d2i_ISSUING_DIST_POINT
+#define i2d_CRL_DIST_POINTS GRPC_SHADOW_i2d_CRL_DIST_POINTS
+#define i2d_DIST_POINT GRPC_SHADOW_i2d_DIST_POINT
+#define i2d_DIST_POINT_NAME GRPC_SHADOW_i2d_DIST_POINT_NAME
+#define i2d_ISSUING_DIST_POINT GRPC_SHADOW_i2d_ISSUING_DIST_POINT
+#define v3_crld GRPC_SHADOW_v3_crld
+#define v3_freshest_crl GRPC_SHADOW_v3_freshest_crl
+#define v3_idp GRPC_SHADOW_v3_idp
+#define i2s_ASN1_ENUMERATED_TABLE GRPC_SHADOW_i2s_ASN1_ENUMERATED_TABLE
+#define v3_crl_reason GRPC_SHADOW_v3_crl_reason
+#define EXTENDED_KEY_USAGE_free GRPC_SHADOW_EXTENDED_KEY_USAGE_free
+#define EXTENDED_KEY_USAGE_it GRPC_SHADOW_EXTENDED_KEY_USAGE_it
+#define EXTENDED_KEY_USAGE_new GRPC_SHADOW_EXTENDED_KEY_USAGE_new
+#define d2i_EXTENDED_KEY_USAGE GRPC_SHADOW_d2i_EXTENDED_KEY_USAGE
+#define i2d_EXTENDED_KEY_USAGE GRPC_SHADOW_i2d_EXTENDED_KEY_USAGE
+#define v3_ext_ku GRPC_SHADOW_v3_ext_ku
+#define v3_ocsp_accresp GRPC_SHADOW_v3_ocsp_accresp
+#define EDIPARTYNAME_free GRPC_SHADOW_EDIPARTYNAME_free
+#define EDIPARTYNAME_it GRPC_SHADOW_EDIPARTYNAME_it
+#define EDIPARTYNAME_new GRPC_SHADOW_EDIPARTYNAME_new
+#define GENERAL_NAMES_free GRPC_SHADOW_GENERAL_NAMES_free
+#define GENERAL_NAMES_it GRPC_SHADOW_GENERAL_NAMES_it
+#define GENERAL_NAMES_new GRPC_SHADOW_GENERAL_NAMES_new
+#define GENERAL_NAME_cmp GRPC_SHADOW_GENERAL_NAME_cmp
+#define GENERAL_NAME_dup GRPC_SHADOW_GENERAL_NAME_dup
+#define GENERAL_NAME_free GRPC_SHADOW_GENERAL_NAME_free
+#define GENERAL_NAME_get0_otherName GRPC_SHADOW_GENERAL_NAME_get0_otherName
+#define GENERAL_NAME_get0_value GRPC_SHADOW_GENERAL_NAME_get0_value
+#define GENERAL_NAME_it GRPC_SHADOW_GENERAL_NAME_it
+#define GENERAL_NAME_new GRPC_SHADOW_GENERAL_NAME_new
+#define GENERAL_NAME_set0_othername GRPC_SHADOW_GENERAL_NAME_set0_othername
+#define GENERAL_NAME_set0_value GRPC_SHADOW_GENERAL_NAME_set0_value
+#define OTHERNAME_cmp GRPC_SHADOW_OTHERNAME_cmp
+#define OTHERNAME_free GRPC_SHADOW_OTHERNAME_free
+#define OTHERNAME_it GRPC_SHADOW_OTHERNAME_it
+#define OTHERNAME_new GRPC_SHADOW_OTHERNAME_new
+#define d2i_EDIPARTYNAME GRPC_SHADOW_d2i_EDIPARTYNAME
+#define d2i_GENERAL_NAME GRPC_SHADOW_d2i_GENERAL_NAME
+#define d2i_GENERAL_NAMES GRPC_SHADOW_d2i_GENERAL_NAMES
+#define d2i_OTHERNAME GRPC_SHADOW_d2i_OTHERNAME
+#define i2d_EDIPARTYNAME GRPC_SHADOW_i2d_EDIPARTYNAME
+#define i2d_GENERAL_NAME GRPC_SHADOW_i2d_GENERAL_NAME
+#define i2d_GENERAL_NAMES GRPC_SHADOW_i2d_GENERAL_NAMES
+#define i2d_OTHERNAME GRPC_SHADOW_i2d_OTHERNAME
+#define v3_ns_ia5_list GRPC_SHADOW_v3_ns_ia5_list
+#define ACCESS_DESCRIPTION_free GRPC_SHADOW_ACCESS_DESCRIPTION_free
+#define ACCESS_DESCRIPTION_it GRPC_SHADOW_ACCESS_DESCRIPTION_it
+#define ACCESS_DESCRIPTION_new GRPC_SHADOW_ACCESS_DESCRIPTION_new
+#define AUTHORITY_INFO_ACCESS_free GRPC_SHADOW_AUTHORITY_INFO_ACCESS_free
+#define AUTHORITY_INFO_ACCESS_it GRPC_SHADOW_AUTHORITY_INFO_ACCESS_it
+#define AUTHORITY_INFO_ACCESS_new GRPC_SHADOW_AUTHORITY_INFO_ACCESS_new
+#define d2i_ACCESS_DESCRIPTION GRPC_SHADOW_d2i_ACCESS_DESCRIPTION
+#define d2i_AUTHORITY_INFO_ACCESS GRPC_SHADOW_d2i_AUTHORITY_INFO_ACCESS
+#define i2a_ACCESS_DESCRIPTION GRPC_SHADOW_i2a_ACCESS_DESCRIPTION
+#define i2d_ACCESS_DESCRIPTION GRPC_SHADOW_i2d_ACCESS_DESCRIPTION
+#define i2d_AUTHORITY_INFO_ACCESS GRPC_SHADOW_i2d_AUTHORITY_INFO_ACCESS
+#define v3_info GRPC_SHADOW_v3_info
+#define v3_sinfo GRPC_SHADOW_v3_sinfo
+#define v3_crl_num GRPC_SHADOW_v3_crl_num
+#define v3_delta_crl GRPC_SHADOW_v3_delta_crl
+#define v3_inhibit_anyp GRPC_SHADOW_v3_inhibit_anyp
+#define X509V3_EXT_add GRPC_SHADOW_X509V3_EXT_add
+#define X509V3_EXT_add_alias GRPC_SHADOW_X509V3_EXT_add_alias
+#define X509V3_EXT_add_list GRPC_SHADOW_X509V3_EXT_add_list
+#define X509V3_EXT_cleanup GRPC_SHADOW_X509V3_EXT_cleanup
+#define X509V3_EXT_d2i GRPC_SHADOW_X509V3_EXT_d2i
+#define X509V3_EXT_free GRPC_SHADOW_X509V3_EXT_free
+#define X509V3_EXT_get GRPC_SHADOW_X509V3_EXT_get
+#define X509V3_EXT_get_nid GRPC_SHADOW_X509V3_EXT_get_nid
+#define X509V3_add1_i2d GRPC_SHADOW_X509V3_add1_i2d
+#define X509V3_add_standard_extensions GRPC_SHADOW_X509V3_add_standard_extensions
+#define X509V3_get_d2i GRPC_SHADOW_X509V3_get_d2i
+#define GENERAL_SUBTREE_free GRPC_SHADOW_GENERAL_SUBTREE_free
+#define GENERAL_SUBTREE_it GRPC_SHADOW_GENERAL_SUBTREE_it
+#define GENERAL_SUBTREE_new GRPC_SHADOW_GENERAL_SUBTREE_new
+#define NAME_CONSTRAINTS_check GRPC_SHADOW_NAME_CONSTRAINTS_check
+#define NAME_CONSTRAINTS_free GRPC_SHADOW_NAME_CONSTRAINTS_free
+#define NAME_CONSTRAINTS_it GRPC_SHADOW_NAME_CONSTRAINTS_it
+#define NAME_CONSTRAINTS_new GRPC_SHADOW_NAME_CONSTRAINTS_new
+#define v3_name_constraints GRPC_SHADOW_v3_name_constraints
+#define v3_pci GRPC_SHADOW_v3_pci
+#define PROXY_CERT_INFO_EXTENSION_free GRPC_SHADOW_PROXY_CERT_INFO_EXTENSION_free
+#define PROXY_CERT_INFO_EXTENSION_it GRPC_SHADOW_PROXY_CERT_INFO_EXTENSION_it
+#define PROXY_CERT_INFO_EXTENSION_new GRPC_SHADOW_PROXY_CERT_INFO_EXTENSION_new
+#define PROXY_POLICY_free GRPC_SHADOW_PROXY_POLICY_free
+#define PROXY_POLICY_it GRPC_SHADOW_PROXY_POLICY_it
+#define PROXY_POLICY_new GRPC_SHADOW_PROXY_POLICY_new
+#define d2i_PROXY_CERT_INFO_EXTENSION GRPC_SHADOW_d2i_PROXY_CERT_INFO_EXTENSION
+#define d2i_PROXY_POLICY GRPC_SHADOW_d2i_PROXY_POLICY
+#define i2d_PROXY_CERT_INFO_EXTENSION GRPC_SHADOW_i2d_PROXY_CERT_INFO_EXTENSION
+#define i2d_PROXY_POLICY GRPC_SHADOW_i2d_PROXY_POLICY
+#define POLICY_CONSTRAINTS_free GRPC_SHADOW_POLICY_CONSTRAINTS_free
+#define POLICY_CONSTRAINTS_it GRPC_SHADOW_POLICY_CONSTRAINTS_it
+#define POLICY_CONSTRAINTS_new GRPC_SHADOW_POLICY_CONSTRAINTS_new
+#define v3_policy_constraints GRPC_SHADOW_v3_policy_constraints
+#define PKEY_USAGE_PERIOD_free GRPC_SHADOW_PKEY_USAGE_PERIOD_free
+#define PKEY_USAGE_PERIOD_it GRPC_SHADOW_PKEY_USAGE_PERIOD_it
+#define PKEY_USAGE_PERIOD_new GRPC_SHADOW_PKEY_USAGE_PERIOD_new
+#define d2i_PKEY_USAGE_PERIOD GRPC_SHADOW_d2i_PKEY_USAGE_PERIOD
+#define i2d_PKEY_USAGE_PERIOD GRPC_SHADOW_i2d_PKEY_USAGE_PERIOD
+#define v3_pkey_usage_period GRPC_SHADOW_v3_pkey_usage_period
+#define POLICY_MAPPINGS_it GRPC_SHADOW_POLICY_MAPPINGS_it
+#define POLICY_MAPPING_free GRPC_SHADOW_POLICY_MAPPING_free
+#define POLICY_MAPPING_it GRPC_SHADOW_POLICY_MAPPING_it
+#define POLICY_MAPPING_new GRPC_SHADOW_POLICY_MAPPING_new
+#define v3_policy_mappings GRPC_SHADOW_v3_policy_mappings
+#define X509V3_EXT_print GRPC_SHADOW_X509V3_EXT_print
+#define X509V3_EXT_print_fp GRPC_SHADOW_X509V3_EXT_print_fp
+#define X509V3_EXT_val_prn GRPC_SHADOW_X509V3_EXT_val_prn
+#define X509V3_extensions_print GRPC_SHADOW_X509V3_extensions_print
+#define X509_PURPOSE_add GRPC_SHADOW_X509_PURPOSE_add
+#define X509_PURPOSE_cleanup GRPC_SHADOW_X509_PURPOSE_cleanup
+#define X509_PURPOSE_get0 GRPC_SHADOW_X509_PURPOSE_get0
+#define X509_PURPOSE_get0_name GRPC_SHADOW_X509_PURPOSE_get0_name
+#define X509_PURPOSE_get0_sname GRPC_SHADOW_X509_PURPOSE_get0_sname
+#define X509_PURPOSE_get_by_id GRPC_SHADOW_X509_PURPOSE_get_by_id
+#define X509_PURPOSE_get_by_sname GRPC_SHADOW_X509_PURPOSE_get_by_sname
+#define X509_PURPOSE_get_count GRPC_SHADOW_X509_PURPOSE_get_count
+#define X509_PURPOSE_get_id GRPC_SHADOW_X509_PURPOSE_get_id
+#define X509_PURPOSE_get_trust GRPC_SHADOW_X509_PURPOSE_get_trust
+#define X509_PURPOSE_set GRPC_SHADOW_X509_PURPOSE_set
+#define X509_check_akid GRPC_SHADOW_X509_check_akid
+#define X509_check_ca GRPC_SHADOW_X509_check_ca
+#define X509_check_issued GRPC_SHADOW_X509_check_issued
+#define X509_check_purpose GRPC_SHADOW_X509_check_purpose
+#define X509_supported_extension GRPC_SHADOW_X509_supported_extension
+#define i2s_ASN1_OCTET_STRING GRPC_SHADOW_i2s_ASN1_OCTET_STRING
+#define s2i_ASN1_OCTET_STRING GRPC_SHADOW_s2i_ASN1_OCTET_STRING
+#define v3_skey_id GRPC_SHADOW_v3_skey_id
+#define SXNETID_free GRPC_SHADOW_SXNETID_free
+#define SXNETID_it GRPC_SHADOW_SXNETID_it
+#define SXNETID_new GRPC_SHADOW_SXNETID_new
+#define SXNET_add_id_INTEGER GRPC_SHADOW_SXNET_add_id_INTEGER
+#define SXNET_add_id_asc GRPC_SHADOW_SXNET_add_id_asc
+#define SXNET_add_id_ulong GRPC_SHADOW_SXNET_add_id_ulong
+#define SXNET_free GRPC_SHADOW_SXNET_free
+#define SXNET_get_id_INTEGER GRPC_SHADOW_SXNET_get_id_INTEGER
+#define SXNET_get_id_asc GRPC_SHADOW_SXNET_get_id_asc
+#define SXNET_get_id_ulong GRPC_SHADOW_SXNET_get_id_ulong
+#define SXNET_it GRPC_SHADOW_SXNET_it
+#define SXNET_new GRPC_SHADOW_SXNET_new
+#define d2i_SXNET GRPC_SHADOW_d2i_SXNET
+#define d2i_SXNETID GRPC_SHADOW_d2i_SXNETID
+#define i2d_SXNET GRPC_SHADOW_i2d_SXNET
+#define i2d_SXNETID GRPC_SHADOW_i2d_SXNETID
+#define v3_sxnet GRPC_SHADOW_v3_sxnet
+#define X509V3_NAME_from_section GRPC_SHADOW_X509V3_NAME_from_section
+#define X509V3_add_value GRPC_SHADOW_X509V3_add_value
+#define X509V3_add_value_bool GRPC_SHADOW_X509V3_add_value_bool
+#define X509V3_add_value_bool_nf GRPC_SHADOW_X509V3_add_value_bool_nf
+#define X509V3_add_value_int GRPC_SHADOW_X509V3_add_value_int
+#define X509V3_add_value_uchar GRPC_SHADOW_X509V3_add_value_uchar
+#define X509V3_conf_free GRPC_SHADOW_X509V3_conf_free
+#define X509V3_get_value_bool GRPC_SHADOW_X509V3_get_value_bool
+#define X509V3_get_value_int GRPC_SHADOW_X509V3_get_value_int
+#define X509V3_parse_list GRPC_SHADOW_X509V3_parse_list
+#define X509_REQ_get1_email GRPC_SHADOW_X509_REQ_get1_email
+#define X509_check_email GRPC_SHADOW_X509_check_email
+#define X509_check_host GRPC_SHADOW_X509_check_host
+#define X509_check_ip GRPC_SHADOW_X509_check_ip
+#define X509_check_ip_asc GRPC_SHADOW_X509_check_ip_asc
+#define X509_email_free GRPC_SHADOW_X509_email_free
+#define X509_get1_email GRPC_SHADOW_X509_get1_email
+#define X509_get1_ocsp GRPC_SHADOW_X509_get1_ocsp
+#define a2i_IPADDRESS GRPC_SHADOW_a2i_IPADDRESS
+#define a2i_IPADDRESS_NC GRPC_SHADOW_a2i_IPADDRESS_NC
+#define a2i_ipadd GRPC_SHADOW_a2i_ipadd
+#define hex_to_string GRPC_SHADOW_hex_to_string
+#define i2s_ASN1_ENUMERATED GRPC_SHADOW_i2s_ASN1_ENUMERATED
+#define i2s_ASN1_INTEGER GRPC_SHADOW_i2s_ASN1_INTEGER
+#define name_cmp GRPC_SHADOW_name_cmp
+#define s2i_ASN1_INTEGER GRPC_SHADOW_s2i_ASN1_INTEGER
+#define string_to_hex GRPC_SHADOW_string_to_hex
+#define PKCS7_get_raw_certificates GRPC_SHADOW_PKCS7_get_raw_certificates
+#define pkcs7_bundle GRPC_SHADOW_pkcs7_bundle
+#define pkcs7_parse_header GRPC_SHADOW_pkcs7_parse_header
+#define PKCS7_bundle_CRLs GRPC_SHADOW_PKCS7_bundle_CRLs
+#define PKCS7_bundle_certificates GRPC_SHADOW_PKCS7_bundle_certificates
+#define PKCS7_get_CRLs GRPC_SHADOW_PKCS7_get_CRLs
+#define PKCS7_get_PEM_CRLs GRPC_SHADOW_PKCS7_get_PEM_CRLs
+#define PKCS7_get_PEM_certificates GRPC_SHADOW_PKCS7_get_PEM_certificates
+#define PKCS7_get_certificates GRPC_SHADOW_PKCS7_get_certificates
+#define PKCS8_marshal_encrypted_private_key GRPC_SHADOW_PKCS8_marshal_encrypted_private_key
+#define PKCS8_parse_encrypted_private_key GRPC_SHADOW_PKCS8_parse_encrypted_private_key
+#define pkcs12_key_gen GRPC_SHADOW_pkcs12_key_gen
+#define pkcs8_pbe_decrypt GRPC_SHADOW_pkcs8_pbe_decrypt
+#define EVP_PKCS82PKEY GRPC_SHADOW_EVP_PKCS82PKEY
+#define EVP_PKEY2PKCS8 GRPC_SHADOW_EVP_PKEY2PKCS8
+#define PKCS12_PBE_add GRPC_SHADOW_PKCS12_PBE_add
+#define PKCS12_free GRPC_SHADOW_PKCS12_free
+#define PKCS12_get_key_and_certs GRPC_SHADOW_PKCS12_get_key_and_certs
+#define PKCS12_parse GRPC_SHADOW_PKCS12_parse
+#define PKCS12_verify_mac GRPC_SHADOW_PKCS12_verify_mac
+#define PKCS8_PRIV_KEY_INFO_free GRPC_SHADOW_PKCS8_PRIV_KEY_INFO_free
+#define PKCS8_PRIV_KEY_INFO_it GRPC_SHADOW_PKCS8_PRIV_KEY_INFO_it
+#define PKCS8_PRIV_KEY_INFO_new GRPC_SHADOW_PKCS8_PRIV_KEY_INFO_new
+#define PKCS8_decrypt GRPC_SHADOW_PKCS8_decrypt
+#define PKCS8_encrypt GRPC_SHADOW_PKCS8_encrypt
+#define d2i_PKCS12 GRPC_SHADOW_d2i_PKCS12
+#define d2i_PKCS12_bio GRPC_SHADOW_d2i_PKCS12_bio
+#define d2i_PKCS12_fp GRPC_SHADOW_d2i_PKCS12_fp
+#define d2i_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_d2i_PKCS8_PRIV_KEY_INFO
+#define i2d_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_i2d_PKCS8_PRIV_KEY_INFO
+#define PKCS5_pbe2_decrypt_init GRPC_SHADOW_PKCS5_pbe2_decrypt_init
+#define PKCS5_pbe2_encrypt_init GRPC_SHADOW_PKCS5_pbe2_encrypt_init
+
+#endif /* GRPC_SHADOW_BORINGSSL_SYMBOLS */
+
+#endif /* GRPC_CORE_TSI_GRPC_SHADOW_BORINGSSL_H */
diff --git a/third_party/grpc/src/core/tsi/local_transport_security.cc b/third_party/grpc/src/core/tsi/local_transport_security.cc
new file mode 100644
index 0000000..99fd56d
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/local_transport_security.cc
@@ -0,0 +1,209 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/local_transport_security.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/tsi/transport_security_grpc.h"
+
+/* Main struct for local TSI zero-copy frame protector. */
+typedef struct local_zero_copy_grpc_protector {
+  tsi_zero_copy_grpc_protector base;
+} local_zero_copy_grpc_protector;
+
+/* Main struct for local TSI handshaker result. */
+typedef struct local_tsi_handshaker_result {
+  tsi_handshaker_result base;
+  bool is_client;
+} local_tsi_handshaker_result;
+
+/* Main struct for local TSI handshaker. */
+typedef struct local_tsi_handshaker {
+  tsi_handshaker base;
+  bool is_client;
+} local_tsi_handshaker;
+
+/* --- tsi_zero_copy_grpc_protector methods implementation. --- */
+
+static tsi_result local_zero_copy_grpc_protector_protect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  if (self == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid nullptr arguments to zero-copy grpc protect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_slice_buffer_move_into(unprotected_slices, protected_slices);
+  return TSI_OK;
+}
+
+static tsi_result local_zero_copy_grpc_protector_unprotect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  if (self == nullptr || unprotected_slices == nullptr ||
+      protected_slices == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid nullptr arguments to zero-copy grpc unprotect.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  grpc_slice_buffer_move_into(protected_slices, unprotected_slices);
+  return TSI_OK;
+}
+
+static void local_zero_copy_grpc_protector_destroy(
+    tsi_zero_copy_grpc_protector* self) {
+  gpr_free(self);
+}
+
+static const tsi_zero_copy_grpc_protector_vtable
+    local_zero_copy_grpc_protector_vtable = {
+        local_zero_copy_grpc_protector_protect,
+        local_zero_copy_grpc_protector_unprotect,
+        local_zero_copy_grpc_protector_destroy};
+
+tsi_result local_zero_copy_grpc_protector_create(
+    tsi_zero_copy_grpc_protector** protector) {
+  if (grpc_core::ExecCtx::Get() == nullptr || protector == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Invalid nullptr arguments to local_zero_copy_grpc_protector create.");
+    return TSI_INVALID_ARGUMENT;
+  }
+  local_zero_copy_grpc_protector* impl =
+      static_cast<local_zero_copy_grpc_protector*>(gpr_zalloc(sizeof(*impl)));
+  impl->base.vtable = &local_zero_copy_grpc_protector_vtable;
+  *protector = &impl->base;
+  return TSI_OK;
+}
+
+/* --- tsi_handshaker_result methods implementation. --- */
+
+static tsi_result handshaker_result_extract_peer(
+    const tsi_handshaker_result* self, tsi_peer* peer) {
+  return TSI_OK;
+}
+
+static tsi_result handshaker_result_create_zero_copy_grpc_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector) {
+  if (self == nullptr || protector == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Invalid arguments to create_zero_copy_grpc_protector()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  tsi_result ok = local_zero_copy_grpc_protector_create(protector);
+  if (ok != TSI_OK) {
+    gpr_log(GPR_ERROR, "Failed to create zero-copy grpc protector");
+  }
+  return ok;
+}
+
+static void handshaker_result_destroy(tsi_handshaker_result* self) {
+  if (self == nullptr) {
+    return;
+  }
+  local_tsi_handshaker_result* result =
+      reinterpret_cast<local_tsi_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  gpr_free(result);
+}
+
+static const tsi_handshaker_result_vtable result_vtable = {
+    handshaker_result_extract_peer,
+    handshaker_result_create_zero_copy_grpc_protector,
+    nullptr, /* handshaker_result_create_frame_protector */
+    nullptr, /* handshaker_result_get_unused_bytes */
+    handshaker_result_destroy};
+
+static tsi_result create_handshaker_result(bool is_client,
+                                           tsi_handshaker_result** self) {
+  if (self == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to create_handshaker_result()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  local_tsi_handshaker_result* result =
+      static_cast<local_tsi_handshaker_result*>(gpr_zalloc(sizeof(*result)));
+  result->is_client = is_client;
+  result->base.vtable = &result_vtable;
+  *self = &result->base;
+  return TSI_OK;
+}
+
+/* --- tsi_handshaker methods implementation. --- */
+
+static tsi_result handshaker_next(
+    tsi_handshaker* self, const unsigned char* received_bytes,
+    size_t received_bytes_size, const unsigned char** bytes_to_send,
+    size_t* bytes_to_send_size, tsi_handshaker_result** result,
+    tsi_handshaker_on_next_done_cb cb, void* user_data) {
+  if (self == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to handshaker_next()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* Note that there is no interaction between TSI peers, and all operations are
+   * local.
+   */
+  local_tsi_handshaker* handshaker =
+      reinterpret_cast<local_tsi_handshaker*>(self);
+  *bytes_to_send_size = 0;
+  create_handshaker_result(handshaker->is_client, result);
+  return TSI_OK;
+}
+
+static void handshaker_destroy(tsi_handshaker* self) {
+  if (self == nullptr) {
+    return;
+  }
+  local_tsi_handshaker* handshaker =
+      reinterpret_cast<local_tsi_handshaker*>(self);
+  gpr_free(handshaker);
+}
+
+static const tsi_handshaker_vtable handshaker_vtable = {
+    nullptr, /* get_bytes_to_send_to_peer -- deprecated */
+    nullptr, /* process_bytes_from_peer   -- deprecated */
+    nullptr, /* get_result                -- deprecated */
+    nullptr, /* extract_peer              -- deprecated */
+    nullptr, /* create_frame_protector    -- deprecated */
+    handshaker_destroy,
+    handshaker_next,
+    nullptr, /* shutdown */
+};
+
+tsi_result local_tsi_handshaker_create(bool is_client, tsi_handshaker** self) {
+  if (self == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid arguments to local_tsi_handshaker_create()");
+    return TSI_INVALID_ARGUMENT;
+  }
+  local_tsi_handshaker* handshaker =
+      static_cast<local_tsi_handshaker*>(gpr_zalloc(sizeof(*handshaker)));
+  handshaker->is_client = is_client;
+  handshaker->base.vtable = &handshaker_vtable;
+  *self = &handshaker->base;
+  return TSI_OK;
+}
diff --git a/third_party/grpc/src/core/tsi/local_transport_security.h b/third_party/grpc/src/core/tsi/local_transport_security.h
new file mode 100644
index 0000000..17213ec
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/local_transport_security.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_LOCAL_TRANSPORT_SECURITY_H
+#define GRPC_CORE_TSI_LOCAL_TRANSPORT_SECURITY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/grpc.h>
+
+#include "src/core/tsi/transport_security.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+#define TSI_LOCAL_NUM_OF_PEER_PROPERTIES 1
+#define TSI_LOCAL_PROCESS_ID_PEER_PROPERTY "process_id"
+
+/**
+ * Main struct for local TSI handshaker. All APIs in the header are
+ * thread-comptabile.
+ */
+typedef struct local_tsi_handshaker local_tsi_handshaker;
+
+/**
+ * This method creates a local TSI handshaker instance.
+ *
+ * - is_client: boolean value indicating if the handshaker is used at the client
+ *   (is_client = true) or server (is_client = false) side. The parameter is
+ *   added for future extension.
+ * - self: address of local TSI handshaker instance to be returned from the
+ *   method.
+ *
+ * It returns TSI_OK on success and an error status code on failure.
+ */
+tsi_result local_tsi_handshaker_create(bool is_client, tsi_handshaker** self);
+
+#endif /* GRPC_CORE_TSI_LOCAL_TRANSPORT_SECURITY_H */
diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session.h b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session.h
new file mode 100644
index 0000000..e847267
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H
+#define GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/grpc_shadow_boringssl.h"
+
+#include <grpc/slice.h>
+
+extern "C" {
+#include <openssl/ssl.h>
+}
+
+#include "src/core/lib/gprpp/ref_counted.h"
+
+// The main purpose of code here is to provide means to cache SSL sessions
+// in a way that they can be shared between connections.
+//
+// SSL_SESSION stands for single instance of session and is not generally safe
+// to share between SSL contexts with different lifetimes. It happens because
+// not all SSL implementations guarantee immutability of SSL_SESSION object.
+// See SSL_SESSION documentation in BoringSSL and OpenSSL for more details.
+
+namespace tsi {
+
+struct SslSessionDeleter {
+  void operator()(SSL_SESSION* session) { SSL_SESSION_free(session); }
+};
+
+typedef std::unique_ptr<SSL_SESSION, SslSessionDeleter> SslSessionPtr;
+
+/// SslCachedSession is an immutable thread-safe storage for single session
+/// representation. It provides means to share SSL session data (e.g. TLS
+/// ticket) between encrypted connections regardless of SSL context lifetime.
+class SslCachedSession {
+ public:
+  // Not copyable nor movable.
+  SslCachedSession(const SslCachedSession&) = delete;
+  SslCachedSession& operator=(const SslCachedSession&) = delete;
+
+  /// Create single cached instance of \a session.
+  static grpc_core::UniquePtr<SslCachedSession> Create(SslSessionPtr session);
+
+  virtual ~SslCachedSession() = default;
+
+  /// Returns a copy of previously cached session.
+  virtual SslSessionPtr CopySession() const GRPC_ABSTRACT;
+
+  GRPC_ABSTRACT_BASE_CLASS
+
+ protected:
+  SslCachedSession() = default;
+};
+
+}  // namespace tsi
+
+#endif /* GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H */
diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc
new file mode 100644
index 0000000..0da5a96
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/ssl/session_cache/ssl_session.h"
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+// BoringSSL allows SSL_SESSION to outlive SSL and SSL_CTX objects which are
+// re-created by gRPC on every certificate rotation or subchannel creation.
+// BoringSSL guarantees that SSL_SESSION is immutable so it's safe to share
+// the same original session object between different threads and connections.
+
+namespace tsi {
+namespace {
+
+class BoringSslCachedSession : public SslCachedSession {
+ public:
+  BoringSslCachedSession(SslSessionPtr session)
+      : session_(std::move(session)) {}
+
+  SslSessionPtr CopySession() const override {
+    // SslSessionPtr will dereference on destruction.
+    SSL_SESSION_up_ref(session_.get());
+    return SslSessionPtr(session_.get());
+  }
+
+ private:
+  SslSessionPtr session_;
+};
+
+}  // namespace
+
+grpc_core::UniquePtr<SslCachedSession> SslCachedSession::Create(
+    SslSessionPtr session) {
+  return grpc_core::UniquePtr<SslCachedSession>(
+      grpc_core::New<BoringSslCachedSession>(std::move(session)));
+}
+
+}  // namespace tsi
+
+#endif /* OPENSSL_IS_BORINGSSL */
diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.cc b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.cc
new file mode 100644
index 0000000..f9184bc
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.cc
@@ -0,0 +1,212 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/gprpp/mutex_lock.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/tsi/ssl/session_cache/ssl_session.h"
+#include "src/core/tsi/ssl/session_cache/ssl_session_cache.h"
+
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+namespace tsi {
+
+static void cache_key_avl_destroy(void* key, void* unused) {}
+
+static void* cache_key_avl_copy(void* key, void* unused) { return key; }
+
+static long cache_key_avl_compare(void* key1, void* key2, void* unused) {
+  return grpc_slice_cmp(*static_cast<grpc_slice*>(key1),
+                        *static_cast<grpc_slice*>(key2));
+}
+
+static void cache_value_avl_destroy(void* value, void* unused) {}
+
+static void* cache_value_avl_copy(void* value, void* unused) { return value; }
+
+// AVL only stores pointers, ownership belonges to the linked list.
+static const grpc_avl_vtable cache_avl_vtable = {
+    cache_key_avl_destroy,   cache_key_avl_copy,   cache_key_avl_compare,
+    cache_value_avl_destroy, cache_value_avl_copy,
+};
+
+/// Node for single cached session.
+class SslSessionLRUCache::Node {
+ public:
+  Node(const grpc_slice& key, SslSessionPtr session) : key_(key) {
+    SetSession(std::move(session));
+  }
+
+  ~Node() { grpc_slice_unref_internal(key_); }
+
+  // Not copyable nor movable.
+  Node(const Node&) = delete;
+  Node& operator=(const Node&) = delete;
+
+  void* AvlKey() { return &key_; }
+
+  /// Returns a copy of the node's cache session.
+  SslSessionPtr CopySession() const { return session_->CopySession(); }
+
+  /// Set the \a session (which is moved) for the node.
+  void SetSession(SslSessionPtr session) {
+    session_ = SslCachedSession::Create(std::move(session));
+  }
+
+ private:
+  friend class SslSessionLRUCache;
+
+  grpc_slice key_;
+  grpc_core::UniquePtr<SslCachedSession> session_;
+
+  Node* next_ = nullptr;
+  Node* prev_ = nullptr;
+};
+
+SslSessionLRUCache::SslSessionLRUCache(size_t capacity) : capacity_(capacity) {
+  GPR_ASSERT(capacity > 0);
+  gpr_mu_init(&lock_);
+  entry_by_key_ = grpc_avl_create(&cache_avl_vtable);
+}
+
+SslSessionLRUCache::~SslSessionLRUCache() {
+  Node* node = use_order_list_head_;
+  while (node) {
+    Node* next = node->next_;
+    grpc_core::Delete(node);
+    node = next;
+  }
+  grpc_avl_unref(entry_by_key_, nullptr);
+  gpr_mu_destroy(&lock_);
+}
+
+size_t SslSessionLRUCache::Size() {
+  grpc_core::MutexLock lock(&lock_);
+  return use_order_list_size_;
+}
+
+SslSessionLRUCache::Node* SslSessionLRUCache::FindLocked(
+    const grpc_slice& key) {
+  void* value =
+      grpc_avl_get(entry_by_key_, const_cast<grpc_slice*>(&key), nullptr);
+  if (value == nullptr) {
+    return nullptr;
+  }
+  Node* node = static_cast<Node*>(value);
+  // Move to the beginning.
+  Remove(node);
+  PushFront(node);
+  AssertInvariants();
+  return node;
+}
+
+void SslSessionLRUCache::Put(const char* key, SslSessionPtr session) {
+  grpc_core::MutexLock lock(&lock_);
+  Node* node = FindLocked(grpc_slice_from_static_string(key));
+  if (node != nullptr) {
+    node->SetSession(std::move(session));
+    return;
+  }
+  grpc_slice key_slice = grpc_slice_from_copied_string(key);
+  node = grpc_core::New<Node>(key_slice, std::move(session));
+  PushFront(node);
+  entry_by_key_ = grpc_avl_add(entry_by_key_, node->AvlKey(), node, nullptr);
+  AssertInvariants();
+  if (use_order_list_size_ > capacity_) {
+    GPR_ASSERT(use_order_list_tail_);
+    node = use_order_list_tail_;
+    Remove(node);
+    // Order matters, key is destroyed after deleting node.
+    entry_by_key_ = grpc_avl_remove(entry_by_key_, node->AvlKey(), nullptr);
+    grpc_core::Delete(node);
+    AssertInvariants();
+  }
+}
+
+SslSessionPtr SslSessionLRUCache::Get(const char* key) {
+  grpc_core::MutexLock lock(&lock_);
+  // Key is only used for lookups.
+  grpc_slice key_slice = grpc_slice_from_static_string(key);
+  Node* node = FindLocked(key_slice);
+  if (node == nullptr) {
+    return nullptr;
+  }
+  return node->CopySession();
+}
+
+void SslSessionLRUCache::Remove(SslSessionLRUCache::Node* node) {
+  if (node->prev_ == nullptr) {
+    use_order_list_head_ = node->next_;
+  } else {
+    node->prev_->next_ = node->next_;
+  }
+  if (node->next_ == nullptr) {
+    use_order_list_tail_ = node->prev_;
+  } else {
+    node->next_->prev_ = node->prev_;
+  }
+  GPR_ASSERT(use_order_list_size_ >= 1);
+  use_order_list_size_--;
+}
+
+void SslSessionLRUCache::PushFront(SslSessionLRUCache::Node* node) {
+  if (use_order_list_head_ == nullptr) {
+    use_order_list_head_ = node;
+    use_order_list_tail_ = node;
+    node->next_ = nullptr;
+    node->prev_ = nullptr;
+  } else {
+    node->next_ = use_order_list_head_;
+    node->next_->prev_ = node;
+    use_order_list_head_ = node;
+    node->prev_ = nullptr;
+  }
+  use_order_list_size_++;
+}
+
+#ifndef NDEBUG
+static size_t calculate_tree_size(grpc_avl_node* node) {
+  if (node == nullptr) {
+    return 0;
+  }
+  return 1 + calculate_tree_size(node->left) + calculate_tree_size(node->right);
+}
+
+void SslSessionLRUCache::AssertInvariants() {
+  size_t size = 0;
+  Node* prev = nullptr;
+  Node* current = use_order_list_head_;
+  while (current != nullptr) {
+    size++;
+    GPR_ASSERT(current->prev_ == prev);
+    void* node = grpc_avl_get(entry_by_key_, current->AvlKey(), nullptr);
+    GPR_ASSERT(node == current);
+    prev = current;
+    current = current->next_;
+  }
+  GPR_ASSERT(prev == use_order_list_tail_);
+  GPR_ASSERT(size == use_order_list_size_);
+  GPR_ASSERT(calculate_tree_size(entry_by_key_.root) == use_order_list_size_);
+}
+#else
+void SslSessionLRUCache::AssertInvariants() {}
+#endif
+
+}  // namespace tsi
diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.h b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.h
new file mode 100644
index 0000000..37fa2d1
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H
+#define GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/grpc_shadow_boringssl.h"
+
+#include <grpc/slice.h>
+#include <grpc/support/sync.h>
+
+extern "C" {
+#include <openssl/ssl.h>
+}
+
+#include "src/core/lib/avl/avl.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/ref_counted.h"
+#include "src/core/tsi/ssl/session_cache/ssl_session.h"
+
+/// Cache for SSL sessions for sessions resumption.
+///
+/// Older sessions may be evicted from the cache using LRU policy if capacity
+/// limit is hit. All sessions are associated with some key, usually server
+/// name. Note that servers are required to share session ticket encryption keys
+/// in order for cache to be effective.
+///
+/// This class is thread safe.
+
+namespace tsi {
+
+class SslSessionLRUCache : public grpc_core::RefCounted<SslSessionLRUCache> {
+ public:
+  /// Create new LRU cache with the given capacity.
+  static grpc_core::RefCountedPtr<SslSessionLRUCache> Create(size_t capacity) {
+    return grpc_core::MakeRefCounted<SslSessionLRUCache>(capacity);
+  }
+
+  // Not copyable nor movable.
+  SslSessionLRUCache(const SslSessionLRUCache&) = delete;
+  SslSessionLRUCache& operator=(const SslSessionLRUCache&) = delete;
+
+  /// Returns current number of sessions in the cache.
+  size_t Size();
+  /// Add \a session in the cache using \a key. This operation may discard older
+  /// sessions.
+  void Put(const char* key, SslSessionPtr session);
+  /// Returns the session from the cache associated with \a key or null if not
+  /// found.
+  SslSessionPtr Get(const char* key);
+
+ private:
+  // So New() can call our private ctor.
+  template <typename T, typename... Args>
+  friend T* grpc_core::New(Args&&... args);
+
+  // So Delete() can call our private dtor.
+  template <typename T>
+  friend void grpc_core::Delete(T*);
+
+  class Node;
+
+  explicit SslSessionLRUCache(size_t capacity);
+  ~SslSessionLRUCache();
+
+  Node* FindLocked(const grpc_slice& key);
+  void Remove(Node* node);
+  void PushFront(Node* node);
+  void AssertInvariants();
+
+  gpr_mu lock_;
+  size_t capacity_;
+
+  Node* use_order_list_head_ = nullptr;
+  Node* use_order_list_tail_ = nullptr;
+  size_t use_order_list_size_ = 0;
+  grpc_avl entry_by_key_;
+};
+
+}  // namespace tsi
+
+#endif /* GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H */
diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc
new file mode 100644
index 0000000..61c036c
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/ssl/session_cache/ssl_session.h"
+
+#include <grpc/support/log.h>
+
+#ifndef OPENSSL_IS_BORINGSSL
+
+// OpenSSL invalidates SSL_SESSION on SSL destruction making it pointless
+// to cache sessions. The workaround is to serialize (relatively expensive)
+// session into binary blob and re-create it from blob on every handshake.
+// Note that it's safe to keep serialized session outside of SSL lifetime
+// as openssl performs all necessary validation while attempting to use a
+// session and creates a new one if something is wrong (e.g. server changed
+// set of allowed codecs).
+
+namespace tsi {
+namespace {
+
+class OpenSslCachedSession : public SslCachedSession {
+ public:
+  OpenSslCachedSession(SslSessionPtr session) {
+    int size = i2d_SSL_SESSION(session.get(), nullptr);
+    GPR_ASSERT(size > 0);
+    grpc_slice slice = grpc_slice_malloc(size_t(size));
+    unsigned char* start = GRPC_SLICE_START_PTR(slice);
+    int second_size = i2d_SSL_SESSION(session.get(), &start);
+    GPR_ASSERT(size == second_size);
+    serialized_session_ = slice;
+  }
+
+  virtual ~OpenSslCachedSession() { grpc_slice_unref(serialized_session_); }
+
+  SslSessionPtr CopySession() const override {
+    const unsigned char* data = GRPC_SLICE_START_PTR(serialized_session_);
+    size_t length = GRPC_SLICE_LENGTH(serialized_session_);
+    SSL_SESSION* session = d2i_SSL_SESSION(nullptr, &data, length);
+    if (session == nullptr) {
+      return SslSessionPtr();
+    }
+    return SslSessionPtr(session);
+  }
+
+ private:
+  grpc_slice serialized_session_;
+};
+
+}  // namespace
+
+grpc_core::UniquePtr<SslCachedSession> SslCachedSession::Create(
+    SslSessionPtr session) {
+  return grpc_core::UniquePtr<SslCachedSession>(
+      grpc_core::New<OpenSslCachedSession>(std::move(session)));
+}
+
+}  // namespace tsi
+
+#endif /* OPENSSL_IS_BORINGSSL */
diff --git a/third_party/grpc/src/core/tsi/ssl_transport_security.cc b/third_party/grpc/src/core/tsi/ssl_transport_security.cc
new file mode 100644
index 0000000..fb6ea19
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/ssl_transport_security.cc
@@ -0,0 +1,1971 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/grpc_shadow_boringssl.h"
+
+#include "src/core/tsi/ssl_transport_security.h"
+
+#include <limits.h>
+#include <string.h>
+
+/* TODO(jboeuf): refactor inet_ntop into a portability header. */
+/* Note: for whomever reads this and tries to refactor this, this
+   can't be in grpc, it has to be in gpr. */
+#ifdef GPR_WINDOWS
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#endif
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd_id.h>
+
+extern "C" {
+#include <openssl/bio.h>
+#include <openssl/crypto.h> /* For OPENSSL_free */
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+}
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/tsi/ssl/session_cache/ssl_session_cache.h"
+#include "src/core/tsi/ssl_types.h"
+#include "src/core/tsi/transport_security.h"
+
+/* --- Constants. ---*/
+
+#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384
+#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024
+#define TSI_SSL_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE 1024
+
+/* Putting a macro like this and littering the source file with #if is really
+   bad practice.
+   TODO(jboeuf): refactor all the #if / #endif in a separate module. */
+#ifndef TSI_OPENSSL_ALPN_SUPPORT
+#define TSI_OPENSSL_ALPN_SUPPORT 1
+#endif
+
+/* TODO(jboeuf): I have not found a way to get this number dynamically from the
+   SSL structure. This is what we would ultimately want though... */
+#define TSI_SSL_MAX_PROTECTION_OVERHEAD 100
+
+/* --- Structure definitions. ---*/
+
+struct tsi_ssl_root_certs_store {
+  X509_STORE* store;
+};
+
+struct tsi_ssl_handshaker_factory {
+  const tsi_ssl_handshaker_factory_vtable* vtable;
+  gpr_refcount refcount;
+};
+
+struct tsi_ssl_client_handshaker_factory {
+  tsi_ssl_handshaker_factory base;
+  SSL_CTX* ssl_context;
+  unsigned char* alpn_protocol_list;
+  size_t alpn_protocol_list_length;
+  grpc_core::RefCountedPtr<tsi::SslSessionLRUCache> session_cache;
+};
+
+struct tsi_ssl_server_handshaker_factory {
+  /* Several contexts to support SNI.
+     The tsi_peer array contains the subject names of the server certificates
+     associated with the contexts at the same index.  */
+  tsi_ssl_handshaker_factory base;
+  SSL_CTX** ssl_contexts;
+  tsi_peer* ssl_context_x509_subject_names;
+  size_t ssl_context_count;
+  unsigned char* alpn_protocol_list;
+  size_t alpn_protocol_list_length;
+};
+
+typedef struct {
+  tsi_handshaker base;
+  SSL* ssl;
+  BIO* network_io;
+  tsi_result result;
+  unsigned char* outgoing_bytes_buffer;
+  size_t outgoing_bytes_buffer_size;
+  tsi_ssl_handshaker_factory* factory_ref;
+} tsi_ssl_handshaker;
+
+typedef struct {
+  tsi_handshaker_result base;
+  SSL* ssl;
+  BIO* network_io;
+  unsigned char* unused_bytes;
+  size_t unused_bytes_size;
+} tsi_ssl_handshaker_result;
+
+typedef struct {
+  tsi_frame_protector base;
+  SSL* ssl;
+  BIO* network_io;
+  unsigned char* buffer;
+  size_t buffer_size;
+  size_t buffer_offset;
+} tsi_ssl_frame_protector;
+
+/* --- Library Initialization. ---*/
+
+static gpr_once g_init_openssl_once = GPR_ONCE_INIT;
+static int g_ssl_ctx_ex_factory_index = -1;
+static const unsigned char kSslSessionIdContext[] = {'g', 'r', 'p', 'c'};
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+static gpr_mu* g_openssl_mutexes = nullptr;
+static void openssl_locking_cb(int mode, int type, const char* file,
+                               int line) GRPC_UNUSED;
+static unsigned long openssl_thread_id_cb(void) GRPC_UNUSED;
+
+static void openssl_locking_cb(int mode, int type, const char* file, int line) {
+  if (mode & CRYPTO_LOCK) {
+    gpr_mu_lock(&g_openssl_mutexes[type]);
+  } else {
+    gpr_mu_unlock(&g_openssl_mutexes[type]);
+  }
+}
+
+static unsigned long openssl_thread_id_cb(void) {
+  return static_cast<unsigned long>(gpr_thd_currentid());
+}
+#endif
+
+static void init_openssl(void) {
+#if OPENSSL_API_COMPAT >= 0x10100000L
+  OPENSSL_init_ssl(0, NULL);
+#else
+  SSL_library_init();
+  SSL_load_error_strings();
+  OpenSSL_add_all_algorithms();
+#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+  if (!CRYPTO_get_locking_callback()) {
+    int num_locks = CRYPTO_num_locks();
+    GPR_ASSERT(num_locks > 0);
+    g_openssl_mutexes = static_cast<gpr_mu*>(
+        gpr_malloc(static_cast<size_t>(num_locks) * sizeof(gpr_mu)));
+    for (int i = 0; i < num_locks; i++) {
+      gpr_mu_init(&g_openssl_mutexes[i]);
+    }
+    CRYPTO_set_locking_callback(openssl_locking_cb);
+    CRYPTO_set_id_callback(openssl_thread_id_cb);
+  } else {
+    gpr_log(GPR_INFO, "OpenSSL callback has already been set.");
+  }
+#endif
+  g_ssl_ctx_ex_factory_index =
+      SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
+  GPR_ASSERT(g_ssl_ctx_ex_factory_index != -1);
+}
+
+/* --- Ssl utils. ---*/
+
+static const char* ssl_error_string(int error) {
+  switch (error) {
+    case SSL_ERROR_NONE:
+      return "SSL_ERROR_NONE";
+    case SSL_ERROR_ZERO_RETURN:
+      return "SSL_ERROR_ZERO_RETURN";
+    case SSL_ERROR_WANT_READ:
+      return "SSL_ERROR_WANT_READ";
+    case SSL_ERROR_WANT_WRITE:
+      return "SSL_ERROR_WANT_WRITE";
+    case SSL_ERROR_WANT_CONNECT:
+      return "SSL_ERROR_WANT_CONNECT";
+    case SSL_ERROR_WANT_ACCEPT:
+      return "SSL_ERROR_WANT_ACCEPT";
+    case SSL_ERROR_WANT_X509_LOOKUP:
+      return "SSL_ERROR_WANT_X509_LOOKUP";
+    case SSL_ERROR_SYSCALL:
+      return "SSL_ERROR_SYSCALL";
+    case SSL_ERROR_SSL:
+      return "SSL_ERROR_SSL";
+    default:
+      return "Unknown error";
+  }
+}
+
+/* TODO(jboeuf): Remove when we are past the debugging phase with this code. */
+static void ssl_log_where_info(const SSL* ssl, int where, int flag,
+                               const char* msg) {
+  if ((where & flag) && tsi_tracing_enabled.enabled()) {
+    gpr_log(GPR_INFO, "%20.20s - %30.30s  - %5.10s", msg,
+            SSL_state_string_long(ssl), SSL_state_string(ssl));
+  }
+}
+
+/* Used for debugging. TODO(jboeuf): Remove when code is mature enough. */
+static void ssl_info_callback(const SSL* ssl, int where, int ret) {
+  if (ret == 0) {
+    gpr_log(GPR_ERROR, "ssl_info_callback: error occurred.\n");
+    return;
+  }
+
+  ssl_log_where_info(ssl, where, SSL_CB_LOOP, "LOOP");
+  ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_START, "HANDSHAKE START");
+  ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE");
+}
+
+/* Returns 1 if name looks like an IP address, 0 otherwise.
+   This is a very rough heuristic, and only handles IPv6 in hexadecimal form. */
+static int looks_like_ip_address(const char* name) {
+  size_t i;
+  size_t dot_count = 0;
+  size_t num_size = 0;
+  for (i = 0; i < strlen(name); i++) {
+    if (name[i] == ':') {
+      /* IPv6 Address in hexadecimal form, : is not allowed in DNS names. */
+      return 1;
+    }
+    if (name[i] >= '0' && name[i] <= '9') {
+      if (num_size > 3) return 0;
+      num_size++;
+    } else if (name[i] == '.') {
+      if (dot_count > 3 || num_size == 0) return 0;
+      dot_count++;
+      num_size = 0;
+    } else {
+      return 0;
+    }
+  }
+  if (dot_count < 3 || num_size == 0) return 0;
+  return 1;
+}
+
+/* Gets the subject CN from an X509 cert. */
+static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8,
+                                           size_t* utf8_size) {
+  int common_name_index = -1;
+  X509_NAME_ENTRY* common_name_entry = nullptr;
+  ASN1_STRING* common_name_asn1 = nullptr;
+  X509_NAME* subject_name = X509_get_subject_name(cert);
+  int utf8_returned_size = 0;
+  if (subject_name == nullptr) {
+    gpr_log(GPR_INFO, "Could not get subject name from certificate.");
+    return TSI_NOT_FOUND;
+  }
+  common_name_index =
+      X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1);
+  if (common_name_index == -1) {
+    gpr_log(GPR_INFO, "Could not get common name of subject from certificate.");
+    return TSI_NOT_FOUND;
+  }
+  common_name_entry = X509_NAME_get_entry(subject_name, common_name_index);
+  if (common_name_entry == nullptr) {
+    gpr_log(GPR_ERROR, "Could not get common name entry from certificate.");
+    return TSI_INTERNAL_ERROR;
+  }
+  common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
+  if (common_name_asn1 == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Could not get common name entry asn1 from certificate.");
+    return TSI_INTERNAL_ERROR;
+  }
+  utf8_returned_size = ASN1_STRING_to_UTF8(utf8, common_name_asn1);
+  if (utf8_returned_size < 0) {
+    gpr_log(GPR_ERROR, "Could not extract utf8 from asn1 string.");
+    return TSI_OUT_OF_RESOURCES;
+  }
+  *utf8_size = static_cast<size_t>(utf8_returned_size);
+  return TSI_OK;
+}
+
+/* Gets the subject CN of an X509 cert as a tsi_peer_property. */
+static tsi_result peer_property_from_x509_common_name(
+    X509* cert, tsi_peer_property* property) {
+  unsigned char* common_name;
+  size_t common_name_size;
+  tsi_result result =
+      ssl_get_x509_common_name(cert, &common_name, &common_name_size);
+  if (result != TSI_OK) {
+    if (result == TSI_NOT_FOUND) {
+      common_name = nullptr;
+      common_name_size = 0;
+    } else {
+      return result;
+    }
+  }
+  result = tsi_construct_string_peer_property(
+      TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY,
+      common_name == nullptr ? "" : reinterpret_cast<const char*>(common_name),
+      common_name_size, property);
+  OPENSSL_free(common_name);
+  return result;
+}
+
+/* Gets the X509 cert in PEM format as a tsi_peer_property. */
+static tsi_result add_pem_certificate(X509* cert, tsi_peer_property* property) {
+  BIO* bio = BIO_new(BIO_s_mem());
+  if (!PEM_write_bio_X509(bio, cert)) {
+    BIO_free(bio);
+    return TSI_INTERNAL_ERROR;
+  }
+  char* contents;
+  long len = BIO_get_mem_data(bio, &contents);
+  if (len <= 0) {
+    BIO_free(bio);
+    return TSI_INTERNAL_ERROR;
+  }
+  tsi_result result = tsi_construct_string_peer_property(
+      TSI_X509_PEM_CERT_PROPERTY, (const char*)contents,
+      static_cast<size_t>(len), property);
+  BIO_free(bio);
+  return result;
+}
+
+/* Gets the subject SANs from an X509 cert as a tsi_peer_property. */
+static tsi_result add_subject_alt_names_properties_to_peer(
+    tsi_peer* peer, GENERAL_NAMES* subject_alt_names,
+    size_t subject_alt_name_count) {
+  size_t i;
+  tsi_result result = TSI_OK;
+
+  /* Reset for DNS entries filtering. */
+  peer->property_count -= subject_alt_name_count;
+
+  for (i = 0; i < subject_alt_name_count; i++) {
+    GENERAL_NAME* subject_alt_name =
+        sk_GENERAL_NAME_value(subject_alt_names, TSI_SIZE_AS_SIZE(i));
+    /* Filter out the non-dns entries names. */
+    if (subject_alt_name->type == GEN_DNS) {
+      unsigned char* name = nullptr;
+      int name_size;
+      name_size = ASN1_STRING_to_UTF8(&name, subject_alt_name->d.dNSName);
+      if (name_size < 0) {
+        gpr_log(GPR_ERROR, "Could not get utf8 from asn1 string.");
+        result = TSI_INTERNAL_ERROR;
+        break;
+      }
+      result = tsi_construct_string_peer_property(
+          TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY,
+          reinterpret_cast<const char*>(name), static_cast<size_t>(name_size),
+          &peer->properties[peer->property_count++]);
+      OPENSSL_free(name);
+    } else if (subject_alt_name->type == GEN_IPADD) {
+      char ntop_buf[INET6_ADDRSTRLEN];
+      int af;
+
+      if (subject_alt_name->d.iPAddress->length == 4) {
+        af = AF_INET;
+      } else if (subject_alt_name->d.iPAddress->length == 16) {
+        af = AF_INET6;
+      } else {
+        gpr_log(GPR_ERROR, "SAN IP Address contained invalid IP");
+        result = TSI_INTERNAL_ERROR;
+        break;
+      }
+      const char* name = inet_ntop(af, subject_alt_name->d.iPAddress->data,
+                                   ntop_buf, INET6_ADDRSTRLEN);
+      if (name == nullptr) {
+        gpr_log(GPR_ERROR, "Could not get IP string from asn1 octet.");
+        result = TSI_INTERNAL_ERROR;
+        break;
+      }
+
+      result = tsi_construct_string_peer_property_from_cstring(
+          TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, name,
+          &peer->properties[peer->property_count++]);
+    }
+    if (result != TSI_OK) break;
+  }
+  return result;
+}
+
+/* Gets information about the peer's X509 cert as a tsi_peer object. */
+static tsi_result peer_from_x509(X509* cert, int include_certificate_type,
+                                 tsi_peer* peer) {
+  /* TODO(jboeuf): Maybe add more properties. */
+  GENERAL_NAMES* subject_alt_names = static_cast<GENERAL_NAMES*>(
+      X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
+  int subject_alt_name_count =
+      (subject_alt_names != nullptr)
+          ? static_cast<int>(sk_GENERAL_NAME_num(subject_alt_names))
+          : 0;
+  size_t property_count;
+  tsi_result result;
+  GPR_ASSERT(subject_alt_name_count >= 0);
+  property_count = (include_certificate_type ? static_cast<size_t>(1) : 0) +
+                   2 /* common name, certificate */ +
+                   static_cast<size_t>(subject_alt_name_count);
+  result = tsi_construct_peer(property_count, peer);
+  if (result != TSI_OK) return result;
+  do {
+    if (include_certificate_type) {
+      result = tsi_construct_string_peer_property_from_cstring(
+          TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE,
+          &peer->properties[0]);
+      if (result != TSI_OK) break;
+    }
+    result = peer_property_from_x509_common_name(
+        cert, &peer->properties[include_certificate_type ? 1 : 0]);
+    if (result != TSI_OK) break;
+
+    result = add_pem_certificate(
+        cert, &peer->properties[include_certificate_type ? 2 : 1]);
+    if (result != TSI_OK) break;
+
+    if (subject_alt_name_count != 0) {
+      result = add_subject_alt_names_properties_to_peer(
+          peer, subject_alt_names, static_cast<size_t>(subject_alt_name_count));
+      if (result != TSI_OK) break;
+    }
+  } while (0);
+
+  if (subject_alt_names != nullptr) {
+    sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free);
+  }
+  if (result != TSI_OK) tsi_peer_destruct(peer);
+  return result;
+}
+
+/* Logs the SSL error stack. */
+static void log_ssl_error_stack(void) {
+  unsigned long err;
+  while ((err = ERR_get_error()) != 0) {
+    char details[256];
+    ERR_error_string_n(static_cast<uint32_t>(err), details, sizeof(details));
+    gpr_log(GPR_ERROR, "%s", details);
+  }
+}
+
+/* Performs an SSL_read and handle errors. */
+static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes,
+                              size_t* unprotected_bytes_size) {
+  int read_from_ssl;
+  GPR_ASSERT(*unprotected_bytes_size <= INT_MAX);
+  read_from_ssl = SSL_read(ssl, unprotected_bytes,
+                           static_cast<int>(*unprotected_bytes_size));
+  if (read_from_ssl <= 0) {
+    read_from_ssl = SSL_get_error(ssl, read_from_ssl);
+    switch (read_from_ssl) {
+      case SSL_ERROR_ZERO_RETURN: /* Received a close_notify alert. */
+      case SSL_ERROR_WANT_READ:   /* We need more data to finish the frame. */
+        *unprotected_bytes_size = 0;
+        return TSI_OK;
+      case SSL_ERROR_WANT_WRITE:
+        gpr_log(
+            GPR_ERROR,
+            "Peer tried to renegotiate SSL connection. This is unsupported.");
+        return TSI_UNIMPLEMENTED;
+      case SSL_ERROR_SSL:
+        gpr_log(GPR_ERROR, "Corruption detected.");
+        log_ssl_error_stack();
+        return TSI_DATA_CORRUPTED;
+      default:
+        gpr_log(GPR_ERROR, "SSL_read failed with error %s.",
+                ssl_error_string(read_from_ssl));
+        return TSI_PROTOCOL_FAILURE;
+    }
+  }
+  *unprotected_bytes_size = static_cast<size_t>(read_from_ssl);
+  return TSI_OK;
+}
+
+/* Performs an SSL_write and handle errors. */
+static tsi_result do_ssl_write(SSL* ssl, unsigned char* unprotected_bytes,
+                               size_t unprotected_bytes_size) {
+  int ssl_write_result;
+  GPR_ASSERT(unprotected_bytes_size <= INT_MAX);
+  ssl_write_result = SSL_write(ssl, unprotected_bytes,
+                               static_cast<int>(unprotected_bytes_size));
+  if (ssl_write_result < 0) {
+    ssl_write_result = SSL_get_error(ssl, ssl_write_result);
+    if (ssl_write_result == SSL_ERROR_WANT_READ) {
+      gpr_log(GPR_ERROR,
+              "Peer tried to renegotiate SSL connection. This is unsupported.");
+      return TSI_UNIMPLEMENTED;
+    } else {
+      gpr_log(GPR_ERROR, "SSL_write failed with error %s.",
+              ssl_error_string(ssl_write_result));
+      return TSI_INTERNAL_ERROR;
+    }
+  }
+  return TSI_OK;
+}
+
+/* Loads an in-memory PEM certificate chain into the SSL context. */
+static tsi_result ssl_ctx_use_certificate_chain(SSL_CTX* context,
+                                                const char* pem_cert_chain,
+                                                size_t pem_cert_chain_size) {
+  tsi_result result = TSI_OK;
+  X509* certificate = nullptr;
+  BIO* pem;
+  GPR_ASSERT(pem_cert_chain_size <= INT_MAX);
+  pem = BIO_new_mem_buf((void*)pem_cert_chain,
+                        static_cast<int>(pem_cert_chain_size));
+  if (pem == nullptr) return TSI_OUT_OF_RESOURCES;
+
+  do {
+    certificate = PEM_read_bio_X509_AUX(pem, nullptr, nullptr, (void*)"");
+    if (certificate == nullptr) {
+      result = TSI_INVALID_ARGUMENT;
+      break;
+    }
+    if (!SSL_CTX_use_certificate(context, certificate)) {
+      result = TSI_INVALID_ARGUMENT;
+      break;
+    }
+    while (1) {
+      X509* certificate_authority =
+          PEM_read_bio_X509(pem, nullptr, nullptr, (void*)"");
+      if (certificate_authority == nullptr) {
+        ERR_clear_error();
+        break; /* Done reading. */
+      }
+      if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) {
+        X509_free(certificate_authority);
+        result = TSI_INVALID_ARGUMENT;
+        break;
+      }
+      /* We don't need to free certificate_authority as its ownership has been
+         transfered to the context. That is not the case for certificate though.
+       */
+    }
+  } while (0);
+
+  if (certificate != nullptr) X509_free(certificate);
+  BIO_free(pem);
+  return result;
+}
+
+/* Loads an in-memory PEM private key into the SSL context. */
+static tsi_result ssl_ctx_use_private_key(SSL_CTX* context, const char* pem_key,
+                                          size_t pem_key_size) {
+  tsi_result result = TSI_OK;
+  EVP_PKEY* private_key = nullptr;
+  BIO* pem;
+  GPR_ASSERT(pem_key_size <= INT_MAX);
+  pem = BIO_new_mem_buf((void*)pem_key, static_cast<int>(pem_key_size));
+  if (pem == nullptr) return TSI_OUT_OF_RESOURCES;
+  do {
+    private_key = PEM_read_bio_PrivateKey(pem, nullptr, nullptr, (void*)"");
+    if (private_key == nullptr) {
+      result = TSI_INVALID_ARGUMENT;
+      break;
+    }
+    if (!SSL_CTX_use_PrivateKey(context, private_key)) {
+      result = TSI_INVALID_ARGUMENT;
+      break;
+    }
+  } while (0);
+  if (private_key != nullptr) EVP_PKEY_free(private_key);
+  BIO_free(pem);
+  return result;
+}
+
+/* Loads in-memory PEM verification certs into the SSL context and optionally
+   returns the verification cert names (root_names can be NULL). */
+static tsi_result x509_store_load_certs(X509_STORE* cert_store,
+                                        const char* pem_roots,
+                                        size_t pem_roots_size,
+                                        STACK_OF(X509_NAME) * *root_names) {
+  tsi_result result = TSI_OK;
+  size_t num_roots = 0;
+  X509* root = nullptr;
+  X509_NAME* root_name = nullptr;
+  BIO* pem;
+  GPR_ASSERT(pem_roots_size <= INT_MAX);
+  pem = BIO_new_mem_buf((void*)pem_roots, static_cast<int>(pem_roots_size));
+  if (cert_store == nullptr) return TSI_INVALID_ARGUMENT;
+  if (pem == nullptr) return TSI_OUT_OF_RESOURCES;
+  if (root_names != nullptr) {
+    *root_names = sk_X509_NAME_new_null();
+    if (*root_names == nullptr) return TSI_OUT_OF_RESOURCES;
+  }
+
+  while (1) {
+    root = PEM_read_bio_X509_AUX(pem, nullptr, nullptr, (void*)"");
+    if (root == nullptr) {
+      ERR_clear_error();
+      break; /* We're at the end of stream. */
+    }
+    if (root_names != nullptr) {
+      root_name = X509_get_subject_name(root);
+      if (root_name == nullptr) {
+        gpr_log(GPR_ERROR, "Could not get name from root certificate.");
+        result = TSI_INVALID_ARGUMENT;
+        break;
+      }
+      root_name = X509_NAME_dup(root_name);
+      if (root_name == nullptr) {
+        result = TSI_OUT_OF_RESOURCES;
+        break;
+      }
+      sk_X509_NAME_push(*root_names, root_name);
+      root_name = nullptr;
+    }
+    if (!X509_STORE_add_cert(cert_store, root)) {
+      gpr_log(GPR_ERROR, "Could not add root certificate to ssl context.");
+      result = TSI_INTERNAL_ERROR;
+      break;
+    }
+    X509_free(root);
+    num_roots++;
+  }
+
+  if (num_roots == 0) {
+    gpr_log(GPR_ERROR, "Could not load any root certificate.");
+    result = TSI_INVALID_ARGUMENT;
+  }
+
+  if (result != TSI_OK) {
+    if (root != nullptr) X509_free(root);
+    if (root_names != nullptr) {
+      sk_X509_NAME_pop_free(*root_names, X509_NAME_free);
+      *root_names = nullptr;
+      if (root_name != nullptr) X509_NAME_free(root_name);
+    }
+  }
+  BIO_free(pem);
+  return result;
+}
+
+static tsi_result ssl_ctx_load_verification_certs(SSL_CTX* context,
+                                                  const char* pem_roots,
+                                                  size_t pem_roots_size,
+                                                  STACK_OF(X509_NAME) *
+                                                      *root_name) {
+  X509_STORE* cert_store = SSL_CTX_get_cert_store(context);
+  return x509_store_load_certs(cert_store, pem_roots, pem_roots_size,
+                               root_name);
+}
+
+/* Populates the SSL context with a private key and a cert chain, and sets the
+   cipher list and the ephemeral ECDH key. */
+static tsi_result populate_ssl_context(
+    SSL_CTX* context, const tsi_ssl_pem_key_cert_pair* key_cert_pair,
+    const char* cipher_list) {
+  tsi_result result = TSI_OK;
+  if (key_cert_pair != nullptr) {
+    if (key_cert_pair->cert_chain != nullptr) {
+      result = ssl_ctx_use_certificate_chain(context, key_cert_pair->cert_chain,
+                                             strlen(key_cert_pair->cert_chain));
+      if (result != TSI_OK) {
+        gpr_log(GPR_ERROR, "Invalid cert chain file.");
+        return result;
+      }
+    }
+    if (key_cert_pair->private_key != nullptr) {
+      result = ssl_ctx_use_private_key(context, key_cert_pair->private_key,
+                                       strlen(key_cert_pair->private_key));
+      if (result != TSI_OK || !SSL_CTX_check_private_key(context)) {
+        gpr_log(GPR_ERROR, "Invalid private key.");
+        return result != TSI_OK ? result : TSI_INVALID_ARGUMENT;
+      }
+    }
+  }
+  if ((cipher_list != nullptr) &&
+      !SSL_CTX_set_cipher_list(context, cipher_list)) {
+    gpr_log(GPR_ERROR, "Invalid cipher list: %s.", cipher_list);
+    return TSI_INVALID_ARGUMENT;
+  }
+  {
+    EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+    if (!SSL_CTX_set_tmp_ecdh(context, ecdh)) {
+      gpr_log(GPR_ERROR, "Could not set ephemeral ECDH key.");
+      EC_KEY_free(ecdh);
+      return TSI_INTERNAL_ERROR;
+    }
+    SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
+    EC_KEY_free(ecdh);
+  }
+  return TSI_OK;
+}
+
+/* Extracts the CN and the SANs from an X509 cert as a peer object. */
+static tsi_result extract_x509_subject_names_from_pem_cert(const char* pem_cert,
+                                                           tsi_peer* peer) {
+  tsi_result result = TSI_OK;
+  X509* cert = nullptr;
+  BIO* pem;
+  pem = BIO_new_mem_buf((void*)pem_cert, static_cast<int>(strlen(pem_cert)));
+  if (pem == nullptr) return TSI_OUT_OF_RESOURCES;
+
+  cert = PEM_read_bio_X509(pem, nullptr, nullptr, (void*)"");
+  if (cert == nullptr) {
+    gpr_log(GPR_ERROR, "Invalid certificate");
+    result = TSI_INVALID_ARGUMENT;
+  } else {
+    result = peer_from_x509(cert, 0, peer);
+  }
+  if (cert != nullptr) X509_free(cert);
+  BIO_free(pem);
+  return result;
+}
+
+/* Builds the alpn protocol name list according to rfc 7301. */
+static tsi_result build_alpn_protocol_name_list(
+    const char** alpn_protocols, uint16_t num_alpn_protocols,
+    unsigned char** protocol_name_list, size_t* protocol_name_list_length) {
+  uint16_t i;
+  unsigned char* current;
+  *protocol_name_list = nullptr;
+  *protocol_name_list_length = 0;
+  if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT;
+  for (i = 0; i < num_alpn_protocols; i++) {
+    size_t length =
+        alpn_protocols[i] == nullptr ? 0 : strlen(alpn_protocols[i]);
+    if (length == 0 || length > 255) {
+      gpr_log(GPR_ERROR, "Invalid protocol name length: %d.",
+              static_cast<int>(length));
+      return TSI_INVALID_ARGUMENT;
+    }
+    *protocol_name_list_length += length + 1;
+  }
+  *protocol_name_list =
+      static_cast<unsigned char*>(gpr_malloc(*protocol_name_list_length));
+  if (*protocol_name_list == nullptr) return TSI_OUT_OF_RESOURCES;
+  current = *protocol_name_list;
+  for (i = 0; i < num_alpn_protocols; i++) {
+    size_t length = strlen(alpn_protocols[i]);
+    *(current++) = static_cast<uint8_t>(length); /* max checked above. */
+    memcpy(current, alpn_protocols[i], length);
+    current += length;
+  }
+  /* Safety check. */
+  if ((current < *protocol_name_list) ||
+      (static_cast<uintptr_t>(current - *protocol_name_list) !=
+       *protocol_name_list_length)) {
+    return TSI_INTERNAL_ERROR;
+  }
+  return TSI_OK;
+}
+
+// The verification callback is used for clients that don't really care about
+// the server's certificate, but we need to pull it anyway, in case a higher
+// layer wants to look at it. In this case the verification may fail, but
+// we don't really care.
+static int NullVerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) {
+  return 1;
+}
+
+/* --- tsi_ssl_root_certs_store methods implementation. ---*/
+
+tsi_ssl_root_certs_store* tsi_ssl_root_certs_store_create(
+    const char* pem_roots) {
+  if (pem_roots == nullptr) {
+    gpr_log(GPR_ERROR, "The root certificates are empty.");
+    return nullptr;
+  }
+  tsi_ssl_root_certs_store* root_store = static_cast<tsi_ssl_root_certs_store*>(
+      gpr_zalloc(sizeof(tsi_ssl_root_certs_store)));
+  if (root_store == nullptr) {
+    gpr_log(GPR_ERROR, "Could not allocate buffer for ssl_root_certs_store.");
+    return nullptr;
+  }
+  root_store->store = X509_STORE_new();
+  if (root_store->store == nullptr) {
+    gpr_log(GPR_ERROR, "Could not allocate buffer for X509_STORE.");
+    gpr_free(root_store);
+    return nullptr;
+  }
+  tsi_result result = x509_store_load_certs(root_store->store, pem_roots,
+                                            strlen(pem_roots), nullptr);
+  if (result != TSI_OK) {
+    gpr_log(GPR_ERROR, "Could not load root certificates.");
+    X509_STORE_free(root_store->store);
+    gpr_free(root_store);
+    return nullptr;
+  }
+  return root_store;
+}
+
+void tsi_ssl_root_certs_store_destroy(tsi_ssl_root_certs_store* self) {
+  if (self == nullptr) return;
+  X509_STORE_free(self->store);
+  gpr_free(self);
+}
+
+/* --- tsi_ssl_session_cache methods implementation. ---*/
+
+tsi_ssl_session_cache* tsi_ssl_session_cache_create_lru(size_t capacity) {
+  /* Pointer will be dereferenced by unref call. */
+  return reinterpret_cast<tsi_ssl_session_cache*>(
+      tsi::SslSessionLRUCache::Create(capacity).release());
+}
+
+void tsi_ssl_session_cache_ref(tsi_ssl_session_cache* cache) {
+  /* Pointer will be dereferenced by unref call. */
+  reinterpret_cast<tsi::SslSessionLRUCache*>(cache)->Ref().release();
+}
+
+void tsi_ssl_session_cache_unref(tsi_ssl_session_cache* cache) {
+  reinterpret_cast<tsi::SslSessionLRUCache*>(cache)->Unref();
+}
+
+/* --- tsi_frame_protector methods implementation. ---*/
+
+static tsi_result ssl_protector_protect(tsi_frame_protector* self,
+                                        const unsigned char* unprotected_bytes,
+                                        size_t* unprotected_bytes_size,
+                                        unsigned char* protected_output_frames,
+                                        size_t* protected_output_frames_size) {
+  tsi_ssl_frame_protector* impl =
+      reinterpret_cast<tsi_ssl_frame_protector*>(self);
+  int read_from_ssl;
+  size_t available;
+  tsi_result result = TSI_OK;
+
+  /* First see if we have some pending data in the SSL BIO. */
+  int pending_in_ssl = static_cast<int>(BIO_pending(impl->network_io));
+  if (pending_in_ssl > 0) {
+    *unprotected_bytes_size = 0;
+    GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
+    read_from_ssl = BIO_read(impl->network_io, protected_output_frames,
+                             static_cast<int>(*protected_output_frames_size));
+    if (read_from_ssl < 0) {
+      gpr_log(GPR_ERROR,
+              "Could not read from BIO even though some data is pending");
+      return TSI_INTERNAL_ERROR;
+    }
+    *protected_output_frames_size = static_cast<size_t>(read_from_ssl);
+    return TSI_OK;
+  }
+
+  /* Now see if we can send a complete frame. */
+  available = impl->buffer_size - impl->buffer_offset;
+  if (available > *unprotected_bytes_size) {
+    /* If we cannot, just copy the data in our internal buffer. */
+    memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes,
+           *unprotected_bytes_size);
+    impl->buffer_offset += *unprotected_bytes_size;
+    *protected_output_frames_size = 0;
+    return TSI_OK;
+  }
+
+  /* If we can, prepare the buffer, send it to SSL_write and read. */
+  memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, available);
+  result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_size);
+  if (result != TSI_OK) return result;
+
+  GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
+  read_from_ssl = BIO_read(impl->network_io, protected_output_frames,
+                           static_cast<int>(*protected_output_frames_size));
+  if (read_from_ssl < 0) {
+    gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write.");
+    return TSI_INTERNAL_ERROR;
+  }
+  *protected_output_frames_size = static_cast<size_t>(read_from_ssl);
+  *unprotected_bytes_size = available;
+  impl->buffer_offset = 0;
+  return TSI_OK;
+}
+
+static tsi_result ssl_protector_protect_flush(
+    tsi_frame_protector* self, unsigned char* protected_output_frames,
+    size_t* protected_output_frames_size, size_t* still_pending_size) {
+  tsi_result result = TSI_OK;
+  tsi_ssl_frame_protector* impl =
+      reinterpret_cast<tsi_ssl_frame_protector*>(self);
+  int read_from_ssl = 0;
+  int pending;
+
+  if (impl->buffer_offset != 0) {
+    result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_offset);
+    if (result != TSI_OK) return result;
+    impl->buffer_offset = 0;
+  }
+
+  pending = static_cast<int>(BIO_pending(impl->network_io));
+  GPR_ASSERT(pending >= 0);
+  *still_pending_size = static_cast<size_t>(pending);
+  if (*still_pending_size == 0) return TSI_OK;
+
+  GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
+  read_from_ssl = BIO_read(impl->network_io, protected_output_frames,
+                           static_cast<int>(*protected_output_frames_size));
+  if (read_from_ssl <= 0) {
+    gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write.");
+    return TSI_INTERNAL_ERROR;
+  }
+  *protected_output_frames_size = static_cast<size_t>(read_from_ssl);
+  pending = static_cast<int>(BIO_pending(impl->network_io));
+  GPR_ASSERT(pending >= 0);
+  *still_pending_size = static_cast<size_t>(pending);
+  return TSI_OK;
+}
+
+static tsi_result ssl_protector_unprotect(
+    tsi_frame_protector* self, const unsigned char* protected_frames_bytes,
+    size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes,
+    size_t* unprotected_bytes_size) {
+  tsi_result result = TSI_OK;
+  int written_into_ssl = 0;
+  size_t output_bytes_size = *unprotected_bytes_size;
+  size_t output_bytes_offset = 0;
+  tsi_ssl_frame_protector* impl =
+      reinterpret_cast<tsi_ssl_frame_protector*>(self);
+
+  /* First, try to read remaining data from ssl. */
+  result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size);
+  if (result != TSI_OK) return result;
+  if (*unprotected_bytes_size == output_bytes_size) {
+    /* We have read everything we could and cannot process any more input. */
+    *protected_frames_bytes_size = 0;
+    return TSI_OK;
+  }
+  output_bytes_offset = *unprotected_bytes_size;
+  unprotected_bytes += output_bytes_offset;
+  *unprotected_bytes_size = output_bytes_size - output_bytes_offset;
+
+  /* Then, try to write some data to ssl. */
+  GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX);
+  written_into_ssl = BIO_write(impl->network_io, protected_frames_bytes,
+                               static_cast<int>(*protected_frames_bytes_size));
+  if (written_into_ssl < 0) {
+    gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d",
+            written_into_ssl);
+    return TSI_INTERNAL_ERROR;
+  }
+  *protected_frames_bytes_size = static_cast<size_t>(written_into_ssl);
+
+  /* Now try to read some data again. */
+  result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size);
+  if (result == TSI_OK) {
+    /* Don't forget to output the total number of bytes read. */
+    *unprotected_bytes_size += output_bytes_offset;
+  }
+  return result;
+}
+
+static void ssl_protector_destroy(tsi_frame_protector* self) {
+  tsi_ssl_frame_protector* impl =
+      reinterpret_cast<tsi_ssl_frame_protector*>(self);
+  if (impl->buffer != nullptr) gpr_free(impl->buffer);
+  if (impl->ssl != nullptr) SSL_free(impl->ssl);
+  if (impl->network_io != nullptr) BIO_free(impl->network_io);
+  gpr_free(self);
+}
+
+static const tsi_frame_protector_vtable frame_protector_vtable = {
+    ssl_protector_protect,
+    ssl_protector_protect_flush,
+    ssl_protector_unprotect,
+    ssl_protector_destroy,
+};
+
+/* --- tsi_server_handshaker_factory methods implementation. --- */
+
+static void tsi_ssl_handshaker_factory_destroy(
+    tsi_ssl_handshaker_factory* self) {
+  if (self == nullptr) return;
+
+  if (self->vtable != nullptr && self->vtable->destroy != nullptr) {
+    self->vtable->destroy(self);
+  }
+  /* Note, we don't free(self) here because this object is always directly
+   * embedded in another object. If tsi_ssl_handshaker_factory_init allocates
+   * any memory, it should be free'd here. */
+}
+
+static tsi_ssl_handshaker_factory* tsi_ssl_handshaker_factory_ref(
+    tsi_ssl_handshaker_factory* self) {
+  if (self == nullptr) return nullptr;
+  gpr_refn(&self->refcount, 1);
+  return self;
+}
+
+static void tsi_ssl_handshaker_factory_unref(tsi_ssl_handshaker_factory* self) {
+  if (self == nullptr) return;
+
+  if (gpr_unref(&self->refcount)) {
+    tsi_ssl_handshaker_factory_destroy(self);
+  }
+}
+
+static tsi_ssl_handshaker_factory_vtable handshaker_factory_vtable = {nullptr};
+
+/* Initializes a tsi_ssl_handshaker_factory object. Caller is responsible for
+ * allocating memory for the factory. */
+static void tsi_ssl_handshaker_factory_init(
+    tsi_ssl_handshaker_factory* factory) {
+  GPR_ASSERT(factory != nullptr);
+
+  factory->vtable = &handshaker_factory_vtable;
+  gpr_ref_init(&factory->refcount, 1);
+}
+
+/* --- tsi_handshaker_result methods implementation. ---*/
+
+static tsi_result ssl_handshaker_result_extract_peer(
+    const tsi_handshaker_result* self, tsi_peer* peer) {
+  tsi_result result = TSI_OK;
+  const unsigned char* alpn_selected = nullptr;
+  unsigned int alpn_selected_len;
+  const tsi_ssl_handshaker_result* impl =
+      reinterpret_cast<const tsi_ssl_handshaker_result*>(self);
+  X509* peer_cert = SSL_get_peer_certificate(impl->ssl);
+  if (peer_cert != nullptr) {
+    result = peer_from_x509(peer_cert, 1, peer);
+    X509_free(peer_cert);
+    if (result != TSI_OK) return result;
+  }
+#if TSI_OPENSSL_ALPN_SUPPORT
+  SSL_get0_alpn_selected(impl->ssl, &alpn_selected, &alpn_selected_len);
+#endif /* TSI_OPENSSL_ALPN_SUPPORT */
+  if (alpn_selected == nullptr) {
+    /* Try npn. */
+    SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected,
+                                   &alpn_selected_len);
+  }
+
+  // 1 is for session reused property.
+  size_t new_property_count = peer->property_count + 1;
+  if (alpn_selected != nullptr) new_property_count++;
+  tsi_peer_property* new_properties = static_cast<tsi_peer_property*>(
+      gpr_zalloc(sizeof(*new_properties) * new_property_count));
+  for (size_t i = 0; i < peer->property_count; i++) {
+    new_properties[i] = peer->properties[i];
+  }
+  if (peer->properties != nullptr) gpr_free(peer->properties);
+  peer->properties = new_properties;
+
+  if (alpn_selected != nullptr) {
+    result = tsi_construct_string_peer_property(
+        TSI_SSL_ALPN_SELECTED_PROTOCOL,
+        reinterpret_cast<const char*>(alpn_selected), alpn_selected_len,
+        &peer->properties[peer->property_count]);
+    if (result != TSI_OK) return result;
+    peer->property_count++;
+  }
+
+  const char* session_reused = SSL_session_reused(impl->ssl) ? "true" : "false";
+  result = tsi_construct_string_peer_property_from_cstring(
+      TSI_SSL_SESSION_REUSED_PEER_PROPERTY, session_reused,
+      &peer->properties[peer->property_count]);
+  if (result != TSI_OK) return result;
+  peer->property_count++;
+
+  return result;
+}
+
+static tsi_result ssl_handshaker_result_create_frame_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_frame_protector** protector) {
+  size_t actual_max_output_protected_frame_size =
+      TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND;
+  tsi_ssl_handshaker_result* impl =
+      reinterpret_cast<tsi_ssl_handshaker_result*>(
+          const_cast<tsi_handshaker_result*>(self));
+  tsi_ssl_frame_protector* protector_impl =
+      static_cast<tsi_ssl_frame_protector*>(
+          gpr_zalloc(sizeof(*protector_impl)));
+
+  if (max_output_protected_frame_size != nullptr) {
+    if (*max_output_protected_frame_size >
+        TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND) {
+      *max_output_protected_frame_size =
+          TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND;
+    } else if (*max_output_protected_frame_size <
+               TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND) {
+      *max_output_protected_frame_size =
+          TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND;
+    }
+    actual_max_output_protected_frame_size = *max_output_protected_frame_size;
+  }
+  protector_impl->buffer_size =
+      actual_max_output_protected_frame_size - TSI_SSL_MAX_PROTECTION_OVERHEAD;
+  protector_impl->buffer =
+      static_cast<unsigned char*>(gpr_malloc(protector_impl->buffer_size));
+  if (protector_impl->buffer == nullptr) {
+    gpr_log(GPR_ERROR,
+            "Could not allocated buffer for tsi_ssl_frame_protector.");
+    gpr_free(protector_impl);
+    return TSI_INTERNAL_ERROR;
+  }
+
+  /* Transfer ownership of ssl and network_io to the frame protector. */
+  protector_impl->ssl = impl->ssl;
+  impl->ssl = nullptr;
+  protector_impl->network_io = impl->network_io;
+  impl->network_io = nullptr;
+  protector_impl->base.vtable = &frame_protector_vtable;
+  *protector = &protector_impl->base;
+  return TSI_OK;
+}
+
+static tsi_result ssl_handshaker_result_get_unused_bytes(
+    const tsi_handshaker_result* self, const unsigned char** bytes,
+    size_t* bytes_size) {
+  const tsi_ssl_handshaker_result* impl =
+      reinterpret_cast<const tsi_ssl_handshaker_result*>(self);
+  *bytes_size = impl->unused_bytes_size;
+  *bytes = impl->unused_bytes;
+  return TSI_OK;
+}
+
+static void ssl_handshaker_result_destroy(tsi_handshaker_result* self) {
+  tsi_ssl_handshaker_result* impl =
+      reinterpret_cast<tsi_ssl_handshaker_result*>(self);
+  SSL_free(impl->ssl);
+  BIO_free(impl->network_io);
+  gpr_free(impl->unused_bytes);
+  gpr_free(impl);
+}
+
+static const tsi_handshaker_result_vtable handshaker_result_vtable = {
+    ssl_handshaker_result_extract_peer,
+    nullptr, /* create_zero_copy_grpc_protector */
+    ssl_handshaker_result_create_frame_protector,
+    ssl_handshaker_result_get_unused_bytes,
+    ssl_handshaker_result_destroy,
+};
+
+static tsi_result ssl_handshaker_result_create(
+    tsi_ssl_handshaker* handshaker, const unsigned char* unused_bytes,
+    size_t unused_bytes_size, tsi_handshaker_result** handshaker_result) {
+  if (handshaker == nullptr || handshaker_result == nullptr ||
+      (unused_bytes_size > 0 && unused_bytes == nullptr)) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  tsi_ssl_handshaker_result* result =
+      static_cast<tsi_ssl_handshaker_result*>(gpr_zalloc(sizeof(*result)));
+  result->base.vtable = &handshaker_result_vtable;
+  /* Transfer ownership of ssl and network_io to the handshaker result. */
+  result->ssl = handshaker->ssl;
+  handshaker->ssl = nullptr;
+  result->network_io = handshaker->network_io;
+  handshaker->network_io = nullptr;
+  if (unused_bytes_size > 0) {
+    result->unused_bytes =
+        static_cast<unsigned char*>(gpr_malloc(unused_bytes_size));
+    memcpy(result->unused_bytes, unused_bytes, unused_bytes_size);
+  }
+  result->unused_bytes_size = unused_bytes_size;
+  *handshaker_result = &result->base;
+  return TSI_OK;
+}
+
+/* --- tsi_handshaker methods implementation. ---*/
+
+static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(
+    tsi_ssl_handshaker* impl, unsigned char* bytes, size_t* bytes_size) {
+  int bytes_read_from_ssl = 0;
+  if (bytes == nullptr || bytes_size == nullptr || *bytes_size == 0 ||
+      *bytes_size > INT_MAX) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  GPR_ASSERT(*bytes_size <= INT_MAX);
+  bytes_read_from_ssl =
+      BIO_read(impl->network_io, bytes, static_cast<int>(*bytes_size));
+  if (bytes_read_from_ssl < 0) {
+    *bytes_size = 0;
+    if (!BIO_should_retry(impl->network_io)) {
+      impl->result = TSI_INTERNAL_ERROR;
+      return impl->result;
+    } else {
+      return TSI_OK;
+    }
+  }
+  *bytes_size = static_cast<size_t>(bytes_read_from_ssl);
+  return BIO_pending(impl->network_io) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA;
+}
+
+static tsi_result ssl_handshaker_get_result(tsi_ssl_handshaker* impl) {
+  if ((impl->result == TSI_HANDSHAKE_IN_PROGRESS) &&
+      SSL_is_init_finished(impl->ssl)) {
+    impl->result = TSI_OK;
+  }
+  return impl->result;
+}
+
+static tsi_result ssl_handshaker_process_bytes_from_peer(
+    tsi_ssl_handshaker* impl, const unsigned char* bytes, size_t* bytes_size) {
+  int bytes_written_into_ssl_size = 0;
+  if (bytes == nullptr || bytes_size == nullptr || *bytes_size > INT_MAX) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  GPR_ASSERT(*bytes_size <= INT_MAX);
+  bytes_written_into_ssl_size =
+      BIO_write(impl->network_io, bytes, static_cast<int>(*bytes_size));
+  if (bytes_written_into_ssl_size < 0) {
+    gpr_log(GPR_ERROR, "Could not write to memory BIO.");
+    impl->result = TSI_INTERNAL_ERROR;
+    return impl->result;
+  }
+  *bytes_size = static_cast<size_t>(bytes_written_into_ssl_size);
+
+  if (ssl_handshaker_get_result(impl) != TSI_HANDSHAKE_IN_PROGRESS) {
+    impl->result = TSI_OK;
+    return impl->result;
+  } else {
+    /* Get ready to get some bytes from SSL. */
+    int ssl_result = SSL_do_handshake(impl->ssl);
+    ssl_result = SSL_get_error(impl->ssl, ssl_result);
+    switch (ssl_result) {
+      case SSL_ERROR_WANT_READ:
+        if (BIO_pending(impl->network_io) == 0) {
+          /* We need more data. */
+          return TSI_INCOMPLETE_DATA;
+        } else {
+          return TSI_OK;
+        }
+      case SSL_ERROR_NONE:
+        return TSI_OK;
+      default: {
+        char err_str[256];
+        ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str));
+        gpr_log(GPR_ERROR, "Handshake failed with fatal error %s: %s.",
+                ssl_error_string(ssl_result), err_str);
+        impl->result = TSI_PROTOCOL_FAILURE;
+        return impl->result;
+      }
+    }
+  }
+}
+
+static void ssl_handshaker_destroy(tsi_handshaker* self) {
+  tsi_ssl_handshaker* impl = reinterpret_cast<tsi_ssl_handshaker*>(self);
+  SSL_free(impl->ssl);
+  BIO_free(impl->network_io);
+  gpr_free(impl->outgoing_bytes_buffer);
+  tsi_ssl_handshaker_factory_unref(impl->factory_ref);
+  gpr_free(impl);
+}
+
+static tsi_result ssl_handshaker_next(
+    tsi_handshaker* self, const unsigned char* received_bytes,
+    size_t received_bytes_size, const unsigned char** bytes_to_send,
+    size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result,
+    tsi_handshaker_on_next_done_cb cb, void* user_data) {
+  /* Input sanity check.  */
+  if ((received_bytes_size > 0 && received_bytes == nullptr) ||
+      bytes_to_send == nullptr || bytes_to_send_size == nullptr ||
+      handshaker_result == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  /* If there are received bytes, process them first.  */
+  tsi_ssl_handshaker* impl = reinterpret_cast<tsi_ssl_handshaker*>(self);
+  tsi_result status = TSI_OK;
+  size_t bytes_consumed = received_bytes_size;
+  if (received_bytes_size > 0) {
+    status = ssl_handshaker_process_bytes_from_peer(impl, received_bytes,
+                                                    &bytes_consumed);
+    if (status != TSI_OK) return status;
+  }
+  /* Get bytes to send to the peer, if available.  */
+  size_t offset = 0;
+  do {
+    size_t to_send_size = impl->outgoing_bytes_buffer_size - offset;
+    status = ssl_handshaker_get_bytes_to_send_to_peer(
+        impl, impl->outgoing_bytes_buffer + offset, &to_send_size);
+    offset += to_send_size;
+    if (status == TSI_INCOMPLETE_DATA) {
+      impl->outgoing_bytes_buffer_size *= 2;
+      impl->outgoing_bytes_buffer = static_cast<unsigned char*>(gpr_realloc(
+          impl->outgoing_bytes_buffer, impl->outgoing_bytes_buffer_size));
+    }
+  } while (status == TSI_INCOMPLETE_DATA);
+  if (status != TSI_OK) return status;
+  *bytes_to_send = impl->outgoing_bytes_buffer;
+  *bytes_to_send_size = offset;
+  /* If handshake completes, create tsi_handshaker_result.  */
+  if (ssl_handshaker_get_result(impl) == TSI_HANDSHAKE_IN_PROGRESS) {
+    *handshaker_result = nullptr;
+  } else {
+    size_t unused_bytes_size = received_bytes_size - bytes_consumed;
+    const unsigned char* unused_bytes =
+        unused_bytes_size == 0 ? nullptr : received_bytes + bytes_consumed;
+    status = ssl_handshaker_result_create(impl, unused_bytes, unused_bytes_size,
+                                          handshaker_result);
+    if (status == TSI_OK) {
+      /* Indicates that the handshake has completed and that a handshaker_result
+       * has been created. */
+      self->handshaker_result_created = true;
+    }
+  }
+  return status;
+}
+
+static const tsi_handshaker_vtable handshaker_vtable = {
+    nullptr, /* get_bytes_to_send_to_peer -- deprecated */
+    nullptr, /* process_bytes_from_peer   -- deprecated */
+    nullptr, /* get_result                -- deprecated */
+    nullptr, /* extract_peer              -- deprecated */
+    nullptr, /* create_frame_protector    -- deprecated */
+    ssl_handshaker_destroy,
+    ssl_handshaker_next,
+    nullptr, /* shutdown */
+};
+
+/* --- tsi_ssl_handshaker_factory common methods. --- */
+
+static void tsi_ssl_handshaker_resume_session(
+    SSL* ssl, tsi::SslSessionLRUCache* session_cache) {
+  const char* server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  if (server_name == nullptr) {
+    return;
+  }
+  tsi::SslSessionPtr session = session_cache->Get(server_name);
+  if (session != nullptr) {
+    // SSL_set_session internally increments reference counter.
+    SSL_set_session(ssl, session.get());
+  }
+}
+
+static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client,
+                                            const char* server_name_indication,
+                                            tsi_ssl_handshaker_factory* factory,
+                                            tsi_handshaker** handshaker) {
+  SSL* ssl = SSL_new(ctx);
+  BIO* network_io = nullptr;
+  BIO* ssl_io = nullptr;
+  tsi_ssl_handshaker* impl = nullptr;
+  *handshaker = nullptr;
+  if (ctx == nullptr) {
+    gpr_log(GPR_ERROR, "SSL Context is null. Should never happen.");
+    return TSI_INTERNAL_ERROR;
+  }
+  if (ssl == nullptr) {
+    return TSI_OUT_OF_RESOURCES;
+  }
+  SSL_set_info_callback(ssl, ssl_info_callback);
+
+  if (!BIO_new_bio_pair(&network_io, 0, &ssl_io, 0)) {
+    gpr_log(GPR_ERROR, "BIO_new_bio_pair failed.");
+    SSL_free(ssl);
+    return TSI_OUT_OF_RESOURCES;
+  }
+  SSL_set_bio(ssl, ssl_io, ssl_io);
+  if (is_client) {
+    int ssl_result;
+    SSL_set_connect_state(ssl);
+    if (server_name_indication != nullptr) {
+      if (!SSL_set_tlsext_host_name(ssl, server_name_indication)) {
+        gpr_log(GPR_ERROR, "Invalid server name indication %s.",
+                server_name_indication);
+        SSL_free(ssl);
+        BIO_free(network_io);
+        return TSI_INTERNAL_ERROR;
+      }
+    }
+    tsi_ssl_client_handshaker_factory* client_factory =
+        reinterpret_cast<tsi_ssl_client_handshaker_factory*>(factory);
+    if (client_factory->session_cache != nullptr) {
+      tsi_ssl_handshaker_resume_session(ssl,
+                                        client_factory->session_cache.get());
+    }
+    ssl_result = SSL_do_handshake(ssl);
+    ssl_result = SSL_get_error(ssl, ssl_result);
+    if (ssl_result != SSL_ERROR_WANT_READ) {
+      gpr_log(GPR_ERROR,
+              "Unexpected error received from first SSL_do_handshake call: %s",
+              ssl_error_string(ssl_result));
+      SSL_free(ssl);
+      BIO_free(network_io);
+      return TSI_INTERNAL_ERROR;
+    }
+  } else {
+    SSL_set_accept_state(ssl);
+  }
+
+  impl = static_cast<tsi_ssl_handshaker*>(gpr_zalloc(sizeof(*impl)));
+  impl->ssl = ssl;
+  impl->network_io = network_io;
+  impl->result = TSI_HANDSHAKE_IN_PROGRESS;
+  impl->outgoing_bytes_buffer_size =
+      TSI_SSL_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE;
+  impl->outgoing_bytes_buffer =
+      static_cast<unsigned char*>(gpr_zalloc(impl->outgoing_bytes_buffer_size));
+  impl->base.vtable = &handshaker_vtable;
+  impl->factory_ref = tsi_ssl_handshaker_factory_ref(factory);
+
+  *handshaker = &impl->base;
+  return TSI_OK;
+}
+
+static int select_protocol_list(const unsigned char** out,
+                                unsigned char* outlen,
+                                const unsigned char* client_list,
+                                size_t client_list_len,
+                                const unsigned char* server_list,
+                                size_t server_list_len) {
+  const unsigned char* client_current = client_list;
+  while (static_cast<unsigned int>(client_current - client_list) <
+         client_list_len) {
+    unsigned char client_current_len = *(client_current++);
+    const unsigned char* server_current = server_list;
+    while ((server_current >= server_list) &&
+           static_cast<uintptr_t>(server_current - server_list) <
+               server_list_len) {
+      unsigned char server_current_len = *(server_current++);
+      if ((client_current_len == server_current_len) &&
+          !memcmp(client_current, server_current, server_current_len)) {
+        *out = server_current;
+        *outlen = server_current_len;
+        return SSL_TLSEXT_ERR_OK;
+      }
+      server_current += server_current_len;
+    }
+    client_current += client_current_len;
+  }
+  return SSL_TLSEXT_ERR_NOACK;
+}
+
+/* --- tsi_ssl_client_handshaker_factory methods implementation. --- */
+
+tsi_result tsi_ssl_client_handshaker_factory_create_handshaker(
+    tsi_ssl_client_handshaker_factory* self, const char* server_name_indication,
+    tsi_handshaker** handshaker) {
+  return create_tsi_ssl_handshaker(self->ssl_context, 1, server_name_indication,
+                                   &self->base, handshaker);
+}
+
+void tsi_ssl_client_handshaker_factory_unref(
+    tsi_ssl_client_handshaker_factory* self) {
+  if (self == nullptr) return;
+  tsi_ssl_handshaker_factory_unref(&self->base);
+}
+
+static void tsi_ssl_client_handshaker_factory_destroy(
+    tsi_ssl_handshaker_factory* factory) {
+  if (factory == nullptr) return;
+  tsi_ssl_client_handshaker_factory* self =
+      reinterpret_cast<tsi_ssl_client_handshaker_factory*>(factory);
+  if (self->ssl_context != nullptr) SSL_CTX_free(self->ssl_context);
+  if (self->alpn_protocol_list != nullptr) gpr_free(self->alpn_protocol_list);
+  self->session_cache.reset();
+  gpr_free(self);
+}
+
+static int client_handshaker_factory_npn_callback(SSL* ssl, unsigned char** out,
+                                                  unsigned char* outlen,
+                                                  const unsigned char* in,
+                                                  unsigned int inlen,
+                                                  void* arg) {
+  tsi_ssl_client_handshaker_factory* factory =
+      static_cast<tsi_ssl_client_handshaker_factory*>(arg);
+  return select_protocol_list((const unsigned char**)out, outlen,
+                              factory->alpn_protocol_list,
+                              factory->alpn_protocol_list_length, in, inlen);
+}
+
+/* --- tsi_ssl_server_handshaker_factory methods implementation. --- */
+
+tsi_result tsi_ssl_server_handshaker_factory_create_handshaker(
+    tsi_ssl_server_handshaker_factory* self, tsi_handshaker** handshaker) {
+  if (self->ssl_context_count == 0) return TSI_INVALID_ARGUMENT;
+  /* Create the handshaker with the first context. We will switch if needed
+     because of SNI in ssl_server_handshaker_factory_servername_callback.  */
+  return create_tsi_ssl_handshaker(self->ssl_contexts[0], 0, nullptr,
+                                   &self->base, handshaker);
+}
+
+void tsi_ssl_server_handshaker_factory_unref(
+    tsi_ssl_server_handshaker_factory* self) {
+  if (self == nullptr) return;
+  tsi_ssl_handshaker_factory_unref(&self->base);
+}
+
+static void tsi_ssl_server_handshaker_factory_destroy(
+    tsi_ssl_handshaker_factory* factory) {
+  if (factory == nullptr) return;
+  tsi_ssl_server_handshaker_factory* self =
+      reinterpret_cast<tsi_ssl_server_handshaker_factory*>(factory);
+  size_t i;
+  for (i = 0; i < self->ssl_context_count; i++) {
+    if (self->ssl_contexts[i] != nullptr) {
+      SSL_CTX_free(self->ssl_contexts[i]);
+      tsi_peer_destruct(&self->ssl_context_x509_subject_names[i]);
+    }
+  }
+  if (self->ssl_contexts != nullptr) gpr_free(self->ssl_contexts);
+  if (self->ssl_context_x509_subject_names != nullptr) {
+    gpr_free(self->ssl_context_x509_subject_names);
+  }
+  if (self->alpn_protocol_list != nullptr) gpr_free(self->alpn_protocol_list);
+  gpr_free(self);
+}
+
+static int does_entry_match_name(const char* entry, size_t entry_length,
+                                 const char* name) {
+  const char* dot;
+  const char* name_subdomain = nullptr;
+  size_t name_length = strlen(name);
+  size_t name_subdomain_length;
+  if (entry_length == 0) return 0;
+
+  /* Take care of '.' terminations. */
+  if (name[name_length - 1] == '.') {
+    name_length--;
+  }
+  if (entry[entry_length - 1] == '.') {
+    entry_length--;
+    if (entry_length == 0) return 0;
+  }
+
+  if ((name_length == entry_length) &&
+      strncmp(name, entry, entry_length) == 0) {
+    return 1; /* Perfect match. */
+  }
+  if (entry[0] != '*') return 0;
+
+  /* Wildchar subdomain matching. */
+  if (entry_length < 3 || entry[1] != '.') { /* At least *.x */
+    gpr_log(GPR_ERROR, "Invalid wildchar entry.");
+    return 0;
+  }
+  name_subdomain = strchr(name, '.');
+  if (name_subdomain == nullptr) return 0;
+  name_subdomain_length = strlen(name_subdomain);
+  if (name_subdomain_length < 2) return 0;
+  name_subdomain++; /* Starts after the dot. */
+  name_subdomain_length--;
+  entry += 2; /* Remove *. */
+  entry_length -= 2;
+  dot = strchr(name_subdomain, '.');
+  if ((dot == nullptr) || (dot == &name_subdomain[name_subdomain_length - 1])) {
+    gpr_log(GPR_ERROR, "Invalid toplevel subdomain: %s", name_subdomain);
+    return 0;
+  }
+  if (name_subdomain[name_subdomain_length - 1] == '.') {
+    name_subdomain_length--;
+  }
+  return ((entry_length > 0) && (name_subdomain_length == entry_length) &&
+          strncmp(entry, name_subdomain, entry_length) == 0);
+}
+
+static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap,
+                                                             void* arg) {
+  tsi_ssl_server_handshaker_factory* impl =
+      static_cast<tsi_ssl_server_handshaker_factory*>(arg);
+  size_t i = 0;
+  const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  if (servername == nullptr || strlen(servername) == 0) {
+    return SSL_TLSEXT_ERR_NOACK;
+  }
+
+  for (i = 0; i < impl->ssl_context_count; i++) {
+    if (tsi_ssl_peer_matches_name(&impl->ssl_context_x509_subject_names[i],
+                                  servername)) {
+      SSL_set_SSL_CTX(ssl, impl->ssl_contexts[i]);
+      return SSL_TLSEXT_ERR_OK;
+    }
+  }
+  gpr_log(GPR_ERROR, "No match found for server name: %s.", servername);
+  return SSL_TLSEXT_ERR_ALERT_WARNING;
+}
+
+#if TSI_OPENSSL_ALPN_SUPPORT
+static int server_handshaker_factory_alpn_callback(
+    SSL* ssl, const unsigned char** out, unsigned char* outlen,
+    const unsigned char* in, unsigned int inlen, void* arg) {
+  tsi_ssl_server_handshaker_factory* factory =
+      static_cast<tsi_ssl_server_handshaker_factory*>(arg);
+  return select_protocol_list(out, outlen, in, inlen,
+                              factory->alpn_protocol_list,
+                              factory->alpn_protocol_list_length);
+}
+#endif /* TSI_OPENSSL_ALPN_SUPPORT */
+
+static int server_handshaker_factory_npn_advertised_callback(
+    SSL* ssl, const unsigned char** out, unsigned int* outlen, void* arg) {
+  tsi_ssl_server_handshaker_factory* factory =
+      static_cast<tsi_ssl_server_handshaker_factory*>(arg);
+  *out = factory->alpn_protocol_list;
+  GPR_ASSERT(factory->alpn_protocol_list_length <= UINT_MAX);
+  *outlen = static_cast<unsigned int>(factory->alpn_protocol_list_length);
+  return SSL_TLSEXT_ERR_OK;
+}
+
+/// This callback is called when new \a session is established and ready to
+/// be cached. This session can be reused for new connections to similar
+/// servers at later point of time.
+/// It's intended to be used with SSL_CTX_sess_set_new_cb function.
+///
+/// It returns 1 if callback takes ownership over \a session and 0 otherwise.
+static int server_handshaker_factory_new_session_callback(
+    SSL* ssl, SSL_SESSION* session) {
+  SSL_CTX* ssl_context = SSL_get_SSL_CTX(ssl);
+  if (ssl_context == nullptr) {
+    return 0;
+  }
+  void* arg = SSL_CTX_get_ex_data(ssl_context, g_ssl_ctx_ex_factory_index);
+  tsi_ssl_client_handshaker_factory* factory =
+      static_cast<tsi_ssl_client_handshaker_factory*>(arg);
+  const char* server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  if (server_name == nullptr) {
+    return 0;
+  }
+  factory->session_cache->Put(server_name, tsi::SslSessionPtr(session));
+  // Return 1 to indicate transfered ownership over the given session.
+  return 1;
+}
+
+/* --- tsi_ssl_handshaker_factory constructors. --- */
+
+static tsi_ssl_handshaker_factory_vtable client_handshaker_factory_vtable = {
+    tsi_ssl_client_handshaker_factory_destroy};
+
+tsi_result tsi_create_ssl_client_handshaker_factory(
+    const tsi_ssl_pem_key_cert_pair* pem_key_cert_pair,
+    const char* pem_root_certs, const char* cipher_suites,
+    const char** alpn_protocols, uint16_t num_alpn_protocols,
+    tsi_ssl_client_handshaker_factory** factory) {
+  tsi_ssl_client_handshaker_options options;
+  memset(&options, 0, sizeof(options));
+  options.pem_key_cert_pair = pem_key_cert_pair;
+  options.pem_root_certs = pem_root_certs;
+  options.cipher_suites = cipher_suites;
+  options.alpn_protocols = alpn_protocols;
+  options.num_alpn_protocols = num_alpn_protocols;
+  return tsi_create_ssl_client_handshaker_factory_with_options(&options,
+                                                               factory);
+}
+
+tsi_result tsi_create_ssl_client_handshaker_factory_with_options(
+    const tsi_ssl_client_handshaker_options* options,
+    tsi_ssl_client_handshaker_factory** factory) {
+  SSL_CTX* ssl_context = nullptr;
+  tsi_ssl_client_handshaker_factory* impl = nullptr;
+  tsi_result result = TSI_OK;
+
+  gpr_once_init(&g_init_openssl_once, init_openssl);
+
+  if (factory == nullptr) return TSI_INVALID_ARGUMENT;
+  *factory = nullptr;
+  if (options->pem_root_certs == nullptr && options->root_store == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+
+#if defined(OPENSSL_NO_TLS1_2_METHOD) || OPENSSL_API_COMPAT >= 0x10100000L
+  ssl_context = SSL_CTX_new(TLS_method());
+#else
+  ssl_context = SSL_CTX_new(TLSv1_2_method());
+#endif
+  if (ssl_context == nullptr) {
+    gpr_log(GPR_ERROR, "Could not create ssl context.");
+    return TSI_INVALID_ARGUMENT;
+  }
+
+  impl = static_cast<tsi_ssl_client_handshaker_factory*>(
+      gpr_zalloc(sizeof(*impl)));
+  tsi_ssl_handshaker_factory_init(&impl->base);
+  impl->base.vtable = &client_handshaker_factory_vtable;
+  impl->ssl_context = ssl_context;
+  if (options->session_cache != nullptr) {
+    // Unref is called manually on factory destruction.
+    impl->session_cache =
+        reinterpret_cast<tsi::SslSessionLRUCache*>(options->session_cache)
+            ->Ref();
+    SSL_CTX_set_ex_data(ssl_context, g_ssl_ctx_ex_factory_index, impl);
+    SSL_CTX_sess_set_new_cb(ssl_context,
+                            server_handshaker_factory_new_session_callback);
+    SSL_CTX_set_session_cache_mode(ssl_context, SSL_SESS_CACHE_CLIENT);
+  }
+
+  do {
+    result = populate_ssl_context(ssl_context, options->pem_key_cert_pair,
+                                  options->cipher_suites);
+    if (result != TSI_OK) break;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+    // X509_STORE_up_ref is only available since OpenSSL 1.1.
+    if (options->root_store != nullptr) {
+      X509_STORE_up_ref(options->root_store->store);
+      SSL_CTX_set_cert_store(ssl_context, options->root_store->store);
+    }
+#endif
+    if (OPENSSL_VERSION_NUMBER < 0x10100000 || options->root_store == nullptr) {
+      result = ssl_ctx_load_verification_certs(
+          ssl_context, options->pem_root_certs, strlen(options->pem_root_certs),
+          nullptr);
+      if (result != TSI_OK) {
+        gpr_log(GPR_ERROR, "Cannot load server root certificates.");
+        break;
+      }
+    }
+
+    if (options->num_alpn_protocols != 0) {
+      result = build_alpn_protocol_name_list(
+          options->alpn_protocols, options->num_alpn_protocols,
+          &impl->alpn_protocol_list, &impl->alpn_protocol_list_length);
+      if (result != TSI_OK) {
+        gpr_log(GPR_ERROR, "Building alpn list failed with error %s.",
+                tsi_result_to_string(result));
+        break;
+      }
+#if TSI_OPENSSL_ALPN_SUPPORT
+      GPR_ASSERT(impl->alpn_protocol_list_length < UINT_MAX);
+      if (SSL_CTX_set_alpn_protos(
+              ssl_context, impl->alpn_protocol_list,
+              static_cast<unsigned int>(impl->alpn_protocol_list_length))) {
+        gpr_log(GPR_ERROR, "Could not set alpn protocol list to context.");
+        result = TSI_INVALID_ARGUMENT;
+        break;
+      }
+#endif /* TSI_OPENSSL_ALPN_SUPPORT */
+      SSL_CTX_set_next_proto_select_cb(
+          ssl_context, client_handshaker_factory_npn_callback, impl);
+    }
+  } while (0);
+  if (result != TSI_OK) {
+    tsi_ssl_handshaker_factory_unref(&impl->base);
+    return result;
+  }
+  SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, nullptr);
+  /* TODO(jboeuf): Add revocation verification. */
+
+  *factory = impl;
+  return TSI_OK;
+}
+
+static tsi_ssl_handshaker_factory_vtable server_handshaker_factory_vtable = {
+    tsi_ssl_server_handshaker_factory_destroy};
+
+tsi_result tsi_create_ssl_server_handshaker_factory(
+    const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs, const char* pem_client_root_certs,
+    int force_client_auth, const char* cipher_suites,
+    const char** alpn_protocols, uint16_t num_alpn_protocols,
+    tsi_ssl_server_handshaker_factory** factory) {
+  return tsi_create_ssl_server_handshaker_factory_ex(
+      pem_key_cert_pairs, num_key_cert_pairs, pem_client_root_certs,
+      force_client_auth ? TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+                        : TSI_DONT_REQUEST_CLIENT_CERTIFICATE,
+      cipher_suites, alpn_protocols, num_alpn_protocols, factory);
+}
+
+tsi_result tsi_create_ssl_server_handshaker_factory_ex(
+    const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs, const char* pem_client_root_certs,
+    tsi_client_certificate_request_type client_certificate_request,
+    const char* cipher_suites, const char** alpn_protocols,
+    uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory** factory) {
+  tsi_ssl_server_handshaker_options options;
+  memset(&options, 0, sizeof(options));
+  options.pem_key_cert_pairs = pem_key_cert_pairs;
+  options.num_key_cert_pairs = num_key_cert_pairs;
+  options.pem_client_root_certs = pem_client_root_certs;
+  options.client_certificate_request = client_certificate_request;
+  options.cipher_suites = cipher_suites;
+  options.alpn_protocols = alpn_protocols;
+  options.num_alpn_protocols = num_alpn_protocols;
+  return tsi_create_ssl_server_handshaker_factory_with_options(&options,
+                                                               factory);
+}
+
+tsi_result tsi_create_ssl_server_handshaker_factory_with_options(
+    const tsi_ssl_server_handshaker_options* options,
+    tsi_ssl_server_handshaker_factory** factory) {
+  tsi_ssl_server_handshaker_factory* impl = nullptr;
+  tsi_result result = TSI_OK;
+  size_t i = 0;
+
+  gpr_once_init(&g_init_openssl_once, init_openssl);
+
+  if (factory == nullptr) return TSI_INVALID_ARGUMENT;
+  *factory = nullptr;
+  if (options->num_key_cert_pairs == 0 ||
+      options->pem_key_cert_pairs == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+
+  impl = static_cast<tsi_ssl_server_handshaker_factory*>(
+      gpr_zalloc(sizeof(*impl)));
+  tsi_ssl_handshaker_factory_init(&impl->base);
+  impl->base.vtable = &server_handshaker_factory_vtable;
+
+  impl->ssl_contexts = static_cast<SSL_CTX**>(
+      gpr_zalloc(options->num_key_cert_pairs * sizeof(SSL_CTX*)));
+  impl->ssl_context_x509_subject_names = static_cast<tsi_peer*>(
+      gpr_zalloc(options->num_key_cert_pairs * sizeof(tsi_peer)));
+  if (impl->ssl_contexts == nullptr ||
+      impl->ssl_context_x509_subject_names == nullptr) {
+    tsi_ssl_handshaker_factory_unref(&impl->base);
+    return TSI_OUT_OF_RESOURCES;
+  }
+  impl->ssl_context_count = options->num_key_cert_pairs;
+
+  if (options->num_alpn_protocols > 0) {
+    result = build_alpn_protocol_name_list(
+        options->alpn_protocols, options->num_alpn_protocols,
+        &impl->alpn_protocol_list, &impl->alpn_protocol_list_length);
+    if (result != TSI_OK) {
+      tsi_ssl_handshaker_factory_unref(&impl->base);
+      return result;
+    }
+  }
+
+  for (i = 0; i < options->num_key_cert_pairs; i++) {
+    do {
+#if defined(OPENSSL_NO_TLS1_2_METHOD) || OPENSSL_API_COMPAT >= 0x10100000L
+      impl->ssl_contexts[i] = SSL_CTX_new(TLS_method());
+#else
+      impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method());
+#endif
+      if (impl->ssl_contexts[i] == nullptr) {
+        gpr_log(GPR_ERROR, "Could not create ssl context.");
+        result = TSI_OUT_OF_RESOURCES;
+        break;
+      }
+      result = populate_ssl_context(impl->ssl_contexts[i],
+                                    &options->pem_key_cert_pairs[i],
+                                    options->cipher_suites);
+      if (result != TSI_OK) break;
+
+      // TODO(elessar): Provide ability to disable session ticket keys.
+
+      // Allow client cache sessions (it's needed for OpenSSL only).
+      int set_sid_ctx_result = SSL_CTX_set_session_id_context(
+          impl->ssl_contexts[i], kSslSessionIdContext,
+          GPR_ARRAY_SIZE(kSslSessionIdContext));
+      if (set_sid_ctx_result == 0) {
+        gpr_log(GPR_ERROR, "Failed to set session id context.");
+        result = TSI_INTERNAL_ERROR;
+        break;
+      }
+
+      if (options->session_ticket_key != nullptr) {
+        if (SSL_CTX_set_tlsext_ticket_keys(
+                impl->ssl_contexts[i],
+                const_cast<char*>(options->session_ticket_key),
+                options->session_ticket_key_size) == 0) {
+          gpr_log(GPR_ERROR, "Invalid STEK size.");
+          result = TSI_INVALID_ARGUMENT;
+          break;
+        }
+      }
+
+      if (options->pem_client_root_certs != nullptr) {
+        STACK_OF(X509_NAME)* root_names = nullptr;
+        result = ssl_ctx_load_verification_certs(
+            impl->ssl_contexts[i], options->pem_client_root_certs,
+            strlen(options->pem_client_root_certs), &root_names);
+        if (result != TSI_OK) {
+          gpr_log(GPR_ERROR, "Invalid verification certs.");
+          break;
+        }
+        SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names);
+      }
+      switch (options->client_certificate_request) {
+        case TSI_DONT_REQUEST_CLIENT_CERTIFICATE:
+          SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_NONE, nullptr);
+          break;
+        case TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+          SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER,
+                             NullVerifyCallback);
+          break;
+        case TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
+          SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, nullptr);
+          break;
+        case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
+          SSL_CTX_set_verify(impl->ssl_contexts[i],
+                             SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+                             NullVerifyCallback);
+          break;
+        case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
+          SSL_CTX_set_verify(impl->ssl_contexts[i],
+                             SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+                             nullptr);
+          break;
+      }
+      /* TODO(jboeuf): Add revocation verification. */
+
+      result = extract_x509_subject_names_from_pem_cert(
+          options->pem_key_cert_pairs[i].cert_chain,
+          &impl->ssl_context_x509_subject_names[i]);
+      if (result != TSI_OK) break;
+
+      SSL_CTX_set_tlsext_servername_callback(
+          impl->ssl_contexts[i],
+          ssl_server_handshaker_factory_servername_callback);
+      SSL_CTX_set_tlsext_servername_arg(impl->ssl_contexts[i], impl);
+#if TSI_OPENSSL_ALPN_SUPPORT
+      SSL_CTX_set_alpn_select_cb(impl->ssl_contexts[i],
+                                 server_handshaker_factory_alpn_callback, impl);
+#endif /* TSI_OPENSSL_ALPN_SUPPORT */
+      SSL_CTX_set_next_protos_advertised_cb(
+          impl->ssl_contexts[i],
+          server_handshaker_factory_npn_advertised_callback, impl);
+    } while (0);
+
+    if (result != TSI_OK) {
+      tsi_ssl_handshaker_factory_unref(&impl->base);
+      return result;
+    }
+  }
+
+  *factory = impl;
+  return TSI_OK;
+}
+
+/* --- tsi_ssl utils. --- */
+
+int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) {
+  size_t i = 0;
+  size_t san_count = 0;
+  const tsi_peer_property* cn_property = nullptr;
+  int like_ip = looks_like_ip_address(name);
+
+  /* Check the SAN first. */
+  for (i = 0; i < peer->property_count; i++) {
+    const tsi_peer_property* property = &peer->properties[i];
+    if (property->name == nullptr) continue;
+    if (strcmp(property->name,
+               TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) {
+      san_count++;
+
+      if (!like_ip && does_entry_match_name(property->value.data,
+                                            property->value.length, name)) {
+        return 1;
+      } else if (like_ip &&
+                 strncmp(name, property->value.data, property->value.length) ==
+                     0 &&
+                 strlen(name) == property->value.length) {
+        /* IP Addresses are exact matches only. */
+        return 1;
+      }
+    } else if (strcmp(property->name,
+                      TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) {
+      cn_property = property;
+    }
+  }
+
+  /* If there's no SAN, try the CN, but only if its not like an IP Address */
+  if (san_count == 0 && cn_property != nullptr && !like_ip) {
+    if (does_entry_match_name(cn_property->value.data,
+                              cn_property->value.length, name)) {
+      return 1;
+    }
+  }
+
+  return 0; /* Not found. */
+}
+
+/* --- Testing support. --- */
+const tsi_ssl_handshaker_factory_vtable* tsi_ssl_handshaker_factory_swap_vtable(
+    tsi_ssl_handshaker_factory* factory,
+    tsi_ssl_handshaker_factory_vtable* new_vtable) {
+  GPR_ASSERT(factory != nullptr);
+  GPR_ASSERT(factory->vtable != nullptr);
+
+  const tsi_ssl_handshaker_factory_vtable* orig_vtable = factory->vtable;
+  factory->vtable = new_vtable;
+  return orig_vtable;
+}
diff --git a/third_party/grpc/src/core/tsi/ssl_transport_security.h b/third_party/grpc/src/core/tsi/ssl_transport_security.h
new file mode 100644
index 0000000..cabf583
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/ssl_transport_security.h
@@ -0,0 +1,314 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H
+#define GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/transport_security_interface.h"
+
+/* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for X509 certs. */
+#define TSI_X509_CERTIFICATE_TYPE "X509"
+
+/* This property is of type TSI_PEER_PROPERTY_STRING.  */
+#define TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY "x509_subject_common_name"
+#define TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY \
+  "x509_subject_alternative_name"
+#define TSI_SSL_SESSION_REUSED_PEER_PROPERTY "ssl_session_reused"
+
+#define TSI_X509_PEM_CERT_PROPERTY "x509_pem_cert"
+
+#define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol"
+
+/* --- tsi_ssl_root_certs_store object ---
+
+   This object stores SSL root certificates. It can be shared by multiple SSL
+   context. */
+typedef struct tsi_ssl_root_certs_store tsi_ssl_root_certs_store;
+
+/* Given a NULL-terminated string containing the PEM encoding of the root
+   certificates, creates a tsi_ssl_root_certs_store object. */
+tsi_ssl_root_certs_store* tsi_ssl_root_certs_store_create(
+    const char* pem_roots);
+
+/* Destroys the tsi_ssl_root_certs_store object. */
+void tsi_ssl_root_certs_store_destroy(tsi_ssl_root_certs_store* self);
+
+/* --- tsi_ssl_session_cache object ---
+
+   Cache for SSL sessions for sessions resumption.  */
+
+typedef struct tsi_ssl_session_cache tsi_ssl_session_cache;
+
+/* Create LRU cache for SSL sessions with \a capacity.  */
+tsi_ssl_session_cache* tsi_ssl_session_cache_create_lru(size_t capacity);
+
+/* Increment reference counter of \a cache.  */
+void tsi_ssl_session_cache_ref(tsi_ssl_session_cache* cache);
+
+/* Decrement reference counter of \a cache.  */
+void tsi_ssl_session_cache_unref(tsi_ssl_session_cache* cache);
+
+/* --- tsi_ssl_client_handshaker_factory object ---
+
+   This object creates a client tsi_handshaker objects implemented in terms of
+   the TLS 1.2 specificiation.  */
+
+typedef struct tsi_ssl_client_handshaker_factory
+    tsi_ssl_client_handshaker_factory;
+
+/* Object that holds a private key / certificate chain pair in PEM format. */
+typedef struct {
+  /* private_key is the NULL-terminated string containing the PEM encoding of
+     the client's private key. */
+  const char* private_key;
+
+  /* cert_chain is the NULL-terminated string containing the PEM encoding of
+     the client's certificate chain. */
+  const char* cert_chain;
+} tsi_ssl_pem_key_cert_pair;
+
+/* TO BE DEPRECATED.
+   Creates a client handshaker factory.
+   - pem_key_cert_pair is a pointer to the object containing client's private
+     key and certificate chain. This parameter can be NULL if the client does
+     not have such a key/cert pair.
+   - pem_roots_cert is the NULL-terminated string containing the PEM encoding of
+     the server root certificates.
+   - cipher_suites contains an optional list of the ciphers that the client
+     supports. The format of this string is described in:
+     https://www.openssl.org/docs/apps/ciphers.html.
+     This parameter can be set to NULL to use the default set of ciphers.
+     TODO(jboeuf): Revisit the format of this parameter.
+   - alpn_protocols is an array containing the NULL terminated protocol names
+     that the handshakers created with this factory support. This parameter can
+     be NULL.
+   - num_alpn_protocols is the number of alpn protocols and associated lengths
+     specified. If this parameter is 0, the other alpn parameters must be NULL.
+   - factory is the address of the factory pointer to be created.
+
+   - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+     where a parameter is invalid.  */
+tsi_result tsi_create_ssl_client_handshaker_factory(
+    const tsi_ssl_pem_key_cert_pair* pem_key_cert_pair,
+    const char* pem_root_certs, const char* cipher_suites,
+    const char** alpn_protocols, uint16_t num_alpn_protocols,
+    tsi_ssl_client_handshaker_factory** factory);
+
+typedef struct {
+  /* pem_key_cert_pair is a pointer to the object containing client's private
+     key and certificate chain. This parameter can be NULL if the client does
+     not have such a key/cert pair. */
+  const tsi_ssl_pem_key_cert_pair* pem_key_cert_pair;
+  /* pem_roots_cert is the NULL-terminated string containing the PEM encoding of
+     the client root certificates. */
+  const char* pem_root_certs;
+  /* root_store is a pointer to the ssl_root_certs_store object. If root_store
+    is not nullptr and SSL implementation permits, root_store will be used as
+    root certificates. Otherwise, pem_roots_cert will be used to load server
+    root certificates. */
+  const tsi_ssl_root_certs_store* root_store;
+  /* cipher_suites contains an optional list of the ciphers that the client
+     supports. The format of this string is described in:
+     https://www.openssl.org/docs/apps/ciphers.html.
+     This parameter can be set to NULL to use the default set of ciphers.
+     TODO(jboeuf): Revisit the format of this parameter. */
+  const char* cipher_suites;
+  /* alpn_protocols is an array containing the NULL terminated protocol names
+     that the handshakers created with this factory support. This parameter can
+     be NULL. */
+  const char** alpn_protocols;
+  /* num_alpn_protocols is the number of alpn protocols and associated lengths
+     specified. If this parameter is 0, the other alpn parameters must be
+     NULL. */
+  size_t num_alpn_protocols;
+  /* ssl_session_cache is a cache for reusable client-side sessions. */
+  tsi_ssl_session_cache* session_cache;
+} tsi_ssl_client_handshaker_options;
+
+/* Creates a client handshaker factory.
+   - options is the options used to create a factory.
+   - factory is the address of the factory pointer to be created.
+
+   - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+     where a parameter is invalid. */
+tsi_result tsi_create_ssl_client_handshaker_factory_with_options(
+    const tsi_ssl_client_handshaker_options* options,
+    tsi_ssl_client_handshaker_factory** factory);
+
+/* Creates a client handshaker.
+  - self is the factory from which the handshaker will be created.
+  - server_name_indication indicates the name of the server the client is
+    trying to connect to which will be relayed to the server using the SNI
+    extension.
+  - handshaker is the address of the handshaker pointer to be created.
+
+  - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+    where a parameter is invalid.  */
+tsi_result tsi_ssl_client_handshaker_factory_create_handshaker(
+    tsi_ssl_client_handshaker_factory* self, const char* server_name_indication,
+    tsi_handshaker** handshaker);
+
+/* Decrements reference count of the handshaker factory. Handshaker factory will
+ * be destroyed once no references exist. */
+void tsi_ssl_client_handshaker_factory_unref(
+    tsi_ssl_client_handshaker_factory* factory);
+
+/* --- tsi_ssl_server_handshaker_factory object ---
+
+   This object creates a client tsi_handshaker objects implemented in terms of
+   the TLS 1.2 specificiation.  */
+
+typedef struct tsi_ssl_server_handshaker_factory
+    tsi_ssl_server_handshaker_factory;
+
+/* TO BE DEPRECATED.
+   Creates a server handshaker factory.
+   - pem_key_cert_pairs is an array private key / certificate chains of the
+     server.
+   - num_key_cert_pairs is the number of items in the pem_key_cert_pairs array.
+   - pem_root_certs is the NULL-terminated string containing the PEM encoding
+     of the client root certificates. This parameter may be NULL if the server
+     does not want the client to be authenticated with SSL.
+   - cipher_suites contains an optional list of the ciphers that the server
+     supports. The format of this string is described in:
+     https://www.openssl.org/docs/apps/ciphers.html.
+     This parameter can be set to NULL to use the default set of ciphers.
+     TODO(jboeuf): Revisit the format of this parameter.
+   - alpn_protocols is an array containing the NULL terminated protocol names
+     that the handshakers created with this factory support. This parameter can
+     be NULL.
+   - num_alpn_protocols is the number of alpn protocols and associated lengths
+     specified. If this parameter is 0, the other alpn parameters must be NULL.
+   - factory is the address of the factory pointer to be created.
+
+   - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+     where a parameter is invalid.  */
+tsi_result tsi_create_ssl_server_handshaker_factory(
+    const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs, const char* pem_client_root_certs,
+    int force_client_auth, const char* cipher_suites,
+    const char** alpn_protocols, uint16_t num_alpn_protocols,
+    tsi_ssl_server_handshaker_factory** factory);
+
+/* TO BE DEPRECATED.
+   Same as tsi_create_ssl_server_handshaker_factory method except uses
+   tsi_client_certificate_request_type to support more ways to handle client
+   certificate authentication.
+   - client_certificate_request, if set to non-zero will force the client to
+     authenticate with an SSL cert. Note that this option is ignored if
+     pem_client_root_certs is NULL or pem_client_roots_certs_size is 0 */
+tsi_result tsi_create_ssl_server_handshaker_factory_ex(
+    const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs,
+    size_t num_key_cert_pairs, const char* pem_client_root_certs,
+    tsi_client_certificate_request_type client_certificate_request,
+    const char* cipher_suites, const char** alpn_protocols,
+    uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory** factory);
+
+typedef struct {
+  /* pem_key_cert_pairs is an array private key / certificate chains of the
+     server. */
+  const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs;
+  /* num_key_cert_pairs is the number of items in the pem_key_cert_pairs
+     array. */
+  size_t num_key_cert_pairs;
+  /* pem_root_certs is the NULL-terminated string containing the PEM encoding
+     of the server root certificates. This parameter may be NULL if the server
+     does not want the client to be authenticated with SSL. */
+  const char* pem_client_root_certs;
+  /* client_certificate_request, if set to non-zero will force the client to
+     authenticate with an SSL cert. Note that this option is ignored if
+     pem_client_root_certs is NULL or pem_client_roots_certs_size is 0. */
+  tsi_client_certificate_request_type client_certificate_request;
+  /* cipher_suites contains an optional list of the ciphers that the server
+     supports. The format of this string is described in:
+     https://www.openssl.org/docs/apps/ciphers.html.
+     This parameter can be set to NULL to use the default set of ciphers.
+     TODO(jboeuf): Revisit the format of this parameter. */
+  const char* cipher_suites;
+  /* alpn_protocols is an array containing the NULL terminated protocol names
+     that the handshakers created with this factory support. This parameter can
+     be NULL. */
+  const char** alpn_protocols;
+  /* num_alpn_protocols is the number of alpn protocols and associated lengths
+     specified. If this parameter is 0, the other alpn parameters must be
+     NULL. */
+  uint16_t num_alpn_protocols;
+  /* session_ticket_key is optional key for encrypting session keys. If paramter
+     is not specified it must be NULL. */
+  const char* session_ticket_key;
+  /* session_ticket_key_size is a size of session ticket encryption key. */
+  size_t session_ticket_key_size;
+} tsi_ssl_server_handshaker_options;
+
+/* Creates a server handshaker factory.
+   - options is the options used to create a factory.
+   - factory is the address of the factory pointer to be created.
+
+   - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+     where a parameter is invalid. */
+tsi_result tsi_create_ssl_server_handshaker_factory_with_options(
+    const tsi_ssl_server_handshaker_options* options,
+    tsi_ssl_server_handshaker_factory** factory);
+
+/* Creates a server handshaker.
+  - self is the factory from which the handshaker will be created.
+  - handshaker is the address of the handshaker pointer to be created.
+
+  - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+    where a parameter is invalid.  */
+tsi_result tsi_ssl_server_handshaker_factory_create_handshaker(
+    tsi_ssl_server_handshaker_factory* self, tsi_handshaker** handshaker);
+
+/* Decrements reference count of the handshaker factory. Handshaker factory will
+ * be destroyed once no references exist. */
+void tsi_ssl_server_handshaker_factory_unref(
+    tsi_ssl_server_handshaker_factory* self);
+
+/* Util that checks that an ssl peer matches a specific name.
+   Still TODO(jboeuf):
+   - handle mixed case.
+   - handle %encoded chars.
+   - handle public suffix wildchar more strictly (e.g. *.co.uk) */
+int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name);
+
+/* --- Testing support. ---
+
+   These functions and typedefs are not intended to be used outside of testing.
+   */
+
+/* Base type of client and server handshaker factories. */
+typedef struct tsi_ssl_handshaker_factory tsi_ssl_handshaker_factory;
+
+/* Function pointer to handshaker_factory destructor. */
+typedef void (*tsi_ssl_handshaker_factory_destructor)(
+    tsi_ssl_handshaker_factory* factory);
+
+/* Virtual table for tsi_ssl_handshaker_factory. */
+typedef struct {
+  tsi_ssl_handshaker_factory_destructor destroy;
+} tsi_ssl_handshaker_factory_vtable;
+
+/* Set destructor of handshaker_factory to new_destructor, returns previous
+   destructor. */
+const tsi_ssl_handshaker_factory_vtable* tsi_ssl_handshaker_factory_swap_vtable(
+    tsi_ssl_handshaker_factory* factory,
+    tsi_ssl_handshaker_factory_vtable* new_vtable);
+
+#endif /* GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H */
diff --git a/third_party/grpc/src/core/tsi/ssl_types.h b/third_party/grpc/src/core/tsi/ssl_types.h
new file mode 100644
index 0000000..0ce5e2e
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/ssl_types.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_SSL_TYPES_H
+#define GRPC_CORE_TSI_SSL_TYPES_H
+
+/* A collection of macros to cast between various integer types that are
+ * used differently between BoringSSL and OpenSSL:
+ * TSI_INT_AS_SIZE(x):  convert 'int x' to a length parameter for an OpenSSL
+ *                      function
+ * TSI_SIZE_AS_SIZE(x): convert 'size_t x' to a length parameter for an OpenSSL
+ *                      function
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/tsi/grpc_shadow_boringssl.h"
+
+#include <openssl/ssl.h>
+
+#ifdef OPENSSL_IS_BORINGSSL
+#define TSI_INT_AS_SIZE(x) ((size_t)(x))
+#define TSI_SIZE_AS_SIZE(x) (x)
+#else
+#define TSI_INT_AS_SIZE(x) (x)
+#define TSI_SIZE_AS_SIZE(x) ((int)(x))
+#endif
+
+#endif /* GRPC_CORE_TSI_SSL_TYPES_H */
diff --git a/third_party/grpc/src/core/tsi/transport_security.cc b/third_party/grpc/src/core/tsi/transport_security.cc
new file mode 100644
index 0000000..078a917
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/transport_security.cc
@@ -0,0 +1,357 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/transport_security.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Tracing. --- */
+
+grpc_core::TraceFlag tsi_tracing_enabled(false, "tsi");
+
+/* --- tsi_result common implementation. --- */
+
+const char* tsi_result_to_string(tsi_result result) {
+  switch (result) {
+    case TSI_OK:
+      return "TSI_OK";
+    case TSI_UNKNOWN_ERROR:
+      return "TSI_UNKNOWN_ERROR";
+    case TSI_INVALID_ARGUMENT:
+      return "TSI_INVALID_ARGUMENT";
+    case TSI_PERMISSION_DENIED:
+      return "TSI_PERMISSION_DENIED";
+    case TSI_INCOMPLETE_DATA:
+      return "TSI_INCOMPLETE_DATA";
+    case TSI_FAILED_PRECONDITION:
+      return "TSI_FAILED_PRECONDITION";
+    case TSI_UNIMPLEMENTED:
+      return "TSI_UNIMPLEMENTED";
+    case TSI_INTERNAL_ERROR:
+      return "TSI_INTERNAL_ERROR";
+    case TSI_DATA_CORRUPTED:
+      return "TSI_DATA_CORRUPTED";
+    case TSI_NOT_FOUND:
+      return "TSI_NOT_FOUND";
+    case TSI_PROTOCOL_FAILURE:
+      return "TSI_PROTOCOL_FAILURE";
+    case TSI_HANDSHAKE_IN_PROGRESS:
+      return "TSI_HANDSHAKE_IN_PROGRESS";
+    case TSI_OUT_OF_RESOURCES:
+      return "TSI_OUT_OF_RESOURCES";
+    case TSI_ASYNC:
+      return "TSI_ASYNC";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+/* --- tsi_frame_protector common implementation. ---
+
+   Calls specific implementation after state/input validation. */
+
+tsi_result tsi_frame_protector_protect(tsi_frame_protector* self,
+                                       const unsigned char* unprotected_bytes,
+                                       size_t* unprotected_bytes_size,
+                                       unsigned char* protected_output_frames,
+                                       size_t* protected_output_frames_size) {
+  if (self == nullptr || self->vtable == nullptr ||
+      unprotected_bytes == nullptr || unprotected_bytes_size == nullptr ||
+      protected_output_frames == nullptr ||
+      protected_output_frames_size == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->protect == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->protect(self, unprotected_bytes, unprotected_bytes_size,
+                               protected_output_frames,
+                               protected_output_frames_size);
+}
+
+tsi_result tsi_frame_protector_protect_flush(
+    tsi_frame_protector* self, unsigned char* protected_output_frames,
+    size_t* protected_output_frames_size, size_t* still_pending_size) {
+  if (self == nullptr || self->vtable == nullptr ||
+      protected_output_frames == nullptr ||
+      protected_output_frames_size == nullptr ||
+      still_pending_size == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->protect_flush == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->protect_flush(self, protected_output_frames,
+                                     protected_output_frames_size,
+                                     still_pending_size);
+}
+
+tsi_result tsi_frame_protector_unprotect(
+    tsi_frame_protector* self, const unsigned char* protected_frames_bytes,
+    size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes,
+    size_t* unprotected_bytes_size) {
+  if (self == nullptr || self->vtable == nullptr ||
+      protected_frames_bytes == nullptr ||
+      protected_frames_bytes_size == nullptr || unprotected_bytes == nullptr ||
+      unprotected_bytes_size == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->unprotect == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->unprotect(self, protected_frames_bytes,
+                                 protected_frames_bytes_size, unprotected_bytes,
+                                 unprotected_bytes_size);
+}
+
+void tsi_frame_protector_destroy(tsi_frame_protector* self) {
+  if (self == nullptr) return;
+  self->vtable->destroy(self);
+}
+
+/* --- tsi_handshaker common implementation. ---
+
+   Calls specific implementation after state/input validation. */
+
+tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self,
+                                                    unsigned char* bytes,
+                                                    size_t* bytes_size) {
+  if (self == nullptr || self->vtable == nullptr || bytes == nullptr ||
+      bytes_size == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
+  if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN;
+  if (self->vtable->get_bytes_to_send_to_peer == nullptr)
+    return TSI_UNIMPLEMENTED;
+  return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size);
+}
+
+tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self,
+                                                  const unsigned char* bytes,
+                                                  size_t* bytes_size) {
+  if (self == nullptr || self->vtable == nullptr || bytes == nullptr ||
+      bytes_size == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
+  if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN;
+  if (self->vtable->process_bytes_from_peer == nullptr)
+    return TSI_UNIMPLEMENTED;
+  return self->vtable->process_bytes_from_peer(self, bytes, bytes_size);
+}
+
+tsi_result tsi_handshaker_get_result(tsi_handshaker* self) {
+  if (self == nullptr || self->vtable == nullptr) return TSI_INVALID_ARGUMENT;
+  if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
+  if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN;
+  if (self->vtable->get_result == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->get_result(self);
+}
+
+tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer) {
+  if (self == nullptr || self->vtable == nullptr || peer == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  memset(peer, 0, sizeof(tsi_peer));
+  if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
+  if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN;
+  if (tsi_handshaker_get_result(self) != TSI_OK) {
+    return TSI_FAILED_PRECONDITION;
+  }
+  if (self->vtable->extract_peer == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->extract_peer(self, peer);
+}
+
+tsi_result tsi_handshaker_create_frame_protector(
+    tsi_handshaker* self, size_t* max_protected_frame_size,
+    tsi_frame_protector** protector) {
+  tsi_result result;
+  if (self == nullptr || self->vtable == nullptr || protector == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
+  if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN;
+  if (tsi_handshaker_get_result(self) != TSI_OK) return TSI_FAILED_PRECONDITION;
+  if (self->vtable->create_frame_protector == nullptr) return TSI_UNIMPLEMENTED;
+  result = self->vtable->create_frame_protector(self, max_protected_frame_size,
+                                                protector);
+  if (result == TSI_OK) {
+    self->frame_protector_created = true;
+  }
+  return result;
+}
+
+tsi_result tsi_handshaker_next(
+    tsi_handshaker* self, const unsigned char* received_bytes,
+    size_t received_bytes_size, const unsigned char** bytes_to_send,
+    size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result,
+    tsi_handshaker_on_next_done_cb cb, void* user_data) {
+  if (self == nullptr || self->vtable == nullptr) return TSI_INVALID_ARGUMENT;
+  if (self->handshaker_result_created) return TSI_FAILED_PRECONDITION;
+  if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN;
+  if (self->vtable->next == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->next(self, received_bytes, received_bytes_size,
+                            bytes_to_send, bytes_to_send_size,
+                            handshaker_result, cb, user_data);
+}
+
+void tsi_handshaker_shutdown(tsi_handshaker* self) {
+  if (self == nullptr || self->vtable == nullptr) return;
+  if (self->vtable->shutdown != nullptr) {
+    self->vtable->shutdown(self);
+  }
+  self->handshake_shutdown = true;
+}
+
+void tsi_handshaker_destroy(tsi_handshaker* self) {
+  if (self == nullptr) return;
+  self->vtable->destroy(self);
+}
+
+/* --- tsi_handshaker_result implementation. --- */
+
+tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result* self,
+                                              tsi_peer* peer) {
+  if (self == nullptr || self->vtable == nullptr || peer == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  memset(peer, 0, sizeof(tsi_peer));
+  if (self->vtable->extract_peer == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->extract_peer(self, peer);
+}
+
+tsi_result tsi_handshaker_result_create_frame_protector(
+    const tsi_handshaker_result* self, size_t* max_protected_frame_size,
+    tsi_frame_protector** protector) {
+  if (self == nullptr || self->vtable == nullptr || protector == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->create_frame_protector == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->create_frame_protector(self, max_protected_frame_size,
+                                              protector);
+}
+
+tsi_result tsi_handshaker_result_get_unused_bytes(
+    const tsi_handshaker_result* self, const unsigned char** bytes,
+    size_t* bytes_size) {
+  if (self == nullptr || self->vtable == nullptr || bytes == nullptr ||
+      bytes_size == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->get_unused_bytes == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->get_unused_bytes(self, bytes, bytes_size);
+}
+
+void tsi_handshaker_result_destroy(tsi_handshaker_result* self) {
+  if (self == nullptr) return;
+  self->vtable->destroy(self);
+}
+
+/* --- tsi_peer implementation. --- */
+
+tsi_peer_property tsi_init_peer_property(void) {
+  tsi_peer_property property;
+  memset(&property, 0, sizeof(tsi_peer_property));
+  return property;
+}
+
+static void tsi_peer_destroy_list_property(tsi_peer_property* children,
+                                           size_t child_count) {
+  size_t i;
+  for (i = 0; i < child_count; i++) {
+    tsi_peer_property_destruct(&children[i]);
+  }
+  gpr_free(children);
+}
+
+void tsi_peer_property_destruct(tsi_peer_property* property) {
+  if (property->name != nullptr) {
+    gpr_free(property->name);
+  }
+  if (property->value.data != nullptr) {
+    gpr_free(property->value.data);
+  }
+  *property = tsi_init_peer_property(); /* Reset everything to 0. */
+}
+
+void tsi_peer_destruct(tsi_peer* self) {
+  if (self == nullptr) return;
+  if (self->properties != nullptr) {
+    tsi_peer_destroy_list_property(self->properties, self->property_count);
+    self->properties = nullptr;
+  }
+  self->property_count = 0;
+}
+
+tsi_result tsi_construct_allocated_string_peer_property(
+    const char* name, size_t value_length, tsi_peer_property* property) {
+  *property = tsi_init_peer_property();
+  if (name != nullptr) property->name = gpr_strdup(name);
+  if (value_length > 0) {
+    property->value.data = static_cast<char*>(gpr_zalloc(value_length));
+    property->value.length = value_length;
+  }
+  return TSI_OK;
+}
+
+tsi_result tsi_construct_string_peer_property_from_cstring(
+    const char* name, const char* value, tsi_peer_property* property) {
+  return tsi_construct_string_peer_property(name, value, strlen(value),
+                                            property);
+}
+
+tsi_result tsi_construct_string_peer_property(const char* name,
+                                              const char* value,
+                                              size_t value_length,
+                                              tsi_peer_property* property) {
+  tsi_result result = tsi_construct_allocated_string_peer_property(
+      name, value_length, property);
+  if (result != TSI_OK) return result;
+  if (value_length > 0) {
+    memcpy(property->value.data, value, value_length);
+  }
+  return TSI_OK;
+}
+
+tsi_result tsi_construct_peer(size_t property_count, tsi_peer* peer) {
+  memset(peer, 0, sizeof(tsi_peer));
+  if (property_count > 0) {
+    peer->properties = static_cast<tsi_peer_property*>(
+        gpr_zalloc(property_count * sizeof(tsi_peer_property)));
+    peer->property_count = property_count;
+  }
+  return TSI_OK;
+}
+
+const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* peer,
+                                                       const char* name) {
+  size_t i;
+  if (peer == nullptr) return nullptr;
+  for (i = 0; i < peer->property_count; i++) {
+    const tsi_peer_property* property = &peer->properties[i];
+    if (name == nullptr && property->name == nullptr) {
+      return property;
+    }
+    if (name != nullptr && property->name != nullptr &&
+        strcmp(property->name, name) == 0) {
+      return property;
+    }
+  }
+  return nullptr;
+}
diff --git a/third_party/grpc/src/core/tsi/transport_security.h b/third_party/grpc/src/core/tsi/transport_security.h
new file mode 100644
index 0000000..482d300
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/transport_security.h
@@ -0,0 +1,130 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_H
+#define GRPC_CORE_TSI_TRANSPORT_SECURITY_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdbool.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/tsi/transport_security_interface.h"
+
+extern grpc_core::TraceFlag tsi_tracing_enabled;
+
+/* Base for tsi_frame_protector implementations.
+   See transport_security_interface.h for documentation. */
+typedef struct {
+  tsi_result (*protect)(tsi_frame_protector* self,
+                        const unsigned char* unprotected_bytes,
+                        size_t* unprotected_bytes_size,
+                        unsigned char* protected_output_frames,
+                        size_t* protected_output_frames_size);
+  tsi_result (*protect_flush)(tsi_frame_protector* self,
+                              unsigned char* protected_output_frames,
+                              size_t* protected_output_frames_size,
+                              size_t* still_pending_size);
+  tsi_result (*unprotect)(tsi_frame_protector* self,
+                          const unsigned char* protected_frames_bytes,
+                          size_t* protected_frames_bytes_size,
+                          unsigned char* unprotected_bytes,
+                          size_t* unprotected_bytes_size);
+  void (*destroy)(tsi_frame_protector* self);
+} tsi_frame_protector_vtable;
+
+struct tsi_frame_protector {
+  const tsi_frame_protector_vtable* vtable;
+};
+
+/* Base for tsi_handshaker implementations.
+   See transport_security_interface.h for documentation. */
+typedef struct {
+  tsi_result (*get_bytes_to_send_to_peer)(tsi_handshaker* self,
+                                          unsigned char* bytes,
+                                          size_t* bytes_size);
+  tsi_result (*process_bytes_from_peer)(tsi_handshaker* self,
+                                        const unsigned char* bytes,
+                                        size_t* bytes_size);
+  tsi_result (*get_result)(tsi_handshaker* self);
+  tsi_result (*extract_peer)(tsi_handshaker* self, tsi_peer* peer);
+  tsi_result (*create_frame_protector)(tsi_handshaker* self,
+                                       size_t* max_protected_frame_size,
+                                       tsi_frame_protector** protector);
+  void (*destroy)(tsi_handshaker* self);
+  tsi_result (*next)(tsi_handshaker* self, const unsigned char* received_bytes,
+                     size_t received_bytes_size,
+                     const unsigned char** bytes_to_send,
+                     size_t* bytes_to_send_size,
+                     tsi_handshaker_result** handshaker_result,
+                     tsi_handshaker_on_next_done_cb cb, void* user_data);
+  void (*shutdown)(tsi_handshaker* self);
+} tsi_handshaker_vtable;
+
+struct tsi_handshaker {
+  const tsi_handshaker_vtable* vtable;
+  bool frame_protector_created;
+  bool handshaker_result_created;
+  bool handshake_shutdown;
+};
+
+/* Base for tsi_handshaker_result implementations.
+   See transport_security_interface.h for documentation.
+   The exec_ctx parameter in create_zero_copy_grpc_protector is supposed to be
+   of type grpc_exec_ctx*, but we're using void* instead to avoid making the TSI
+   API depend on grpc. The create_zero_copy_grpc_protector() method is only used
+   in grpc, where we do need the exec_ctx passed through, but the API still
+   needs to compile in other applications, where grpc_exec_ctx is not defined.
+*/
+typedef struct {
+  tsi_result (*extract_peer)(const tsi_handshaker_result* self, tsi_peer* peer);
+  tsi_result (*create_zero_copy_grpc_protector)(
+      const tsi_handshaker_result* self,
+      size_t* max_output_protected_frame_size,
+      tsi_zero_copy_grpc_protector** protector);
+  tsi_result (*create_frame_protector)(const tsi_handshaker_result* self,
+                                       size_t* max_output_protected_frame_size,
+                                       tsi_frame_protector** protector);
+  tsi_result (*get_unused_bytes)(const tsi_handshaker_result* self,
+                                 const unsigned char** bytes,
+                                 size_t* bytes_size);
+  void (*destroy)(tsi_handshaker_result* self);
+} tsi_handshaker_result_vtable;
+
+struct tsi_handshaker_result {
+  const tsi_handshaker_result_vtable* vtable;
+};
+
+/* Peer and property construction/destruction functions. */
+tsi_result tsi_construct_peer(size_t property_count, tsi_peer* peer);
+tsi_peer_property tsi_init_peer_property(void);
+void tsi_peer_property_destruct(tsi_peer_property* property);
+tsi_result tsi_construct_string_peer_property(const char* name,
+                                              const char* value,
+                                              size_t value_length,
+                                              tsi_peer_property* property);
+tsi_result tsi_construct_allocated_string_peer_property(
+    const char* name, size_t value_length, tsi_peer_property* property);
+tsi_result tsi_construct_string_peer_property_from_cstring(
+    const char* name, const char* value, tsi_peer_property* property);
+const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* peer,
+                                                       const char* name);
+/* Utils. */
+char* tsi_strdup(const char* src); /* Sadly, no strdup in C89. */
+
+#endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_H */
diff --git a/third_party/grpc/src/core/tsi/transport_security_grpc.cc b/third_party/grpc/src/core/tsi/transport_security_grpc.cc
new file mode 100644
index 0000000..c73a6e3
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/transport_security_grpc.cc
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/core/tsi/transport_security_grpc.h"
+
+/* This method creates a tsi_zero_copy_grpc_protector object.  */
+tsi_result tsi_handshaker_result_create_zero_copy_grpc_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector) {
+  if (self == nullptr || self->vtable == nullptr || protector == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->create_zero_copy_grpc_protector == nullptr) {
+    return TSI_UNIMPLEMENTED;
+  }
+  return self->vtable->create_zero_copy_grpc_protector(
+      self, max_output_protected_frame_size, protector);
+}
+
+/* --- tsi_zero_copy_grpc_protector common implementation. ---
+
+   Calls specific implementation after state/input validation. */
+
+tsi_result tsi_zero_copy_grpc_protector_protect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices) {
+  if (self == nullptr || self->vtable == nullptr ||
+      unprotected_slices == nullptr || protected_slices == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->protect == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->protect(self, unprotected_slices, protected_slices);
+}
+
+tsi_result tsi_zero_copy_grpc_protector_unprotect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices) {
+  if (self == nullptr || self->vtable == nullptr ||
+      protected_slices == nullptr || unprotected_slices == nullptr) {
+    return TSI_INVALID_ARGUMENT;
+  }
+  if (self->vtable->unprotect == nullptr) return TSI_UNIMPLEMENTED;
+  return self->vtable->unprotect(self, protected_slices, unprotected_slices);
+}
+
+void tsi_zero_copy_grpc_protector_destroy(tsi_zero_copy_grpc_protector* self) {
+  if (self == nullptr) return;
+  self->vtable->destroy(self);
+}
diff --git a/third_party/grpc/src/core/tsi/transport_security_grpc.h b/third_party/grpc/src/core/tsi/transport_security_grpc.h
new file mode 100644
index 0000000..d3bb04d
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/transport_security_grpc.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H
+#define GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/slice_buffer.h>
+#include "src/core/tsi/transport_security.h"
+
+/* This method creates a tsi_zero_copy_grpc_protector object. It return TSI_OK
+   assuming there is no fatal error.
+   The caller is responsible for destroying the protector.  */
+tsi_result tsi_handshaker_result_create_zero_copy_grpc_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_zero_copy_grpc_protector** protector);
+
+/* -- tsi_zero_copy_grpc_protector object --  */
+
+/* Outputs protected frames.
+   - unprotected_slices is the unprotected data to be protected.
+   - protected_slices is the protected output frames. One or more frames
+     may be produced in this protect function.
+   - This method returns TSI_OK in case of success or a specific error code in
+     case of failure.  */
+tsi_result tsi_zero_copy_grpc_protector_protect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices,
+    grpc_slice_buffer* protected_slices);
+
+/* Outputs unprotected bytes.
+   - protected_slices is the bytes of protected frames.
+   - unprotected_slices is the unprotected output data.
+   - This method returns TSI_OK in case of success. Success includes cases where
+     there is not enough data to output in which case unprotected_slices has 0
+     bytes.  */
+tsi_result tsi_zero_copy_grpc_protector_unprotect(
+    tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices,
+    grpc_slice_buffer* unprotected_slices);
+
+/* Destroys the tsi_zero_copy_grpc_protector object.  */
+void tsi_zero_copy_grpc_protector_destroy(tsi_zero_copy_grpc_protector* self);
+
+/* Base for tsi_zero_copy_grpc_protector implementations.  */
+typedef struct {
+  tsi_result (*protect)(tsi_zero_copy_grpc_protector* self,
+                        grpc_slice_buffer* unprotected_slices,
+                        grpc_slice_buffer* protected_slices);
+  tsi_result (*unprotect)(tsi_zero_copy_grpc_protector* self,
+                          grpc_slice_buffer* protected_slices,
+                          grpc_slice_buffer* unprotected_slices);
+  void (*destroy)(tsi_zero_copy_grpc_protector* self);
+} tsi_zero_copy_grpc_protector_vtable;
+
+struct tsi_zero_copy_grpc_protector {
+  const tsi_zero_copy_grpc_protector_vtable* vtable;
+};
+
+#endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H */
diff --git a/third_party/grpc/src/core/tsi/transport_security_interface.h b/third_party/grpc/src/core/tsi/transport_security_interface.h
new file mode 100644
index 0000000..7a0cdc3
--- /dev/null
+++ b/third_party/grpc/src/core/tsi/transport_security_interface.h
@@ -0,0 +1,464 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H
+#define GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "src/core/lib/debug/trace.h"
+
+/* --- tsi result ---  */
+
+typedef enum {
+  TSI_OK = 0,
+  TSI_UNKNOWN_ERROR = 1,
+  TSI_INVALID_ARGUMENT = 2,
+  TSI_PERMISSION_DENIED = 3,
+  TSI_INCOMPLETE_DATA = 4,
+  TSI_FAILED_PRECONDITION = 5,
+  TSI_UNIMPLEMENTED = 6,
+  TSI_INTERNAL_ERROR = 7,
+  TSI_DATA_CORRUPTED = 8,
+  TSI_NOT_FOUND = 9,
+  TSI_PROTOCOL_FAILURE = 10,
+  TSI_HANDSHAKE_IN_PROGRESS = 11,
+  TSI_OUT_OF_RESOURCES = 12,
+  TSI_ASYNC = 13,
+  TSI_HANDSHAKE_SHUTDOWN = 14,
+} tsi_result;
+
+typedef enum {
+  // Default option
+  TSI_DONT_REQUEST_CLIENT_CERTIFICATE,
+  TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+  TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY,
+  TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY,
+  TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY,
+} tsi_client_certificate_request_type;
+
+const char* tsi_result_to_string(tsi_result result);
+
+/* --- tsi tracing --- */
+
+extern grpc_core::TraceFlag tsi_tracing_enabled;
+
+/* -- tsi_zero_copy_grpc_protector object --
+
+  This object protects and unprotects grpc slice buffers with zero or minimized
+  memory copy once the handshake is done. Implementations of this object must be
+  thread compatible. This object depends on grpc and the details of this object
+  is defined in transport_security_grpc.h.  */
+
+typedef struct tsi_zero_copy_grpc_protector tsi_zero_copy_grpc_protector;
+
+/* --- tsi_frame_protector object ---
+
+  This object protects and unprotects buffers once the handshake is done.
+  Implementations of this object must be thread compatible.  */
+
+typedef struct tsi_frame_protector tsi_frame_protector;
+
+/* Outputs protected frames.
+   - unprotected_bytes is an input only parameter and points to the data
+     to be protected.
+   - unprotected_bytes_size is an input/output parameter used by the caller to
+     specify how many bytes are available in unprotected_bytes. The output
+     value is the number of bytes consumed during the call.
+   - protected_output_frames points to a buffer allocated by the caller that
+     will be written.
+   - protected_output_frames_size is an input/output parameter used by the
+     caller to specify how many bytes are available in protected_output_frames.
+     As an output, this value indicates the number of bytes written.
+   - This method returns TSI_OK in case of success or a specific error code in
+     case of failure. Note that even if all the input unprotected bytes are
+     consumed, they may not have been processed into the returned protected
+     output frames. The caller should call the protect_flush method
+     to make sure that there are no more protected bytes buffered in the
+     protector.
+
+   A typical way to call this method would be:
+
+   ------------------------------------------------------------------------
+   unsigned char protected_buffer[4096];
+   size_t protected_buffer_size = sizeof(protected_buffer);
+   tsi_result result = TSI_OK;
+   while (message_size > 0) {
+     size_t protected_buffer_size_to_send = protected_buffer_size;
+     size_t processed_message_size = message_size;
+     result = tsi_frame_protector_protect(protector,
+                                          message_bytes,
+                                          &processed_message_size,
+                                          protected_buffer,
+                                          &protected_buffer_size_to_send);
+     if (result != TSI_OK) break;
+     send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send);
+     message_bytes += processed_message_size;
+     message_size -= processed_message_size;
+
+     // Don't forget to flush.
+     if (message_size == 0) {
+       size_t still_pending_size;
+       do {
+         protected_buffer_size_to_send = protected_buffer_size;
+         result = tsi_frame_protector_protect_flush(
+             protector, protected_buffer,
+             &protected_buffer_size_to_send, &still_pending_size);
+         if (result != TSI_OK) break;
+         send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send);
+       } while (still_pending_size > 0);
+     }
+   }
+
+   if (result != TSI_OK) HandleError(result);
+   ------------------------------------------------------------------------  */
+tsi_result tsi_frame_protector_protect(tsi_frame_protector* self,
+                                       const unsigned char* unprotected_bytes,
+                                       size_t* unprotected_bytes_size,
+                                       unsigned char* protected_output_frames,
+                                       size_t* protected_output_frames_size);
+
+/* Indicates that we need to flush the bytes buffered in the protector and get
+   the resulting frame.
+   - protected_output_frames points to a buffer allocated by the caller that
+     will be written.
+   - protected_output_frames_size is an input/output parameter used by the
+     caller to specify how many bytes are available in protected_output_frames.
+   - still_pending_bytes is an output parameter indicating the number of bytes
+     that still need to be flushed from the protector.*/
+tsi_result tsi_frame_protector_protect_flush(
+    tsi_frame_protector* self, unsigned char* protected_output_frames,
+    size_t* protected_output_frames_size, size_t* still_pending_size);
+
+/* Outputs unprotected bytes.
+   - protected_frames_bytes is an input only parameter and points to the
+     protected frames to be unprotected.
+   - protected_frames_bytes_size is an input/output only parameter used by the
+     caller to specify how many bytes are available in protected_bytes. The
+     output value is the number of bytes consumed during the call.
+     Implementations will buffer up to a frame of protected data.
+   - unprotected_bytes points to a buffer allocated by the caller that will be
+     written.
+   - unprotected_bytes_size is an input/output parameter used by the caller to
+     specify how many bytes are available in unprotected_bytes. This
+     value is expected to be at most max_protected_frame_size minus overhead
+     which means that max_protected_frame_size is a safe bet. The output value
+     is the number of bytes actually written.
+     If *unprotected_bytes_size is unchanged, there may be more data remaining
+     to unprotect, and the caller should call this function again.
+
+   - This method returns TSI_OK in case of success. Success includes cases where
+     there is not enough data to output a frame in which case
+     unprotected_bytes_size will be set to 0 and cases where the internal buffer
+     needs to be read before new protected data can be processed in which case
+     protected_frames_size will be set to 0.  */
+tsi_result tsi_frame_protector_unprotect(
+    tsi_frame_protector* self, const unsigned char* protected_frames_bytes,
+    size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes,
+    size_t* unprotected_bytes_size);
+
+/* Destroys the tsi_frame_protector object.  */
+void tsi_frame_protector_destroy(tsi_frame_protector* self);
+
+/* --- tsi_peer objects ---
+
+   tsi_peer objects are a set of properties. The peer owns the properties.  */
+
+/* This property is of type TSI_PEER_PROPERTY_STRING.  */
+#define TSI_CERTIFICATE_TYPE_PEER_PROPERTY "certificate_type"
+
+/* Property values may contain NULL characters just like C++ strings.
+   The length field gives the length of the string. */
+typedef struct tsi_peer_property {
+  char* name;
+  struct {
+    char* data;
+    size_t length;
+  } value;
+} tsi_peer_property;
+
+typedef struct {
+  tsi_peer_property* properties;
+  size_t property_count;
+} tsi_peer;
+
+/* Destructs the tsi_peer object. */
+void tsi_peer_destruct(tsi_peer* self);
+
+/*  --- tsi_handshaker_result object ---
+
+  This object contains all necessary handshake results and data such as peer
+  info, negotiated keys, unused handshake bytes, when the handshake completes.
+  Implementations of this object must be thread compatible.  */
+
+typedef struct tsi_handshaker_result tsi_handshaker_result;
+
+/* This method extracts tsi peer. It returns TSI_OK assuming there is no fatal
+   error.
+   The caller is responsible for destructing the peer.  */
+tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result* self,
+                                              tsi_peer* peer);
+
+/* This method creates a tsi_frame_protector object. It returns TSI_OK assuming
+   there is no fatal error.
+   The caller is responsible for destroying the protector.  */
+tsi_result tsi_handshaker_result_create_frame_protector(
+    const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
+    tsi_frame_protector** protector);
+
+/* This method returns the unused bytes from the handshake. It returns TSI_OK
+   assuming there is no fatal error.
+   Ownership of the bytes is retained by the handshaker result. As a
+   consequence, the caller must not free the bytes.  */
+tsi_result tsi_handshaker_result_get_unused_bytes(
+    const tsi_handshaker_result* self, const unsigned char** bytes,
+    size_t* byte_size);
+
+/* This method releases the tsi_handshaker_handshaker object. After this method
+   is called, no other method can be called on the object.  */
+void tsi_handshaker_result_destroy(tsi_handshaker_result* self);
+
+/* --- tsi_handshaker objects ----
+
+   Implementations of this object must be thread compatible.
+
+   ------------------------------------------------------------------------
+
+   A typical usage supporting both synchronous and asynchronous TSI handshaker
+   implementations would be:
+
+   ------------------------------------------------------------------------
+
+   typedef struct {
+     tsi_handshaker *handshaker;
+     tsi_handshaker_result *handshaker_result;
+     unsigned char *handshake_buffer;
+     size_t handshake_buffer_size;
+     ...
+   } security_handshaker;
+
+   void do_handshake(security_handshaker *h, ...) {
+     // Start the handshake by the calling do_handshake_next.
+     do_handshake_next(h, NULL, 0);
+     ...
+   }
+
+   // This method is the callback function when data is received from the
+   // peer. This method will read bytes into the handshake buffer and call
+   // do_handshake_next.
+   void on_handshake_data_received_from_peer(void *user_data) {
+     security_handshaker *h = (security_handshaker *)user_data;
+     size_t bytes_received_size = h->handshake_buffer_size;
+     read_bytes_from_peer(h->handshake_buffer, &bytes_received_size);
+     do_handshake_next(h, h->handshake_buffer, bytes_received_size);
+   }
+
+   // This method processes a step of handshake, calling tsi_handshaker_next.
+   void do_handshake_next(security_handshaker *h,
+                          const unsigned char* bytes_received,
+                          size_t bytes_received_size) {
+     tsi_result status = TSI_OK;
+     unsigned char *bytes_to_send = NULL;
+     size_t bytes_to_send_size = 0;
+     tsi_handshaker_result *result = NULL;
+     status = tsi_handshaker_next(
+         handshaker, bytes_received, bytes_received_size, &bytes_to_send,
+         &bytes_to_send_size, &result, on_handshake_next_done, h);
+     // If TSI handshaker is asynchronous, on_handshake_next_done will be
+     // executed inside tsi_handshaker_next.
+     if (status == TSI_ASYNC) return;
+     // If TSI handshaker is synchronous, invoke callback directly in this
+     // thread.
+     on_handshake_next_done(status, (void *)h, bytes_to_send,
+                            bytes_to_send_size, result);
+   }
+
+   // This is the callback function to execute after tsi_handshaker_next.
+   // It is passed to tsi_handshaker_next as a function parameter.
+   void on_handshake_next_done(
+       tsi_result status, void *user_data, const unsigned char *bytes_to_send,
+       size_t bytes_to_send_size, tsi_handshaker_result *result) {
+     security_handshaker *h = (security_handshaker *)user_data;
+     if (status == TSI_INCOMPLETE_DATA) {
+       // Schedule an asynchronous read from the peer. If handshake data are
+       // received, on_handshake_data_received_from_peer will be called.
+       async_read_from_peer(..., ..., on_handshake_data_received_from_peer);
+       return;
+     }
+     if (status != TSI_OK) return;
+
+     if (bytes_to_send_size > 0) {
+       send_bytes_to_peer(bytes_to_send, bytes_to_send_size);
+     }
+
+     if (result != NULL) {
+       // Handshake completed.
+       h->result = result;
+       // Check the Peer.
+       tsi_peer peer;
+       status = tsi_handshaker_result_extract_peer(result, &peer);
+       if (status != TSI_OK) return;
+       status = check_peer(&peer);
+       tsi_peer_destruct(&peer);
+       if (status != TSI_OK) return;
+
+       // Create the protector.
+       tsi_frame_protector* protector = NULL;
+       status = tsi_handshaker_result_create_frame_protector(result, NULL,
+                                                             &protector);
+       if (status != TSI_OK) return;
+
+       // Do not forget to unprotect outstanding data if any.
+       ....
+     }
+   }
+   ------------------------------------------------------------------------   */
+typedef struct tsi_handshaker tsi_handshaker;
+
+/* TODO(jiangtaoli2016): Cleans up deprecated methods when we are ready. */
+
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead.
+   Gets bytes that need to be sent to the peer.
+   - bytes is the buffer that will be written with the data to be sent to the
+     peer.
+   - bytes_size is an input/output parameter specifying the capacity of the
+     bytes parameter as input and the number of bytes written as output.
+   Returns TSI_OK if all the data to send to the peer has been written or if
+   nothing has to be sent to the peer (in which base bytes_size outputs to 0),
+   otherwise returns TSI_INCOMPLETE_DATA which indicates that this method
+   needs to be called again to get all the bytes to send to the peer (there
+   was more data to write than the specified bytes_size). In case of a fatal
+   error in the handshake, another specific error code is returned.  */
+tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self,
+                                                    unsigned char* bytes,
+                                                    size_t* bytes_size);
+
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead.
+   Processes bytes received from the peer.
+   - bytes is the buffer containing the data.
+   - bytes_size is an input/output parameter specifying the size of the data as
+     input and the number of bytes consumed as output.
+   Return TSI_OK if the handshake has all the data it needs to process,
+   otherwise return TSI_INCOMPLETE_DATA which indicates that this method
+   needs to be called again to complete the data needed for processing. In
+   case of a fatal error in the handshake, another specific error code is
+   returned.  */
+tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self,
+                                                  const unsigned char* bytes,
+                                                  size_t* bytes_size);
+
+/* TO BE DEPRECATED SOON.
+   Gets the result of the handshaker.
+   Returns TSI_OK if the hanshake completed successfully and there has been no
+   errors. Returns TSI_HANDSHAKE_IN_PROGRESS if the handshaker is not done yet
+   but no error has been encountered so far. Otherwise the handshaker failed
+   with the returned error.  */
+tsi_result tsi_handshaker_get_result(tsi_handshaker* self);
+
+/* TO BE DEPRECATED SOON.
+   Returns 1 if the handshake is in progress, 0 otherwise.  */
+#define tsi_handshaker_is_in_progress(h) \
+  (tsi_handshaker_get_result((h)) == TSI_HANDSHAKE_IN_PROGRESS)
+
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_result_extract_peer instead.
+   This method may return TSI_FAILED_PRECONDITION if
+   tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise
+   assuming the handshaker is not in a fatal error state.
+   The caller is responsible for destructing the peer.  */
+tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer);
+
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_result_create_frame_protector
+   instead.
+   This method creates a tsi_frame_protector object after the handshake phase
+   is done. After this method has been called successfully, the only method
+   that can be called on this object is Destroy.
+   - max_output_protected_frame_size is an input/output parameter specifying the
+     desired max output protected frame size as input and outputing the actual
+     max output frame size as the output. Passing NULL is OK and will result in
+     the implementation choosing the default maximum protected frame size. Note
+     that this size only applies to outgoing frames (generated with
+     tsi_frame_protector_protect) and not incoming frames (input of
+     tsi_frame_protector_unprotect).
+   - protector is an output parameter pointing to the newly created
+     tsi_frame_protector object.
+   This method may return TSI_FAILED_PRECONDITION if
+   tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise assuming
+   the handshaker is not in a fatal error state.
+   The caller is responsible for destroying the protector.  */
+tsi_result tsi_handshaker_create_frame_protector(
+    tsi_handshaker* self, size_t* max_output_protected_frame_size,
+    tsi_frame_protector** protector);
+
+/* Callback function definition for tsi_handshaker_next.
+   - status indicates the status of the next operation.
+   - user_data is the argument to callback function passed from the caller.
+   - bytes_to_send is the data buffer to be sent to the peer.
+   - bytes_to_send_size is the size of data buffer to be sent to the peer.
+   - handshaker_result is the result of handshake when the handshake completes,
+     is NULL otherwise.  */
+typedef void (*tsi_handshaker_on_next_done_cb)(
+    tsi_result status, void* user_data, const unsigned char* bytes_to_send,
+    size_t bytes_to_send_size, tsi_handshaker_result* handshaker_result);
+
+/* Conduct a next step of the handshake.
+   - received_bytes is the buffer containing the data received from the peer.
+   - received_bytes_size is the size of the data received from the peer.
+   - bytes_to_send is the data buffer to be sent to the peer.
+   - bytes_to_send_size is the size of data buffer to be sent to the peer.
+   - handshaker_result is the result of handshake if the handshake completes.
+   - cb is the callback function defined above. It can be NULL for synchronous
+     TSI handshaker implementation.
+   - user_data is the argument to callback function passed from the caller.
+   This method returns TSI_ASYNC if the TSI handshaker implementation is
+   asynchronous, and in this case, the callback is guaranteed to run in another
+   thread owned by TSI. It returns TSI_OK if the handshake completes or if
+   there are data to send to the peer, otherwise returns TSI_INCOMPLETE_DATA
+   which indicates that this method needs to be called again with more data
+   from the peer. In case of a fatal error in the handshake, another specific
+   error code is returned.
+   The caller is responsible for destroying the handshaker_result. However,
+   the caller should not free bytes_to_send, as the buffer is owned by the
+   tsi_handshaker object.  */
+tsi_result tsi_handshaker_next(
+    tsi_handshaker* self, const unsigned char* received_bytes,
+    size_t received_bytes_size, const unsigned char** bytes_to_send,
+    size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result,
+    tsi_handshaker_on_next_done_cb cb, void* user_data);
+
+/* This method shuts down a TSI handshake that is in progress.
+ *
+ * This method will be invoked when TSI handshake should be terminated before
+ * being finished in order to free any resources being used.
+ */
+void tsi_handshaker_shutdown(tsi_handshaker* self);
+
+/* This method releases the tsi_handshaker object. After this method is called,
+   no other method can be called on the object.  */
+void tsi_handshaker_destroy(tsi_handshaker* self);
+
+/* This method initializes the necessary shared objects used for tsi
+   implementation.  */
+void tsi_init();
+
+/* This method destroys the shared objects created by tsi_init.  */
+void tsi_destroy();
+
+#endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H */
diff --git a/third_party/grpc/src/cpp/README.md b/third_party/grpc/src/cpp/README.md
new file mode 100644
index 0000000..4ec9133
--- /dev/null
+++ b/third_party/grpc/src/cpp/README.md
@@ -0,0 +1,83 @@
+
+# Overview
+
+A C++ implementation of gRPC
+
+# To start using gRPC C++
+
+In the C++ world, there's no universally accepted standard for managing project dependencies.
+Therefore, gRPC supports several major build systems, which should satisfy most users.
+
+## bazel
+
+We recommend using Bazel for projects that use gRPC as it will give you the best developer experience
+(easy handling of dependencies that support bazel & fast builds).
+
+To add gRPC as a dependency in bazel:
+1. determine commit SHA for the grpc release you want to use
+2. Use the [http_archive](https://docs.bazel.build/versions/master/be/workspace.html#http_archive) bazel rule to include gRPC source
+  ```
+  http_archive(
+      name = "com_github_grpc_grpc",
+      urls = [
+          "https://github.com/grpc/grpc/archive/YOUR_GRPC_COMMIT_SHA.tar.gz",
+      ],
+      strip_prefix = "grpc-YOUR_GRPC_COMMIT_SHA",
+  )
+
+  load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps")
+
+  grpc_deps()
+  ```
+
+NOTE: currently bazel is only supported for building gRPC on Linux.
+
+## make
+
+Currently the default choice for building on UNIX based systems is `make`.
+
+To install gRPC for C++ on your system using `make`, follow the [Building gRPC C++](../../BUILDING.md)
+instructions to build from source and then install locally using `make install`.
+This also installs the protocol buffer compiler `protoc` (if you don't have it already),
+and the C++ gRPC plugin for `protoc`.
+
+WARNING: After installing with `make install` there is no easy way to uninstall, which can cause issues
+if you later want to remove the grpc and/or protobuf installation or upgrade to a newer version.
+
+## cmake
+
+`cmake` is the default build option on Windows, but also works on Linux, MacOS. `cmake` has good
+support for crosscompiling and can be used for targeting Android platform.
+
+If your project is using cmake, there are several ways to add gRPC dependency.
+- install gRPC via cmake first and then locate it with `find_package(gRPC CONFIG)`. [Example](../../examples/cpp/helloworld/CMakeLists.txt)
+- via cmake's `ExternalProject_Add` using a technique called "superbuild". [Example](../../examples/cpp/helloworld/cmake_externalproject/CMakeLists.txt)
+- add gRPC source tree to your project (preferrably as a git submodule) and add it to your cmake project with `add_subdirectory`. [Example](../../examples/cpp/helloworld/CMakeLists.txt)
+
+## Packaging systems
+
+There's no standard packaging system for C++. We've looked into supporting some (e.g. Conan and vcpkg) but we are not there yet.
+Contributions and community-maintained packages for popular packaging systems are welcome!
+
+
+## Examples & Additional Documentation
+
+You can find out how to build and run our simplest gRPC C++ example in our
+[C++ quick start](../../examples/cpp).
+
+For more detailed documentation on using gRPC in C++ , see our main
+documentation site at [grpc.io](https://grpc.io), specifically:
+
+* [Overview](https://grpc.io/docs/): An introduction to gRPC with a simple
+  Hello World example in all our supported languages, including C++.
+* [gRPC Basics - C++](https://grpc.io/docs/tutorials/basic/c.html):
+  A tutorial that steps you through creating a simple gRPC C++ example
+  application.
+* [Asynchronous Basics - C++](https://grpc.io/docs/tutorials/async/helloasync-cpp.html):
+  A tutorial that shows you how to use gRPC C++'s asynchronous/non-blocking
+  APIs.
+
+
+# To start developing gRPC C++
+
+For instructions on how to build gRPC C++ from source, follow the [Building gRPC C++](../../BUILDING.md) instructions.
diff --git a/third_party/grpc/src/cpp/client/channel_cc.cc b/third_party/grpc/src/cpp/client/channel_cc.cc
new file mode 100644
index 0000000..a31d0b3
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/channel_cc.cc
@@ -0,0 +1,255 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/channel.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <cstring>
+#include <memory>
+#include <mutex>
+
+#include <grpc/grpc.h>
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/impl/codegen/call_op_set.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/impl/rpc_method.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/status.h>
+#include <grpcpp/support/time.h>
+#include "src/core/lib/gpr/env.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/surface/completion_queue.h"
+
+namespace grpc {
+
+static internal::GrpcLibraryInitializer g_gli_initializer;
+Channel::Channel(
+    const grpc::string& host, grpc_channel* channel,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators)
+    : host_(host), c_channel_(channel) {
+  interceptor_creators_ = std::move(interceptor_creators);
+  g_gli_initializer.summon();
+}
+
+Channel::~Channel() {
+  grpc_channel_destroy(c_channel_);
+  if (callback_cq_ != nullptr) {
+    callback_cq_->Shutdown();
+  }
+}
+
+namespace {
+
+inline grpc_slice SliceFromArray(const char* arr, size_t len) {
+  return g_core_codegen_interface->grpc_slice_from_copied_buffer(arr, len);
+}
+
+grpc::string GetChannelInfoField(grpc_channel* channel,
+                                 grpc_channel_info* channel_info,
+                                 char*** channel_info_field) {
+  char* value = nullptr;
+  memset(channel_info, 0, sizeof(*channel_info));
+  *channel_info_field = &value;
+  grpc_channel_get_info(channel, channel_info);
+  if (value == nullptr) return "";
+  grpc::string result = value;
+  gpr_free(value);
+  return result;
+}
+
+}  // namespace
+
+grpc::string Channel::GetLoadBalancingPolicyName() const {
+  grpc_channel_info channel_info;
+  return GetChannelInfoField(c_channel_, &channel_info,
+                             &channel_info.lb_policy_name);
+}
+
+grpc::string Channel::GetServiceConfigJSON() const {
+  grpc_channel_info channel_info;
+  return GetChannelInfoField(c_channel_, &channel_info,
+                             &channel_info.service_config_json);
+}
+
+namespace experimental {
+
+void ChannelResetConnectionBackoff(Channel* channel) {
+  grpc_channel_reset_connect_backoff(channel->c_channel_);
+}
+
+}  // namespace experimental
+
+internal::Call Channel::CreateCallInternal(const internal::RpcMethod& method,
+                                           ClientContext* context,
+                                           CompletionQueue* cq,
+                                           size_t interceptor_pos) {
+  const bool kRegistered = method.channel_tag() && context->authority().empty();
+  grpc_call* c_call = nullptr;
+  if (kRegistered) {
+    c_call = grpc_channel_create_registered_call(
+        c_channel_, context->propagate_from_call_,
+        context->propagation_options_.c_bitmask(), cq->cq(),
+        method.channel_tag(), context->raw_deadline(), nullptr);
+  } else {
+    const string* host_str = nullptr;
+    if (!context->authority_.empty()) {
+      host_str = &context->authority_;
+    } else if (!host_.empty()) {
+      host_str = &host_;
+    }
+    grpc_slice method_slice =
+        SliceFromArray(method.name(), strlen(method.name()));
+    grpc_slice host_slice;
+    if (host_str != nullptr) {
+      host_slice = SliceFromCopiedString(*host_str);
+    }
+    c_call = grpc_channel_create_call(
+        c_channel_, context->propagate_from_call_,
+        context->propagation_options_.c_bitmask(), cq->cq(), method_slice,
+        host_str == nullptr ? nullptr : &host_slice, context->raw_deadline(),
+        nullptr);
+    grpc_slice_unref(method_slice);
+    if (host_str != nullptr) {
+      grpc_slice_unref(host_slice);
+    }
+  }
+  grpc_census_call_set_context(c_call, context->census_context());
+
+  // ClientRpcInfo should be set before call because set_call also checks
+  // whether the call has been cancelled, and if the call was cancelled, we
+  // should notify the interceptors too/
+  auto* info =
+      context->set_client_rpc_info(method.name(), method.method_type(), this,
+                                   interceptor_creators_, interceptor_pos);
+  context->set_call(c_call, shared_from_this());
+
+  return internal::Call(c_call, this, cq, info);
+}
+
+internal::Call Channel::CreateCall(const internal::RpcMethod& method,
+                                   ClientContext* context,
+                                   CompletionQueue* cq) {
+  return CreateCallInternal(method, context, cq, 0);
+}
+
+void Channel::PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                               internal::Call* call) {
+  ops->FillOps(
+      call);  // Make a copy of call. It's fine since Call just has pointers
+}
+
+void* Channel::RegisterMethod(const char* method) {
+  return grpc_channel_register_call(
+      c_channel_, method, host_.empty() ? nullptr : host_.c_str(), nullptr);
+}
+
+grpc_connectivity_state Channel::GetState(bool try_to_connect) {
+  return grpc_channel_check_connectivity_state(c_channel_, try_to_connect);
+}
+
+namespace {
+
+class TagSaver final : public internal::CompletionQueueTag {
+ public:
+  explicit TagSaver(void* tag) : tag_(tag) {}
+  ~TagSaver() override {}
+  bool FinalizeResult(void** tag, bool* status) override {
+    *tag = tag_;
+    delete this;
+    return true;
+  }
+
+ private:
+  void* tag_;
+};
+
+}  // namespace
+
+void Channel::NotifyOnStateChangeImpl(grpc_connectivity_state last_observed,
+                                      gpr_timespec deadline,
+                                      CompletionQueue* cq, void* tag) {
+  TagSaver* tag_saver = new TagSaver(tag);
+  grpc_channel_watch_connectivity_state(c_channel_, last_observed, deadline,
+                                        cq->cq(), tag_saver);
+}
+
+bool Channel::WaitForStateChangeImpl(grpc_connectivity_state last_observed,
+                                     gpr_timespec deadline) {
+  CompletionQueue cq;
+  bool ok = false;
+  void* tag = nullptr;
+  NotifyOnStateChangeImpl(last_observed, deadline, &cq, nullptr);
+  cq.Next(&tag, &ok);
+  GPR_ASSERT(tag == nullptr);
+  return ok;
+}
+
+namespace {
+class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+ public:
+  ShutdownCallback() { functor_run = &ShutdownCallback::Run; }
+  // TakeCQ takes ownership of the cq into the shutdown callback
+  // so that the shutdown callback will be responsible for destroying it
+  void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
+
+  // The Run function will get invoked by the completion queue library
+  // when the shutdown is actually complete
+  static void Run(grpc_experimental_completion_queue_functor* cb, int) {
+    auto* callback = static_cast<ShutdownCallback*>(cb);
+    delete callback->cq_;
+    delete callback;
+  }
+
+ private:
+  CompletionQueue* cq_ = nullptr;
+};
+}  // namespace
+
+CompletionQueue* Channel::CallbackCQ() {
+  // TODO(vjpai): Consider using a single global CQ for the default CQ
+  // if there is no explicit per-channel CQ registered
+  std::lock_guard<std::mutex> l(mu_);
+  if (callback_cq_ == nullptr) {
+    auto* shutdown_callback = new ShutdownCallback;
+    callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{
+        GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
+        shutdown_callback});
+
+    // Transfer ownership of the new cq to its own shutdown callback
+    shutdown_callback->TakeCQ(callback_cq_);
+  }
+  return callback_cq_;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/client_context.cc b/third_party/grpc/src/cpp/client/client_context.cc
new file mode 100644
index 0000000..c9ea3e5
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/client_context.cc
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/client_context.h>
+
+#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 <grpcpp/impl/codegen/interceptor_common.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/support/time.h>
+
+namespace grpc {
+
+class DefaultGlobalClientCallbacks final
+    : public ClientContext::GlobalCallbacks {
+ public:
+  ~DefaultGlobalClientCallbacks() override {}
+  void DefaultConstructor(ClientContext* context) override {}
+  void Destructor(ClientContext* context) override {}
+};
+
+static internal::GrpcLibraryInitializer g_gli_initializer;
+static DefaultGlobalClientCallbacks* g_default_client_callbacks =
+    new DefaultGlobalClientCallbacks();
+static ClientContext::GlobalCallbacks* g_client_callbacks =
+    g_default_client_callbacks;
+
+ClientContext::ClientContext()
+    : initial_metadata_received_(false),
+      wait_for_ready_(false),
+      wait_for_ready_explicitly_set_(false),
+      idempotent_(false),
+      cacheable_(false),
+      call_(nullptr),
+      call_canceled_(false),
+      deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)),
+      census_context_(nullptr),
+      propagate_from_call_(nullptr),
+      initial_metadata_corked_(false) {
+  g_client_callbacks->DefaultConstructor(this);
+}
+
+ClientContext::~ClientContext() {
+  if (call_) {
+    grpc_call_unref(call_);
+  }
+  g_client_callbacks->Destructor(this);
+}
+
+std::unique_ptr<ClientContext> ClientContext::FromServerContext(
+    const ServerContext& context, PropagationOptions options) {
+  std::unique_ptr<ClientContext> ctx(new ClientContext);
+  ctx->propagate_from_call_ = context.call_;
+  ctx->propagation_options_ = options;
+  return ctx;
+}
+
+void ClientContext::AddMetadata(const grpc::string& meta_key,
+                                const grpc::string& meta_value) {
+  send_initial_metadata_.insert(std::make_pair(meta_key, meta_value));
+}
+
+void ClientContext::set_call(grpc_call* call,
+                             const std::shared_ptr<Channel>& channel) {
+  std::unique_lock<std::mutex> lock(mu_);
+  GPR_ASSERT(call_ == nullptr);
+  call_ = call;
+  channel_ = channel;
+  if (creds_ && !creds_->ApplyToCall(call_)) {
+    // TODO(yashykt): should interceptors also see this status?
+    SendCancelToInterceptors();
+    grpc_call_cancel_with_status(call, GRPC_STATUS_CANCELLED,
+                                 "Failed to set credentials to rpc.", nullptr);
+  }
+  if (call_canceled_) {
+    SendCancelToInterceptors();
+    grpc_call_cancel(call_, nullptr);
+  }
+}
+
+void ClientContext::set_compression_algorithm(
+    grpc_compression_algorithm algorithm) {
+  compression_algorithm_ = algorithm;
+  const char* algorithm_name = nullptr;
+  if (!grpc_compression_algorithm_name(algorithm, &algorithm_name)) {
+    gpr_log(GPR_ERROR, "Name for compression algorithm '%d' unknown.",
+            algorithm);
+    abort();
+  }
+  GPR_ASSERT(algorithm_name != nullptr);
+  AddMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name);
+}
+
+void ClientContext::TryCancel() {
+  std::unique_lock<std::mutex> lock(mu_);
+  if (call_) {
+    SendCancelToInterceptors();
+    grpc_call_cancel(call_, nullptr);
+  } else {
+    call_canceled_ = true;
+  }
+}
+
+void ClientContext::SendCancelToInterceptors() {
+  internal::CancelInterceptorBatchMethods cancel_methods;
+  for (size_t i = 0; i < rpc_info_.interceptors_.size(); i++) {
+    rpc_info_.RunInterceptor(&cancel_methods, i);
+  }
+}
+
+grpc::string ClientContext::peer() const {
+  grpc::string peer;
+  if (call_) {
+    char* c_peer = grpc_call_get_peer(call_);
+    peer = c_peer;
+    gpr_free(c_peer);
+  }
+  return peer;
+}
+
+void ClientContext::SetGlobalCallbacks(GlobalCallbacks* client_callbacks) {
+  GPR_ASSERT(g_client_callbacks == g_default_client_callbacks);
+  GPR_ASSERT(client_callbacks != nullptr);
+  GPR_ASSERT(client_callbacks != g_default_client_callbacks);
+  g_client_callbacks = client_callbacks;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/client_interceptor.cc b/third_party/grpc/src/cpp/client/client_interceptor.cc
new file mode 100644
index 0000000..3a5cac9
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/client_interceptor.cc
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/impl/codegen/client_interceptor.h>
+
+namespace grpc {
+
+namespace internal {
+experimental::ClientInterceptorFactoryInterface*
+    g_global_client_interceptor_factory = nullptr;
+}
+
+namespace experimental {
+void RegisterGlobalClientInterceptorFactory(
+    ClientInterceptorFactoryInterface* factory) {
+  internal::g_global_client_interceptor_factory = factory;
+}
+}  // namespace experimental
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/create_channel.cc b/third_party/grpc/src/cpp/client/create_channel.cc
new file mode 100644
index 0000000..457daa6
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/create_channel.cc
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <memory>
+
+#include <grpcpp/channel.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/channel_arguments.h>
+
+#include "src/cpp/client/create_channel_internal.h"
+
+namespace grpc {
+class ChannelArguments;
+
+std::shared_ptr<Channel> CreateChannel(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds) {
+  return CreateCustomChannel(target, creds, ChannelArguments());
+}
+
+std::shared_ptr<Channel> CreateCustomChannel(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds,
+    const ChannelArguments& args) {
+  GrpcLibraryCodegen init_lib;  // We need to call init in case of a bad creds.
+  return creds ? creds->CreateChannel(target, args)
+               : CreateChannelInternal(
+                     "",
+                     grpc_lame_client_channel_create(
+                         nullptr, GRPC_STATUS_INVALID_ARGUMENT,
+                         "Invalid credentials."),
+                     std::vector<std::unique_ptr<
+                         experimental::ClientInterceptorFactoryInterface>>());
+}
+
+namespace experimental {
+/// Create a new \em custom \a Channel pointing to \a target with \a
+/// interceptors being invoked per call.
+///
+/// \warning For advanced use and testing ONLY. Override default channel
+/// arguments only if necessary.
+///
+/// \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 (one on which all operations
+/// fail) is returned.
+/// \param args Options for channel creation.
+std::shared_ptr<Channel> CreateCustomChannelWithInterceptors(
+    const grpc::string& target,
+    const std::shared_ptr<ChannelCredentials>& creds,
+    const ChannelArguments& args,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators) {
+  return creds ? creds->CreateChannelWithInterceptors(
+                     target, args, std::move(interceptor_creators))
+               : CreateChannelInternal(
+                     "",
+                     grpc_lame_client_channel_create(
+                         nullptr, GRPC_STATUS_INVALID_ARGUMENT,
+                         "Invalid credentials."),
+                     std::vector<std::unique_ptr<
+                         experimental::ClientInterceptorFactoryInterface>>());
+}
+}  // namespace experimental
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/create_channel_internal.cc b/third_party/grpc/src/cpp/client/create_channel_internal.cc
new file mode 100644
index 0000000..a0efb97
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/create_channel_internal.cc
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <memory>
+
+#include <grpcpp/channel.h>
+
+struct grpc_channel;
+
+namespace grpc {
+
+std::shared_ptr<Channel> CreateChannelInternal(
+    const grpc::string& host, grpc_channel* c_channel,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators) {
+  return std::shared_ptr<Channel>(
+      new Channel(host, c_channel, std::move(interceptor_creators)));
+}
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/create_channel_internal.h b/third_party/grpc/src/cpp/client/create_channel_internal.h
new file mode 100644
index 0000000..a90c92c
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/create_channel_internal.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H
+#define GRPC_INTERNAL_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H
+
+#include <memory>
+
+#include <grpcpp/impl/codegen/client_interceptor.h>
+#include <grpcpp/support/config.h>
+
+struct grpc_channel;
+
+namespace grpc {
+class Channel;
+
+std::shared_ptr<Channel> CreateChannelInternal(
+    const grpc::string& host, grpc_channel* c_channel,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators);
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_CLIENT_CREATE_CHANNEL_INTERNAL_H
diff --git a/third_party/grpc/src/cpp/client/create_channel_posix.cc b/third_party/grpc/src/cpp/client/create_channel_posix.cc
new file mode 100644
index 0000000..3affc1e
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/create_channel_posix.cc
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/grpc.h>
+#include <grpc/grpc_posix.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/impl/grpc_library.h>
+
+#include "src/cpp/client/create_channel_internal.h"
+
+namespace grpc {
+
+#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
+
+std::shared_ptr<Channel> CreateInsecureChannelFromFd(const grpc::string& target,
+                                                     int fd) {
+  internal::GrpcLibrary init_lib;
+  init_lib.init();
+  return CreateChannelInternal(
+      "", grpc_insecure_channel_create_from_fd(target.c_str(), fd, nullptr),
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+}
+
+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),
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+}
+
+namespace experimental {
+
+std::shared_ptr<Channel> CreateCustomInsecureChannelWithInterceptorsFromFd(
+    const grpc::string& target, int fd, const ChannelArguments& args,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators) {
+  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),
+      std::move(interceptor_creators));
+}
+
+}  // namespace experimental
+
+#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/credentials_cc.cc b/third_party/grpc/src/cpp/client/credentials_cc.cc
new file mode 100644
index 0000000..2a0f06f
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/credentials_cc.cc
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/impl/grpc_library.h>
+#include <grpcpp/security/credentials.h>
+
+namespace grpc {
+
+static internal::GrpcLibraryInitializer g_gli_initializer;
+ChannelCredentials::ChannelCredentials() { g_gli_initializer.summon(); }
+
+ChannelCredentials::~ChannelCredentials() {}
+
+CallCredentials::CallCredentials() { g_gli_initializer.summon(); }
+
+CallCredentials::~CallCredentials() {}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/cronet_credentials.cc b/third_party/grpc/src/cpp/client/cronet_credentials.cc
new file mode 100644
index 0000000..b280176
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/cronet_credentials.cc
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/security/credentials.h>
+
+#include <grpc/grpc_cronet.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/support/channel_arguments.h>
+#include "src/cpp/client/create_channel_internal.h"
+
+namespace grpc {
+
+class CronetChannelCredentialsImpl final : public ChannelCredentials {
+ public:
+  CronetChannelCredentialsImpl(void* engine) : engine_(engine) {}
+
+  std::shared_ptr<grpc::Channel> CreateChannel(
+      const string& target, const grpc::ChannelArguments& args) override {
+    return CreateChannelWithInterceptors(
+        target, args,
+        std::vector<std::unique_ptr<
+            experimental::ClientInterceptorFactoryInterface>>());
+  }
+
+  SecureChannelCredentials* AsSecureCredentials() override { return nullptr; }
+
+ private:
+  std::shared_ptr<grpc::Channel> CreateChannelWithInterceptors(
+      const string& target, const grpc::ChannelArguments& args,
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators) override {
+    grpc_channel_args channel_args;
+    args.SetChannelArgs(&channel_args);
+    return CreateChannelInternal(
+        "",
+        grpc_cronet_secure_channel_create(engine_, target.c_str(),
+                                          &channel_args, nullptr),
+        std::move(interceptor_creators));
+  }
+  void* engine_;
+};
+
+std::shared_ptr<ChannelCredentials> CronetChannelCredentials(void* engine) {
+  return std::shared_ptr<ChannelCredentials>(
+      new CronetChannelCredentialsImpl(engine));
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/generic_stub.cc b/third_party/grpc/src/cpp/client/generic_stub.cc
new file mode 100644
index 0000000..f61c1b5
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/generic_stub.cc
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <functional>
+
+#include <grpcpp/generic/generic_stub.h>
+#include <grpcpp/impl/rpc_method.h>
+#include <grpcpp/support/client_callback.h>
+
+namespace grpc {
+
+namespace {
+std::unique_ptr<GenericClientAsyncReaderWriter> CallInternal(
+    ChannelInterface* channel, ClientContext* context,
+    const grpc::string& method, CompletionQueue* cq, bool start, void* tag) {
+  return std::unique_ptr<GenericClientAsyncReaderWriter>(
+      internal::ClientAsyncReaderWriterFactory<ByteBuffer, ByteBuffer>::Create(
+          channel, cq,
+          internal::RpcMethod(method.c_str(),
+                              internal::RpcMethod::BIDI_STREAMING),
+          context, start, tag));
+}
+
+}  // namespace
+
+// begin a call to a named method
+std::unique_ptr<GenericClientAsyncReaderWriter> GenericStub::Call(
+    ClientContext* context, const grpc::string& method, CompletionQueue* cq,
+    void* tag) {
+  return CallInternal(channel_.get(), context, method, cq, true, tag);
+}
+
+// setup a call to a named method
+std::unique_ptr<GenericClientAsyncReaderWriter> GenericStub::PrepareCall(
+    ClientContext* context, const grpc::string& method, CompletionQueue* cq) {
+  return CallInternal(channel_.get(), context, method, cq, false, nullptr);
+}
+
+// setup a unary call to a named method
+std::unique_ptr<GenericClientAsyncResponseReader> GenericStub::PrepareUnaryCall(
+    ClientContext* context, const grpc::string& method,
+    const ByteBuffer& request, CompletionQueue* cq) {
+  return std::unique_ptr<GenericClientAsyncResponseReader>(
+      internal::ClientAsyncResponseReaderFactory<ByteBuffer>::Create(
+          channel_.get(), cq,
+          internal::RpcMethod(method.c_str(), internal::RpcMethod::NORMAL_RPC),
+          context, request, false));
+}
+
+void GenericStub::experimental_type::UnaryCall(
+    ClientContext* context, const grpc::string& method,
+    const ByteBuffer* request, ByteBuffer* response,
+    std::function<void(Status)> on_completion) {
+  internal::CallbackUnaryCall(
+      stub_->channel_.get(),
+      internal::RpcMethod(method.c_str(), internal::RpcMethod::NORMAL_RPC),
+      context, request, response, std::move(on_completion));
+}
+
+void GenericStub::experimental_type::PrepareBidiStreamingCall(
+    ClientContext* context, const grpc::string& method,
+    experimental::ClientBidiReactor<ByteBuffer, ByteBuffer>* reactor) {
+  internal::ClientCallbackReaderWriterFactory<ByteBuffer, ByteBuffer>::Create(
+      stub_->channel_.get(),
+      internal::RpcMethod(method.c_str(), internal::RpcMethod::BIDI_STREAMING),
+      context, reactor);
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/insecure_credentials.cc b/third_party/grpc/src/cpp/client/insecure_credentials.cc
new file mode 100644
index 0000000..241ce91
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/insecure_credentials.cc
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/security/credentials.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/support/channel_arguments.h>
+#include <grpcpp/support/config.h>
+#include "src/cpp/client/create_channel_internal.h"
+
+namespace grpc {
+
+namespace {
+class InsecureChannelCredentialsImpl final : public ChannelCredentials {
+ public:
+  std::shared_ptr<grpc::Channel> CreateChannel(
+      const string& target, const grpc::ChannelArguments& args) override {
+    return CreateChannelWithInterceptors(
+        target, args,
+        std::vector<std::unique_ptr<
+            experimental::ClientInterceptorFactoryInterface>>());
+  }
+
+  std::shared_ptr<grpc::Channel> CreateChannelWithInterceptors(
+      const string& target, const grpc::ChannelArguments& args,
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators) override {
+    grpc_channel_args channel_args;
+    args.SetChannelArgs(&channel_args);
+    return CreateChannelInternal(
+        "",
+        grpc_insecure_channel_create(target.c_str(), &channel_args, nullptr),
+        std::move(interceptor_creators));
+  }
+
+  SecureChannelCredentials* AsSecureCredentials() override { return nullptr; }
+};
+}  // namespace
+
+std::shared_ptr<ChannelCredentials> InsecureChannelCredentials() {
+  return std::shared_ptr<ChannelCredentials>(
+      new InsecureChannelCredentialsImpl());
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/secure_credentials.cc b/third_party/grpc/src/cpp/client/secure_credentials.cc
new file mode 100644
index 0000000..4d0ed35
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/secure_credentials.cc
@@ -0,0 +1,324 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 "src/cpp/client/secure_credentials.h"
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/channel_arguments.h>
+#include "src/cpp/client/create_channel_internal.h"
+#include "src/cpp/common/secure_auth_context.h"
+
+namespace grpc {
+
+static internal::GrpcLibraryInitializer g_gli_initializer;
+SecureChannelCredentials::SecureChannelCredentials(
+    grpc_channel_credentials* c_creds)
+    : c_creds_(c_creds) {
+  g_gli_initializer.summon();
+}
+
+std::shared_ptr<grpc::Channel> SecureChannelCredentials::CreateChannel(
+    const string& target, const grpc::ChannelArguments& args) {
+  return CreateChannelWithInterceptors(
+      target, args,
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+}
+
+std::shared_ptr<grpc::Channel>
+SecureChannelCredentials::CreateChannelWithInterceptors(
+    const string& target, const grpc::ChannelArguments& args,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators) {
+  grpc_channel_args channel_args;
+  args.SetChannelArgs(&channel_args);
+  return CreateChannelInternal(
+      args.GetSslTargetNameOverride(),
+      grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args,
+                                 nullptr),
+      std::move(interceptor_creators));
+}
+
+SecureCallCredentials::SecureCallCredentials(grpc_call_credentials* c_creds)
+    : c_creds_(c_creds) {
+  g_gli_initializer.summon();
+}
+
+bool SecureCallCredentials::ApplyToCall(grpc_call* call) {
+  return grpc_call_set_credentials(call, c_creds_) == GRPC_CALL_OK;
+}
+
+namespace {
+std::shared_ptr<ChannelCredentials> WrapChannelCredentials(
+    grpc_channel_credentials* creds) {
+  return creds == nullptr ? nullptr
+                          : std::shared_ptr<ChannelCredentials>(
+                                new SecureChannelCredentials(creds));
+}
+
+std::shared_ptr<CallCredentials> WrapCallCredentials(
+    grpc_call_credentials* creds) {
+  return creds == nullptr ? nullptr
+                          : std::shared_ptr<CallCredentials>(
+                                new SecureCallCredentials(creds));
+}
+}  // namespace
+
+std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials() {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  return WrapChannelCredentials(grpc_google_default_credentials_create());
+}
+
+// Builds SSL Credentials given SSL specific options
+std::shared_ptr<ChannelCredentials> SslCredentials(
+    const SslCredentialsOptions& options) {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
+      options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
+
+  grpc_channel_credentials* c_creds = grpc_ssl_credentials_create(
+      options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
+      options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair, nullptr,
+      nullptr);
+  return WrapChannelCredentials(c_creds);
+}
+
+namespace experimental {
+
+// Builds ALTS Credentials given ALTS specific options
+std::shared_ptr<ChannelCredentials> AltsCredentials(
+    const AltsCredentialsOptions& options) {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  grpc_alts_credentials_options* c_options =
+      grpc_alts_credentials_client_options_create();
+  for (auto service_account = options.target_service_accounts.begin();
+       service_account != options.target_service_accounts.end();
+       service_account++) {
+    grpc_alts_credentials_client_options_add_target_service_account(
+        c_options, service_account->c_str());
+  }
+  grpc_channel_credentials* c_creds = grpc_alts_credentials_create(c_options);
+  grpc_alts_credentials_options_destroy(c_options);
+  return WrapChannelCredentials(c_creds);
+}
+
+// Builds Local Credentials
+std::shared_ptr<ChannelCredentials> LocalCredentials(
+    grpc_local_connect_type type) {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  return WrapChannelCredentials(grpc_local_credentials_create(type));
+}
+
+}  // namespace experimental
+
+// Builds credentials for use when running in GCE
+std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials() {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  return WrapCallCredentials(
+      grpc_google_compute_engine_credentials_create(nullptr));
+}
+
+// Builds JWT credentials.
+std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
+    const grpc::string& json_key, long token_lifetime_seconds) {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  if (token_lifetime_seconds <= 0) {
+    gpr_log(GPR_ERROR,
+            "Trying to create JWTCredentials with non-positive lifetime");
+    return WrapCallCredentials(nullptr);
+  }
+  gpr_timespec lifetime =
+      gpr_time_from_seconds(token_lifetime_seconds, GPR_TIMESPAN);
+  return WrapCallCredentials(grpc_service_account_jwt_access_credentials_create(
+      json_key.c_str(), lifetime, nullptr));
+}
+
+// Builds refresh token credentials.
+std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
+    const grpc::string& json_refresh_token) {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  return WrapCallCredentials(grpc_google_refresh_token_credentials_create(
+      json_refresh_token.c_str(), nullptr));
+}
+
+// Builds access token credentials.
+std::shared_ptr<CallCredentials> AccessTokenCredentials(
+    const grpc::string& access_token) {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  return WrapCallCredentials(
+      grpc_access_token_credentials_create(access_token.c_str(), nullptr));
+}
+
+// Builds IAM credentials.
+std::shared_ptr<CallCredentials> GoogleIAMCredentials(
+    const grpc::string& authorization_token,
+    const grpc::string& authority_selector) {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  return WrapCallCredentials(grpc_google_iam_credentials_create(
+      authorization_token.c_str(), authority_selector.c_str(), nullptr));
+}
+
+// Combines one channel credentials and one call credentials into a channel
+// composite credentials.
+std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
+    const std::shared_ptr<ChannelCredentials>& channel_creds,
+    const std::shared_ptr<CallCredentials>& call_creds) {
+  // Note that we are not saving shared_ptrs to the two credentials passed in
+  // here. This is OK because the underlying C objects (i.e., channel_creds and
+  // call_creds) into grpc_composite_credentials_create will see their refcounts
+  // incremented.
+  SecureChannelCredentials* s_channel_creds =
+      channel_creds->AsSecureCredentials();
+  SecureCallCredentials* s_call_creds = call_creds->AsSecureCredentials();
+  if (s_channel_creds && s_call_creds) {
+    return WrapChannelCredentials(grpc_composite_channel_credentials_create(
+        s_channel_creds->GetRawCreds(), s_call_creds->GetRawCreds(), nullptr));
+  }
+  return nullptr;
+}
+
+std::shared_ptr<CallCredentials> CompositeCallCredentials(
+    const std::shared_ptr<CallCredentials>& creds1,
+    const std::shared_ptr<CallCredentials>& creds2) {
+  SecureCallCredentials* s_creds1 = creds1->AsSecureCredentials();
+  SecureCallCredentials* s_creds2 = creds2->AsSecureCredentials();
+  if (s_creds1 != nullptr && s_creds2 != nullptr) {
+    return WrapCallCredentials(grpc_composite_call_credentials_create(
+        s_creds1->GetRawCreds(), s_creds2->GetRawCreds(), nullptr));
+  }
+  return nullptr;
+}
+
+void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) {
+  if (wrapper == nullptr) return;
+  MetadataCredentialsPluginWrapper* w =
+      static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
+  delete w;
+}
+
+int MetadataCredentialsPluginWrapper::GetMetadata(
+    void* wrapper, grpc_auth_metadata_context context,
+    grpc_credentials_plugin_metadata_cb cb, void* user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t* num_creds_md, grpc_status_code* status,
+    const char** error_details) {
+  GPR_ASSERT(wrapper);
+  MetadataCredentialsPluginWrapper* w =
+      static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
+  if (!w->plugin_) {
+    *num_creds_md = 0;
+    *status = GRPC_STATUS_OK;
+    *error_details = nullptr;
+    return true;
+  }
+  if (w->plugin_->IsBlocking()) {
+    // Asynchronous return.
+    w->thread_pool_->Add([w, context, cb, user_data] {
+      w->MetadataCredentialsPluginWrapper::InvokePlugin(
+          context, cb, user_data, nullptr, nullptr, nullptr, nullptr);
+    });
+    return 0;
+  } else {
+    // Synchronous return.
+    w->InvokePlugin(context, cb, user_data, creds_md, num_creds_md, status,
+                    error_details);
+    return 1;
+  }
+}
+
+namespace {
+
+void UnrefMetadata(const std::vector<grpc_metadata>& md) {
+  for (auto it = md.begin(); it != md.end(); ++it) {
+    grpc_slice_unref(it->key);
+    grpc_slice_unref(it->value);
+  }
+}
+
+}  // namespace
+
+void MetadataCredentialsPluginWrapper::InvokePlugin(
+    grpc_auth_metadata_context context, grpc_credentials_plugin_metadata_cb cb,
+    void* user_data, grpc_metadata creds_md[4], size_t* num_creds_md,
+    grpc_status_code* status_code, const char** error_details) {
+  std::multimap<grpc::string, grpc::string> metadata;
+
+  // const_cast is safe since the SecureAuthContext only inc/dec the refcount
+  // and the object is passed as a const ref to plugin_->GetMetadata.
+  SecureAuthContext cpp_channel_auth_context(
+      const_cast<grpc_auth_context*>(context.channel_auth_context));
+
+  Status status = plugin_->GetMetadata(context.service_url, context.method_name,
+                                       cpp_channel_auth_context, &metadata);
+  std::vector<grpc_metadata> md;
+  for (auto it = metadata.begin(); it != metadata.end(); ++it) {
+    grpc_metadata md_entry;
+    md_entry.key = SliceFromCopiedString(it->first);
+    md_entry.value = SliceFromCopiedString(it->second);
+    md_entry.flags = 0;
+    md.push_back(md_entry);
+  }
+  if (creds_md != nullptr) {
+    // Synchronous return.
+    if (md.size() > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) {
+      *num_creds_md = 0;
+      *status_code = GRPC_STATUS_INTERNAL;
+      *error_details = gpr_strdup(
+          "blocking plugin credentials returned too many metadata keys");
+      UnrefMetadata(md);
+    } else {
+      for (const auto& elem : md) {
+        creds_md[*num_creds_md].key = elem.key;
+        creds_md[*num_creds_md].value = elem.value;
+        creds_md[*num_creds_md].flags = elem.flags;
+        ++(*num_creds_md);
+      }
+      *status_code = static_cast<grpc_status_code>(status.error_code());
+      *error_details =
+          status.ok() ? nullptr : gpr_strdup(status.error_message().c_str());
+    }
+  } else {
+    // Asynchronous return.
+    cb(user_data, md.empty() ? nullptr : &md[0], md.size(),
+       static_cast<grpc_status_code>(status.error_code()),
+       status.error_message().c_str());
+    UnrefMetadata(md);
+  }
+}
+
+MetadataCredentialsPluginWrapper::MetadataCredentialsPluginWrapper(
+    std::unique_ptr<MetadataCredentialsPlugin> plugin)
+    : thread_pool_(CreateDefaultThreadPool()), plugin_(std::move(plugin)) {}
+
+std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
+    std::unique_ptr<MetadataCredentialsPlugin> plugin) {
+  GrpcLibraryCodegen init;  // To call grpc_init().
+  const char* type = plugin->GetType();
+  MetadataCredentialsPluginWrapper* wrapper =
+      new MetadataCredentialsPluginWrapper(std::move(plugin));
+  grpc_metadata_credentials_plugin c_plugin = {
+      MetadataCredentialsPluginWrapper::GetMetadata,
+      MetadataCredentialsPluginWrapper::Destroy, wrapper, type};
+  return WrapCallCredentials(
+      grpc_metadata_credentials_create_from_plugin(c_plugin, nullptr));
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/client/secure_credentials.h b/third_party/grpc/src/cpp/client/secure_credentials.h
new file mode 100644
index 0000000..4918bd5
--- /dev/null
+++ b/third_party/grpc/src/cpp/client/secure_credentials.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H
+#define GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H
+
+#include <grpc/grpc_security.h>
+
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/support/config.h>
+
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/cpp/server/thread_pool_interface.h"
+
+namespace grpc {
+
+class SecureChannelCredentials final : public ChannelCredentials {
+ public:
+  explicit SecureChannelCredentials(grpc_channel_credentials* c_creds);
+  ~SecureChannelCredentials() {
+    if (c_creds_ != nullptr) c_creds_->Unref();
+  }
+  grpc_channel_credentials* GetRawCreds() { return c_creds_; }
+
+  std::shared_ptr<grpc::Channel> CreateChannel(
+      const string& target, const grpc::ChannelArguments& args) override;
+
+  SecureChannelCredentials* AsSecureCredentials() override { return this; }
+
+ private:
+  std::shared_ptr<grpc::Channel> CreateChannelWithInterceptors(
+      const string& target, const grpc::ChannelArguments& args,
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+          interceptor_creators) override;
+  grpc_channel_credentials* const c_creds_;
+};
+
+class SecureCallCredentials final : public CallCredentials {
+ public:
+  explicit SecureCallCredentials(grpc_call_credentials* c_creds);
+  ~SecureCallCredentials() {
+    if (c_creds_ != nullptr) c_creds_->Unref();
+  }
+  grpc_call_credentials* GetRawCreds() { return c_creds_; }
+
+  bool ApplyToCall(grpc_call* call) override;
+  SecureCallCredentials* AsSecureCredentials() override { return this; }
+
+ private:
+  grpc_call_credentials* const c_creds_;
+};
+
+class MetadataCredentialsPluginWrapper final : private GrpcLibraryCodegen {
+ public:
+  static void Destroy(void* wrapper);
+  static int GetMetadata(
+      void* wrapper, grpc_auth_metadata_context context,
+      grpc_credentials_plugin_metadata_cb cb, void* user_data,
+      grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+      size_t* num_creds_md, grpc_status_code* status,
+      const char** error_details);
+
+  explicit MetadataCredentialsPluginWrapper(
+      std::unique_ptr<MetadataCredentialsPlugin> plugin);
+
+ private:
+  void InvokePlugin(
+      grpc_auth_metadata_context context,
+      grpc_credentials_plugin_metadata_cb cb, void* user_data,
+      grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+      size_t* num_creds_md, grpc_status_code* status_code,
+      const char** error_details);
+  std::unique_ptr<ThreadPoolInterface> thread_pool_;
+  std::unique_ptr<MetadataCredentialsPlugin> plugin_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_CLIENT_SECURE_CREDENTIALS_H
diff --git a/third_party/grpc/src/cpp/codegen/codegen_init.cc b/third_party/grpc/src/cpp/codegen/codegen_init.cc
new file mode 100644
index 0000000..684d721
--- /dev/null
+++ b/third_party/grpc/src/cpp/codegen/codegen_init.cc
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/impl/codegen/core_codegen_interface.h>
+#include <grpcpp/impl/codegen/grpc_library.h>
+
+/// Null-initializes the global gRPC variables for the codegen library. These
+/// stay null in the absence of of grpc++ library. In this case, no gRPC
+/// features such as the ability to perform calls will be available. Trying to
+/// perform them would result in a segmentation fault when trying to deference
+/// the following nulled globals. These should be associated with actual
+/// as part of the instantiation of a \a grpc::GrpcLibraryInitializer variable.
+
+grpc::CoreCodegenInterface* grpc::g_core_codegen_interface;
+grpc::GrpcLibraryInterface* grpc::g_glip;
diff --git a/third_party/grpc/src/cpp/common/alarm.cc b/third_party/grpc/src/cpp/common/alarm.cc
new file mode 100644
index 0000000..148f0b9b
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/alarm.cc
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/alarm.h>
+
+#include <memory>
+
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/time.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/surface/completion_queue.h"
+
+#include <grpc/support/log.h>
+#include "src/core/lib/debug/trace.h"
+
+namespace grpc_impl {
+
+namespace internal {
+class AlarmImpl : public ::grpc::internal::CompletionQueueTag {
+ public:
+  AlarmImpl() : cq_(nullptr), tag_(nullptr) {
+    gpr_ref_init(&refs_, 1);
+    grpc_timer_init_unset(&timer_);
+  }
+  ~AlarmImpl() {
+    grpc_core::ExecCtx exec_ctx;
+    if (cq_ != nullptr) {
+      GRPC_CQ_INTERNAL_UNREF(cq_, "alarm");
+    }
+  }
+  bool FinalizeResult(void** tag, bool* status) override {
+    *tag = tag_;
+    Unref();
+    return true;
+  }
+  void Set(::grpc::CompletionQueue* cq, gpr_timespec deadline, void* tag) {
+    grpc_core::ExecCtx exec_ctx;
+    GRPC_CQ_INTERNAL_REF(cq->cq(), "alarm");
+    cq_ = cq->cq();
+    tag_ = tag;
+    GPR_ASSERT(grpc_cq_begin_op(cq_, this));
+    GRPC_CLOSURE_INIT(&on_alarm_,
+                      [](void* arg, grpc_error* error) {
+                        // queue the op on the completion queue
+                        AlarmImpl* alarm = static_cast<AlarmImpl*>(arg);
+                        alarm->Ref();
+                        grpc_cq_end_op(
+                            alarm->cq_, alarm, error,
+                            [](void* arg, grpc_cq_completion* completion) {},
+                            arg, &alarm->completion_);
+                      },
+                      this, grpc_schedule_on_exec_ctx);
+    grpc_timer_init(&timer_, grpc_timespec_to_millis_round_up(deadline),
+                    &on_alarm_);
+  }
+  void Set(gpr_timespec deadline, std::function<void(bool)> f) {
+    grpc_core::ExecCtx exec_ctx;
+    // Don't use any CQ at all. Instead just use the timer to fire the function
+    callback_ = std::move(f);
+    Ref();
+    GRPC_CLOSURE_INIT(&on_alarm_,
+                      [](void* arg, grpc_error* error) {
+                        AlarmImpl* alarm = static_cast<AlarmImpl*>(arg);
+                        alarm->callback_(error == GRPC_ERROR_NONE);
+                        alarm->Unref();
+                      },
+                      this, grpc_schedule_on_exec_ctx);
+    grpc_timer_init(&timer_, grpc_timespec_to_millis_round_up(deadline),
+                    &on_alarm_);
+  }
+  void Cancel() {
+    grpc_core::ExecCtx exec_ctx;
+    grpc_timer_cancel(&timer_);
+  }
+  void Destroy() {
+    Cancel();
+    Unref();
+  }
+
+ private:
+  void Ref() { gpr_ref(&refs_); }
+  void Unref() {
+    if (gpr_unref(&refs_)) {
+      delete this;
+    }
+  }
+
+  grpc_timer timer_;
+  gpr_refcount refs_;
+  grpc_closure on_alarm_;
+  grpc_cq_completion completion_;
+  // completion queue where events about this alarm will be posted
+  grpc_completion_queue* cq_;
+  void* tag_;
+  std::function<void(bool)> callback_;
+};
+}  // namespace internal
+
+static ::grpc::internal::GrpcLibraryInitializer g_gli_initializer;
+
+Alarm::Alarm() : alarm_(new internal::AlarmImpl()) {
+  g_gli_initializer.summon();
+}
+
+void Alarm::SetInternal(::grpc::CompletionQueue* cq, gpr_timespec deadline,
+                        void* tag) {
+  // Note that we know that alarm_ is actually an internal::AlarmImpl
+  // but we declared it as the base pointer to avoid a forward declaration
+  // or exposing core data structures in the C++ public headers.
+  // Thus it is safe to use a static_cast to the subclass here, and the
+  // C++ style guide allows us to do so in this case
+  static_cast<internal::AlarmImpl*>(alarm_)->Set(cq, deadline, tag);
+}
+
+void Alarm::SetInternal(gpr_timespec deadline, std::function<void(bool)> f) {
+  // Note that we know that alarm_ is actually an internal::AlarmImpl
+  // but we declared it as the base pointer to avoid a forward declaration
+  // or exposing core data structures in the C++ public headers.
+  // Thus it is safe to use a static_cast to the subclass here, and the
+  // C++ style guide allows us to do so in this case
+  static_cast<internal::AlarmImpl*>(alarm_)->Set(deadline, std::move(f));
+}
+
+Alarm::~Alarm() {
+  if (alarm_ != nullptr) {
+    static_cast<internal::AlarmImpl*>(alarm_)->Destroy();
+  }
+}
+
+void Alarm::Cancel() { static_cast<internal::AlarmImpl*>(alarm_)->Cancel(); }
+}  // namespace grpc_impl
diff --git a/third_party/grpc/src/cpp/common/auth_property_iterator.cc b/third_party/grpc/src/cpp/common/auth_property_iterator.cc
new file mode 100644
index 0000000..fbb18e9
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/auth_property_iterator.cc
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/security/auth_context.h>
+
+#include <grpc/grpc_security.h>
+
+namespace grpc {
+
+AuthPropertyIterator::AuthPropertyIterator()
+    : property_(nullptr), ctx_(nullptr), index_(0), name_(nullptr) {}
+
+AuthPropertyIterator::AuthPropertyIterator(
+    const grpc_auth_property* property, const grpc_auth_property_iterator* iter)
+    : property_(property),
+      ctx_(iter->ctx),
+      index_(iter->index),
+      name_(iter->name) {}
+
+AuthPropertyIterator::~AuthPropertyIterator() {}
+
+AuthPropertyIterator& AuthPropertyIterator::operator++() {
+  grpc_auth_property_iterator iter = {ctx_, index_, name_};
+  property_ = grpc_auth_property_iterator_next(&iter);
+  ctx_ = iter.ctx;
+  index_ = iter.index;
+  name_ = iter.name;
+  return *this;
+}
+
+AuthPropertyIterator AuthPropertyIterator::operator++(int) {
+  AuthPropertyIterator tmp(*this);
+  operator++();
+  return tmp;
+}
+
+bool AuthPropertyIterator::operator==(const AuthPropertyIterator& rhs) const {
+  if (property_ == nullptr || rhs.property_ == nullptr) {
+    return property_ == rhs.property_;
+  } else {
+    return index_ == rhs.index_;
+  }
+}
+
+bool AuthPropertyIterator::operator!=(const AuthPropertyIterator& rhs) const {
+  return !operator==(rhs);
+}
+
+const AuthProperty AuthPropertyIterator::operator*() {
+  return std::pair<grpc::string_ref, grpc::string_ref>(
+      property_->name,
+      grpc::string_ref(property_->value, property_->value_length));
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/channel_arguments.cc b/third_party/grpc/src/cpp/common/channel_arguments.cc
new file mode 100644
index 0000000..214d72f
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/channel_arguments.cc
@@ -0,0 +1,218 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/support/channel_arguments.h>
+
+#include <sstream>
+
+#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/support/log.h>
+#include <grpcpp/grpcpp.h>
+#include <grpcpp/resource_quota.h>
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/socket_mutator.h"
+
+namespace grpc {
+
+ChannelArguments::ChannelArguments() {
+  // This will be ignored if used on the server side.
+  SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, "grpc-c++/" + Version());
+}
+
+ChannelArguments::ChannelArguments(const ChannelArguments& other)
+    : strings_(other.strings_) {
+  args_.reserve(other.args_.size());
+  auto list_it_dst = strings_.begin();
+  auto list_it_src = other.strings_.begin();
+  for (auto a = other.args_.begin(); a != other.args_.end(); ++a) {
+    grpc_arg ap;
+    ap.type = a->type;
+    GPR_ASSERT(list_it_src->c_str() == a->key);
+    ap.key = const_cast<char*>(list_it_dst->c_str());
+    ++list_it_src;
+    ++list_it_dst;
+    switch (a->type) {
+      case GRPC_ARG_INTEGER:
+        ap.value.integer = a->value.integer;
+        break;
+      case GRPC_ARG_STRING:
+        GPR_ASSERT(list_it_src->c_str() == a->value.string);
+        ap.value.string = const_cast<char*>(list_it_dst->c_str());
+        ++list_it_src;
+        ++list_it_dst;
+        break;
+      case GRPC_ARG_POINTER:
+        ap.value.pointer = a->value.pointer;
+        ap.value.pointer.p = a->value.pointer.vtable->copy(ap.value.pointer.p);
+        break;
+    }
+    args_.push_back(ap);
+  }
+}
+
+ChannelArguments::~ChannelArguments() {
+  grpc_core::ExecCtx exec_ctx;
+  for (auto it = args_.begin(); it != args_.end(); ++it) {
+    if (it->type == GRPC_ARG_POINTER) {
+      it->value.pointer.vtable->destroy(it->value.pointer.p);
+    }
+  }
+}
+
+void ChannelArguments::Swap(ChannelArguments& other) {
+  args_.swap(other.args_);
+  strings_.swap(other.strings_);
+}
+
+void ChannelArguments::SetCompressionAlgorithm(
+    grpc_compression_algorithm algorithm) {
+  SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, algorithm);
+}
+
+void ChannelArguments::SetGrpclbFallbackTimeout(int fallback_timeout) {
+  SetInt(GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS, fallback_timeout);
+}
+
+void ChannelArguments::SetSocketMutator(grpc_socket_mutator* mutator) {
+  if (!mutator) {
+    return;
+  }
+  grpc_arg mutator_arg = grpc_socket_mutator_to_arg(mutator);
+  bool replaced = false;
+  grpc_core::ExecCtx exec_ctx;
+  for (auto it = args_.begin(); it != args_.end(); ++it) {
+    if (it->type == mutator_arg.type &&
+        grpc::string(it->key) == grpc::string(mutator_arg.key)) {
+      GPR_ASSERT(!replaced);
+      it->value.pointer.vtable->destroy(it->value.pointer.p);
+      it->value.pointer = mutator_arg.value.pointer;
+      replaced = true;
+    }
+  }
+
+  if (!replaced) {
+    strings_.push_back(grpc::string(mutator_arg.key));
+    args_.push_back(mutator_arg);
+    args_.back().key = const_cast<char*>(strings_.back().c_str());
+  }
+}
+
+// Note: a second call to this will add in front the result of the first call.
+// An example is calling this on a copy of ChannelArguments which already has a
+// prefix. The user can build up a prefix string by calling this multiple times,
+// each with more significant identifier.
+void ChannelArguments::SetUserAgentPrefix(
+    const grpc::string& user_agent_prefix) {
+  if (user_agent_prefix.empty()) {
+    return;
+  }
+  bool replaced = false;
+  auto strings_it = strings_.begin();
+  for (auto it = args_.begin(); it != args_.end(); ++it) {
+    const grpc_arg& arg = *it;
+    ++strings_it;
+    if (arg.type == GRPC_ARG_STRING) {
+      if (grpc::string(arg.key) == GRPC_ARG_PRIMARY_USER_AGENT_STRING) {
+        GPR_ASSERT(arg.value.string == strings_it->c_str());
+        *(strings_it) = user_agent_prefix + " " + arg.value.string;
+        it->value.string = const_cast<char*>(strings_it->c_str());
+        replaced = true;
+        break;
+      }
+      ++strings_it;
+    }
+  }
+  if (!replaced) {
+    SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, user_agent_prefix);
+  }
+}
+
+void ChannelArguments::SetResourceQuota(
+    const grpc::ResourceQuota& resource_quota) {
+  SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA,
+                       resource_quota.c_resource_quota(),
+                       grpc_resource_quota_arg_vtable());
+}
+
+void ChannelArguments::SetMaxReceiveMessageSize(int size) {
+  SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, size);
+}
+
+void ChannelArguments::SetMaxSendMessageSize(int size) {
+  SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, size);
+}
+
+void ChannelArguments::SetLoadBalancingPolicyName(
+    const grpc::string& lb_policy_name) {
+  SetString(GRPC_ARG_LB_POLICY_NAME, lb_policy_name);
+}
+
+void ChannelArguments::SetServiceConfigJSON(
+    const grpc::string& service_config_json) {
+  SetString(GRPC_ARG_SERVICE_CONFIG, service_config_json);
+}
+
+void ChannelArguments::SetInt(const grpc::string& key, int value) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_INTEGER;
+  strings_.push_back(key);
+  arg.key = const_cast<char*>(strings_.back().c_str());
+  arg.value.integer = value;
+
+  args_.push_back(arg);
+}
+
+void ChannelArguments::SetPointer(const grpc::string& key, void* value) {
+  static const grpc_arg_pointer_vtable vtable = {
+      &PointerVtableMembers::Copy, &PointerVtableMembers::Destroy,
+      &PointerVtableMembers::Compare};
+  SetPointerWithVtable(key, value, &vtable);
+}
+
+void ChannelArguments::SetPointerWithVtable(
+    const grpc::string& key, void* value,
+    const grpc_arg_pointer_vtable* vtable) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_POINTER;
+  strings_.push_back(key);
+  arg.key = const_cast<char*>(strings_.back().c_str());
+  arg.value.pointer.p = vtable->copy(value);
+  arg.value.pointer.vtable = vtable;
+  args_.push_back(arg);
+}
+
+void ChannelArguments::SetString(const grpc::string& key,
+                                 const grpc::string& value) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  strings_.push_back(key);
+  arg.key = const_cast<char*>(strings_.back().c_str());
+  strings_.push_back(value);
+  arg.value.string = const_cast<char*>(strings_.back().c_str());
+
+  args_.push_back(arg);
+}
+
+void ChannelArguments::SetChannelArgs(grpc_channel_args* channel_args) const {
+  channel_args->num_args = args_.size();
+  if (channel_args->num_args > 0) {
+    channel_args->args = const_cast<grpc_arg*>(&args_[0]);
+  }
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/channel_filter.cc b/third_party/grpc/src/cpp/common/channel_filter.cc
new file mode 100644
index 0000000..422e7bb
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/channel_filter.cc
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <string.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/cpp/common/channel_filter.h"
+
+#include <grpcpp/impl/codegen/slice.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_slices(SliceFromCopiedString(key),
+                                        SliceFromCopiedString(value));
+  GRPC_LOG_IF_ERROR("MetadataBatch::AddMetadata",
+                    grpc_metadata_batch_link_head(batch_, storage));
+  return storage;
+}
+
+// ChannelData
+
+void ChannelData::StartTransportOp(grpc_channel_element* elem,
+                                   TransportOp* op) {
+  grpc_channel_next_op(elem, op->op());
+}
+
+void ChannelData::GetInfo(grpc_channel_element* elem,
+                          const grpc_channel_info* channel_info) {
+  grpc_channel_next_get_info(elem, channel_info);
+}
+
+// CallData
+
+void CallData::StartTransportStreamOpBatch(grpc_call_element* elem,
+                                           TransportStreamOpBatch* op) {
+  grpc_call_next_op(elem, op->op());
+}
+
+void CallData::SetPollsetOrPollsetSet(grpc_call_element* elem,
+                                      grpc_polling_entity* pollent) {
+  grpc_call_stack_ignore_set_pollset_or_pollset_set(elem, pollent);
+}
+
+// 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 = *static_cast<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/third_party/grpc/src/cpp/common/channel_filter.h b/third_party/grpc/src/cpp/common/channel_filter.h
new file mode 100644
index 0000000..5e569c9
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/channel_filter.h
@@ -0,0 +1,397 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPCXX_CHANNEL_FILTER_H
+#define GRPCXX_CHANNEL_FILTER_H
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpcpp/impl/codegen/config.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
+
+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_->goaway_error != GRPC_ERROR_NONE; }
+
+  // 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_batch struct.
+class TransportStreamOpBatch {
+ 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 TransportStreamOpBatch object does.
+  explicit TransportStreamOpBatch(grpc_transport_stream_op_batch* op)
+      : op_(op),
+        send_initial_metadata_(
+            op->send_initial_metadata
+                ? op->payload->send_initial_metadata.send_initial_metadata
+                : nullptr),
+        send_trailing_metadata_(
+            op->send_trailing_metadata
+                ? op->payload->send_trailing_metadata.send_trailing_metadata
+                : nullptr),
+        recv_initial_metadata_(
+            op->recv_initial_metadata
+                ? op->payload->recv_initial_metadata.recv_initial_metadata
+                : nullptr),
+        recv_trailing_metadata_(
+            op->recv_trailing_metadata
+                ? op->payload->recv_trailing_metadata.recv_trailing_metadata
+                : nullptr) {}
+
+  grpc_transport_stream_op_batch* 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 ? &send_initial_metadata_ : nullptr;
+  }
+  MetadataBatch* send_trailing_metadata() {
+    return op_->send_trailing_metadata ? &send_trailing_metadata_ : nullptr;
+  }
+  MetadataBatch* recv_initial_metadata() {
+    return op_->recv_initial_metadata ? &recv_initial_metadata_ : nullptr;
+  }
+  MetadataBatch* recv_trailing_metadata() {
+    return op_->recv_trailing_metadata ? &recv_trailing_metadata_ : nullptr;
+  }
+
+  uint32_t* send_initial_metadata_flags() const {
+    return op_->send_initial_metadata ? &op_->payload->send_initial_metadata
+                                             .send_initial_metadata_flags
+                                      : nullptr;
+  }
+
+  grpc_closure* recv_initial_metadata_ready() const {
+    return op_->recv_initial_metadata
+               ? op_->payload->recv_initial_metadata.recv_initial_metadata_ready
+               : nullptr;
+  }
+  void set_recv_initial_metadata_ready(grpc_closure* closure) {
+    op_->payload->recv_initial_metadata.recv_initial_metadata_ready = closure;
+  }
+
+  grpc_core::OrphanablePtr<grpc_core::ByteStream>* send_message() const {
+    return op_->send_message ? &op_->payload->send_message.send_message
+                             : nullptr;
+  }
+  void set_send_message(
+      grpc_core::OrphanablePtr<grpc_core::ByteStream> send_message) {
+    op_->send_message = true;
+    op_->payload->send_message.send_message = std::move(send_message);
+  }
+
+  grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message() const {
+    return op_->recv_message ? op_->payload->recv_message.recv_message
+                             : nullptr;
+  }
+  void set_recv_message(
+      grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message) {
+    op_->recv_message = true;
+    op_->payload->recv_message.recv_message = recv_message;
+  }
+
+  census_context* get_census_context() const {
+    return static_cast<census_context*>(
+        op_->payload->context[GRPC_CONTEXT_TRACING].value);
+  }
+
+  const gpr_atm* get_peer_string() const {
+    if (op_->send_initial_metadata &&
+        op_->payload->send_initial_metadata.peer_string != nullptr) {
+      return op_->payload->send_initial_metadata.peer_string;
+    } else if (op_->recv_initial_metadata &&
+               op_->payload->recv_initial_metadata.peer_string != nullptr) {
+      return op_->payload->recv_initial_metadata.peer_string;
+    } else {
+      return nullptr;
+    }
+  }
+
+ private:
+  grpc_transport_stream_op_batch* 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:
+  ChannelData() {}
+  virtual ~ChannelData() {}
+
+  // TODO(roth): Come up with a more C++-like API for the channel element.
+
+  /// Initializes the channel data.
+  virtual grpc_error* Init(grpc_channel_element* elem,
+                           grpc_channel_element_args* args) {
+    return GRPC_ERROR_NONE;
+  }
+
+  // Called before destruction.
+  virtual void Destroy(grpc_channel_element* elem) {}
+
+  virtual void StartTransportOp(grpc_channel_element* elem, TransportOp* op);
+
+  virtual void GetInfo(grpc_channel_element* elem,
+                       const grpc_channel_info* channel_info);
+};
+
+/// Represents call data.
+class CallData {
+ public:
+  CallData() {}
+  virtual ~CallData() {}
+
+  // TODO(roth): Come up with a more C++-like API for the call element.
+
+  /// Initializes the call data.
+  virtual grpc_error* Init(grpc_call_element* elem,
+                           const grpc_call_element_args* args) {
+    return GRPC_ERROR_NONE;
+  }
+
+  // Called before destruction.
+  virtual void Destroy(grpc_call_element* elem,
+                       const grpc_call_final_info* final_info,
+                       grpc_closure* then_call_closure) {}
+
+  /// Starts a new stream operation.
+  virtual void StartTransportStreamOpBatch(grpc_call_element* elem,
+                                           TransportStreamOpBatch* op);
+
+  /// Sets a pollset or pollset set.
+  virtual void SetPollsetOrPollsetSet(grpc_call_element* elem,
+                                      grpc_polling_entity* pollent);
+};
+
+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 final {
+ public:
+  static const size_t channel_data_size = sizeof(ChannelDataType);
+
+  static grpc_error* InitChannelElement(grpc_channel_element* elem,
+                                        grpc_channel_element_args* args) {
+    // Construct the object in the already-allocated memory.
+    ChannelDataType* channel_data = new (elem->channel_data) ChannelDataType();
+    return channel_data->Init(elem, args);
+  }
+
+  static void DestroyChannelElement(grpc_channel_element* elem) {
+    ChannelDataType* channel_data =
+        static_cast<ChannelDataType*>(elem->channel_data);
+    channel_data->Destroy(elem);
+    channel_data->~ChannelDataType();
+  }
+
+  static void StartTransportOp(grpc_channel_element* elem,
+                               grpc_transport_op* op) {
+    ChannelDataType* channel_data =
+        static_cast<ChannelDataType*>(elem->channel_data);
+    TransportOp op_wrapper(op);
+    channel_data->StartTransportOp(elem, &op_wrapper);
+  }
+
+  static void GetChannelInfo(grpc_channel_element* elem,
+                             const grpc_channel_info* channel_info) {
+    ChannelDataType* channel_data =
+        static_cast<ChannelDataType*>(elem->channel_data);
+    channel_data->GetInfo(elem, channel_info);
+  }
+
+  static const size_t call_data_size = sizeof(CallDataType);
+
+  static grpc_error* InitCallElement(grpc_call_element* elem,
+                                     const grpc_call_element_args* args) {
+    // Construct the object in the already-allocated memory.
+    CallDataType* call_data = new (elem->call_data) CallDataType();
+    return call_data->Init(elem, args);
+  }
+
+  static void DestroyCallElement(grpc_call_element* elem,
+                                 const grpc_call_final_info* final_info,
+                                 grpc_closure* then_call_closure) {
+    CallDataType* call_data = static_cast<CallDataType*>(elem->call_data);
+    call_data->Destroy(elem, final_info, then_call_closure);
+    call_data->~CallDataType();
+  }
+
+  static void StartTransportStreamOpBatch(grpc_call_element* elem,
+                                          grpc_transport_stream_op_batch* op) {
+    CallDataType* call_data = static_cast<CallDataType*>(elem->call_data);
+    TransportStreamOpBatch op_wrapper(op);
+    call_data->StartTransportStreamOpBatch(elem, &op_wrapper);
+  }
+
+  static void SetPollsetOrPollsetSet(grpc_call_element* elem,
+                                     grpc_polling_entity* pollent) {
+    CallDataType* call_data = static_cast<CallDataType*>(elem->call_data);
+    call_data->SetPollsetOrPollsetSet(elem, pollent);
+  }
+};
+
+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::StartTransportStreamOpBatch, FilterType::StartTransportOp,
+       FilterType::call_data_size, FilterType::InitCallElement,
+       FilterType::SetPollsetOrPollsetSet, FilterType::DestroyCallElement,
+       FilterType::channel_data_size, FilterType::InitChannelElement,
+       FilterType::DestroyChannelElement, FilterType::GetChannelInfo, name}};
+  internal::channel_filters->push_back(filter_record);
+}
+
+}  // namespace grpc
+
+#endif  // GRPCXX_CHANNEL_FILTER_H
diff --git a/third_party/grpc/src/cpp/common/completion_queue_cc.cc b/third_party/grpc/src/cpp/common/completion_queue_cc.cc
new file mode 100644
index 0000000..d93a54a
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/completion_queue_cc.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/completion_queue.h>
+
+#include <memory>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/time.h>
+
+namespace grpc {
+
+static internal::GrpcLibraryInitializer g_gli_initializer;
+
+// 'CompletionQueue' constructor can safely call GrpcLibraryCodegen(false) here
+// i.e not have GrpcLibraryCodegen call grpc_init(). This is because, to create
+// a 'grpc_completion_queue' instance (which is being passed as the input to
+// this constructor), one must have already called grpc_init().
+CompletionQueue::CompletionQueue(grpc_completion_queue* take)
+    : GrpcLibraryCodegen(false), cq_(take) {
+  InitialAvalanching();
+}
+
+void CompletionQueue::Shutdown() {
+  g_gli_initializer.summon();
+  CompleteAvalanching();
+}
+
+void CompletionQueue::CompleteAvalanching() {
+  // Check if this was the last avalanching operation
+  if (gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_,
+                                   static_cast<gpr_atm>(-1)) == 1) {
+    grpc_completion_queue_shutdown(cq_);
+  }
+}
+
+CompletionQueue::NextStatus CompletionQueue::AsyncNextInternal(
+    void** tag, bool* ok, gpr_timespec deadline) {
+  for (;;) {
+    auto ev = grpc_completion_queue_next(cq_, deadline, nullptr);
+    switch (ev.type) {
+      case GRPC_QUEUE_TIMEOUT:
+        return TIMEOUT;
+      case GRPC_QUEUE_SHUTDOWN:
+        return SHUTDOWN;
+      case GRPC_OP_COMPLETE:
+        auto core_cq_tag = static_cast<internal::CompletionQueueTag*>(ev.tag);
+        *ok = ev.success != 0;
+        *tag = core_cq_tag;
+        if (core_cq_tag->FinalizeResult(tag, ok)) {
+          return GOT_EVENT;
+        }
+        break;
+    }
+  }
+}
+
+CompletionQueue::CompletionQueueTLSCache::CompletionQueueTLSCache(
+    CompletionQueue* cq)
+    : cq_(cq), flushed_(false) {
+  grpc_completion_queue_thread_local_cache_init(cq_->cq_);
+}
+
+CompletionQueue::CompletionQueueTLSCache::~CompletionQueueTLSCache() {
+  GPR_ASSERT(flushed_);
+}
+
+bool CompletionQueue::CompletionQueueTLSCache::Flush(void** tag, bool* ok) {
+  int res = 0;
+  void* res_tag;
+  flushed_ = true;
+  if (grpc_completion_queue_thread_local_cache_flush(cq_->cq_, &res_tag,
+                                                     &res)) {
+    auto core_cq_tag = static_cast<internal::CompletionQueueTag*>(res_tag);
+    *ok = res == 1;
+    if (core_cq_tag->FinalizeResult(tag, ok)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/core_codegen.cc b/third_party/grpc/src/cpp/common/core_codegen.cc
new file mode 100644
index 0000000..cfaa2e7
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/core_codegen.cc
@@ -0,0 +1,228 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/impl/codegen/core_codegen.h>
+
+#include <stdlib.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/byte_buffer_reader.h>
+#include <grpc/grpc.h>
+#include <grpc/slice.h>
+#include <grpc/slice_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/sync.h>
+#include <grpcpp/support/config.h>
+
+#include "src/core/lib/profiling/timers.h"
+
+struct grpc_byte_buffer;
+
+namespace grpc {
+
+const grpc_completion_queue_factory*
+CoreCodegen::grpc_completion_queue_factory_lookup(
+    const grpc_completion_queue_attributes* attributes) {
+  return ::grpc_completion_queue_factory_lookup(attributes);
+}
+
+grpc_completion_queue* CoreCodegen::grpc_completion_queue_create(
+    const grpc_completion_queue_factory* factory,
+    const grpc_completion_queue_attributes* attributes, void* reserved) {
+  return ::grpc_completion_queue_create(factory, attributes, reserved);
+}
+
+grpc_completion_queue* CoreCodegen::grpc_completion_queue_create_for_next(
+    void* reserved) {
+  return ::grpc_completion_queue_create_for_next(reserved);
+}
+
+grpc_completion_queue* CoreCodegen::grpc_completion_queue_create_for_pluck(
+    void* reserved) {
+  return ::grpc_completion_queue_create_for_pluck(reserved);
+}
+
+void CoreCodegen::grpc_completion_queue_destroy(grpc_completion_queue* cq) {
+  ::grpc_completion_queue_destroy(cq);
+}
+
+grpc_event CoreCodegen::grpc_completion_queue_pluck(grpc_completion_queue* cq,
+                                                    void* tag,
+                                                    gpr_timespec deadline,
+                                                    void* reserved) {
+  return ::grpc_completion_queue_pluck(cq, tag, deadline, reserved);
+}
+
+void* CoreCodegen::gpr_malloc(size_t size) { return ::gpr_malloc(size); }
+
+void CoreCodegen::gpr_free(void* p) { return ::gpr_free(p); }
+
+void CoreCodegen::grpc_init() { ::grpc_init(); }
+void CoreCodegen::grpc_shutdown() { ::grpc_shutdown(); }
+
+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); }
+
+grpc_byte_buffer* CoreCodegen::grpc_byte_buffer_copy(grpc_byte_buffer* bb) {
+  return ::grpc_byte_buffer_copy(bb);
+}
+
+void CoreCodegen::grpc_byte_buffer_destroy(grpc_byte_buffer* bb) {
+  ::grpc_byte_buffer_destroy(bb);
+}
+
+size_t CoreCodegen::grpc_byte_buffer_length(grpc_byte_buffer* bb) {
+  return ::grpc_byte_buffer_length(bb);
+}
+
+grpc_call_error CoreCodegen::grpc_call_start_batch(grpc_call* call,
+                                                   const grpc_op* ops,
+                                                   size_t nops, void* tag,
+                                                   void* reserved) {
+  return ::grpc_call_start_batch(call, ops, nops, tag, reserved);
+}
+
+grpc_call_error CoreCodegen::grpc_call_cancel_with_status(
+    grpc_call* call, grpc_status_code status, const char* description,
+    void* reserved) {
+  return ::grpc_call_cancel_with_status(call, status, description, reserved);
+}
+void CoreCodegen::grpc_call_ref(grpc_call* call) { ::grpc_call_ref(call); }
+void CoreCodegen::grpc_call_unref(grpc_call* call) { ::grpc_call_unref(call); }
+void* CoreCodegen::grpc_call_arena_alloc(grpc_call* call, size_t length) {
+  return ::grpc_call_arena_alloc(call, length);
+}
+
+int CoreCodegen::grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader,
+                                              grpc_byte_buffer* buffer) {
+  return ::grpc_byte_buffer_reader_init(reader, buffer);
+}
+
+void CoreCodegen::grpc_byte_buffer_reader_destroy(
+    grpc_byte_buffer_reader* reader) {
+  ::grpc_byte_buffer_reader_destroy(reader);
+}
+
+int CoreCodegen::grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader,
+                                              grpc_slice* slice) {
+  return ::grpc_byte_buffer_reader_next(reader, slice);
+}
+
+grpc_byte_buffer* CoreCodegen::grpc_raw_byte_buffer_create(grpc_slice* slice,
+                                                           size_t nslices) {
+  return ::grpc_raw_byte_buffer_create(slice, nslices);
+}
+
+grpc_slice CoreCodegen::grpc_slice_new_with_user_data(void* p, size_t len,
+                                                      void (*destroy)(void*),
+                                                      void* user_data) {
+  return ::grpc_slice_new_with_user_data(p, len, destroy, user_data);
+}
+
+grpc_slice CoreCodegen::grpc_slice_new_with_len(void* p, size_t len,
+                                                void (*destroy)(void*,
+                                                                size_t)) {
+  return ::grpc_slice_new_with_len(p, len, destroy);
+}
+
+grpc_slice CoreCodegen::grpc_empty_slice() { return ::grpc_empty_slice(); }
+
+grpc_slice CoreCodegen::grpc_slice_malloc(size_t length) {
+  return ::grpc_slice_malloc(length);
+}
+
+void CoreCodegen::grpc_slice_unref(grpc_slice slice) {
+  ::grpc_slice_unref(slice);
+}
+
+grpc_slice CoreCodegen::grpc_slice_ref(grpc_slice slice) {
+  return ::grpc_slice_ref(slice);
+}
+
+grpc_slice CoreCodegen::grpc_slice_split_tail(grpc_slice* s, size_t split) {
+  return ::grpc_slice_split_tail(s, split);
+}
+
+grpc_slice CoreCodegen::grpc_slice_split_head(grpc_slice* s, size_t split) {
+  return ::grpc_slice_split_head(s, split);
+}
+
+grpc_slice CoreCodegen::grpc_slice_sub(grpc_slice s, size_t begin, size_t end) {
+  return ::grpc_slice_sub(s, begin, end);
+}
+
+grpc_slice CoreCodegen::grpc_slice_from_static_buffer(const void* buffer,
+                                                      size_t length) {
+  return ::grpc_slice_from_static_buffer(buffer, length);
+}
+
+grpc_slice CoreCodegen::grpc_slice_from_copied_buffer(const void* buffer,
+                                                      size_t length) {
+  return ::grpc_slice_from_copied_buffer(static_cast<const char*>(buffer),
+                                         length);
+}
+
+void CoreCodegen::grpc_slice_buffer_add(grpc_slice_buffer* sb,
+                                        grpc_slice slice) {
+  ::grpc_slice_buffer_add(sb, slice);
+}
+
+void CoreCodegen::grpc_slice_buffer_pop(grpc_slice_buffer* sb) {
+  ::grpc_slice_buffer_pop(sb);
+}
+
+void CoreCodegen::grpc_metadata_array_init(grpc_metadata_array* array) {
+  ::grpc_metadata_array_init(array);
+}
+
+void CoreCodegen::grpc_metadata_array_destroy(grpc_metadata_array* array) {
+  ::grpc_metadata_array_destroy(array);
+}
+
+const Status& CoreCodegen::ok() { return grpc::Status::OK; }
+
+const Status& CoreCodegen::cancelled() { return grpc::Status::CANCELLED; }
+
+gpr_timespec CoreCodegen::gpr_inf_future(gpr_clock_type type) {
+  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, const char* file,
+                              int line) {
+  gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "assertion failed: %s",
+          failed_assertion);
+  abort();
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/insecure_create_auth_context.cc b/third_party/grpc/src/cpp/common/insecure_create_auth_context.cc
new file mode 100644
index 0000000..4e5cbd0
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/insecure_create_auth_context.cc
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <memory>
+
+#include <grpc/grpc.h>
+#include <grpcpp/security/auth_context.h>
+
+namespace grpc {
+
+std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call) {
+  (void)call;
+  return std::shared_ptr<const AuthContext>();
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/resource_quota_cc.cc b/third_party/grpc/src/cpp/common/resource_quota_cc.cc
new file mode 100644
index 0000000..276e5f7
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/resource_quota_cc.cc
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpc/grpc.h>
+#include <grpcpp/resource_quota.h>
+
+namespace grpc {
+
+ResourceQuota::ResourceQuota() : impl_(grpc_resource_quota_create(nullptr)) {}
+
+ResourceQuota::ResourceQuota(const grpc::string& name)
+    : impl_(grpc_resource_quota_create(name.c_str())) {}
+
+ResourceQuota::~ResourceQuota() { grpc_resource_quota_unref(impl_); }
+
+ResourceQuota& ResourceQuota::Resize(size_t new_size) {
+  grpc_resource_quota_resize(impl_, new_size);
+  return *this;
+}
+
+ResourceQuota& ResourceQuota::SetMaxThreads(int new_max_threads) {
+  grpc_resource_quota_set_max_threads(impl_, new_max_threads);
+  return *this;
+}
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/rpc_method.cc b/third_party/grpc/src/cpp/common/rpc_method.cc
new file mode 100644
index 0000000..a47dd3e
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/rpc_method.cc
@@ -0,0 +1,21 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/impl/rpc_method.h>
+
+namespace grpc {}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/secure_auth_context.cc b/third_party/grpc/src/cpp/common/secure_auth_context.cc
new file mode 100644
index 0000000..7a2b5af
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/secure_auth_context.cc
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 "src/cpp/common/secure_auth_context.h"
+
+#include <grpc/grpc_security.h>
+
+namespace grpc {
+
+std::vector<grpc::string_ref> SecureAuthContext::GetPeerIdentity() const {
+  if (ctx_ == nullptr) {
+    return std::vector<grpc::string_ref>();
+  }
+  grpc_auth_property_iterator iter =
+      grpc_auth_context_peer_identity(ctx_.get());
+  std::vector<grpc::string_ref> identity;
+  const grpc_auth_property* property = nullptr;
+  while ((property = grpc_auth_property_iterator_next(&iter))) {
+    identity.push_back(
+        grpc::string_ref(property->value, property->value_length));
+  }
+  return identity;
+}
+
+grpc::string SecureAuthContext::GetPeerIdentityPropertyName() const {
+  if (ctx_ == nullptr) {
+    return "";
+  }
+  const char* name = grpc_auth_context_peer_identity_property_name(ctx_.get());
+  return name == nullptr ? "" : name;
+}
+
+std::vector<grpc::string_ref> SecureAuthContext::FindPropertyValues(
+    const grpc::string& name) const {
+  if (ctx_ == nullptr) {
+    return std::vector<grpc::string_ref>();
+  }
+  grpc_auth_property_iterator iter =
+      grpc_auth_context_find_properties_by_name(ctx_.get(), name.c_str());
+  const grpc_auth_property* property = nullptr;
+  std::vector<grpc::string_ref> values;
+  while ((property = grpc_auth_property_iterator_next(&iter))) {
+    values.push_back(grpc::string_ref(property->value, property->value_length));
+  }
+  return values;
+}
+
+AuthPropertyIterator SecureAuthContext::begin() const {
+  if (ctx_ != nullptr) {
+    grpc_auth_property_iterator iter =
+        grpc_auth_context_property_iterator(ctx_.get());
+    const grpc_auth_property* property =
+        grpc_auth_property_iterator_next(&iter);
+    return AuthPropertyIterator(property, &iter);
+  } else {
+    return end();
+  }
+}
+
+AuthPropertyIterator SecureAuthContext::end() const {
+  return AuthPropertyIterator();
+}
+
+void SecureAuthContext::AddProperty(const grpc::string& key,
+                                    const grpc::string_ref& value) {
+  if (ctx_ == nullptr) return;
+  grpc_auth_context_add_property(ctx_.get(), key.c_str(), value.data(),
+                                 value.size());
+}
+
+bool SecureAuthContext::SetPeerIdentityPropertyName(const grpc::string& name) {
+  if (ctx_ == nullptr) return false;
+  return grpc_auth_context_set_peer_identity_property_name(ctx_.get(),
+                                                           name.c_str()) != 0;
+}
+
+bool SecureAuthContext::IsPeerAuthenticated() const {
+  if (ctx_ == nullptr) return false;
+  return grpc_auth_context_peer_is_authenticated(ctx_.get()) != 0;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/secure_auth_context.h b/third_party/grpc/src/cpp/common/secure_auth_context.h
new file mode 100644
index 0000000..2e8f793
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/secure_auth_context.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H
+#define GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H
+
+#include <grpcpp/security/auth_context.h>
+
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/security/context/security_context.h"
+
+namespace grpc {
+
+class SecureAuthContext final : public AuthContext {
+ public:
+  explicit SecureAuthContext(grpc_auth_context* ctx)
+      : ctx_(ctx != nullptr ? ctx->Ref() : nullptr) {}
+
+  ~SecureAuthContext() override = default;
+
+  bool IsPeerAuthenticated() const override;
+
+  std::vector<grpc::string_ref> GetPeerIdentity() const override;
+
+  grpc::string GetPeerIdentityPropertyName() const override;
+
+  std::vector<grpc::string_ref> FindPropertyValues(
+      const grpc::string& name) const override;
+
+  AuthPropertyIterator begin() const override;
+
+  AuthPropertyIterator end() const override;
+
+  void AddProperty(const grpc::string& key,
+                   const grpc::string_ref& value) override;
+
+  virtual bool SetPeerIdentityPropertyName(const grpc::string& name) override;
+
+ private:
+  grpc_core::RefCountedPtr<grpc_auth_context> ctx_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H
diff --git a/third_party/grpc/src/cpp/common/secure_channel_arguments.cc b/third_party/grpc/src/cpp/common/secure_channel_arguments.cc
new file mode 100644
index 0000000..2fb8ea4
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/secure_channel_arguments.cc
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/support/channel_arguments.h>
+
+#include <grpc/grpc_security.h>
+#include "src/core/lib/channel/channel_args.h"
+
+namespace grpc {
+
+void ChannelArguments::SetSslTargetNameOverride(const grpc::string& name) {
+  SetString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, name);
+}
+
+grpc::string ChannelArguments::GetSslTargetNameOverride() const {
+  for (unsigned int i = 0; i < args_.size(); i++) {
+    if (grpc::string(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == args_[i].key) {
+      return args_[i].value.string;
+    }
+  }
+  return "";
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/secure_create_auth_context.cc b/third_party/grpc/src/cpp/common/secure_create_auth_context.cc
new file mode 100644
index 0000000..908c466
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/secure_create_auth_context.cc
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <memory>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+#include <grpcpp/security/auth_context.h>
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/cpp/common/secure_auth_context.h"
+
+namespace grpc {
+
+std::shared_ptr<const AuthContext> CreateAuthContext(grpc_call* call) {
+  if (call == nullptr) {
+    return std::shared_ptr<const AuthContext>();
+  }
+  grpc_core::RefCountedPtr<grpc_auth_context> ctx(grpc_call_auth_context(call));
+  return std::make_shared<SecureAuthContext>(ctx.get());
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/common/version_cc.cc b/third_party/grpc/src/cpp/common/version_cc.cc
new file mode 100644
index 0000000..768b0c6
--- /dev/null
+++ b/third_party/grpc/src/cpp/common/version_cc.cc
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+/* This file is autogenerated from:
+   templates/src/core/surface/version.c.template */
+
+#include <grpcpp/grpcpp.h>
+
+namespace grpc {
+grpc::string Version() { return "1.18.0"; }
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/filters/census/channel_filter.cc b/third_party/grpc/src/cpp/ext/filters/census/channel_filter.cc
new file mode 100644
index 0000000..4ac684d
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/channel_filter.cc
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/channel_filter.h"
+
+namespace grpc {
+
+grpc_error* CensusChannelData::Init(grpc_channel_element* elem,
+                                    grpc_channel_element_args* args) {
+  return GRPC_ERROR_NONE;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/filters/census/channel_filter.h b/third_party/grpc/src/cpp/ext/filters/census/channel_filter.h
new file mode 100644
index 0000000..0b7c682
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/channel_filter.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H
+#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/context.h"
+
+namespace grpc {
+
+class CensusChannelData : public ChannelData {
+ public:
+  grpc_error* Init(grpc_channel_element* elem,
+                   grpc_channel_element_args* args) override;
+};
+
+}  // namespace grpc
+
+#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H */
diff --git a/third_party/grpc/src/cpp/ext/filters/census/client_filter.cc b/third_party/grpc/src/cpp/ext/filters/census/client_filter.cc
new file mode 100644
index 0000000..940d42d
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/client_filter.cc
@@ -0,0 +1,165 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/client_filter.h"
+
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "opencensus/stats/stats.h"
+#include "src/core/lib/surface/call.h"
+#include "src/cpp/ext/filters/census/grpc_plugin.h"
+#include "src/cpp/ext/filters/census/measures.h"
+
+namespace grpc {
+
+constexpr uint32_t CensusClientCallData::kMaxTraceContextLen;
+constexpr uint32_t CensusClientCallData::kMaxTagsLen;
+
+namespace {
+
+void FilterTrailingMetadata(grpc_metadata_batch* b, uint64_t* elapsed_time) {
+  if (b->idx.named.grpc_server_stats_bin != nullptr) {
+    ServerStatsDeserialize(
+        reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(
+            GRPC_MDVALUE(b->idx.named.grpc_server_stats_bin->md))),
+        GRPC_SLICE_LENGTH(GRPC_MDVALUE(b->idx.named.grpc_server_stats_bin->md)),
+        elapsed_time);
+    grpc_metadata_batch_remove(b, b->idx.named.grpc_server_stats_bin);
+  }
+}
+
+}  // namespace
+
+void CensusClientCallData::OnDoneRecvTrailingMetadataCb(void* user_data,
+                                                        grpc_error* error) {
+  grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data);
+  CensusClientCallData* calld =
+      reinterpret_cast<CensusClientCallData*>(elem->call_data);
+  GPR_ASSERT(calld != nullptr);
+  if (error == GRPC_ERROR_NONE) {
+    GPR_ASSERT(calld->recv_trailing_metadata_ != nullptr);
+    FilterTrailingMetadata(calld->recv_trailing_metadata_,
+                           &calld->elapsed_time_);
+  }
+  GRPC_CLOSURE_RUN(calld->initial_on_done_recv_trailing_metadata_,
+                   GRPC_ERROR_REF(error));
+}
+
+void CensusClientCallData::OnDoneRecvMessageCb(void* user_data,
+                                               grpc_error* error) {
+  grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data);
+  CensusClientCallData* calld =
+      reinterpret_cast<CensusClientCallData*>(elem->call_data);
+  CensusChannelData* channeld =
+      reinterpret_cast<CensusChannelData*>(elem->channel_data);
+  GPR_ASSERT(calld != nullptr);
+  GPR_ASSERT(channeld != nullptr);
+  // Stream messages are no longer valid after receiving trailing metadata.
+  if ((*calld->recv_message_) != nullptr) {
+    calld->recv_message_count_++;
+  }
+  GRPC_CLOSURE_RUN(calld->initial_on_done_recv_message_, GRPC_ERROR_REF(error));
+}
+
+void CensusClientCallData::StartTransportStreamOpBatch(
+    grpc_call_element* elem, TransportStreamOpBatch* op) {
+  if (op->send_initial_metadata() != nullptr) {
+    census_context* ctxt = op->get_census_context();
+    GenerateClientContext(
+        qualified_method_, &context_,
+        (ctxt == nullptr) ? nullptr : reinterpret_cast<CensusContext*>(ctxt));
+    size_t tracing_len = TraceContextSerialize(context_.Context(), tracing_buf_,
+                                               kMaxTraceContextLen);
+    if (tracing_len > 0) {
+      GRPC_LOG_IF_ERROR(
+          "census grpc_filter",
+          grpc_metadata_batch_add_tail(
+              op->send_initial_metadata()->batch(), &tracing_bin_,
+              grpc_mdelem_from_slices(
+                  GRPC_MDSTR_GRPC_TRACE_BIN,
+                  grpc_slice_from_copied_buffer(tracing_buf_, tracing_len))));
+    }
+    grpc_slice tags = grpc_empty_slice();
+    // TODO: Add in tagging serialization.
+    size_t encoded_tags_len = StatsContextSerialize(kMaxTagsLen, &tags);
+    if (encoded_tags_len > 0) {
+      GRPC_LOG_IF_ERROR(
+          "census grpc_filter",
+          grpc_metadata_batch_add_tail(
+              op->send_initial_metadata()->batch(), &stats_bin_,
+              grpc_mdelem_from_slices(GRPC_MDSTR_GRPC_TAGS_BIN, tags)));
+    }
+  }
+
+  if (op->send_message() != nullptr) {
+    ++sent_message_count_;
+  }
+  if (op->recv_message() != nullptr) {
+    recv_message_ = op->op()->payload->recv_message.recv_message;
+    initial_on_done_recv_message_ =
+        op->op()->payload->recv_message.recv_message_ready;
+    op->op()->payload->recv_message.recv_message_ready = &on_done_recv_message_;
+  }
+  if (op->recv_trailing_metadata() != nullptr) {
+    recv_trailing_metadata_ = op->recv_trailing_metadata()->batch();
+    initial_on_done_recv_trailing_metadata_ =
+        op->op()->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
+    op->op()->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
+        &on_done_recv_trailing_metadata_;
+  }
+  // Call next op.
+  grpc_call_next_op(elem, op->op());
+}
+
+grpc_error* CensusClientCallData::Init(grpc_call_element* elem,
+                                       const grpc_call_element_args* args) {
+  path_ = grpc_slice_ref_internal(args->path);
+  start_time_ = absl::Now();
+  method_ = GetMethod(&path_);
+  qualified_method_ = absl::StrCat("Sent.", method_);
+  GRPC_CLOSURE_INIT(&on_done_recv_message_, OnDoneRecvMessageCb, elem,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&on_done_recv_trailing_metadata_,
+                    OnDoneRecvTrailingMetadataCb, elem,
+                    grpc_schedule_on_exec_ctx);
+  return GRPC_ERROR_NONE;
+}
+
+void CensusClientCallData::Destroy(grpc_call_element* elem,
+                                   const grpc_call_final_info* final_info,
+                                   grpc_closure* then_call_closure) {
+  const uint64_t request_size = GetOutgoingDataSize(final_info);
+  const uint64_t response_size = GetIncomingDataSize(final_info);
+  double latency_ms = absl::ToDoubleMilliseconds(absl::Now() - start_time_);
+  ::opencensus::stats::Record(
+      {{RpcClientSentBytesPerRpc(), static_cast<double>(request_size)},
+       {RpcClientReceivedBytesPerRpc(), static_cast<double>(response_size)},
+       {RpcClientRoundtripLatency(), latency_ms},
+       {RpcClientServerLatency(),
+        ToDoubleMilliseconds(absl::Nanoseconds(elapsed_time_))},
+       {RpcClientSentMessagesPerRpc(), sent_message_count_},
+       {RpcClientReceivedMessagesPerRpc(), recv_message_count_}},
+      {{ClientMethodTagKey(), method_},
+       {ClientStatusTagKey(), StatusCodeToString(final_info->final_status)}});
+  grpc_slice_unref_internal(path_);
+  context_.EndSpan();
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/filters/census/client_filter.h b/third_party/grpc/src/cpp/ext/filters/census/client_filter.h
new file mode 100644
index 0000000..8510228
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/client_filter.h
@@ -0,0 +1,104 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H
+#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "absl/strings/string_view.h"
+#include "absl/time/time.h"
+#include "src/cpp/ext/filters/census/channel_filter.h"
+#include "src/cpp/ext/filters/census/context.h"
+
+namespace grpc {
+
+// A CallData class will be created for every grpc call within a channel. It is
+// used to store data and methods specific to that call. CensusClientCallData is
+// thread-compatible, however typically only 1 thread should be interacting with
+// a call at a time.
+class CensusClientCallData : public CallData {
+ public:
+  // Maximum size of trace context is sent on the wire.
+  static constexpr uint32_t kMaxTraceContextLen = 64;
+  // Maximum size of tags that are sent on the wire.
+  static constexpr uint32_t kMaxTagsLen = 2048;
+
+  CensusClientCallData()
+      : recv_trailing_metadata_(nullptr),
+        initial_on_done_recv_trailing_metadata_(nullptr),
+        initial_on_done_recv_message_(nullptr),
+        elapsed_time_(0),
+        recv_message_(nullptr),
+        recv_message_count_(0),
+        sent_message_count_(0) {
+    memset(&stats_bin_, 0, sizeof(grpc_linked_mdelem));
+    memset(&tracing_bin_, 0, sizeof(grpc_linked_mdelem));
+    memset(&path_, 0, sizeof(grpc_slice));
+    memset(&on_done_recv_trailing_metadata_, 0, sizeof(grpc_closure));
+    memset(&on_done_recv_message_, 0, sizeof(grpc_closure));
+  }
+
+  grpc_error* Init(grpc_call_element* elem,
+                   const grpc_call_element_args* args) override;
+
+  void Destroy(grpc_call_element* elem, const grpc_call_final_info* final_info,
+               grpc_closure* then_call_closure) override;
+
+  void StartTransportStreamOpBatch(grpc_call_element* elem,
+                                   TransportStreamOpBatch* op) override;
+
+  static void OnDoneRecvTrailingMetadataCb(void* user_data, grpc_error* error);
+
+  static void OnDoneSendInitialMetadataCb(void* user_data, grpc_error* error);
+
+  static void OnDoneRecvMessageCb(void* user_data, grpc_error* error);
+
+ private:
+  CensusContext context_;
+  // Metadata elements for tracing and census stats data.
+  grpc_linked_mdelem stats_bin_;
+  grpc_linked_mdelem tracing_bin_;
+  // Client method.
+  absl::string_view method_;
+  std::string qualified_method_;
+  grpc_slice path_;
+  // The recv trailing metadata callbacks.
+  grpc_metadata_batch* recv_trailing_metadata_;
+  grpc_closure* initial_on_done_recv_trailing_metadata_;
+  grpc_closure on_done_recv_trailing_metadata_;
+  // recv message
+  grpc_closure* initial_on_done_recv_message_;
+  grpc_closure on_done_recv_message_;
+  // Start time (for measuring latency).
+  absl::Time start_time_;
+  // Server elapsed time in nanoseconds.
+  uint64_t elapsed_time_;
+  // The received message--may be null.
+  grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message_;
+  // Number of messages in this RPC.
+  uint64_t recv_message_count_;
+  uint64_t sent_message_count_;
+  // Buffer needed for grpc_slice to reference when adding trace context
+  // metatdata to outgoing message.
+  char tracing_buf_[kMaxTraceContextLen];
+};
+
+}  // namespace grpc
+
+#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H */
diff --git a/third_party/grpc/src/cpp/ext/filters/census/context.cc b/third_party/grpc/src/cpp/ext/filters/census/context.cc
new file mode 100644
index 0000000..1605903
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/context.cc
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/context.h"
+
+namespace grpc {
+
+using ::opencensus::trace::Span;
+using ::opencensus::trace::SpanContext;
+
+void GenerateServerContext(absl::string_view tracing, absl::string_view stats,
+                           absl::string_view primary_role,
+                           absl::string_view method, CensusContext* context) {
+  // Destruct the current CensusContext to free the Span memory before
+  // overwriting it below.
+  context->~CensusContext();
+  GrpcTraceContext trace_ctxt;
+  if (TraceContextEncoding::Decode(tracing, &trace_ctxt) !=
+      TraceContextEncoding::kEncodeDecodeFailure) {
+    SpanContext parent_ctx = trace_ctxt.ToSpanContext();
+    if (parent_ctx.IsValid()) {
+      new (context) CensusContext(method, parent_ctx);
+      return;
+    }
+  }
+  new (context) CensusContext(method);
+}
+
+void GenerateClientContext(absl::string_view method, CensusContext* ctxt,
+                           CensusContext* parent_ctxt) {
+  // Destruct the current CensusContext to free the Span memory before
+  // overwriting it below.
+  ctxt->~CensusContext();
+  if (parent_ctxt != nullptr) {
+    SpanContext span_ctxt = parent_ctxt->Context();
+    Span span = parent_ctxt->Span();
+    if (span_ctxt.IsValid()) {
+      new (ctxt) CensusContext(method, &span);
+      return;
+    }
+  }
+  new (ctxt) CensusContext(method);
+}
+
+size_t TraceContextSerialize(const ::opencensus::trace::SpanContext& context,
+                             char* tracing_buf, size_t tracing_buf_size) {
+  GrpcTraceContext trace_ctxt(context);
+  return TraceContextEncoding::Encode(trace_ctxt, tracing_buf,
+                                      tracing_buf_size);
+}
+
+size_t StatsContextSerialize(size_t max_tags_len, grpc_slice* tags) {
+  // TODO: Add implementation. Waiting on stats tagging to be added.
+  return 0;
+}
+
+size_t ServerStatsSerialize(uint64_t server_elapsed_time, char* buf,
+                            size_t buf_size) {
+  return RpcServerStatsEncoding::Encode(server_elapsed_time, buf, buf_size);
+}
+
+size_t ServerStatsDeserialize(const char* buf, size_t buf_size,
+                              uint64_t* server_elapsed_time) {
+  return RpcServerStatsEncoding::Decode(absl::string_view(buf, buf_size),
+                                        server_elapsed_time);
+}
+
+uint64_t GetIncomingDataSize(const grpc_call_final_info* final_info) {
+  return final_info->stats.transport_stream_stats.incoming.data_bytes;
+}
+
+uint64_t GetOutgoingDataSize(const grpc_call_final_info* final_info) {
+  return final_info->stats.transport_stream_stats.outgoing.data_bytes;
+}
+
+SpanContext SpanContextFromCensusContext(const census_context* ctxt) {
+  return reinterpret_cast<const CensusContext*>(ctxt)->Context();
+}
+
+Span SpanFromCensusContext(const census_context* ctxt) {
+  return reinterpret_cast<const CensusContext*>(ctxt)->Span();
+}
+
+absl::string_view StatusCodeToString(grpc_status_code code) {
+  switch (code) {
+    case GRPC_STATUS_OK:
+      return "OK";
+    case GRPC_STATUS_CANCELLED:
+      return "CANCELLED";
+    case GRPC_STATUS_UNKNOWN:
+      return "UNKNOWN";
+    case GRPC_STATUS_INVALID_ARGUMENT:
+      return "INVALID_ARGUMENT";
+    case GRPC_STATUS_DEADLINE_EXCEEDED:
+      return "DEADLINE_EXCEEDED";
+    case GRPC_STATUS_NOT_FOUND:
+      return "NOT_FOUND";
+    case GRPC_STATUS_ALREADY_EXISTS:
+      return "ALREADY_EXISTS";
+    case GRPC_STATUS_PERMISSION_DENIED:
+      return "PERMISSION_DENIED";
+    case GRPC_STATUS_UNAUTHENTICATED:
+      return "UNAUTHENTICATED";
+    case GRPC_STATUS_RESOURCE_EXHAUSTED:
+      return "RESOURCE_EXHAUSTED";
+    case GRPC_STATUS_FAILED_PRECONDITION:
+      return "FAILED_PRECONDITION";
+    case GRPC_STATUS_ABORTED:
+      return "ABORTED";
+    case GRPC_STATUS_OUT_OF_RANGE:
+      return "OUT_OF_RANGE";
+    case GRPC_STATUS_UNIMPLEMENTED:
+      return "UNIMPLEMENTED";
+    case GRPC_STATUS_INTERNAL:
+      return "INTERNAL";
+    case GRPC_STATUS_UNAVAILABLE:
+      return "UNAVAILABLE";
+    case GRPC_STATUS_DATA_LOSS:
+      return "DATA_LOSS";
+    default:
+      // gRPC wants users of this enum to include a default branch so that
+      // adding values is not a breaking change.
+      return "UNKNOWN_STATUS";
+  }
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/filters/census/context.h b/third_party/grpc/src/cpp/ext/filters/census/context.h
new file mode 100644
index 0000000..1643fdd
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/context.h
@@ -0,0 +1,126 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H
+#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/status.h>
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+#include "opencensus/trace/span.h"
+#include "opencensus/trace/span_context.h"
+#include "opencensus/trace/trace_params.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/cpp/common/channel_filter.h"
+#include "src/cpp/ext/filters/census/rpc_encoding.h"
+
+// This is needed because grpc has hardcoded CensusContext with a
+// forward declaration of 'struct census_context;'
+struct census_context;
+
+namespace grpc {
+
+// Thread compatible.
+class CensusContext {
+ public:
+  CensusContext() : span_(::opencensus::trace::Span::BlankSpan()) {}
+
+  explicit CensusContext(absl::string_view name)
+      : span_(::opencensus::trace::Span::StartSpan(name)) {}
+
+  CensusContext(absl::string_view name, const ::opencensus::trace::Span* parent)
+      : span_(::opencensus::trace::Span::StartSpan(name, parent)) {}
+
+  CensusContext(absl::string_view name,
+                const ::opencensus::trace::SpanContext& parent_ctxt)
+      : span_(::opencensus::trace::Span::StartSpanWithRemoteParent(
+            name, parent_ctxt)) {}
+
+  ::opencensus::trace::SpanContext Context() const { return span_.context(); }
+  ::opencensus::trace::Span Span() const { return span_; }
+  void EndSpan() { span_.End(); }
+
+ private:
+  ::opencensus::trace::Span span_;
+};
+
+// Serializes the outgoing trace context. Field IDs are 1 byte followed by
+// field data. A 1 byte version ID is always encoded first.
+size_t TraceContextSerialize(const ::opencensus::trace::SpanContext& context,
+                             char* tracing_buf, size_t tracing_buf_size);
+
+// Serializes the outgoing stats context.  Field IDs are 1 byte followed by
+// field data. A 1 byte version ID is always encoded first. Tags are directly
+// serialized into the given grpc_slice.
+size_t StatsContextSerialize(size_t max_tags_len, grpc_slice* tags);
+
+// Serialize outgoing server stats. Returns the number of bytes serialized.
+size_t ServerStatsSerialize(uint64_t server_elapsed_time, char* buf,
+                            size_t buf_size);
+
+// Deserialize incoming server stats. Returns the number of bytes deserialized.
+size_t ServerStatsDeserialize(const char* buf, size_t buf_size,
+                              uint64_t* server_elapsed_time);
+
+// Deserialize the incoming SpanContext and generate a new server context based
+// on that. This new span will never be a root span. This should only be called
+// with a blank CensusContext as it overwrites it.
+void GenerateServerContext(absl::string_view tracing, absl::string_view stats,
+                           absl::string_view primary_role,
+                           absl::string_view method, CensusContext* context);
+
+// Creates a new client context that is by default a new root context.
+// If the current context is the default context then the newly created
+// span automatically becomes a root span. This should only be called with a
+// blank CensusContext as it overwrites it.
+void GenerateClientContext(absl::string_view method, CensusContext* ctxt,
+                           CensusContext* parent_ctx);
+
+// Returns the incoming data size from the grpc call final info.
+uint64_t GetIncomingDataSize(const grpc_call_final_info* final_info);
+
+// Returns the outgoing data size from the grpc call final info.
+uint64_t GetOutgoingDataSize(const grpc_call_final_info* final_info);
+
+// These helper functions return the SpanContext and Span, respectively
+// associated with the census_context* stored by grpc. The user will need to
+// call this for manual propagation of tracing data.
+::opencensus::trace::SpanContext SpanContextFromCensusContext(
+    const census_context* ctxt);
+::opencensus::trace::Span SpanFromCensusContext(const census_context* ctxt);
+
+// Returns a string representation of the StatusCode enum.
+absl::string_view StatusCodeToString(grpc_status_code code);
+
+inline absl::string_view GetMethod(const grpc_slice* path) {
+  if (GRPC_SLICE_IS_EMPTY(*path)) {
+    return "";
+  }
+  // Check for leading '/' and trim it if present.
+  return absl::StripPrefix(absl::string_view(reinterpret_cast<const char*>(
+                                                 GRPC_SLICE_START_PTR(*path)),
+                                             GRPC_SLICE_LENGTH(*path)),
+                           "/");
+}
+
+}  // namespace grpc
+
+#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H */
diff --git a/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.cc b/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.cc
new file mode 100644
index 0000000..f978ed3
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.cc
@@ -0,0 +1,130 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/grpc_plugin.h"
+
+#include <grpcpp/server_context.h>
+
+#include "opencensus/trace/span.h"
+#include "src/cpp/ext/filters/census/channel_filter.h"
+#include "src/cpp/ext/filters/census/client_filter.h"
+#include "src/cpp/ext/filters/census/measures.h"
+#include "src/cpp/ext/filters/census/server_filter.h"
+
+namespace grpc {
+
+void RegisterOpenCensusPlugin() {
+  RegisterChannelFilter<CensusChannelData, CensusClientCallData>(
+      "opencensus_client", GRPC_CLIENT_CHANNEL, INT_MAX /* priority */,
+      nullptr /* condition function */);
+  RegisterChannelFilter<CensusChannelData, CensusServerCallData>(
+      "opencensus_server", GRPC_SERVER_CHANNEL, INT_MAX /* priority */,
+      nullptr /* condition function */);
+
+  // Access measures to ensure they are initialized. Otherwise, creating a view
+  // before the first RPC would cause an error.
+  RpcClientSentBytesPerRpc();
+  RpcClientReceivedBytesPerRpc();
+  RpcClientRoundtripLatency();
+  RpcClientServerLatency();
+  RpcClientSentMessagesPerRpc();
+  RpcClientReceivedMessagesPerRpc();
+
+  RpcServerSentBytesPerRpc();
+  RpcServerReceivedBytesPerRpc();
+  RpcServerServerLatency();
+  RpcServerSentMessagesPerRpc();
+  RpcServerReceivedMessagesPerRpc();
+}
+
+::opencensus::trace::Span GetSpanFromServerContext(ServerContext* context) {
+  return reinterpret_cast<const CensusContext*>(context->census_context())
+      ->Span();
+}
+
+// These measure definitions should be kept in sync across opencensus
+// implementations--see
+// https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcMeasureConstants.java.
+::opencensus::stats::TagKey ClientMethodTagKey() {
+  static const auto method_tag_key =
+      ::opencensus::stats::TagKey::Register("grpc_client_method");
+  return method_tag_key;
+}
+
+::opencensus::stats::TagKey ClientStatusTagKey() {
+  static const auto status_tag_key =
+      ::opencensus::stats::TagKey::Register("grpc_client_status");
+  return status_tag_key;
+}
+
+::opencensus::stats::TagKey ServerMethodTagKey() {
+  static const auto method_tag_key =
+      ::opencensus::stats::TagKey::Register("grpc_server_method");
+  return method_tag_key;
+}
+
+::opencensus::stats::TagKey ServerStatusTagKey() {
+  static const auto status_tag_key =
+      ::opencensus::stats::TagKey::Register("grpc_server_status");
+  return status_tag_key;
+}
+
+// Client
+ABSL_CONST_INIT const absl::string_view
+    kRpcClientSentMessagesPerRpcMeasureName =
+        "grpc.io/client/sent_messages_per_rpc";
+
+ABSL_CONST_INIT const absl::string_view kRpcClientSentBytesPerRpcMeasureName =
+    "grpc.io/client/sent_bytes_per_rpc";
+
+ABSL_CONST_INIT const absl::string_view
+    kRpcClientReceivedMessagesPerRpcMeasureName =
+        "grpc.io/client/received_messages_per_rpc";
+
+ABSL_CONST_INIT const absl::string_view
+    kRpcClientReceivedBytesPerRpcMeasureName =
+        "grpc.io/client/received_bytes_per_rpc";
+
+ABSL_CONST_INIT const absl::string_view kRpcClientRoundtripLatencyMeasureName =
+    "grpc.io/client/roundtrip_latency";
+
+ABSL_CONST_INIT const absl::string_view kRpcClientServerLatencyMeasureName =
+    "grpc.io/client/server_latency";
+
+// Server
+ABSL_CONST_INIT const absl::string_view
+    kRpcServerSentMessagesPerRpcMeasureName =
+        "grpc.io/server/sent_messages_per_rpc";
+
+ABSL_CONST_INIT const absl::string_view kRpcServerSentBytesPerRpcMeasureName =
+    "grpc.io/server/sent_bytes_per_rpc";
+
+ABSL_CONST_INIT const absl::string_view
+    kRpcServerReceivedMessagesPerRpcMeasureName =
+        "grpc.io/server/received_messages_per_rpc";
+
+ABSL_CONST_INIT const absl::string_view
+    kRpcServerReceivedBytesPerRpcMeasureName =
+        "grpc.io/server/received_bytes_per_rpc";
+
+ABSL_CONST_INIT const absl::string_view kRpcServerServerLatencyMeasureName =
+    "grpc.io/server/server_latency";
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.h b/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.h
new file mode 100644
index 0000000..9e319cb
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H
+#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H
+
+#include <grpc/support/port_platform.h>
+
+#include "absl/strings/string_view.h"
+#include "include/grpcpp/opencensus.h"
+#include "opencensus/stats/stats.h"
+
+namespace grpc {
+
+class ServerContext;
+
+// The tag keys set when recording RPC stats.
+::opencensus::stats::TagKey ClientMethodTagKey();
+::opencensus::stats::TagKey ClientStatusTagKey();
+::opencensus::stats::TagKey ServerMethodTagKey();
+::opencensus::stats::TagKey ServerStatusTagKey();
+
+// Names of measures used by the plugin--users can create views on these
+// measures but should not record data for them.
+extern const absl::string_view kRpcClientSentMessagesPerRpcMeasureName;
+extern const absl::string_view kRpcClientSentBytesPerRpcMeasureName;
+extern const absl::string_view kRpcClientReceivedMessagesPerRpcMeasureName;
+extern const absl::string_view kRpcClientReceivedBytesPerRpcMeasureName;
+extern const absl::string_view kRpcClientRoundtripLatencyMeasureName;
+extern const absl::string_view kRpcClientServerLatencyMeasureName;
+
+extern const absl::string_view kRpcServerSentMessagesPerRpcMeasureName;
+extern const absl::string_view kRpcServerSentBytesPerRpcMeasureName;
+extern const absl::string_view kRpcServerReceivedMessagesPerRpcMeasureName;
+extern const absl::string_view kRpcServerReceivedBytesPerRpcMeasureName;
+extern const absl::string_view kRpcServerServerLatencyMeasureName;
+
+// Canonical gRPC view definitions.
+const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcCumulative();
+const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcCumulative();
+const ::opencensus::stats::ViewDescriptor&
+ClientReceivedMessagesPerRpcCumulative();
+const ::opencensus::stats::ViewDescriptor&
+ClientReceivedBytesPerRpcCumulative();
+const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyCumulative();
+const ::opencensus::stats::ViewDescriptor& ClientServerLatencyCumulative();
+const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsCumulative();
+
+const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcCumulative();
+const ::opencensus::stats::ViewDescriptor&
+ServerReceivedBytesPerRpcCumulative();
+const ::opencensus::stats::ViewDescriptor& ServerServerLatencyCumulative();
+const ::opencensus::stats::ViewDescriptor& ServerStartedCountCumulative();
+const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsCumulative();
+const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcCumulative();
+const ::opencensus::stats::ViewDescriptor&
+ServerReceivedMessagesPerRpcCumulative();
+
+const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcMinute();
+const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcMinute();
+const ::opencensus::stats::ViewDescriptor& ClientReceivedMessagesPerRpcMinute();
+const ::opencensus::stats::ViewDescriptor& ClientReceivedBytesPerRpcMinute();
+const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyMinute();
+const ::opencensus::stats::ViewDescriptor& ClientServerLatencyMinute();
+const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsMinute();
+
+const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcMinute();
+const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcMinute();
+const ::opencensus::stats::ViewDescriptor& ServerReceivedMessagesPerRpcMinute();
+const ::opencensus::stats::ViewDescriptor& ServerReceivedBytesPerRpcMinute();
+const ::opencensus::stats::ViewDescriptor& ServerServerLatencyMinute();
+const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsMinute();
+
+const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcHour();
+const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcHour();
+const ::opencensus::stats::ViewDescriptor& ClientReceivedMessagesPerRpcHour();
+const ::opencensus::stats::ViewDescriptor& ClientReceivedBytesPerRpcHour();
+const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyHour();
+const ::opencensus::stats::ViewDescriptor& ClientServerLatencyHour();
+const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsHour();
+
+const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcHour();
+const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcHour();
+const ::opencensus::stats::ViewDescriptor& ServerReceivedMessagesPerRpcHour();
+const ::opencensus::stats::ViewDescriptor& ServerReceivedBytesPerRpcHour();
+const ::opencensus::stats::ViewDescriptor& ServerServerLatencyHour();
+const ::opencensus::stats::ViewDescriptor& ServerStartedCountHour();
+const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsHour();
+
+}  // namespace grpc
+
+#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H */
diff --git a/third_party/grpc/src/cpp/ext/filters/census/measures.cc b/third_party/grpc/src/cpp/ext/filters/census/measures.cc
new file mode 100644
index 0000000..b522fae
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/measures.cc
@@ -0,0 +1,129 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/measures.h"
+
+#include "opencensus/stats/stats.h"
+#include "src/cpp/ext/filters/census/grpc_plugin.h"
+
+namespace grpc {
+
+using ::opencensus::stats::MeasureDouble;
+using ::opencensus::stats::MeasureInt64;
+
+// These measure definitions should be kept in sync across opencensus
+// implementations--see
+// https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcMeasureConstants.java.
+
+namespace {
+
+// Unit constants
+constexpr char kUnitBytes[] = "By";
+constexpr char kUnitMilliseconds[] = "ms";
+constexpr char kCount[] = "1";
+
+}  // namespace
+
+// Client
+MeasureDouble RpcClientSentBytesPerRpc() {
+  static const auto measure = MeasureDouble::Register(
+      kRpcClientSentBytesPerRpcMeasureName,
+      "Total bytes sent across all request messages per RPC", kUnitBytes);
+  return measure;
+}
+
+MeasureDouble RpcClientReceivedBytesPerRpc() {
+  static const auto measure = MeasureDouble::Register(
+      kRpcClientReceivedBytesPerRpcMeasureName,
+      "Total bytes received across all response messages per RPC", kUnitBytes);
+  return measure;
+}
+
+MeasureDouble RpcClientRoundtripLatency() {
+  static const auto measure = MeasureDouble::Register(
+      kRpcClientRoundtripLatencyMeasureName,
+      "Time between first byte of request sent to last byte of response "
+      "received, or terminal error",
+      kUnitMilliseconds);
+  return measure;
+}
+
+MeasureDouble RpcClientServerLatency() {
+  static const auto measure = MeasureDouble::Register(
+      kRpcClientServerLatencyMeasureName,
+      "Time between first byte of request received to last byte of response "
+      "sent, or terminal error (propagated from the server)",
+      kUnitMilliseconds);
+  return measure;
+}
+
+MeasureInt64 RpcClientSentMessagesPerRpc() {
+  static const auto measure =
+      MeasureInt64::Register(kRpcClientSentMessagesPerRpcMeasureName,
+                             "Number of messages sent per RPC", kCount);
+  return measure;
+}
+
+MeasureInt64 RpcClientReceivedMessagesPerRpc() {
+  static const auto measure =
+      MeasureInt64::Register(kRpcClientReceivedMessagesPerRpcMeasureName,
+                             "Number of messages received per RPC", kCount);
+  return measure;
+}
+
+// Server
+MeasureDouble RpcServerSentBytesPerRpc() {
+  static const auto measure = MeasureDouble::Register(
+      kRpcServerSentBytesPerRpcMeasureName,
+      "Total bytes sent across all messages per RPC", kUnitBytes);
+  return measure;
+}
+
+MeasureDouble RpcServerReceivedBytesPerRpc() {
+  static const auto measure = MeasureDouble::Register(
+      kRpcServerReceivedBytesPerRpcMeasureName,
+      "Total bytes received across all messages per RPC", kUnitBytes);
+  return measure;
+}
+
+MeasureDouble RpcServerServerLatency() {
+  static const auto measure = MeasureDouble::Register(
+      kRpcServerServerLatencyMeasureName,
+      "Time between first byte of request received to last byte of response "
+      "sent, or terminal error",
+      kUnitMilliseconds);
+  return measure;
+}
+
+MeasureInt64 RpcServerSentMessagesPerRpc() {
+  static const auto measure =
+      MeasureInt64::Register(kRpcServerSentMessagesPerRpcMeasureName,
+                             "Number of messages sent per RPC", kCount);
+  return measure;
+}
+
+MeasureInt64 RpcServerReceivedMessagesPerRpc() {
+  static const auto measure =
+      MeasureInt64::Register(kRpcServerReceivedMessagesPerRpcMeasureName,
+                             "Number of messages received per RPC", kCount);
+  return measure;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/filters/census/measures.h b/third_party/grpc/src/cpp/ext/filters/census/measures.h
new file mode 100644
index 0000000..8f8e72a
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/measures.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H
+#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H
+
+#include <grpc/support/port_platform.h>
+
+#include "opencensus/stats/stats.h"
+#include "src/cpp/ext/filters/census/grpc_plugin.h"
+
+namespace grpc {
+
+::opencensus::stats::MeasureInt64 RpcClientSentMessagesPerRpc();
+::opencensus::stats::MeasureDouble RpcClientSentBytesPerRpc();
+::opencensus::stats::MeasureInt64 RpcClientReceivedMessagesPerRpc();
+::opencensus::stats::MeasureDouble RpcClientReceivedBytesPerRpc();
+::opencensus::stats::MeasureDouble RpcClientRoundtripLatency();
+::opencensus::stats::MeasureDouble RpcClientServerLatency();
+::opencensus::stats::MeasureInt64 RpcClientCompletedRpcs();
+
+::opencensus::stats::MeasureInt64 RpcServerSentMessagesPerRpc();
+::opencensus::stats::MeasureDouble RpcServerSentBytesPerRpc();
+::opencensus::stats::MeasureInt64 RpcServerReceivedMessagesPerRpc();
+::opencensus::stats::MeasureDouble RpcServerReceivedBytesPerRpc();
+::opencensus::stats::MeasureDouble RpcServerServerLatency();
+::opencensus::stats::MeasureInt64 RpcServerCompletedRpcs();
+
+}  // namespace grpc
+
+#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H */
diff --git a/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.cc b/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.cc
new file mode 100644
index 0000000..45a66d9
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.cc
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/rpc_encoding.h"
+
+namespace grpc {
+
+constexpr size_t TraceContextEncoding::kGrpcTraceContextSize;
+constexpr size_t TraceContextEncoding::kEncodeDecodeFailure;
+constexpr size_t TraceContextEncoding::kVersionIdSize;
+constexpr size_t TraceContextEncoding::kFieldIdSize;
+constexpr size_t TraceContextEncoding::kVersionIdOffset;
+constexpr size_t TraceContextEncoding::kVersionId;
+
+constexpr size_t RpcServerStatsEncoding::kRpcServerStatsSize;
+constexpr size_t RpcServerStatsEncoding::kEncodeDecodeFailure;
+constexpr size_t RpcServerStatsEncoding::kVersionIdSize;
+constexpr size_t RpcServerStatsEncoding::kFieldIdSize;
+constexpr size_t RpcServerStatsEncoding::kVersionIdOffset;
+constexpr size_t RpcServerStatsEncoding::kVersionId;
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.h b/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.h
new file mode 100644
index 0000000..ffffa60
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.h
@@ -0,0 +1,284 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H
+#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H
+
+#include <grpc/support/port_platform.h>
+
+#include <string.h>
+
+#include "absl/base/internal/endian.h"
+#include "absl/strings/string_view.h"
+#include "opencensus/trace/span_context.h"
+#include "opencensus/trace/span_id.h"
+#include "opencensus/trace/trace_id.h"
+
+namespace grpc {
+
+// TODO: Rename to GrpcTraceContextV0.
+struct GrpcTraceContext {
+  GrpcTraceContext() {}
+
+  explicit GrpcTraceContext(const ::opencensus::trace::SpanContext& ctx) {
+    ctx.trace_id().CopyTo(trace_id);
+    ctx.span_id().CopyTo(span_id);
+    ctx.trace_options().CopyTo(trace_options);
+  }
+
+  ::opencensus::trace::SpanContext ToSpanContext() const {
+    return ::opencensus::trace::SpanContext(
+        ::opencensus::trace::TraceId(trace_id),
+        ::opencensus::trace::SpanId(span_id),
+        ::opencensus::trace::TraceOptions(trace_options));
+  }
+
+  // TODO: For performance:
+  // uint8_t version;
+  // uint8_t trace_id_field_id;
+  uint8_t trace_id[::opencensus::trace::TraceId::kSize];
+  // uint8_t span_id_field_id;
+  uint8_t span_id[::opencensus::trace::SpanId::kSize];
+  // uint8_t trace_options_field_id;
+  uint8_t trace_options[::opencensus::trace::TraceOptions::kSize];
+};
+
+// TraceContextEncoding encapsulates the logic for encoding and decoding of
+// trace contexts.
+class TraceContextEncoding {
+ public:
+  // Size of encoded GrpcTraceContext. (16 + 8 + 1 + 4)
+  static constexpr size_t kGrpcTraceContextSize = 29;
+  // Error value.
+  static constexpr size_t kEncodeDecodeFailure = 0;
+
+  // Deserializes a GrpcTraceContext from the incoming buffer. Returns the
+  // number of bytes deserialized from the buffer. If the incoming buffer is
+  // empty or the encoding version is not supported it will return 0 bytes,
+  // currently only version 0 is supported. If an unknown field ID is
+  // encountered it will return immediately without parsing the rest of the
+  // buffer. Inlined for performance reasons.
+  static size_t Decode(absl::string_view buf, GrpcTraceContext* tc) {
+    if (buf.empty()) {
+      return kEncodeDecodeFailure;
+    }
+    uint8_t version = buf[kVersionIdOffset];
+    // TODO: Support other versions later. Only support version 0 for
+    // now.
+    if (version != kVersionId) {
+      return kEncodeDecodeFailure;
+    }
+
+    size_t pos = kVersionIdSize;
+    while (pos < buf.size()) {
+      size_t bytes_read =
+          ParseField(absl::string_view(&buf[pos], buf.size() - pos), tc);
+      if (bytes_read == 0) {
+        break;
+      } else {
+        pos += bytes_read;
+      }
+    }
+    return pos;
+  }
+
+  // Serializes a GrpcTraceContext into the provided buffer. Returns the number
+  // of bytes serialized into the buffer. If the buffer is not of sufficient
+  // size (it must be at least kGrpcTraceContextSize bytes) it will drop
+  // everything and return 0 bytes serialized. Inlined for performance reasons.
+  static size_t Encode(const GrpcTraceContext& tc, char* buf, size_t buf_size) {
+    if (buf_size < kGrpcTraceContextSize) {
+      return kEncodeDecodeFailure;
+    }
+    buf[kVersionIdOffset] = kVersionId;
+    buf[kTraceIdOffset] = kTraceIdField;
+    memcpy(&buf[kTraceIdOffset + 1], tc.trace_id,
+           opencensus::trace::TraceId::kSize);
+    buf[kSpanIdOffset] = kSpanIdField;
+    memcpy(&buf[kSpanIdOffset + 1], tc.span_id,
+           opencensus::trace::SpanId::kSize);
+    buf[kTraceOptionsOffset] = kTraceOptionsField;
+    memcpy(&buf[kTraceOptionsOffset + 1], tc.trace_options,
+           opencensus::trace::TraceOptions::kSize);
+    return kGrpcTraceContextSize;
+  }
+
+ private:
+  // Parses the next field from the incoming buffer and stores the parsed value
+  // in a GrpcTraceContext struct.  If it does not recognize the field ID it
+  // will return 0, otherwise it returns the number of bytes read.
+  static size_t ParseField(absl::string_view buf, GrpcTraceContext* tc) {
+    // TODO: Add support for multi-byte field IDs.
+    if (buf.empty()) {
+      return 0;
+    }
+    // Field ID is always the first byte in a field.
+    uint32_t field_id = buf[0];
+    size_t bytes_read = kFieldIdSize;
+    switch (field_id) {
+      case kTraceIdField:
+        bytes_read += kTraceIdSize;
+        if (bytes_read > buf.size()) {
+          return 0;
+        }
+        memcpy(tc->trace_id, &buf[kFieldIdSize],
+               opencensus::trace::TraceId::kSize);
+        break;
+      case kSpanIdField:
+        bytes_read += kSpanIdSize;
+        if (bytes_read > buf.size()) {
+          return 0;
+        }
+        memcpy(tc->span_id, &buf[kFieldIdSize],
+               opencensus::trace::SpanId::kSize);
+        break;
+      case kTraceOptionsField:
+        bytes_read += kTraceOptionsSize;
+        if (bytes_read > buf.size()) {
+          return 0;
+        }
+        memcpy(tc->trace_options, &buf[kFieldIdSize],
+               opencensus::trace::TraceOptions::kSize);
+        break;
+      default:  // Invalid field ID
+        return 0;
+    }
+
+    return bytes_read;
+  }
+
+  // Size of Version ID.
+  static constexpr size_t kVersionIdSize = 1;
+  // Size of Field ID.
+  static constexpr size_t kFieldIdSize = 1;
+
+  // Offset and value for currently supported version ID.
+  static constexpr size_t kVersionIdOffset = 0;
+  static constexpr size_t kVersionId = 0;
+
+  // Fixed Field ID values:
+  enum FieldIdValue {
+    kTraceIdField = 0,
+    kSpanIdField = 1,
+    kTraceOptionsField = 2,
+  };
+
+  // Field data sizes in bytes
+  enum FieldSize {
+    kTraceIdSize = 16,
+    kSpanIdSize = 8,
+    kTraceOptionsSize = 1,
+  };
+
+  // Fixed size offsets for field ID start positions during encoding.  Field
+  // data immediately follows.
+  enum FieldIdOffset {
+    kTraceIdOffset = kVersionIdSize,
+    kSpanIdOffset = kTraceIdOffset + kFieldIdSize + kTraceIdSize,
+    kTraceOptionsOffset = kSpanIdOffset + kFieldIdSize + kSpanIdSize,
+  };
+
+  TraceContextEncoding() = delete;
+  TraceContextEncoding(const TraceContextEncoding&) = delete;
+  TraceContextEncoding(TraceContextEncoding&&) = delete;
+  TraceContextEncoding operator=(const TraceContextEncoding&) = delete;
+  TraceContextEncoding operator=(TraceContextEncoding&&) = delete;
+};
+
+// TODO: This may not be needed. Check to see if opencensus requires
+// a trailing server response.
+// RpcServerStatsEncoding encapsulates the logic for encoding and decoding of
+// rpc server stats messages. Rpc server stats consists of a uint64_t time
+// value (server latency in nanoseconds).
+class RpcServerStatsEncoding {
+ public:
+  // Size of encoded RPC server stats.
+  static constexpr size_t kRpcServerStatsSize = 10;
+  // Error value.
+  static constexpr size_t kEncodeDecodeFailure = 0;
+
+  // Deserializes rpc server stats from the incoming 'buf' into *time.  Returns
+  // number of bytes decoded. If the buffer is of insufficient size (it must be
+  // at least kRpcServerStatsSize bytes) or the encoding version or field ID are
+  // unrecognized, *time will be set to 0 and it will return
+  // kEncodeDecodeFailure. Inlined for performance reasons.
+  static size_t Decode(absl::string_view buf, uint64_t* time) {
+    if (buf.size() < kRpcServerStatsSize) {
+      *time = 0;
+      return kEncodeDecodeFailure;
+    }
+
+    uint8_t version = buf[kVersionIdOffset];
+    uint32_t fieldID = buf[kServerElapsedTimeOffset];
+    if (version != kVersionId || fieldID != kServerElapsedTimeField) {
+      *time = 0;
+      return kEncodeDecodeFailure;
+    }
+    *time = absl::little_endian::Load64(
+        &buf[kServerElapsedTimeOffset + kFieldIdSize]);
+    return kRpcServerStatsSize;
+  }
+
+  // Serializes rpc server stats into the provided buffer.  It returns the
+  // number of bytes written to the buffer. If the buffer is smaller than
+  // kRpcServerStatsSize bytes it will return kEncodeDecodeFailure. Inlined for
+  // performance reasons.
+  static size_t Encode(uint64_t time, char* buf, size_t buf_size) {
+    if (buf_size < kRpcServerStatsSize) {
+      return kEncodeDecodeFailure;
+    }
+
+    buf[kVersionIdOffset] = kVersionId;
+    buf[kServerElapsedTimeOffset] = kServerElapsedTimeField;
+    absl::little_endian::Store64(&buf[kServerElapsedTimeOffset + kFieldIdSize],
+                                 time);
+    return kRpcServerStatsSize;
+  }
+
+ private:
+  // Size of Version ID.
+  static constexpr size_t kVersionIdSize = 1;
+  // Size of Field ID.
+  static constexpr size_t kFieldIdSize = 1;
+
+  // Offset and value for currently supported version ID.
+  static constexpr size_t kVersionIdOffset = 0;
+  static constexpr size_t kVersionId = 0;
+
+  enum FieldIdValue {
+    kServerElapsedTimeField = 0,
+  };
+
+  enum FieldSize {
+    kServerElapsedTimeSize = 8,
+  };
+
+  enum FieldIdOffset {
+    kServerElapsedTimeOffset = kVersionIdSize,
+  };
+
+  RpcServerStatsEncoding() = delete;
+  RpcServerStatsEncoding(const RpcServerStatsEncoding&) = delete;
+  RpcServerStatsEncoding(RpcServerStatsEncoding&&) = delete;
+  RpcServerStatsEncoding operator=(const RpcServerStatsEncoding&) = delete;
+  RpcServerStatsEncoding operator=(RpcServerStatsEncoding&&) = delete;
+};
+
+}  // namespace grpc
+
+#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H */
diff --git a/third_party/grpc/src/cpp/ext/filters/census/server_filter.cc b/third_party/grpc/src/cpp/ext/filters/census/server_filter.cc
new file mode 100644
index 0000000..b5f3d5a
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/server_filter.cc
@@ -0,0 +1,198 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/server_filter.h"
+
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "opencensus/stats/stats.h"
+#include "src/core/lib/surface/call.h"
+#include "src/cpp/ext/filters/census/grpc_plugin.h"
+#include "src/cpp/ext/filters/census/measures.h"
+
+namespace grpc {
+
+constexpr uint32_t CensusServerCallData::kMaxServerStatsLen;
+
+namespace {
+
+// server metadata elements
+struct ServerMetadataElements {
+  grpc_slice path;
+  grpc_slice tracing_slice;
+  grpc_slice census_proto;
+};
+
+void FilterInitialMetadata(grpc_metadata_batch* b,
+                           ServerMetadataElements* sml) {
+  if (b->idx.named.path != nullptr) {
+    sml->path = grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.path->md));
+  }
+  if (b->idx.named.grpc_trace_bin != nullptr) {
+    sml->tracing_slice =
+        grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_trace_bin->md));
+    grpc_metadata_batch_remove(b, b->idx.named.grpc_trace_bin);
+  }
+  if (b->idx.named.grpc_tags_bin != nullptr) {
+    sml->census_proto =
+        grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_tags_bin->md));
+    grpc_metadata_batch_remove(b, b->idx.named.grpc_tags_bin);
+  }
+}
+
+}  // namespace
+
+void CensusServerCallData::OnDoneRecvMessageCb(void* user_data,
+                                               grpc_error* error) {
+  grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data);
+  CensusServerCallData* calld =
+      reinterpret_cast<CensusServerCallData*>(elem->call_data);
+  CensusChannelData* channeld =
+      reinterpret_cast<CensusChannelData*>(elem->channel_data);
+  GPR_ASSERT(calld != nullptr);
+  GPR_ASSERT(channeld != nullptr);
+  // Stream messages are no longer valid after receiving trailing metadata.
+  if ((*calld->recv_message_) != nullptr) {
+    ++calld->recv_message_count_;
+  }
+  GRPC_CLOSURE_RUN(calld->initial_on_done_recv_message_, GRPC_ERROR_REF(error));
+}
+
+void CensusServerCallData::OnDoneRecvInitialMetadataCb(void* user_data,
+                                                       grpc_error* error) {
+  grpc_call_element* elem = reinterpret_cast<grpc_call_element*>(user_data);
+  CensusServerCallData* calld =
+      reinterpret_cast<CensusServerCallData*>(elem->call_data);
+  GPR_ASSERT(calld != nullptr);
+  if (error == GRPC_ERROR_NONE) {
+    grpc_metadata_batch* initial_metadata = calld->recv_initial_metadata_;
+    GPR_ASSERT(initial_metadata != nullptr);
+    ServerMetadataElements sml;
+    sml.path = grpc_empty_slice();
+    sml.tracing_slice = grpc_empty_slice();
+    sml.census_proto = grpc_empty_slice();
+    FilterInitialMetadata(initial_metadata, &sml);
+    calld->path_ = grpc_slice_ref_internal(sml.path);
+    calld->method_ = GetMethod(&calld->path_);
+    calld->qualified_method_ = absl::StrCat("Recv.", calld->method_);
+    const char* tracing_str =
+        GRPC_SLICE_IS_EMPTY(sml.tracing_slice)
+            ? ""
+            : reinterpret_cast<const char*>(
+                  GRPC_SLICE_START_PTR(sml.tracing_slice));
+    size_t tracing_str_len = GRPC_SLICE_IS_EMPTY(sml.tracing_slice)
+                                 ? 0
+                                 : GRPC_SLICE_LENGTH(sml.tracing_slice);
+    const char* census_str = GRPC_SLICE_IS_EMPTY(sml.census_proto)
+                                 ? ""
+                                 : reinterpret_cast<const char*>(
+                                       GRPC_SLICE_START_PTR(sml.census_proto));
+    size_t census_str_len = GRPC_SLICE_IS_EMPTY(sml.census_proto)
+                                ? 0
+                                : GRPC_SLICE_LENGTH(sml.census_proto);
+
+    GenerateServerContext(absl::string_view(tracing_str, tracing_str_len),
+                          absl::string_view(census_str, census_str_len),
+                          /*primary_role*/ "", calld->qualified_method_,
+                          &calld->context_);
+
+    grpc_slice_unref_internal(sml.tracing_slice);
+    grpc_slice_unref_internal(sml.census_proto);
+    grpc_slice_unref_internal(sml.path);
+    grpc_census_call_set_context(
+        calld->gc_, reinterpret_cast<census_context*>(&calld->context_));
+  }
+  GRPC_CLOSURE_RUN(calld->initial_on_done_recv_initial_metadata_,
+                   GRPC_ERROR_REF(error));
+}
+
+void CensusServerCallData::StartTransportStreamOpBatch(
+    grpc_call_element* elem, TransportStreamOpBatch* op) {
+  if (op->recv_initial_metadata() != nullptr) {
+    // substitute our callback for the op callback
+    recv_initial_metadata_ = op->recv_initial_metadata()->batch();
+    initial_on_done_recv_initial_metadata_ = op->recv_initial_metadata_ready();
+    op->set_recv_initial_metadata_ready(&on_done_recv_initial_metadata_);
+  }
+  if (op->send_message() != nullptr) {
+    ++sent_message_count_;
+  }
+  if (op->recv_message() != nullptr) {
+    recv_message_ = op->op()->payload->recv_message.recv_message;
+    initial_on_done_recv_message_ =
+        op->op()->payload->recv_message.recv_message_ready;
+    op->op()->payload->recv_message.recv_message_ready = &on_done_recv_message_;
+  }
+  // We need to record the time when the trailing metadata was sent to mark the
+  // completeness of the request.
+  if (op->send_trailing_metadata() != nullptr) {
+    elapsed_time_ = absl::Now() - start_time_;
+    size_t len = ServerStatsSerialize(absl::ToInt64Nanoseconds(elapsed_time_),
+                                      stats_buf_, kMaxServerStatsLen);
+    if (len > 0) {
+      GRPC_LOG_IF_ERROR(
+          "census grpc_filter",
+          grpc_metadata_batch_add_tail(
+              op->send_trailing_metadata()->batch(), &census_bin_,
+              grpc_mdelem_from_slices(
+                  GRPC_MDSTR_GRPC_SERVER_STATS_BIN,
+                  grpc_slice_from_copied_buffer(stats_buf_, len))));
+    }
+  }
+  // Call next op.
+  grpc_call_next_op(elem, op->op());
+}
+
+grpc_error* CensusServerCallData::Init(grpc_call_element* elem,
+                                       const grpc_call_element_args* args) {
+  start_time_ = absl::Now();
+  gc_ =
+      grpc_call_from_top_element(grpc_call_stack_element(args->call_stack, 0));
+  GRPC_CLOSURE_INIT(&on_done_recv_initial_metadata_,
+                    OnDoneRecvInitialMetadataCb, elem,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CLOSURE_INIT(&on_done_recv_message_, OnDoneRecvMessageCb, elem,
+                    grpc_schedule_on_exec_ctx);
+  auth_context_ = grpc_call_auth_context(gc_);
+  return GRPC_ERROR_NONE;
+}
+
+void CensusServerCallData::Destroy(grpc_call_element* elem,
+                                   const grpc_call_final_info* final_info,
+                                   grpc_closure* then_call_closure) {
+  const uint64_t request_size = GetOutgoingDataSize(final_info);
+  const uint64_t response_size = GetIncomingDataSize(final_info);
+  double elapsed_time_ms = absl::ToDoubleMilliseconds(elapsed_time_);
+  grpc_auth_context_release(auth_context_);
+  ::opencensus::stats::Record(
+      {{RpcServerSentBytesPerRpc(), static_cast<double>(response_size)},
+       {RpcServerReceivedBytesPerRpc(), static_cast<double>(request_size)},
+       {RpcServerServerLatency(), elapsed_time_ms},
+       {RpcServerSentMessagesPerRpc(), sent_message_count_},
+       {RpcServerReceivedMessagesPerRpc(), recv_message_count_}},
+      {{ServerMethodTagKey(), method_},
+       {ServerStatusTagKey(), StatusCodeToString(final_info->final_status)}});
+  grpc_slice_unref_internal(path_);
+  context_.EndSpan();
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/filters/census/server_filter.h b/third_party/grpc/src/cpp/ext/filters/census/server_filter.h
new file mode 100644
index 0000000..e393ed3
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/server_filter.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H
+#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "absl/strings/string_view.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "include/grpc/grpc_security.h"
+#include "src/cpp/ext/filters/census/channel_filter.h"
+#include "src/cpp/ext/filters/census/context.h"
+
+namespace grpc {
+
+// A CallData class will be created for every grpc call within a channel. It is
+// used to store data and methods specific to that call. CensusServerCallData is
+// thread-compatible, however typically only 1 thread should be interacting with
+// a call at a time.
+class CensusServerCallData : public CallData {
+ public:
+  // Maximum size of server stats that are sent on the wire.
+  static constexpr uint32_t kMaxServerStatsLen = 16;
+
+  CensusServerCallData()
+      : gc_(nullptr),
+        auth_context_(nullptr),
+        recv_initial_metadata_(nullptr),
+        initial_on_done_recv_initial_metadata_(nullptr),
+        initial_on_done_recv_message_(nullptr),
+        recv_message_(nullptr),
+        recv_message_count_(0),
+        sent_message_count_(0) {
+    memset(&census_bin_, 0, sizeof(grpc_linked_mdelem));
+    memset(&path_, 0, sizeof(grpc_slice));
+    memset(&on_done_recv_initial_metadata_, 0, sizeof(grpc_closure));
+    memset(&on_done_recv_message_, 0, sizeof(grpc_closure));
+  }
+
+  grpc_error* Init(grpc_call_element* elem,
+                   const grpc_call_element_args* args) override;
+
+  void Destroy(grpc_call_element* elem, const grpc_call_final_info* final_info,
+               grpc_closure* then_call_closure) override;
+
+  void StartTransportStreamOpBatch(grpc_call_element* elem,
+                                   TransportStreamOpBatch* op) override;
+
+  static void OnDoneRecvInitialMetadataCb(void* user_data, grpc_error* error);
+
+  static void OnDoneRecvMessageCb(void* user_data, grpc_error* error);
+
+ private:
+  CensusContext context_;
+  // server method
+  absl::string_view method_;
+  std::string qualified_method_;
+  grpc_slice path_;
+  // Pointer to the grpc_call element
+  grpc_call* gc_;
+  // Authorization context for the call.
+  grpc_auth_context* auth_context_;
+  // Metadata element for census stats.
+  grpc_linked_mdelem census_bin_;
+  // recv callback
+  grpc_metadata_batch* recv_initial_metadata_;
+  grpc_closure* initial_on_done_recv_initial_metadata_;
+  grpc_closure on_done_recv_initial_metadata_;
+  // recv message
+  grpc_closure* initial_on_done_recv_message_;
+  grpc_closure on_done_recv_message_;
+  absl::Time start_time_;
+  absl::Duration elapsed_time_;
+  grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message_;
+  uint64_t recv_message_count_;
+  uint64_t sent_message_count_;
+  // Buffer needed for grpc_slice to reference it when adding metatdata to
+  // response.
+  char stats_buf_[kMaxServerStatsLen];
+};
+
+}  // namespace grpc
+
+#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H */
diff --git a/third_party/grpc/src/cpp/ext/filters/census/views.cc b/third_party/grpc/src/cpp/ext/filters/census/views.cc
new file mode 100644
index 0000000..2c0c5f7
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/filters/census/views.cc
@@ -0,0 +1,491 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/ext/filters/census/grpc_plugin.h"
+
+#include "absl/time/time.h"
+#include "opencensus/stats/internal/aggregation_window.h"
+#include "opencensus/stats/internal/set_aggregation_window.h"
+#include "opencensus/stats/stats.h"
+
+namespace grpc {
+
+using ::opencensus::stats::Aggregation;
+using ::opencensus::stats::AggregationWindow;
+using ::opencensus::stats::BucketBoundaries;
+using ::opencensus::stats::ViewDescriptor;
+
+// These measure definitions should be kept in sync across opencensus
+// implementations.
+
+namespace {
+
+Aggregation BytesDistributionAggregation() {
+  return Aggregation::Distribution(BucketBoundaries::Explicit(
+      {0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216,
+       67108864, 268435456, 1073741824, 4294967296}));
+}
+
+Aggregation MillisDistributionAggregation() {
+  return Aggregation::Distribution(BucketBoundaries::Explicit(
+      {0,   0.01, 0.05, 0.1,  0.3,   0.6,   0.8,   1,     2,   3,   4,
+       5,   6,    8,    10,   13,    16,    20,    25,    30,  40,  50,
+       65,  80,   100,  130,  160,   200,   250,   300,   400, 500, 650,
+       800, 1000, 2000, 5000, 10000, 20000, 50000, 100000}));
+}
+
+Aggregation CountDistributionAggregation() {
+  return Aggregation::Distribution(BucketBoundaries::Exponential(17, 1.0, 2.0));
+}
+
+ViewDescriptor MinuteDescriptor() {
+  auto descriptor = ViewDescriptor();
+  SetAggregationWindow(AggregationWindow::Interval(absl::Minutes(1)),
+                       &descriptor);
+  return descriptor;
+}
+
+ViewDescriptor HourDescriptor() {
+  auto descriptor = ViewDescriptor();
+  SetAggregationWindow(AggregationWindow::Interval(absl::Hours(1)),
+                       &descriptor);
+  return descriptor;
+}
+
+}  // namespace
+
+void RegisterOpenCensusViewsForExport() {
+  ClientSentMessagesPerRpcCumulative().RegisterForExport();
+  ClientSentBytesPerRpcCumulative().RegisterForExport();
+  ClientReceivedMessagesPerRpcCumulative().RegisterForExport();
+  ClientReceivedBytesPerRpcCumulative().RegisterForExport();
+  ClientRoundtripLatencyCumulative().RegisterForExport();
+  ClientServerLatencyCumulative().RegisterForExport();
+
+  ServerSentMessagesPerRpcCumulative().RegisterForExport();
+  ServerSentBytesPerRpcCumulative().RegisterForExport();
+  ServerReceivedMessagesPerRpcCumulative().RegisterForExport();
+  ServerReceivedBytesPerRpcCumulative().RegisterForExport();
+  ServerServerLatencyCumulative().RegisterForExport();
+}
+
+// client cumulative
+const ViewDescriptor& ClientSentBytesPerRpcCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/client/sent_bytes_per_rpc/cumulative")
+          .set_measure(kRpcClientSentBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientReceivedBytesPerRpcCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/client/received_bytes_per_rpc/cumulative")
+          .set_measure(kRpcClientReceivedBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientRoundtripLatencyCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/client/roundtrip_latency/cumulative")
+          .set_measure(kRpcClientRoundtripLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientServerLatencyCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/client/server_latency/cumulative")
+          .set_measure(kRpcClientServerLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientCompletedRpcsCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/client/completed_rpcs/cumulative")
+          .set_measure(kRpcClientRoundtripLatencyMeasureName)
+          .set_aggregation(Aggregation::Count())
+          .add_column(ClientMethodTagKey())
+          .add_column(ClientStatusTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientSentMessagesPerRpcCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/client/received_messages_per_rpc/cumulative")
+          .set_measure(kRpcClientSentMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientReceivedMessagesPerRpcCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/client/sent_messages_per_rpc/cumulative")
+          .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+// server cumulative
+const ViewDescriptor& ServerSentBytesPerRpcCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/server/received_bytes_per_rpc/cumulative")
+          .set_measure(kRpcServerSentBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerReceivedBytesPerRpcCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/server/sent_bytes_per_rpc/cumulative")
+          .set_measure(kRpcServerReceivedBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerServerLatencyCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/server/elapsed_time/cumulative")
+          .set_measure(kRpcServerServerLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerCompletedRpcsCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/server/completed_rpcs/cumulative")
+          .set_measure(kRpcServerServerLatencyMeasureName)
+          .set_aggregation(Aggregation::Count())
+          .add_column(ServerMethodTagKey())
+          .add_column(ServerStatusTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerSentMessagesPerRpcCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/server/received_messages_per_rpc/cumulative")
+          .set_measure(kRpcServerSentMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerReceivedMessagesPerRpcCumulative() {
+  const static ViewDescriptor descriptor =
+      ViewDescriptor()
+          .set_name("grpc.io/server/sent_messages_per_rpc/cumulative")
+          .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+// client minute
+const ViewDescriptor& ClientSentBytesPerRpcMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/client/sent_bytes_per_rpc/minute")
+          .set_measure(kRpcClientSentBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientReceivedBytesPerRpcMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/client/received_bytes_per_rpc/minute")
+          .set_measure(kRpcClientReceivedBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientRoundtripLatencyMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/client/roundtrip_latency/minute")
+          .set_measure(kRpcClientRoundtripLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientServerLatencyMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/client/server_latency/minute")
+          .set_measure(kRpcClientServerLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientCompletedRpcsMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/client/completed_rpcs/minute")
+          .set_measure(kRpcClientRoundtripLatencyMeasureName)
+          .set_aggregation(Aggregation::Count())
+          .add_column(ClientMethodTagKey())
+          .add_column(ClientStatusTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientSentMessagesPerRpcMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/client/sent_messages_per_rpc/minute")
+          .set_measure(kRpcClientSentMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientReceivedMessagesPerRpcMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/client/received_messages_per_rpc/minute")
+          .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+// server minute
+const ViewDescriptor& ServerSentBytesPerRpcMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/server/sent_bytes_per_rpc/minute")
+          .set_measure(kRpcServerSentBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerReceivedBytesPerRpcMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/server/received_bytes_per_rpc/minute")
+          .set_measure(kRpcServerReceivedBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerServerLatencyMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/server/server_latency/minute")
+          .set_measure(kRpcServerServerLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerCompletedRpcsMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/server/completed_rpcs/minute")
+          .set_measure(kRpcServerServerLatencyMeasureName)
+          .set_aggregation(Aggregation::Count())
+          .add_column(ServerMethodTagKey())
+          .add_column(ServerStatusTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerSentMessagesPerRpcMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/server/sent_messages_per_rpc/minute")
+          .set_measure(kRpcServerSentMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerReceivedMessagesPerRpcMinute() {
+  const static ViewDescriptor descriptor =
+      MinuteDescriptor()
+          .set_name("grpc.io/server/received_messages_per_rpc/minute")
+          .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+// client hour
+const ViewDescriptor& ClientSentBytesPerRpcHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/client/sent_bytes_per_rpc/hour")
+          .set_measure(kRpcClientSentBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientReceivedBytesPerRpcHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/client/received_bytes_per_rpc/hour")
+          .set_measure(kRpcClientReceivedBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientRoundtripLatencyHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/client/roundtrip_latency/hour")
+          .set_measure(kRpcClientRoundtripLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientServerLatencyHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/client/server_latency/hour")
+          .set_measure(kRpcClientServerLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientCompletedRpcsHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/client/completed_rpcs/hour")
+          .set_measure(kRpcClientRoundtripLatencyMeasureName)
+          .set_aggregation(Aggregation::Count())
+          .add_column(ClientMethodTagKey())
+          .add_column(ClientStatusTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientSentMessagesPerRpcHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/client/sent_messages_per_rpc/hour")
+          .set_measure(kRpcClientSentMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ClientReceivedMessagesPerRpcHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/client/received_messages_per_rpc/hour")
+          .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ClientMethodTagKey());
+  return descriptor;
+}
+
+// server hour
+const ViewDescriptor& ServerSentBytesPerRpcHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/server/sent_bytes_per_rpc/hour")
+          .set_measure(kRpcServerSentBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerReceivedBytesPerRpcHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/server/received_bytes_per_rpc/hour")
+          .set_measure(kRpcServerReceivedBytesPerRpcMeasureName)
+          .set_aggregation(BytesDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerServerLatencyHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/server/server_latency/hour")
+          .set_measure(kRpcServerServerLatencyMeasureName)
+          .set_aggregation(MillisDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerCompletedRpcsHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/server/completed_rpcs/hour")
+          .set_measure(kRpcServerServerLatencyMeasureName)
+          .set_aggregation(Aggregation::Count())
+          .add_column(ServerMethodTagKey())
+          .add_column(ServerStatusTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerSentMessagesPerRpcHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/server/sent_messages_per_rpc/hour")
+          .set_measure(kRpcServerSentMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+const ViewDescriptor& ServerReceivedMessagesPerRpcHour() {
+  const static ViewDescriptor descriptor =
+      HourDescriptor()
+          .set_name("grpc.io/server/received_messages_per_rpc/hour")
+          .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName)
+          .set_aggregation(CountDistributionAggregation())
+          .add_column(ServerMethodTagKey());
+  return descriptor;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/proto_server_reflection.cc b/third_party/grpc/src/cpp/ext/proto_server_reflection.cc
new file mode 100644
index 0000000..f26f007
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/proto_server_reflection.cc
@@ -0,0 +1,212 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <unordered_set>
+#include <vector>
+
+#include <grpcpp/grpcpp.h>
+
+#include "src/cpp/ext/proto_server_reflection.h"
+
+using grpc::Status;
+using grpc::StatusCode;
+using grpc::reflection::v1alpha::ErrorResponse;
+using grpc::reflection::v1alpha::ExtensionNumberResponse;
+using grpc::reflection::v1alpha::ExtensionRequest;
+using grpc::reflection::v1alpha::FileDescriptorResponse;
+using grpc::reflection::v1alpha::ListServiceResponse;
+using grpc::reflection::v1alpha::ServerReflectionRequest;
+using grpc::reflection::v1alpha::ServerReflectionResponse;
+using grpc::reflection::v1alpha::ServiceResponse;
+
+namespace grpc {
+
+ProtoServerReflection::ProtoServerReflection()
+    : descriptor_pool_(protobuf::DescriptorPool::generated_pool()) {}
+
+void ProtoServerReflection::SetServiceList(
+    const std::vector<grpc::string>* services) {
+  services_ = services;
+}
+
+Status ProtoServerReflection::ServerReflectionInfo(
+    ServerContext* context,
+    ServerReaderWriter<ServerReflectionResponse, ServerReflectionRequest>*
+        stream) {
+  ServerReflectionRequest request;
+  ServerReflectionResponse response;
+  Status status;
+  while (stream->Read(&request)) {
+    switch (request.message_request_case()) {
+      case ServerReflectionRequest::MessageRequestCase::kFileByFilename:
+        status = GetFileByName(context, request.file_by_filename(), &response);
+        break;
+      case ServerReflectionRequest::MessageRequestCase::kFileContainingSymbol:
+        status = GetFileContainingSymbol(
+            context, request.file_containing_symbol(), &response);
+        break;
+      case ServerReflectionRequest::MessageRequestCase::
+          kFileContainingExtension:
+        status = GetFileContainingExtension(
+            context, &request.file_containing_extension(), &response);
+        break;
+      case ServerReflectionRequest::MessageRequestCase::
+          kAllExtensionNumbersOfType:
+        status = GetAllExtensionNumbers(
+            context, request.all_extension_numbers_of_type(),
+            response.mutable_all_extension_numbers_response());
+        break;
+      case ServerReflectionRequest::MessageRequestCase::kListServices:
+        status =
+            ListService(context, response.mutable_list_services_response());
+        break;
+      default:
+        status = Status(StatusCode::UNIMPLEMENTED, "");
+    }
+
+    if (!status.ok()) {
+      FillErrorResponse(status, response.mutable_error_response());
+    }
+    response.set_valid_host(request.host());
+    response.set_allocated_original_request(
+        new ServerReflectionRequest(request));
+    stream->Write(response);
+  }
+
+  return Status::OK;
+}
+
+void ProtoServerReflection::FillErrorResponse(const Status& status,
+                                              ErrorResponse* error_response) {
+  error_response->set_error_code(status.error_code());
+  error_response->set_error_message(status.error_message());
+}
+
+Status ProtoServerReflection::ListService(ServerContext* context,
+                                          ListServiceResponse* response) {
+  if (services_ == nullptr) {
+    return Status(StatusCode::NOT_FOUND, "Services not found.");
+  }
+  for (auto it = services_->begin(); it != services_->end(); ++it) {
+    ServiceResponse* service_response = response->add_service();
+    service_response->set_name(*it);
+  }
+  return Status::OK;
+}
+
+Status ProtoServerReflection::GetFileByName(
+    ServerContext* context, const grpc::string& filename,
+    ServerReflectionResponse* response) {
+  if (descriptor_pool_ == nullptr) {
+    return Status::CANCELLED;
+  }
+
+  const protobuf::FileDescriptor* file_desc =
+      descriptor_pool_->FindFileByName(filename);
+  if (file_desc == nullptr) {
+    return Status(StatusCode::NOT_FOUND, "File not found.");
+  }
+  std::unordered_set<grpc::string> seen_files;
+  FillFileDescriptorResponse(file_desc, response, &seen_files);
+  return Status::OK;
+}
+
+Status ProtoServerReflection::GetFileContainingSymbol(
+    ServerContext* context, const grpc::string& symbol,
+    ServerReflectionResponse* response) {
+  if (descriptor_pool_ == nullptr) {
+    return Status::CANCELLED;
+  }
+
+  const protobuf::FileDescriptor* file_desc =
+      descriptor_pool_->FindFileContainingSymbol(symbol);
+  if (file_desc == nullptr) {
+    return Status(StatusCode::NOT_FOUND, "Symbol not found.");
+  }
+  std::unordered_set<grpc::string> seen_files;
+  FillFileDescriptorResponse(file_desc, response, &seen_files);
+  return Status::OK;
+}
+
+Status ProtoServerReflection::GetFileContainingExtension(
+    ServerContext* context, const ExtensionRequest* request,
+    ServerReflectionResponse* response) {
+  if (descriptor_pool_ == nullptr) {
+    return Status::CANCELLED;
+  }
+
+  const protobuf::Descriptor* desc =
+      descriptor_pool_->FindMessageTypeByName(request->containing_type());
+  if (desc == nullptr) {
+    return Status(StatusCode::NOT_FOUND, "Type not found.");
+  }
+
+  const protobuf::FieldDescriptor* field_desc =
+      descriptor_pool_->FindExtensionByNumber(desc,
+                                              request->extension_number());
+  if (field_desc == nullptr) {
+    return Status(StatusCode::NOT_FOUND, "Extension not found.");
+  }
+  std::unordered_set<grpc::string> seen_files;
+  FillFileDescriptorResponse(field_desc->file(), response, &seen_files);
+  return Status::OK;
+}
+
+Status ProtoServerReflection::GetAllExtensionNumbers(
+    ServerContext* context, const grpc::string& type,
+    ExtensionNumberResponse* response) {
+  if (descriptor_pool_ == nullptr) {
+    return Status::CANCELLED;
+  }
+
+  const protobuf::Descriptor* desc =
+      descriptor_pool_->FindMessageTypeByName(type);
+  if (desc == nullptr) {
+    return Status(StatusCode::NOT_FOUND, "Type not found.");
+  }
+
+  std::vector<const protobuf::FieldDescriptor*> extensions;
+  descriptor_pool_->FindAllExtensions(desc, &extensions);
+  for (auto it = extensions.begin(); it != extensions.end(); it++) {
+    response->add_extension_number((*it)->number());
+  }
+  response->set_base_type_name(type);
+  return Status::OK;
+}
+
+void ProtoServerReflection::FillFileDescriptorResponse(
+    const protobuf::FileDescriptor* file_desc,
+    ServerReflectionResponse* response,
+    std::unordered_set<grpc::string>* seen_files) {
+  if (seen_files->find(file_desc->name()) != seen_files->end()) {
+    return;
+  }
+  seen_files->insert(file_desc->name());
+
+  protobuf::FileDescriptorProto file_desc_proto;
+  grpc::string data;
+  file_desc->CopyTo(&file_desc_proto);
+  file_desc_proto.SerializeToString(&data);
+  response->mutable_file_descriptor_response()->add_file_descriptor_proto(data);
+
+  for (int i = 0; i < file_desc->dependency_count(); ++i) {
+    FillFileDescriptorResponse(file_desc->dependency(i), response, seen_files);
+  }
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/ext/proto_server_reflection.h b/third_party/grpc/src/cpp/ext/proto_server_reflection.h
new file mode 100644
index 0000000..bf40c3c
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/proto_server_reflection.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_EXT_PROTO_SERVER_REFLECTION_H
+#define GRPC_INTERNAL_CPP_EXT_PROTO_SERVER_REFLECTION_H
+
+#include <unordered_set>
+#include <vector>
+
+#include <grpcpp/grpcpp.h>
+#include "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h"
+
+namespace grpc {
+
+class ProtoServerReflection final
+    : public reflection::v1alpha::ServerReflection::Service {
+ public:
+  ProtoServerReflection();
+
+  // Add the full names of registered services
+  void SetServiceList(const std::vector<grpc::string>* services);
+
+  // implementation of ServerReflectionInfo(stream ServerReflectionRequest) rpc
+  // in ServerReflection service
+  Status ServerReflectionInfo(
+      ServerContext* context,
+      ServerReaderWriter<reflection::v1alpha::ServerReflectionResponse,
+                         reflection::v1alpha::ServerReflectionRequest>* stream)
+      override;
+
+ private:
+  Status ListService(ServerContext* context,
+                     reflection::v1alpha::ListServiceResponse* response);
+
+  Status GetFileByName(ServerContext* context, const grpc::string& file_name,
+                       reflection::v1alpha::ServerReflectionResponse* response);
+
+  Status GetFileContainingSymbol(
+      ServerContext* context, const grpc::string& symbol,
+      reflection::v1alpha::ServerReflectionResponse* response);
+
+  Status GetFileContainingExtension(
+      ServerContext* context,
+      const reflection::v1alpha::ExtensionRequest* request,
+      reflection::v1alpha::ServerReflectionResponse* response);
+
+  Status GetAllExtensionNumbers(
+      ServerContext* context, const grpc::string& type,
+      reflection::v1alpha::ExtensionNumberResponse* response);
+
+  void FillFileDescriptorResponse(
+      const protobuf::FileDescriptor* file_desc,
+      reflection::v1alpha::ServerReflectionResponse* response,
+      std::unordered_set<grpc::string>* seen_files);
+
+  void FillErrorResponse(const Status& status,
+                         reflection::v1alpha::ErrorResponse* error_response);
+
+  const protobuf::DescriptorPool* descriptor_pool_;
+  const std::vector<string>* services_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_EXT_PROTO_SERVER_REFLECTION_H
diff --git a/third_party/grpc/src/cpp/ext/proto_server_reflection_plugin.cc b/third_party/grpc/src/cpp/ext/proto_server_reflection_plugin.cc
new file mode 100644
index 0000000..ee3ac3f
--- /dev/null
+++ b/third_party/grpc/src/cpp/ext/proto_server_reflection_plugin.cc
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/ext/proto_server_reflection_plugin.h>
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/impl/server_initializer.h>
+#include <grpcpp/server.h>
+
+#include "src/cpp/ext/proto_server_reflection.h"
+
+namespace grpc {
+namespace reflection {
+
+ProtoServerReflectionPlugin::ProtoServerReflectionPlugin()
+    : reflection_service_(new grpc::ProtoServerReflection()) {}
+
+grpc::string ProtoServerReflectionPlugin::name() {
+  return "proto_server_reflection";
+}
+
+void ProtoServerReflectionPlugin::InitServer(grpc::ServerInitializer* si) {
+  si->RegisterService(reflection_service_);
+}
+
+void ProtoServerReflectionPlugin::Finish(grpc::ServerInitializer* si) {
+  reflection_service_->SetServiceList(si->GetServiceList());
+}
+
+void ProtoServerReflectionPlugin::ChangeArguments(const grpc::string& name,
+                                                  void* value) {}
+
+bool ProtoServerReflectionPlugin::has_sync_methods() const {
+  if (reflection_service_) {
+    return reflection_service_->has_synchronous_methods();
+  }
+  return false;
+}
+
+bool ProtoServerReflectionPlugin::has_async_methods() const {
+  if (reflection_service_) {
+    return reflection_service_->has_async_methods();
+  }
+  return false;
+}
+
+static std::unique_ptr< ::grpc::ServerBuilderPlugin> CreateProtoReflection() {
+  return std::unique_ptr< ::grpc::ServerBuilderPlugin>(
+      new ProtoServerReflectionPlugin());
+}
+
+void InitProtoReflectionServerBuilderPlugin() {
+  static bool already_here = false;
+  if (already_here) return;
+  already_here = true;
+  ::grpc::ServerBuilder::InternalAddPluginFactory(&CreateProtoReflection);
+}
+
+// Force InitProtoReflectionServerBuilderPlugin() to be called at static
+// initialization time.
+struct StaticProtoReflectionPluginInitializer {
+  StaticProtoReflectionPluginInitializer() {
+    InitProtoReflectionServerBuilderPlugin();
+  }
+} static_proto_reflection_plugin_initializer;
+
+}  // namespace reflection
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/async_generic_service.cc b/third_party/grpc/src/cpp/server/async_generic_service.cc
new file mode 100644
index 0000000..3061376
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/async_generic_service.cc
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/generic/async_generic_service.h>
+
+#include <grpcpp/server.h>
+
+namespace grpc {
+
+void AsyncGenericService::RequestCall(
+    GenericServerContext* ctx, GenericServerAsyncReaderWriter* reader_writer,
+    CompletionQueue* call_cq, ServerCompletionQueue* notification_cq,
+    void* tag) {
+  server_->RequestAsyncGenericCall(ctx, reader_writer, call_cq, notification_cq,
+                                   tag);
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/channel_argument_option.cc b/third_party/grpc/src/cpp/server/channel_argument_option.cc
new file mode 100644
index 0000000..4d6be90
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/channel_argument_option.cc
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/impl/channel_argument_option.h>
+
+namespace grpc {
+
+std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
+    const grpc::string& name, const grpc::string& value) {
+  class StringOption final : public ServerBuilderOption {
+   public:
+    StringOption(const grpc::string& name, const grpc::string& value)
+        : name_(name), value_(value) {}
+
+    virtual void UpdateArguments(ChannelArguments* args) override {
+      args->SetString(name_, value_);
+    }
+    virtual void UpdatePlugins(
+        std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) override {}
+
+   private:
+    const grpc::string name_;
+    const grpc::string value_;
+  };
+  return std::unique_ptr<ServerBuilderOption>(new StringOption(name, value));
+}
+
+std::unique_ptr<ServerBuilderOption> MakeChannelArgumentOption(
+    const grpc::string& name, int value) {
+  class IntOption final : public ServerBuilderOption {
+   public:
+    IntOption(const grpc::string& name, int value)
+        : name_(name), value_(value) {}
+
+    virtual void UpdateArguments(ChannelArguments* args) override {
+      args->SetInt(name_, value_);
+    }
+    virtual void UpdatePlugins(
+        std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) override {}
+
+   private:
+    const grpc::string name_;
+    const int value_;
+  };
+  return std::unique_ptr<ServerBuilderOption>(new IntOption(name, value));
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/channelz/channelz_service.cc b/third_party/grpc/src/cpp/server/channelz/channelz_service.cc
new file mode 100644
index 0000000..c44a9ac
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/channelz/channelz_service.cc
@@ -0,0 +1,146 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/server/channelz/channelz_service.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+
+namespace grpc {
+
+Status ChannelzService::GetTopChannels(
+    ServerContext* unused, const channelz::v1::GetTopChannelsRequest* request,
+    channelz::v1::GetTopChannelsResponse* response) {
+  char* json_str = grpc_channelz_get_top_channels(request->start_channel_id());
+  if (json_str == nullptr) {
+    return Status(StatusCode::INTERNAL,
+                  "grpc_channelz_get_top_channels returned null");
+  }
+  grpc::protobuf::util::Status s =
+      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  gpr_free(json_str);
+  if (!s.ok()) {
+    return Status(StatusCode::INTERNAL, s.ToString());
+  }
+  return Status::OK;
+}
+
+Status ChannelzService::GetServers(
+    ServerContext* unused, const channelz::v1::GetServersRequest* request,
+    channelz::v1::GetServersResponse* response) {
+  char* json_str = grpc_channelz_get_servers(request->start_server_id());
+  if (json_str == nullptr) {
+    return Status(StatusCode::INTERNAL,
+                  "grpc_channelz_get_servers returned null");
+  }
+  grpc::protobuf::util::Status s =
+      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  gpr_free(json_str);
+  if (!s.ok()) {
+    return Status(StatusCode::INTERNAL, s.ToString());
+  }
+  return Status::OK;
+}
+
+Status ChannelzService::GetServer(ServerContext* unused,
+                                  const channelz::v1::GetServerRequest* request,
+                                  channelz::v1::GetServerResponse* response) {
+  char* json_str = grpc_channelz_get_server(request->server_id());
+  if (json_str == nullptr) {
+    return Status(StatusCode::INTERNAL,
+                  "grpc_channelz_get_server returned null");
+  }
+  grpc::protobuf::util::Status s =
+      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  gpr_free(json_str);
+  if (!s.ok()) {
+    return Status(StatusCode::INTERNAL, s.ToString());
+  }
+  return Status::OK;
+}
+
+Status ChannelzService::GetServerSockets(
+    ServerContext* unused, const channelz::v1::GetServerSocketsRequest* request,
+    channelz::v1::GetServerSocketsResponse* response) {
+  char* json_str = grpc_channelz_get_server_sockets(
+      request->server_id(), request->start_socket_id(), request->max_results());
+  if (json_str == nullptr) {
+    return Status(StatusCode::INTERNAL,
+                  "grpc_channelz_get_server_sockets returned null");
+  }
+  grpc::protobuf::util::Status s =
+      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  gpr_free(json_str);
+  if (!s.ok()) {
+    return Status(StatusCode::INTERNAL, s.ToString());
+  }
+  return Status::OK;
+}
+
+Status ChannelzService::GetChannel(
+    ServerContext* unused, const channelz::v1::GetChannelRequest* request,
+    channelz::v1::GetChannelResponse* response) {
+  char* json_str = grpc_channelz_get_channel(request->channel_id());
+  if (json_str == nullptr) {
+    return Status(StatusCode::NOT_FOUND, "No object found for that ChannelId");
+  }
+  grpc::protobuf::util::Status s =
+      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  gpr_free(json_str);
+  if (!s.ok()) {
+    return Status(StatusCode::INTERNAL, s.ToString());
+  }
+  return Status::OK;
+}
+
+Status ChannelzService::GetSubchannel(
+    ServerContext* unused, const channelz::v1::GetSubchannelRequest* request,
+    channelz::v1::GetSubchannelResponse* response) {
+  char* json_str = grpc_channelz_get_subchannel(request->subchannel_id());
+  if (json_str == nullptr) {
+    return Status(StatusCode::NOT_FOUND,
+                  "No object found for that SubchannelId");
+  }
+  grpc::protobuf::util::Status s =
+      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  gpr_free(json_str);
+  if (!s.ok()) {
+    return Status(StatusCode::INTERNAL, s.ToString());
+  }
+  return Status::OK;
+}
+
+Status ChannelzService::GetSocket(ServerContext* unused,
+                                  const channelz::v1::GetSocketRequest* request,
+                                  channelz::v1::GetSocketResponse* response) {
+  char* json_str = grpc_channelz_get_socket(request->socket_id());
+  if (json_str == nullptr) {
+    return Status(StatusCode::NOT_FOUND, "No object found for that SocketId");
+  }
+  grpc::protobuf::util::Status s =
+      grpc::protobuf::json::JsonStringToMessage(json_str, response);
+  gpr_free(json_str);
+  if (!s.ok()) {
+    return Status(StatusCode::INTERNAL, s.ToString());
+  }
+  return Status::OK;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/channelz/channelz_service.h b/third_party/grpc/src/cpp/server/channelz/channelz_service.h
new file mode 100644
index 0000000..b4a66ba
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/channelz/channelz_service.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_SERVER_CHANNELZ_SERVICE_H
+#define GRPC_INTERNAL_CPP_SERVER_CHANNELZ_SERVICE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpcpp/grpcpp.h>
+#include "src/proto/grpc/channelz/channelz.grpc.pb.h"
+
+namespace grpc {
+
+class ChannelzService final : public channelz::v1::Channelz::Service {
+ private:
+  // implementation of GetTopChannels rpc
+  Status GetTopChannels(
+      ServerContext* unused, const channelz::v1::GetTopChannelsRequest* request,
+      channelz::v1::GetTopChannelsResponse* response) override;
+  // implementation of GetServers rpc
+  Status GetServers(ServerContext* unused,
+                    const channelz::v1::GetServersRequest* request,
+                    channelz::v1::GetServersResponse* response) override;
+  // implementation of GetServer rpc
+  Status GetServer(ServerContext* unused,
+                   const channelz::v1::GetServerRequest* request,
+                   channelz::v1::GetServerResponse* response) override;
+  // implementation of GetServerSockets rpc
+  Status GetServerSockets(
+      ServerContext* unused,
+      const channelz::v1::GetServerSocketsRequest* request,
+      channelz::v1::GetServerSocketsResponse* response) override;
+  // implementation of GetChannel rpc
+  Status GetChannel(ServerContext* unused,
+                    const channelz::v1::GetChannelRequest* request,
+                    channelz::v1::GetChannelResponse* response) override;
+  // implementation of GetSubchannel rpc
+  Status GetSubchannel(ServerContext* unused,
+                       const channelz::v1::GetSubchannelRequest* request,
+                       channelz::v1::GetSubchannelResponse* response) override;
+  // implementation of GetSocket rpc
+  Status GetSocket(ServerContext* unused,
+                   const channelz::v1::GetSocketRequest* request,
+                   channelz::v1::GetSocketResponse* response) override;
+};
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_SERVER_CHANNELZ_SERVICE_H
diff --git a/third_party/grpc/src/cpp/server/channelz/channelz_service_plugin.cc b/third_party/grpc/src/cpp/server/channelz/channelz_service_plugin.cc
new file mode 100644
index 0000000..b93e5b5
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/channelz/channelz_service_plugin.cc
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpcpp/ext/channelz_service_plugin.h>
+#include <grpcpp/impl/server_builder_plugin.h>
+#include <grpcpp/impl/server_initializer.h>
+#include <grpcpp/server.h>
+
+#include "src/cpp/server/channelz/channelz_service.h"
+
+namespace grpc {
+namespace channelz {
+namespace experimental {
+
+class ChannelzServicePlugin : public ::grpc::ServerBuilderPlugin {
+ public:
+  ChannelzServicePlugin() : channelz_service_(new grpc::ChannelzService()) {}
+
+  grpc::string name() override { return "channelz_service"; }
+
+  void InitServer(grpc::ServerInitializer* si) override {
+    si->RegisterService(channelz_service_);
+  }
+
+  void Finish(grpc::ServerInitializer* si) override {}
+
+  void ChangeArguments(const grpc::string& name, void* value) override {}
+
+  bool has_sync_methods() const override {
+    if (channelz_service_) {
+      return channelz_service_->has_synchronous_methods();
+    }
+    return false;
+  }
+
+  bool has_async_methods() const override {
+    if (channelz_service_) {
+      return channelz_service_->has_async_methods();
+    }
+    return false;
+  }
+
+ private:
+  std::shared_ptr<grpc::ChannelzService> channelz_service_;
+};
+
+static std::unique_ptr< ::grpc::ServerBuilderPlugin>
+CreateChannelzServicePlugin() {
+  return std::unique_ptr< ::grpc::ServerBuilderPlugin>(
+      new ChannelzServicePlugin());
+}
+
+void InitChannelzService() {
+  static bool already_here = false;
+  if (already_here) return;
+  already_here = true;
+  ::grpc::ServerBuilder::InternalAddPluginFactory(&CreateChannelzServicePlugin);
+}
+
+}  // namespace experimental
+}  // namespace channelz
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/create_default_thread_pool.cc b/third_party/grpc/src/cpp/server/create_default_thread_pool.cc
new file mode 100644
index 0000000..8ca3e32
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/create_default_thread_pool.cc
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/cpu.h>
+
+#include "src/cpp/server/dynamic_thread_pool.h"
+
+#ifndef GRPC_CUSTOM_DEFAULT_THREAD_POOL
+
+namespace grpc {
+namespace {
+
+ThreadPoolInterface* CreateDefaultThreadPoolImpl() {
+  int cores = gpr_cpu_num_cores();
+  if (!cores) cores = 4;
+  return new DynamicThreadPool(cores);
+}
+
+CreateThreadPoolFunc g_ctp_impl = CreateDefaultThreadPoolImpl;
+
+}  // namespace
+
+ThreadPoolInterface* CreateDefaultThreadPool() { return g_ctp_impl(); }
+
+void SetCreateThreadPool(CreateThreadPoolFunc func) { g_ctp_impl = func; }
+
+}  // namespace grpc
+
+#endif  // !GRPC_CUSTOM_DEFAULT_THREAD_POOL
diff --git a/third_party/grpc/src/cpp/server/dynamic_thread_pool.cc b/third_party/grpc/src/cpp/server/dynamic_thread_pool.cc
new file mode 100644
index 0000000..ef99d64
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/dynamic_thread_pool.cc
@@ -0,0 +1,125 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 "src/cpp/server/dynamic_thread_pool.h"
+
+#include <mutex>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/gprpp/thd.h"
+
+namespace grpc {
+
+DynamicThreadPool::DynamicThread::DynamicThread(DynamicThreadPool* pool)
+    : pool_(pool),
+      thd_("grpcpp_dynamic_pool",
+           [](void* th) {
+             static_cast<DynamicThreadPool::DynamicThread*>(th)->ThreadFunc();
+           },
+           this) {
+  thd_.Start();
+}
+DynamicThreadPool::DynamicThread::~DynamicThread() { thd_.Join(); }
+
+void DynamicThreadPool::DynamicThread::ThreadFunc() {
+  pool_->ThreadFunc();
+  // Now that we have killed ourselves, we should reduce the thread count
+  std::unique_lock<std::mutex> lock(pool_->mu_);
+  pool_->nthreads_--;
+  // Move ourselves to dead list
+  pool_->dead_threads_.push_back(this);
+
+  if ((pool_->shutdown_) && (pool_->nthreads_ == 0)) {
+    pool_->shutdown_cv_.notify_one();
+  }
+}
+
+void DynamicThreadPool::ThreadFunc() {
+  for (;;) {
+    // Wait until work is available or we are shutting down.
+    std::unique_lock<std::mutex> lock(mu_);
+    if (!shutdown_ && callbacks_.empty()) {
+      // If there are too many threads waiting, then quit this thread
+      if (threads_waiting_ >= reserve_threads_) {
+        break;
+      }
+      threads_waiting_++;
+      cv_.wait(lock);
+      threads_waiting_--;
+    }
+    // Drain callbacks before considering shutdown to ensure all work
+    // gets completed.
+    if (!callbacks_.empty()) {
+      auto cb = callbacks_.front();
+      callbacks_.pop();
+      lock.unlock();
+      cb();
+    } else if (shutdown_) {
+      break;
+    }
+  }
+}
+
+DynamicThreadPool::DynamicThreadPool(int reserve_threads)
+    : shutdown_(false),
+      reserve_threads_(reserve_threads),
+      nthreads_(0),
+      threads_waiting_(0) {
+  for (int i = 0; i < reserve_threads_; i++) {
+    std::lock_guard<std::mutex> lock(mu_);
+    nthreads_++;
+    new DynamicThread(this);
+  }
+}
+
+void DynamicThreadPool::ReapThreads(std::list<DynamicThread*>* tlist) {
+  for (auto t = tlist->begin(); t != tlist->end(); t = tlist->erase(t)) {
+    delete *t;
+  }
+}
+
+DynamicThreadPool::~DynamicThreadPool() {
+  std::unique_lock<std::mutex> lock(mu_);
+  shutdown_ = true;
+  cv_.notify_all();
+  while (nthreads_ != 0) {
+    shutdown_cv_.wait(lock);
+  }
+  ReapThreads(&dead_threads_);
+}
+
+void DynamicThreadPool::Add(const std::function<void()>& callback) {
+  std::lock_guard<std::mutex> lock(mu_);
+  // Add works to the callbacks list
+  callbacks_.push(callback);
+  // Increase pool size or notify as needed
+  if (threads_waiting_ == 0) {
+    // Kick off a new thread
+    nthreads_++;
+    new DynamicThread(this);
+  } else {
+    cv_.notify_one();
+  }
+  // Also use this chance to harvest dead threads
+  if (!dead_threads_.empty()) {
+    ReapThreads(&dead_threads_);
+  }
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/dynamic_thread_pool.h b/third_party/grpc/src/cpp/server/dynamic_thread_pool.h
new file mode 100644
index 0000000..5df8cf2
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/dynamic_thread_pool.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_DYNAMIC_THREAD_POOL_H
+#define GRPC_INTERNAL_CPP_DYNAMIC_THREAD_POOL_H
+
+#include <condition_variable>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <queue>
+
+#include <grpcpp/support/config.h>
+
+#include "src/core/lib/gprpp/thd.h"
+#include "src/cpp/server/thread_pool_interface.h"
+
+namespace grpc {
+
+class DynamicThreadPool final : public ThreadPoolInterface {
+ public:
+  explicit DynamicThreadPool(int reserve_threads);
+  ~DynamicThreadPool();
+
+  void Add(const std::function<void()>& callback) override;
+
+ private:
+  class DynamicThread {
+   public:
+    DynamicThread(DynamicThreadPool* pool);
+    ~DynamicThread();
+
+   private:
+    DynamicThreadPool* pool_;
+    grpc_core::Thread thd_;
+    void ThreadFunc();
+  };
+  std::mutex mu_;
+  std::condition_variable cv_;
+  std::condition_variable shutdown_cv_;
+  bool shutdown_;
+  std::queue<std::function<void()>> callbacks_;
+  int reserve_threads_;
+  int nthreads_;
+  int threads_waiting_;
+  std::list<DynamicThread*> dead_threads_;
+
+  void ThreadFunc();
+  static void ReapThreads(std::list<DynamicThread*>* tlist);
+};
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_DYNAMIC_THREAD_POOL_H
diff --git a/third_party/grpc/src/cpp/server/health/default_health_check_service.cc b/third_party/grpc/src/cpp/server/health/default_health_check_service.cc
new file mode 100644
index 0000000..44aebd2
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/health/default_health_check_service.cc
@@ -0,0 +1,499 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <memory>
+#include <mutex>
+
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpcpp/impl/codegen/method_handler_impl.h>
+
+#include "pb_decode.h"
+#include "pb_encode.h"
+#include "src/core/ext/filters/client_channel/health/health.pb.h"
+#include "src/cpp/server/health/default_health_check_service.h"
+
+namespace grpc {
+
+//
+// DefaultHealthCheckService
+//
+
+DefaultHealthCheckService::DefaultHealthCheckService() {
+  services_map_[""].SetServingStatus(SERVING);
+}
+
+void DefaultHealthCheckService::SetServingStatus(
+    const grpc::string& service_name, bool serving) {
+  std::unique_lock<std::mutex> lock(mu_);
+  if (shutdown_) {
+    // Set to NOT_SERVING in case service_name is not in the map.
+    serving = false;
+  }
+  services_map_[service_name].SetServingStatus(serving ? SERVING : NOT_SERVING);
+}
+
+void DefaultHealthCheckService::SetServingStatus(bool serving) {
+  const ServingStatus status = serving ? SERVING : NOT_SERVING;
+  std::unique_lock<std::mutex> lock(mu_);
+  if (shutdown_) {
+    return;
+  }
+  for (auto& p : services_map_) {
+    ServiceData& service_data = p.second;
+    service_data.SetServingStatus(status);
+  }
+}
+
+void DefaultHealthCheckService::Shutdown() {
+  std::unique_lock<std::mutex> lock(mu_);
+  if (shutdown_) {
+    return;
+  }
+  shutdown_ = true;
+  for (auto& p : services_map_) {
+    ServiceData& service_data = p.second;
+    service_data.SetServingStatus(NOT_SERVING);
+  }
+}
+
+DefaultHealthCheckService::ServingStatus
+DefaultHealthCheckService::GetServingStatus(
+    const grpc::string& service_name) const {
+  std::lock_guard<std::mutex> lock(mu_);
+  auto it = services_map_.find(service_name);
+  if (it == services_map_.end()) {
+    return NOT_FOUND;
+  }
+  const ServiceData& service_data = it->second;
+  return service_data.GetServingStatus();
+}
+
+void DefaultHealthCheckService::RegisterCallHandler(
+    const grpc::string& service_name,
+    std::shared_ptr<HealthCheckServiceImpl::CallHandler> handler) {
+  std::unique_lock<std::mutex> lock(mu_);
+  ServiceData& service_data = services_map_[service_name];
+  service_data.AddCallHandler(handler /* copies ref */);
+  HealthCheckServiceImpl::CallHandler* h = handler.get();
+  h->SendHealth(std::move(handler), service_data.GetServingStatus());
+}
+
+void DefaultHealthCheckService::UnregisterCallHandler(
+    const grpc::string& service_name,
+    const std::shared_ptr<HealthCheckServiceImpl::CallHandler>& handler) {
+  std::unique_lock<std::mutex> lock(mu_);
+  auto it = services_map_.find(service_name);
+  if (it == services_map_.end()) return;
+  ServiceData& service_data = it->second;
+  service_data.RemoveCallHandler(handler);
+  if (service_data.Unused()) {
+    services_map_.erase(it);
+  }
+}
+
+DefaultHealthCheckService::HealthCheckServiceImpl*
+DefaultHealthCheckService::GetHealthCheckService(
+    std::unique_ptr<ServerCompletionQueue> cq) {
+  GPR_ASSERT(impl_ == nullptr);
+  impl_.reset(new HealthCheckServiceImpl(this, std::move(cq)));
+  return impl_.get();
+}
+
+//
+// DefaultHealthCheckService::ServiceData
+//
+
+void DefaultHealthCheckService::ServiceData::SetServingStatus(
+    ServingStatus status) {
+  status_ = status;
+  for (auto& call_handler : call_handlers_) {
+    call_handler->SendHealth(call_handler /* copies ref */, status);
+  }
+}
+
+void DefaultHealthCheckService::ServiceData::AddCallHandler(
+    std::shared_ptr<HealthCheckServiceImpl::CallHandler> handler) {
+  call_handlers_.insert(std::move(handler));
+}
+
+void DefaultHealthCheckService::ServiceData::RemoveCallHandler(
+    const std::shared_ptr<HealthCheckServiceImpl::CallHandler>& handler) {
+  call_handlers_.erase(handler);
+}
+
+//
+// DefaultHealthCheckService::HealthCheckServiceImpl
+//
+
+namespace {
+const char kHealthCheckMethodName[] = "/grpc.health.v1.Health/Check";
+const char kHealthWatchMethodName[] = "/grpc.health.v1.Health/Watch";
+}  // namespace
+
+DefaultHealthCheckService::HealthCheckServiceImpl::HealthCheckServiceImpl(
+    DefaultHealthCheckService* database,
+    std::unique_ptr<ServerCompletionQueue> cq)
+    : database_(database), cq_(std::move(cq)) {
+  // Add Check() method.
+  AddMethod(new internal::RpcServiceMethod(
+      kHealthCheckMethodName, internal::RpcMethod::NORMAL_RPC, nullptr));
+  // Add Watch() method.
+  AddMethod(new internal::RpcServiceMethod(
+      kHealthWatchMethodName, internal::RpcMethod::SERVER_STREAMING, nullptr));
+  // Create serving thread.
+  thread_ = std::unique_ptr<::grpc_core::Thread>(
+      new ::grpc_core::Thread("grpc_health_check_service", Serve, this));
+}
+
+DefaultHealthCheckService::HealthCheckServiceImpl::~HealthCheckServiceImpl() {
+  // We will reach here after the server starts shutting down.
+  shutdown_ = true;
+  {
+    std::unique_lock<std::mutex> lock(cq_shutdown_mu_);
+    cq_->Shutdown();
+  }
+  thread_->Join();
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::StartServingThread() {
+  // Request the calls we're interested in.
+  // We do this before starting the serving thread, so that we know it's
+  // done before server startup is complete.
+  CheckCallHandler::CreateAndStart(cq_.get(), database_, this);
+  WatchCallHandler::CreateAndStart(cq_.get(), database_, this);
+  // Start serving thread.
+  thread_->Start();
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::Serve(void* arg) {
+  HealthCheckServiceImpl* service =
+      reinterpret_cast<HealthCheckServiceImpl*>(arg);
+  void* tag;
+  bool ok;
+  while (true) {
+    if (!service->cq_->Next(&tag, &ok)) {
+      // The completion queue is shutting down.
+      GPR_ASSERT(service->shutdown_);
+      break;
+    }
+    auto* next_step = static_cast<CallableTag*>(tag);
+    next_step->Run(ok);
+  }
+}
+
+bool DefaultHealthCheckService::HealthCheckServiceImpl::DecodeRequest(
+    const ByteBuffer& request, grpc::string* service_name) {
+  std::vector<Slice> slices;
+  if (!request.Dump(&slices).ok()) return false;
+  uint8_t* request_bytes = nullptr;
+  size_t request_size = 0;
+  grpc_health_v1_HealthCheckRequest request_struct;
+  request_struct.has_service = false;
+  if (slices.size() == 1) {
+    request_bytes = const_cast<uint8_t*>(slices[0].begin());
+    request_size = slices[0].size();
+  } else if (slices.size() > 1) {
+    request_bytes = static_cast<uint8_t*>(gpr_malloc(request.Length()));
+    uint8_t* copy_to = request_bytes;
+    for (size_t i = 0; i < slices.size(); i++) {
+      memcpy(copy_to, slices[i].begin(), slices[i].size());
+      copy_to += slices[i].size();
+    }
+  }
+  pb_istream_t istream = pb_istream_from_buffer(request_bytes, request_size);
+  bool decode_status = pb_decode(
+      &istream, grpc_health_v1_HealthCheckRequest_fields, &request_struct);
+  if (slices.size() > 1) {
+    gpr_free(request_bytes);
+  }
+  if (!decode_status) return false;
+  *service_name = request_struct.has_service ? request_struct.service : "";
+  return true;
+}
+
+bool DefaultHealthCheckService::HealthCheckServiceImpl::EncodeResponse(
+    ServingStatus status, ByteBuffer* response) {
+  grpc_health_v1_HealthCheckResponse response_struct;
+  response_struct.has_status = true;
+  response_struct.status =
+      status == NOT_FOUND
+          ? grpc_health_v1_HealthCheckResponse_ServingStatus_SERVICE_UNKNOWN
+          : status == SERVING
+                ? grpc_health_v1_HealthCheckResponse_ServingStatus_SERVING
+                : grpc_health_v1_HealthCheckResponse_ServingStatus_NOT_SERVING;
+  pb_ostream_t ostream;
+  memset(&ostream, 0, sizeof(ostream));
+  pb_encode(&ostream, grpc_health_v1_HealthCheckResponse_fields,
+            &response_struct);
+  grpc_slice response_slice = grpc_slice_malloc(ostream.bytes_written);
+  ostream = pb_ostream_from_buffer(GRPC_SLICE_START_PTR(response_slice),
+                                   GRPC_SLICE_LENGTH(response_slice));
+  bool encode_status = pb_encode(
+      &ostream, grpc_health_v1_HealthCheckResponse_fields, &response_struct);
+  if (!encode_status) return false;
+  Slice encoded_response(response_slice, Slice::STEAL_REF);
+  ByteBuffer response_buffer(&encoded_response, 1);
+  response->Swap(&response_buffer);
+  return true;
+}
+
+//
+// DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler
+//
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler::
+    CreateAndStart(ServerCompletionQueue* cq,
+                   DefaultHealthCheckService* database,
+                   HealthCheckServiceImpl* service) {
+  std::shared_ptr<CallHandler> self =
+      std::make_shared<CheckCallHandler>(cq, database, service);
+  CheckCallHandler* handler = static_cast<CheckCallHandler*>(self.get());
+  {
+    std::unique_lock<std::mutex> lock(service->cq_shutdown_mu_);
+    if (service->shutdown_) return;
+    // Request a Check() call.
+    handler->next_ =
+        CallableTag(std::bind(&CheckCallHandler::OnCallReceived, handler,
+                              std::placeholders::_1, std::placeholders::_2),
+                    std::move(self));
+    service->RequestAsyncUnary(0, &handler->ctx_, &handler->request_,
+                               &handler->writer_, cq, cq, &handler->next_);
+  }
+}
+
+DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler::
+    CheckCallHandler(ServerCompletionQueue* cq,
+                     DefaultHealthCheckService* database,
+                     HealthCheckServiceImpl* service)
+    : cq_(cq), database_(database), service_(service), writer_(&ctx_) {}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler::
+    OnCallReceived(std::shared_ptr<CallHandler> self, bool ok) {
+  if (!ok) {
+    // The value of ok being false means that the server is shutting down.
+    return;
+  }
+  // Spawn a new handler instance to serve the next new client. Every handler
+  // instance will deallocate itself when it's done.
+  CreateAndStart(cq_, database_, service_);
+  // Process request.
+  gpr_log(GPR_DEBUG, "[HCS %p] Health check started for handler %p", service_,
+          this);
+  grpc::string service_name;
+  grpc::Status status = Status::OK;
+  ByteBuffer response;
+  if (!service_->DecodeRequest(request_, &service_name)) {
+    status = Status(StatusCode::INVALID_ARGUMENT, "could not parse request");
+  } else {
+    ServingStatus serving_status = database_->GetServingStatus(service_name);
+    if (serving_status == NOT_FOUND) {
+      status = Status(StatusCode::NOT_FOUND, "service name unknown");
+    } else if (!service_->EncodeResponse(serving_status, &response)) {
+      status = Status(StatusCode::INTERNAL, "could not encode response");
+    }
+  }
+  // Send response.
+  {
+    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    if (!service_->shutdown_) {
+      next_ =
+          CallableTag(std::bind(&CheckCallHandler::OnFinishDone, this,
+                                std::placeholders::_1, std::placeholders::_2),
+                      std::move(self));
+      if (status.ok()) {
+        writer_.Finish(response, status, &next_);
+      } else {
+        writer_.FinishWithError(status, &next_);
+      }
+    }
+  }
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler::
+    OnFinishDone(std::shared_ptr<CallHandler> self, bool ok) {
+  if (ok) {
+    gpr_log(GPR_DEBUG, "[HCS %p] Health check call finished for handler %p",
+            service_, this);
+  }
+  self.reset();  // To appease clang-tidy.
+}
+
+//
+// DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler
+//
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    CreateAndStart(ServerCompletionQueue* cq,
+                   DefaultHealthCheckService* database,
+                   HealthCheckServiceImpl* service) {
+  std::shared_ptr<CallHandler> self =
+      std::make_shared<WatchCallHandler>(cq, database, service);
+  WatchCallHandler* handler = static_cast<WatchCallHandler*>(self.get());
+  {
+    std::unique_lock<std::mutex> lock(service->cq_shutdown_mu_);
+    if (service->shutdown_) return;
+    // Request AsyncNotifyWhenDone().
+    handler->on_done_notified_ =
+        CallableTag(std::bind(&WatchCallHandler::OnDoneNotified, handler,
+                              std::placeholders::_1, std::placeholders::_2),
+                    self /* copies ref */);
+    handler->ctx_.AsyncNotifyWhenDone(&handler->on_done_notified_);
+    // Request a Watch() call.
+    handler->next_ =
+        CallableTag(std::bind(&WatchCallHandler::OnCallReceived, handler,
+                              std::placeholders::_1, std::placeholders::_2),
+                    std::move(self));
+    service->RequestAsyncServerStreaming(1, &handler->ctx_, &handler->request_,
+                                         &handler->stream_, cq, cq,
+                                         &handler->next_);
+  }
+}
+
+DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    WatchCallHandler(ServerCompletionQueue* cq,
+                     DefaultHealthCheckService* database,
+                     HealthCheckServiceImpl* service)
+    : cq_(cq), database_(database), service_(service), stream_(&ctx_) {}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    OnCallReceived(std::shared_ptr<CallHandler> self, bool ok) {
+  if (!ok) {
+    // Server shutting down.
+    //
+    // AsyncNotifyWhenDone() needs to be called before the call starts, but the
+    // tag will not pop out if the call never starts (
+    // https://github.com/grpc/grpc/issues/10136). So we need to manually
+    // release the ownership of the handler in this case.
+    GPR_ASSERT(on_done_notified_.ReleaseHandler() != nullptr);
+    return;
+  }
+  // Spawn a new handler instance to serve the next new client. Every handler
+  // instance will deallocate itself when it's done.
+  CreateAndStart(cq_, database_, service_);
+  // Parse request.
+  if (!service_->DecodeRequest(request_, &service_name_)) {
+    SendFinish(std::move(self),
+               Status(StatusCode::INVALID_ARGUMENT, "could not parse request"));
+    return;
+  }
+  // Register the call for updates to the service.
+  gpr_log(GPR_DEBUG,
+          "[HCS %p] Health watch started for service \"%s\" (handler: %p)",
+          service_, service_name_.c_str(), this);
+  database_->RegisterCallHandler(service_name_, std::move(self));
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    SendHealth(std::shared_ptr<CallHandler> self, ServingStatus status) {
+  std::unique_lock<std::mutex> lock(send_mu_);
+  // If there's already a send in flight, cache the new status, and
+  // we'll start a new send for it when the one in flight completes.
+  if (send_in_flight_) {
+    pending_status_ = status;
+    return;
+  }
+  // Start a send.
+  SendHealthLocked(std::move(self), status);
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    SendHealthLocked(std::shared_ptr<CallHandler> self, ServingStatus status) {
+  send_in_flight_ = true;
+  // Construct response.
+  ByteBuffer response;
+  bool success = service_->EncodeResponse(status, &response);
+  // Grab shutdown lock and send response.
+  std::unique_lock<std::mutex> cq_lock(service_->cq_shutdown_mu_);
+  if (service_->shutdown_) {
+    SendFinishLocked(std::move(self), Status::CANCELLED);
+    return;
+  }
+  if (!success) {
+    SendFinishLocked(std::move(self),
+                     Status(StatusCode::INTERNAL, "could not encode response"));
+    return;
+  }
+  next_ = CallableTag(std::bind(&WatchCallHandler::OnSendHealthDone, this,
+                                std::placeholders::_1, std::placeholders::_2),
+                      std::move(self));
+  stream_.Write(response, &next_);
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    OnSendHealthDone(std::shared_ptr<CallHandler> self, bool ok) {
+  if (!ok) {
+    SendFinish(std::move(self), Status::CANCELLED);
+    return;
+  }
+  std::unique_lock<std::mutex> lock(send_mu_);
+  send_in_flight_ = false;
+  // If we got a new status since we started the last send, start a
+  // new send for it.
+  if (pending_status_ != NOT_FOUND) {
+    auto status = pending_status_;
+    pending_status_ = NOT_FOUND;
+    SendHealthLocked(std::move(self), status);
+  }
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    SendFinish(std::shared_ptr<CallHandler> self, const Status& status) {
+  if (finish_called_) return;
+  std::unique_lock<std::mutex> cq_lock(service_->cq_shutdown_mu_);
+  if (service_->shutdown_) return;
+  SendFinishLocked(std::move(self), status);
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    SendFinishLocked(std::shared_ptr<CallHandler> self, const Status& status) {
+  on_finish_done_ =
+      CallableTag(std::bind(&WatchCallHandler::OnFinishDone, this,
+                            std::placeholders::_1, std::placeholders::_2),
+                  std::move(self));
+  stream_.Finish(status, &on_finish_done_);
+  finish_called_ = true;
+}
+
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    OnFinishDone(std::shared_ptr<CallHandler> self, bool ok) {
+  if (ok) {
+    gpr_log(GPR_DEBUG,
+            "[HCS %p] Health watch call finished (service_name: \"%s\", "
+            "handler: %p).",
+            service_, service_name_.c_str(), this);
+  }
+  self.reset();  // To appease clang-tidy.
+}
+
+// TODO(roth): This method currently assumes that there will be only one
+// thread polling the cq and invoking the corresponding callbacks.  If
+// that changes, we will need to add synchronization here.
+void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler::
+    OnDoneNotified(std::shared_ptr<CallHandler> self, bool ok) {
+  GPR_ASSERT(ok);
+  gpr_log(GPR_DEBUG,
+          "[HCS %p] Health watch call is notified done (handler: %p, "
+          "is_cancelled: %d).",
+          service_, this, static_cast<int>(ctx_.IsCancelled()));
+  database_->UnregisterCallHandler(service_name_, self);
+  SendFinish(std::move(self), Status::CANCELLED);
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/health/default_health_check_service.h b/third_party/grpc/src/cpp/server/health/default_health_check_service.h
new file mode 100644
index 0000000..9551cd2
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/health/default_health_check_service.h
@@ -0,0 +1,284 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_SERVER_DEFAULT_HEALTH_CHECK_SERVICE_H
+#define GRPC_INTERNAL_CPP_SERVER_DEFAULT_HEALTH_CHECK_SERVICE_H
+
+#include <atomic>
+#include <mutex>
+#include <set>
+
+#include <grpc/support/log.h>
+#include <grpcpp/grpcpp.h>
+#include <grpcpp/health_check_service_interface.h>
+#include <grpcpp/impl/codegen/async_generic_service.h>
+#include <grpcpp/impl/codegen/async_unary_call.h>
+#include <grpcpp/impl/codegen/service_type.h>
+#include <grpcpp/support/byte_buffer.h>
+
+#include "src/core/lib/gprpp/thd.h"
+
+namespace grpc {
+
+// Default implementation of HealthCheckServiceInterface. Server will create and
+// own it.
+class DefaultHealthCheckService final : public HealthCheckServiceInterface {
+ public:
+  enum ServingStatus { NOT_FOUND, SERVING, NOT_SERVING };
+
+  // The service impl to register with the server.
+  class HealthCheckServiceImpl : public Service {
+   public:
+    // Base class for call handlers.
+    class CallHandler {
+     public:
+      virtual ~CallHandler() = default;
+      virtual void SendHealth(std::shared_ptr<CallHandler> self,
+                              ServingStatus status) = 0;
+    };
+
+    HealthCheckServiceImpl(DefaultHealthCheckService* database,
+                           std::unique_ptr<ServerCompletionQueue> cq);
+
+    ~HealthCheckServiceImpl();
+
+    void StartServingThread();
+
+   private:
+    // A tag that can be called with a bool argument. It's tailored for
+    // CallHandler's use. Before being used, it should be constructed with a
+    // method of CallHandler and a shared pointer to the handler. The
+    // shared pointer will be moved to the invoked function and the function
+    // can only be invoked once. That makes ref counting of the handler easier,
+    // because the shared pointer is not bound to the function and can be gone
+    // once the invoked function returns (if not used any more).
+    class CallableTag {
+     public:
+      using HandlerFunction =
+          std::function<void(std::shared_ptr<CallHandler>, bool)>;
+
+      CallableTag() {}
+
+      CallableTag(HandlerFunction func, std::shared_ptr<CallHandler> handler)
+          : handler_function_(std::move(func)), handler_(std::move(handler)) {
+        GPR_ASSERT(handler_function_ != nullptr);
+        GPR_ASSERT(handler_ != nullptr);
+      }
+
+      // Runs the tag. This should be called only once. The handler is no
+      // longer owned by this tag after this method is invoked.
+      void Run(bool ok) {
+        GPR_ASSERT(handler_function_ != nullptr);
+        GPR_ASSERT(handler_ != nullptr);
+        handler_function_(std::move(handler_), ok);
+      }
+
+      // Releases and returns the shared pointer to the handler.
+      std::shared_ptr<CallHandler> ReleaseHandler() {
+        return std::move(handler_);
+      }
+
+     private:
+      HandlerFunction handler_function_ = nullptr;
+      std::shared_ptr<CallHandler> handler_;
+    };
+
+    // Call handler for Check method.
+    // Each handler takes care of one call. It contains per-call data and it
+    // will access the members of the parent class (i.e.,
+    // DefaultHealthCheckService) for per-service health data.
+    class CheckCallHandler : public CallHandler {
+     public:
+      // Instantiates a CheckCallHandler and requests the next health check
+      // call. The handler object will manage its own lifetime, so no action is
+      // needed from the caller any more regarding that object.
+      static void CreateAndStart(ServerCompletionQueue* cq,
+                                 DefaultHealthCheckService* database,
+                                 HealthCheckServiceImpl* service);
+
+      // This ctor is public because we want to use std::make_shared<> in
+      // CreateAndStart(). This ctor shouldn't be used elsewhere.
+      CheckCallHandler(ServerCompletionQueue* cq,
+                       DefaultHealthCheckService* database,
+                       HealthCheckServiceImpl* service);
+
+      // Not used for Check.
+      void SendHealth(std::shared_ptr<CallHandler> self,
+                      ServingStatus status) override {}
+
+     private:
+      // Called when we receive a call.
+      // Spawns a new handler so that we can keep servicing future calls.
+      void OnCallReceived(std::shared_ptr<CallHandler> self, bool ok);
+
+      // Called when Finish() is done.
+      void OnFinishDone(std::shared_ptr<CallHandler> self, bool ok);
+
+      // The members passed down from HealthCheckServiceImpl.
+      ServerCompletionQueue* cq_;
+      DefaultHealthCheckService* database_;
+      HealthCheckServiceImpl* service_;
+
+      ByteBuffer request_;
+      GenericServerAsyncResponseWriter writer_;
+      ServerContext ctx_;
+
+      CallableTag next_;
+    };
+
+    // Call handler for Watch method.
+    // Each handler takes care of one call. It contains per-call data and it
+    // will access the members of the parent class (i.e.,
+    // DefaultHealthCheckService) for per-service health data.
+    class WatchCallHandler : public CallHandler {
+     public:
+      // Instantiates a WatchCallHandler and requests the next health check
+      // call. The handler object will manage its own lifetime, so no action is
+      // needed from the caller any more regarding that object.
+      static void CreateAndStart(ServerCompletionQueue* cq,
+                                 DefaultHealthCheckService* database,
+                                 HealthCheckServiceImpl* service);
+
+      // This ctor is public because we want to use std::make_shared<> in
+      // CreateAndStart(). This ctor shouldn't be used elsewhere.
+      WatchCallHandler(ServerCompletionQueue* cq,
+                       DefaultHealthCheckService* database,
+                       HealthCheckServiceImpl* service);
+
+      void SendHealth(std::shared_ptr<CallHandler> self,
+                      ServingStatus status) override;
+
+     private:
+      // Called when we receive a call.
+      // Spawns a new handler so that we can keep servicing future calls.
+      void OnCallReceived(std::shared_ptr<CallHandler> self, bool ok);
+
+      // Requires holding send_mu_.
+      void SendHealthLocked(std::shared_ptr<CallHandler> self,
+                            ServingStatus status);
+
+      // When sending a health result finishes.
+      void OnSendHealthDone(std::shared_ptr<CallHandler> self, bool ok);
+
+      void SendFinish(std::shared_ptr<CallHandler> self, const Status& status);
+
+      // Requires holding service_->cq_shutdown_mu_.
+      void SendFinishLocked(std::shared_ptr<CallHandler> self,
+                            const Status& status);
+
+      // Called when Finish() is done.
+      void OnFinishDone(std::shared_ptr<CallHandler> self, bool ok);
+
+      // Called when AsyncNotifyWhenDone() notifies us.
+      void OnDoneNotified(std::shared_ptr<CallHandler> self, bool ok);
+
+      // The members passed down from HealthCheckServiceImpl.
+      ServerCompletionQueue* cq_;
+      DefaultHealthCheckService* database_;
+      HealthCheckServiceImpl* service_;
+
+      ByteBuffer request_;
+      grpc::string service_name_;
+      GenericServerAsyncWriter stream_;
+      ServerContext ctx_;
+
+      std::mutex send_mu_;
+      bool send_in_flight_ = false;               // Guarded by mu_.
+      ServingStatus pending_status_ = NOT_FOUND;  // Guarded by mu_.
+
+      bool finish_called_ = false;
+      CallableTag next_;
+      CallableTag on_done_notified_;
+      CallableTag on_finish_done_;
+    };
+
+    // Handles the incoming requests and drives the completion queue in a loop.
+    static void Serve(void* arg);
+
+    // Returns true on success.
+    static bool DecodeRequest(const ByteBuffer& request,
+                              grpc::string* service_name);
+    static bool EncodeResponse(ServingStatus status, ByteBuffer* response);
+
+    // Needed to appease Windows compilers, which don't seem to allow
+    // nested classes to access protected members in the parent's
+    // superclass.
+    using Service::RequestAsyncServerStreaming;
+    using Service::RequestAsyncUnary;
+
+    DefaultHealthCheckService* database_;
+    std::unique_ptr<ServerCompletionQueue> cq_;
+
+    // To synchronize the operations related to shutdown state of cq_, so that
+    // we don't enqueue new tags into cq_ after it is already shut down.
+    std::mutex cq_shutdown_mu_;
+    std::atomic_bool shutdown_{false};
+    std::unique_ptr<::grpc_core::Thread> thread_;
+  };
+
+  DefaultHealthCheckService();
+
+  void SetServingStatus(const grpc::string& service_name,
+                        bool serving) override;
+  void SetServingStatus(bool serving) override;
+
+  void Shutdown() override;
+
+  ServingStatus GetServingStatus(const grpc::string& service_name) const;
+
+  HealthCheckServiceImpl* GetHealthCheckService(
+      std::unique_ptr<ServerCompletionQueue> cq);
+
+ private:
+  // Stores the current serving status of a service and any call
+  // handlers registered for updates when the service's status changes.
+  class ServiceData {
+   public:
+    void SetServingStatus(ServingStatus status);
+    ServingStatus GetServingStatus() const { return status_; }
+    void AddCallHandler(
+        std::shared_ptr<HealthCheckServiceImpl::CallHandler> handler);
+    void RemoveCallHandler(
+        const std::shared_ptr<HealthCheckServiceImpl::CallHandler>& handler);
+    bool Unused() const {
+      return call_handlers_.empty() && status_ == NOT_FOUND;
+    }
+
+   private:
+    ServingStatus status_ = NOT_FOUND;
+    std::set<std::shared_ptr<HealthCheckServiceImpl::CallHandler>>
+        call_handlers_;
+  };
+
+  void RegisterCallHandler(
+      const grpc::string& service_name,
+      std::shared_ptr<HealthCheckServiceImpl::CallHandler> handler);
+
+  void UnregisterCallHandler(
+      const grpc::string& service_name,
+      const std::shared_ptr<HealthCheckServiceImpl::CallHandler>& handler);
+
+  mutable std::mutex mu_;
+  bool shutdown_ = false;                             // Guarded by mu_.
+  std::map<grpc::string, ServiceData> services_map_;  // Guarded by mu_.
+  std::unique_ptr<HealthCheckServiceImpl> impl_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_SERVER_DEFAULT_HEALTH_CHECK_SERVICE_H
diff --git a/third_party/grpc/src/cpp/server/health/health_check_service.cc b/third_party/grpc/src/cpp/server/health/health_check_service.cc
new file mode 100644
index 0000000..a0fa2d6
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/health/health_check_service.cc
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/health_check_service_interface.h>
+
+namespace grpc {
+namespace {
+bool g_grpc_default_health_check_service_enabled = false;
+}  // namespace
+
+bool DefaultHealthCheckServiceEnabled() {
+  return g_grpc_default_health_check_service_enabled;
+}
+
+void EnableDefaultHealthCheckService(bool enable) {
+  g_grpc_default_health_check_service_enabled = enable;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/health/health_check_service_server_builder_option.cc b/third_party/grpc/src/cpp/server/health/health_check_service_server_builder_option.cc
new file mode 100644
index 0000000..7148265
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/health/health_check_service_server_builder_option.cc
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/ext/health_check_service_server_builder_option.h>
+
+namespace grpc {
+
+HealthCheckServiceServerBuilderOption::HealthCheckServiceServerBuilderOption(
+    std::unique_ptr<HealthCheckServiceInterface> hc)
+    : hc_(std::move(hc)) {}
+// Hand over hc_ to the server.
+void HealthCheckServiceServerBuilderOption::UpdateArguments(
+    ChannelArguments* args) {
+  args->SetPointer(kHealthCheckServiceInterfaceArg, hc_.release());
+}
+
+void HealthCheckServiceServerBuilderOption::UpdatePlugins(
+    std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) {}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/insecure_server_credentials.cc b/third_party/grpc/src/cpp/server/insecure_server_credentials.cc
new file mode 100644
index 0000000..7d749dd
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/insecure_server_credentials.cc
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/security/server_credentials.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+
+namespace grpc {
+namespace {
+class InsecureServerCredentialsImpl final : public ServerCredentials {
+ public:
+  int AddPortToServer(const grpc::string& addr, grpc_server* server) override {
+    return grpc_server_add_insecure_http2_port(server, addr.c_str());
+  }
+  void SetAuthMetadataProcessor(
+      const std::shared_ptr<AuthMetadataProcessor>& processor) override {
+    (void)processor;
+    GPR_ASSERT(0);  // Should not be called on InsecureServerCredentials.
+  }
+};
+}  // namespace
+
+std::shared_ptr<ServerCredentials> InsecureServerCredentials() {
+  return std::shared_ptr<ServerCredentials>(
+      new InsecureServerCredentialsImpl());
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/load_reporter/constants.h b/third_party/grpc/src/cpp/server/load_reporter/constants.h
new file mode 100644
index 0000000..00ad794
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/constants.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H
+#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+namespace grpc {
+namespace load_reporter {
+
+// TODO(juanlishen): Update the version number with the PR number every time
+// there is any change to the server load reporter.
+constexpr uint32_t kVersion = 15853;
+
+// TODO(juanlishen): This window size is from the internal spec for the load
+// reporter. Need to ask the gRPC LB team whether we should make this and the
+// fetching interval configurable.
+constexpr uint32_t kFeedbackSampleWindowSeconds = 10;
+constexpr uint32_t kFetchAndSampleIntervalSeconds = 1;
+
+constexpr size_t kLbIdLength = 8;
+constexpr size_t kIpv4AddressLength = 8;
+constexpr size_t kIpv6AddressLength = 32;
+
+constexpr char kInvalidLbId[] = "<INVALID_LBID_238dsb234890rb>";
+
+// Call statuses.
+
+constexpr char kCallStatusOk[] = "OK";
+constexpr char kCallStatusServerError[] = "5XX";
+constexpr char kCallStatusClientError[] = "4XX";
+
+// Tag keys.
+
+constexpr char kTagKeyToken[] = "token";
+constexpr char kTagKeyHost[] = "host";
+constexpr char kTagKeyUserId[] = "user_id";
+constexpr char kTagKeyStatus[] = "status";
+constexpr char kTagKeyMetricName[] = "metric_name";
+
+// Measure names.
+
+constexpr char kMeasureStartCount[] = "grpc.io/lb/start_count";
+constexpr char kMeasureEndCount[] = "grpc.io/lb/end_count";
+constexpr char kMeasureEndBytesSent[] = "grpc.io/lb/bytes_sent";
+constexpr char kMeasureEndBytesReceived[] = "grpc.io/lb/bytes_received";
+constexpr char kMeasureEndLatencyMs[] = "grpc.io/lb/latency_ms";
+constexpr char kMeasureOtherCallMetric[] = "grpc.io/lb/other_call_metric";
+
+// View names.
+
+constexpr char kViewStartCount[] = "grpc.io/lb_view/start_count";
+constexpr char kViewEndCount[] = "grpc.io/lb_view/end_count";
+constexpr char kViewEndBytesSent[] = "grpc.io/lb_view/bytes_sent";
+constexpr char kViewEndBytesReceived[] = "grpc.io/lb_view/bytes_received";
+constexpr char kViewEndLatencyMs[] = "grpc.io/lb_view/latency_ms";
+constexpr char kViewOtherCallMetricCount[] =
+    "grpc.io/lb_view/other_call_metric_count";
+constexpr char kViewOtherCallMetricValue[] =
+    "grpc.io/lb_view/other_call_metric_value";
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H
diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats.h b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats.h
new file mode 100644
index 0000000..f514b07
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H
+#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include <utility>
+
+namespace grpc {
+namespace load_reporter {
+
+// Reads the CPU stats (in a pair of busy and total numbers) from the system.
+// The units of the stats should be the same.
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl();
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H
diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_linux.cc b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_linux.cc
new file mode 100644
index 0000000..9c1fd0c
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_linux.cc
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX
+
+#include <cstdio>
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl() {
+  uint64_t busy = 0, total = 0;
+  FILE* fp;
+  fp = fopen("/proc/stat", "r");
+  uint64_t user, nice, system, idle;
+  fscanf(fp, "cpu %lu %lu %lu %lu", &user, &nice, &system, &idle);
+  fclose(fp);
+  busy = user + nice + system;
+  total = busy + idle;
+  return std::make_pair(busy, total);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GPR_LINUX
diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_macos.cc b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_macos.cc
new file mode 100644
index 0000000..dbdde30
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_macos.cc
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_APPLE
+
+#include <mach/mach.h>
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl() {
+  uint64_t busy = 0, total = 0;
+  host_cpu_load_info_data_t cpuinfo;
+  mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
+  if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
+                      (host_info_t)&cpuinfo, &count) == KERN_SUCCESS) {
+    for (int i = 0; i < CPU_STATE_MAX; i++) total += cpuinfo.cpu_ticks[i];
+    busy = total - cpuinfo.cpu_ticks[CPU_STATE_IDLE];
+  }
+  return std::make_pair(busy, total);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GPR_APPLE
diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc
new file mode 100644
index 0000000..80fb8b6
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#if !defined(GPR_LINUX) && !defined(GPR_WINDOWS) && !defined(GPR_APPLE)
+
+#include <grpc/support/log.h>
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl() {
+  uint64_t busy = 0, total = 0;
+  gpr_log(GPR_ERROR,
+          "Platforms other than Linux, Windows, and MacOS are not supported.");
+  return std::make_pair(busy, total);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // !defined(GPR_LINUX) && !defined(GPR_WINDOWS) && !defined(GPR_APPLE)
diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_windows.cc b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_windows.cc
new file mode 100644
index 0000000..0a98e84
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_windows.cc
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS
+
+#include <windows.h>
+#include <cstdint>
+
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+namespace {
+
+uint64_t FiletimeToInt(const FILETIME& ft) {
+  ULARGE_INTEGER i;
+  i.LowPart = ft.dwLowDateTime;
+  i.HighPart = ft.dwHighDateTime;
+  return i.QuadPart;
+}
+
+}  // namespace
+
+std::pair<uint64_t, uint64_t> GetCpuStatsImpl() {
+  uint64_t busy = 0, total = 0;
+  FILETIME idle, kernel, user;
+  if (GetSystemTimes(&idle, &kernel, &user) != 0) {
+    total = FiletimeToInt(kernel) + FiletimeToInt(user);
+    busy = total - FiletimeToInt(idle);
+  }
+  return std::make_pair(busy, total);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GPR_WINDOWS
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_data_store.cc b/third_party/grpc/src/cpp/server/load_reporter/load_data_store.cc
new file mode 100644
index 0000000..594473f
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_data_store.cc
@@ -0,0 +1,338 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/impl/codegen/port_platform.h>
+
+#include <stdio.h>
+#include <cstdlib>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "src/core/lib/iomgr/socket_utils.h"
+#include "src/cpp/server/load_reporter/load_data_store.h"
+
+namespace grpc {
+namespace load_reporter {
+
+// Some helper functions.
+namespace {
+
+// Given a map from type K to a set of value type V, finds the set associated
+// with the given key and erases the value from the set. If the set becomes
+// empty, also erases the key-set pair. Returns true if the value is erased
+// successfully.
+template <typename K, typename V>
+bool UnorderedMapOfSetEraseKeyValue(std::unordered_map<K, std::set<V>>& map,
+                                    const K& key, const V& value) {
+  auto it = map.find(key);
+  if (it != map.end()) {
+    size_t erased = it->second.erase(value);
+    if (it->second.size() == 0) {
+      map.erase(it);
+    }
+    return erased;
+  }
+  return false;
+};
+
+// Given a map from type K to a set of value type V, removes the given key and
+// the associated set, and returns the set. Returns an empty set if the key is
+// not found.
+template <typename K, typename V>
+std::set<V> UnorderedMapOfSetExtract(std::unordered_map<K, std::set<V>>& map,
+                                     const K& key) {
+  auto it = map.find(key);
+  if (it != map.end()) {
+    auto set = std::move(it->second);
+    map.erase(it);
+    return set;
+  }
+  return {};
+};
+
+// From a non-empty container, returns a pointer to a random element.
+template <typename C>
+const typename C::value_type* RandomElement(const C& container) {
+  GPR_ASSERT(!container.empty());
+  auto it = container.begin();
+  std::advance(it, std::rand() % container.size());
+  return &(*it);
+}
+
+}  // namespace
+
+LoadRecordKey::LoadRecordKey(const grpc::string& client_ip_and_token,
+                             grpc::string user_id)
+    : user_id_(std::move(user_id)) {
+  GPR_ASSERT(client_ip_and_token.size() >= 2);
+  int ip_hex_size;
+  GPR_ASSERT(sscanf(client_ip_and_token.substr(0, 2).c_str(), "%d",
+                    &ip_hex_size) == 1);
+  GPR_ASSERT(ip_hex_size == 0 || ip_hex_size == kIpv4AddressLength ||
+             ip_hex_size == kIpv6AddressLength);
+  size_t cur_pos = 2;
+  client_ip_hex_ = client_ip_and_token.substr(cur_pos, ip_hex_size);
+  cur_pos += ip_hex_size;
+  if (client_ip_and_token.size() - cur_pos < kLbIdLength) {
+    lb_id_ = kInvalidLbId;
+    lb_tag_ = "";
+  } else {
+    lb_id_ = client_ip_and_token.substr(cur_pos, kLbIdLength);
+    lb_tag_ = client_ip_and_token.substr(cur_pos + kLbIdLength);
+  }
+}
+
+grpc::string LoadRecordKey::GetClientIpBytes() const {
+  if (client_ip_hex_.empty()) {
+    return "";
+  } else if (client_ip_hex_.size() == kIpv4AddressLength) {
+    uint32_t ip_bytes;
+    if (sscanf(client_ip_hex_.c_str(), "%x", &ip_bytes) != 1) {
+      gpr_log(GPR_ERROR,
+              "Can't parse client IP (%s) from a hex string to an integer.",
+              client_ip_hex_.c_str());
+      return "";
+    }
+    ip_bytes = grpc_htonl(ip_bytes);
+    return grpc::string(reinterpret_cast<const char*>(&ip_bytes),
+                        sizeof(ip_bytes));
+  } else if (client_ip_hex_.size() == kIpv6AddressLength) {
+    uint32_t ip_bytes[4];
+    for (size_t i = 0; i < 4; ++i) {
+      if (sscanf(client_ip_hex_.substr(i * 8, (i + 1) * 8).c_str(), "%x",
+                 ip_bytes + i) != 1) {
+        gpr_log(
+            GPR_ERROR,
+            "Can't parse client IP part (%s) from a hex string to an integer.",
+            client_ip_hex_.substr(i * 8, (i + 1) * 8).c_str());
+        return "";
+      }
+      ip_bytes[i] = grpc_htonl(ip_bytes[i]);
+    }
+    return grpc::string(reinterpret_cast<const char*>(ip_bytes),
+                        sizeof(ip_bytes));
+  } else {
+    GPR_UNREACHABLE_CODE(return "");
+  }
+}
+
+LoadRecordValue::LoadRecordValue(grpc::string metric_name, uint64_t num_calls,
+                                 double total_metric_value) {
+  call_metrics_.emplace(std::move(metric_name),
+                        CallMetricValue(num_calls, total_metric_value));
+}
+
+void PerBalancerStore::MergeRow(const LoadRecordKey& key,
+                                const LoadRecordValue& value) {
+  // During suspension, the load data received will be dropped.
+  if (!suspended_) {
+    load_record_map_[key].MergeFrom(value);
+    gpr_log(GPR_DEBUG,
+            "[PerBalancerStore %p] Load data merged (Key: %s, Value: %s).",
+            this, key.ToString().c_str(), value.ToString().c_str());
+  } else {
+    gpr_log(GPR_DEBUG,
+            "[PerBalancerStore %p] Load data dropped (Key: %s, Value: %s).",
+            this, key.ToString().c_str(), value.ToString().c_str());
+  }
+  // We always keep track of num_calls_in_progress_, so that when this
+  // store is resumed, we still have a correct value of
+  // num_calls_in_progress_.
+  GPR_ASSERT(static_cast<int64_t>(num_calls_in_progress_) +
+                 value.GetNumCallsInProgressDelta() >=
+             0);
+  num_calls_in_progress_ += value.GetNumCallsInProgressDelta();
+}
+
+void PerBalancerStore::Suspend() {
+  suspended_ = true;
+  load_record_map_.clear();
+  gpr_log(GPR_DEBUG, "[PerBalancerStore %p] Suspended.", this);
+}
+
+void PerBalancerStore::Resume() {
+  suspended_ = false;
+  gpr_log(GPR_DEBUG, "[PerBalancerStore %p] Resumed.", this);
+}
+
+uint64_t PerBalancerStore::GetNumCallsInProgressForReport() {
+  GPR_ASSERT(!suspended_);
+  last_reported_num_calls_in_progress_ = num_calls_in_progress_;
+  return num_calls_in_progress_;
+}
+
+void PerHostStore::ReportStreamCreated(const grpc::string& lb_id,
+                                       const grpc::string& load_key) {
+  GPR_ASSERT(lb_id != kInvalidLbId);
+  SetUpForNewLbId(lb_id, load_key);
+  // Prior to this one, there was no load balancer receiving report, so we may
+  // have unassigned orphaned stores to assign to this new balancer.
+  // TODO(juanlishen): If the load key of this new stream is the same with
+  // some previously adopted orphan store, we may want to take the orphan to
+  // this stream. Need to discuss with LB team.
+  if (assigned_stores_.size() == 1) {
+    for (const auto& p : per_balancer_stores_) {
+      const grpc::string& other_lb_id = p.first;
+      const std::unique_ptr<PerBalancerStore>& orphaned_store = p.second;
+      if (other_lb_id != lb_id) {
+        orphaned_store->Resume();
+        AssignOrphanedStore(orphaned_store.get(), lb_id);
+      }
+    }
+  }
+  // The first connected balancer will adopt the kInvalidLbId.
+  if (per_balancer_stores_.size() == 1) {
+    SetUpForNewLbId(kInvalidLbId, "");
+    ReportStreamClosed(kInvalidLbId);
+  }
+}
+
+void PerHostStore::ReportStreamClosed(const grpc::string& lb_id) {
+  auto it_store_for_gone_lb = per_balancer_stores_.find(lb_id);
+  GPR_ASSERT(it_store_for_gone_lb != per_balancer_stores_.end());
+  // Remove this closed stream from our records.
+  GPR_ASSERT(UnorderedMapOfSetEraseKeyValue(
+      load_key_to_receiving_lb_ids_, it_store_for_gone_lb->second->load_key(),
+      lb_id));
+  std::set<PerBalancerStore*> orphaned_stores =
+      UnorderedMapOfSetExtract(assigned_stores_, lb_id);
+  // The stores that were assigned to this balancer are orphaned now. They
+  // should be re-assigned to other balancers which are still receiving reports.
+  for (PerBalancerStore* orphaned_store : orphaned_stores) {
+    const grpc::string* new_receiver = nullptr;
+    auto it = load_key_to_receiving_lb_ids_.find(orphaned_store->load_key());
+    if (it != load_key_to_receiving_lb_ids_.end()) {
+      // First, try to pick from the active balancers with the same load key.
+      new_receiver = RandomElement(it->second);
+    } else if (!assigned_stores_.empty()) {
+      // If failed, pick from all the remaining active balancers.
+      new_receiver = &(RandomElement(assigned_stores_)->first);
+    }
+    if (new_receiver != nullptr) {
+      AssignOrphanedStore(orphaned_store, *new_receiver);
+    } else {
+      // Load data for an LB ID that can't be assigned to any stream should
+      // be dropped.
+      orphaned_store->Suspend();
+    }
+  }
+}
+
+PerBalancerStore* PerHostStore::FindPerBalancerStore(
+    const grpc::string& lb_id) const {
+  return per_balancer_stores_.find(lb_id) != per_balancer_stores_.end()
+             ? per_balancer_stores_.find(lb_id)->second.get()
+             : nullptr;
+}
+
+const std::set<PerBalancerStore*>* PerHostStore::GetAssignedStores(
+    const grpc::string& lb_id) const {
+  auto it = assigned_stores_.find(lb_id);
+  if (it == assigned_stores_.end()) return nullptr;
+  return &(it->second);
+}
+
+void PerHostStore::AssignOrphanedStore(PerBalancerStore* orphaned_store,
+                                       const grpc::string& new_receiver) {
+  auto it = assigned_stores_.find(new_receiver);
+  GPR_ASSERT(it != assigned_stores_.end());
+  it->second.insert(orphaned_store);
+  gpr_log(GPR_INFO,
+          "[PerHostStore %p] Re-assigned orphaned store (%p) with original LB"
+          " ID of %s to new receiver %s",
+          this, orphaned_store, orphaned_store->lb_id().c_str(),
+          new_receiver.c_str());
+}
+
+void PerHostStore::SetUpForNewLbId(const grpc::string& lb_id,
+                                   const grpc::string& load_key) {
+  // The top-level caller (i.e., LoadReportService) should guarantee the
+  // lb_id is unique for each reporting stream.
+  GPR_ASSERT(per_balancer_stores_.find(lb_id) == per_balancer_stores_.end());
+  GPR_ASSERT(assigned_stores_.find(lb_id) == assigned_stores_.end());
+  load_key_to_receiving_lb_ids_[load_key].insert(lb_id);
+  std::unique_ptr<PerBalancerStore> per_balancer_store(
+      new PerBalancerStore(lb_id, load_key));
+  assigned_stores_[lb_id] = {per_balancer_store.get()};
+  per_balancer_stores_[lb_id] = std::move(per_balancer_store);
+}
+
+PerBalancerStore* LoadDataStore::FindPerBalancerStore(
+    const string& hostname, const string& lb_id) const {
+  auto it = per_host_stores_.find(hostname);
+  if (it != per_host_stores_.end()) {
+    const PerHostStore& per_host_store = it->second;
+    return per_host_store.FindPerBalancerStore(lb_id);
+  } else {
+    return nullptr;
+  }
+}
+
+void LoadDataStore::MergeRow(const grpc::string& hostname,
+                             const LoadRecordKey& key,
+                             const LoadRecordValue& value) {
+  PerBalancerStore* per_balancer_store =
+      FindPerBalancerStore(hostname, key.lb_id());
+  if (per_balancer_store != nullptr) {
+    per_balancer_store->MergeRow(key, value);
+    return;
+  }
+  // Unknown LB ID. Track it until its number of in-progress calls drops to
+  // zero.
+  int64_t in_progress_delta = value.GetNumCallsInProgressDelta();
+  if (in_progress_delta != 0) {
+    auto it_tracker = unknown_balancer_id_trackers_.find(key.lb_id());
+    if (it_tracker == unknown_balancer_id_trackers_.end()) {
+      gpr_log(
+          GPR_DEBUG,
+          "[LoadDataStore %p] Start tracking unknown balancer (lb_id_: %s).",
+          this, key.lb_id().c_str());
+      unknown_balancer_id_trackers_.insert(
+          {key.lb_id(), static_cast<uint64_t>(in_progress_delta)});
+    } else if ((it_tracker->second += in_progress_delta) == 0) {
+      unknown_balancer_id_trackers_.erase(it_tracker);
+      gpr_log(GPR_DEBUG,
+              "[LoadDataStore %p] Stop tracking unknown balancer (lb_id_: %s).",
+              this, key.lb_id().c_str());
+    }
+  }
+}
+
+const std::set<PerBalancerStore*>* LoadDataStore::GetAssignedStores(
+    const grpc::string& hostname, const grpc::string& lb_id) {
+  auto it = per_host_stores_.find(hostname);
+  if (it == per_host_stores_.end()) return nullptr;
+  return it->second.GetAssignedStores(lb_id);
+}
+
+void LoadDataStore::ReportStreamCreated(const grpc::string& hostname,
+                                        const grpc::string& lb_id,
+                                        const grpc::string& load_key) {
+  per_host_stores_[hostname].ReportStreamCreated(lb_id, load_key);
+}
+
+void LoadDataStore::ReportStreamClosed(const grpc::string& hostname,
+                                       const grpc::string& lb_id) {
+  auto it_per_host_store = per_host_stores_.find(hostname);
+  GPR_ASSERT(it_per_host_store != per_host_stores_.end());
+  it_per_host_store->second.ReportStreamClosed(lb_id);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_data_store.h b/third_party/grpc/src/cpp/server/load_reporter/load_data_store.h
new file mode 100644
index 0000000..dc08ecf
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_data_store.h
@@ -0,0 +1,348 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_DATA_STORE_H
+#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_DATA_STORE_H
+
+#include <grpc/support/port_platform.h>
+
+#include <memory>
+#include <set>
+#include <unordered_map>
+
+#include <grpc/support/log.h>
+#include <grpcpp/impl/codegen/config.h>
+
+#include "src/cpp/server/load_reporter/constants.h"
+
+namespace grpc {
+namespace load_reporter {
+
+// The load data storage is organized in hierarchy. The LoadDataStore is the
+// top-level data store. In LoadDataStore, for each host we keep a
+// PerHostStore, in which for each balancer we keep a PerBalancerStore. Each
+// PerBalancerStore maintains a map of load records, mapping from LoadRecordKey
+// to LoadRecordValue. The LoadRecordValue contains a map of customized call
+// metrics, mapping from a call metric name to the CallMetricValue.
+
+// The value of a customized call metric.
+class CallMetricValue {
+ public:
+  explicit CallMetricValue(uint64_t num_calls = 0,
+                           double total_metric_value = 0)
+      : num_calls_(num_calls), total_metric_value_(total_metric_value) {}
+
+  void MergeFrom(CallMetricValue other) {
+    num_calls_ += other.num_calls_;
+    total_metric_value_ += other.total_metric_value_;
+  }
+
+  // Getters.
+  uint64_t num_calls() const { return num_calls_; }
+  double total_metric_value() const { return total_metric_value_; }
+
+ private:
+  // The number of calls that finished with this metric.
+  uint64_t num_calls_ = 0;
+  // The sum of metric values across all the calls that finished with this
+  // metric.
+  double total_metric_value_ = 0;
+};
+
+// The key of a load record.
+class LoadRecordKey {
+ public:
+  LoadRecordKey(grpc::string lb_id, grpc::string lb_tag, grpc::string user_id,
+                grpc::string client_ip_hex)
+      : lb_id_(std::move(lb_id)),
+        lb_tag_(std::move(lb_tag)),
+        user_id_(std::move(user_id)),
+        client_ip_hex_(std::move(client_ip_hex)) {}
+
+  // Parses the input client_ip_and_token to set client IP, LB ID, and LB tag.
+  LoadRecordKey(const grpc::string& client_ip_and_token, grpc::string user_id);
+
+  grpc::string ToString() const {
+    return "[lb_id_=" + lb_id_ + ", lb_tag_=" + lb_tag_ +
+           ", user_id_=" + user_id_ + ", client_ip_hex_=" + client_ip_hex_ +
+           "]";
+  }
+
+  bool operator==(const LoadRecordKey& other) const {
+    return lb_id_ == other.lb_id_ && lb_tag_ == other.lb_tag_ &&
+           user_id_ == other.user_id_ && client_ip_hex_ == other.client_ip_hex_;
+  }
+
+  // Gets the client IP bytes in network order (i.e., big-endian).
+  grpc::string GetClientIpBytes() const;
+
+  // Getters.
+  const grpc::string& lb_id() const { return lb_id_; }
+  const grpc::string& lb_tag() const { return lb_tag_; }
+  const grpc::string& user_id() const { return user_id_; }
+  const grpc::string& client_ip_hex() const { return client_ip_hex_; }
+
+  struct Hasher {
+    void hash_combine(size_t* seed, const grpc::string& k) const {
+      *seed ^= std::hash<grpc::string>()(k) + 0x9e3779b9 + (*seed << 6) +
+               (*seed >> 2);
+    }
+
+    size_t operator()(const LoadRecordKey& k) const {
+      size_t h = 0;
+      hash_combine(&h, k.lb_id_);
+      hash_combine(&h, k.lb_tag_);
+      hash_combine(&h, k.user_id_);
+      hash_combine(&h, k.client_ip_hex_);
+      return h;
+    }
+  };
+
+ private:
+  grpc::string lb_id_;
+  grpc::string lb_tag_;
+  grpc::string user_id_;
+  grpc::string client_ip_hex_;
+};
+
+// The value of a load record.
+class LoadRecordValue {
+ public:
+  explicit LoadRecordValue(uint64_t start_count = 0, uint64_t ok_count = 0,
+                           uint64_t error_count = 0, uint64_t bytes_sent = 0,
+                           uint64_t bytes_recv = 0, uint64_t latency_ms = 0)
+      : start_count_(start_count),
+        ok_count_(ok_count),
+        error_count_(error_count),
+        bytes_sent_(bytes_sent),
+        bytes_recv_(bytes_recv),
+        latency_ms_(latency_ms) {}
+
+  LoadRecordValue(grpc::string metric_name, uint64_t num_calls,
+                  double total_metric_value);
+
+  void MergeFrom(const LoadRecordValue& other) {
+    start_count_ += other.start_count_;
+    ok_count_ += other.ok_count_;
+    error_count_ += other.error_count_;
+    bytes_sent_ += other.bytes_sent_;
+    bytes_recv_ += other.bytes_recv_;
+    latency_ms_ += other.latency_ms_;
+    for (const auto& p : other.call_metrics_) {
+      const grpc::string& key = p.first;
+      const CallMetricValue& value = p.second;
+      call_metrics_[key].MergeFrom(value);
+    }
+  }
+
+  int64_t GetNumCallsInProgressDelta() const {
+    return static_cast<int64_t>(start_count_ - ok_count_ - error_count_);
+  }
+
+  grpc::string ToString() const {
+    return "[start_count_=" + grpc::to_string(start_count_) +
+           ", ok_count_=" + grpc::to_string(ok_count_) +
+           ", error_count_=" + grpc::to_string(error_count_) +
+           ", bytes_sent_=" + grpc::to_string(bytes_sent_) +
+           ", bytes_recv_=" + grpc::to_string(bytes_recv_) +
+           ", latency_ms_=" + grpc::to_string(latency_ms_) + ", " +
+           grpc::to_string(call_metrics_.size()) + " other call metric(s)]";
+  }
+
+  bool InsertCallMetric(const grpc::string& metric_name,
+                        const CallMetricValue& metric_value) {
+    return call_metrics_.insert({metric_name, metric_value}).second;
+  }
+
+  // Getters.
+  uint64_t start_count() const { return start_count_; }
+  uint64_t ok_count() const { return ok_count_; }
+  uint64_t error_count() const { return error_count_; }
+  uint64_t bytes_sent() const { return bytes_sent_; }
+  uint64_t bytes_recv() const { return bytes_recv_; }
+  uint64_t latency_ms() const { return latency_ms_; }
+  const std::unordered_map<grpc::string, CallMetricValue>& call_metrics()
+      const {
+    return call_metrics_;
+  }
+
+ private:
+  uint64_t start_count_ = 0;
+  uint64_t ok_count_ = 0;
+  uint64_t error_count_ = 0;
+  uint64_t bytes_sent_ = 0;
+  uint64_t bytes_recv_ = 0;
+  uint64_t latency_ms_ = 0;
+  std::unordered_map<grpc::string, CallMetricValue> call_metrics_;
+};
+
+// Stores the data associated with a particular LB ID.
+class PerBalancerStore {
+ public:
+  using LoadRecordMap =
+      std::unordered_map<LoadRecordKey, LoadRecordValue, LoadRecordKey::Hasher>;
+
+  PerBalancerStore(grpc::string lb_id, grpc::string load_key)
+      : lb_id_(std::move(lb_id)), load_key_(std::move(load_key)) {}
+
+  // Merge a load record with the given key and value if the store is not
+  // suspended.
+  void MergeRow(const LoadRecordKey& key, const LoadRecordValue& value);
+
+  // Suspend this store, so that no detailed load data will be recorded.
+  void Suspend();
+  // Resume this store from suspension.
+  void Resume();
+  // Is this store suspended or not?
+  bool IsSuspended() const { return suspended_; }
+
+  bool IsNumCallsInProgressChangedSinceLastReport() const {
+    return num_calls_in_progress_ != last_reported_num_calls_in_progress_;
+  }
+
+  uint64_t GetNumCallsInProgressForReport();
+
+  grpc::string ToString() {
+    return "[PerBalancerStore lb_id_=" + lb_id_ + " load_key_=" + load_key_ +
+           "]";
+  }
+
+  void ClearLoadRecordMap() { load_record_map_.clear(); }
+
+  // Getters.
+  const grpc::string& lb_id() const { return lb_id_; }
+  const grpc::string& load_key() const { return load_key_; }
+  const LoadRecordMap& load_record_map() const { return load_record_map_; }
+
+ private:
+  grpc::string lb_id_;
+  // TODO(juanlishen): Use bytestring protobuf type?
+  grpc::string load_key_;
+  LoadRecordMap load_record_map_;
+  uint64_t num_calls_in_progress_ = 0;
+  uint64_t last_reported_num_calls_in_progress_ = 0;
+  bool suspended_ = false;
+};
+
+// Stores the data associated with a particular host.
+class PerHostStore {
+ public:
+  // When a report stream is created, a PerBalancerStore is created for the
+  // LB ID (guaranteed unique) associated with that stream. If it is the only
+  // active store, adopt all the orphaned stores. If it is the first created
+  // store, adopt the store of kInvalidLbId.
+  void ReportStreamCreated(const grpc::string& lb_id,
+                           const grpc::string& load_key);
+
+  // When a report stream is closed, the PerBalancerStores assigned to the
+  // associate LB ID need to be re-assigned to other active balancers,
+  // ideally with the same load key. If there is no active balancer, we have
+  // to suspend those stores and drop the incoming load data until they are
+  // resumed.
+  void ReportStreamClosed(const grpc::string& lb_id);
+
+  // Returns null if not found. Caller doesn't own the returned store.
+  PerBalancerStore* FindPerBalancerStore(const grpc::string& lb_id) const;
+
+  // Returns null if lb_id is not found. The returned pointer points to the
+  // underlying data structure, which is not owned by the caller.
+  const std::set<PerBalancerStore*>* GetAssignedStores(
+      const grpc::string& lb_id) const;
+
+ private:
+  // Creates a PerBalancerStore for the given LB ID, assigns the store to
+  // itself, and records the LB ID to the load key.
+  void SetUpForNewLbId(const grpc::string& lb_id, const grpc::string& load_key);
+
+  void AssignOrphanedStore(PerBalancerStore* orphaned_store,
+                           const grpc::string& new_receiver);
+
+  std::unordered_map<grpc::string, std::set<grpc::string>>
+      load_key_to_receiving_lb_ids_;
+
+  // Key: LB ID. The key set includes all the LB IDs that have been
+  // allocated for reporting streams so far.
+  // Value: the unique pointer to the PerBalancerStore of the LB ID.
+  std::unordered_map<grpc::string, std::unique_ptr<PerBalancerStore>>
+      per_balancer_stores_;
+
+  // Key: LB ID. The key set includes the LB IDs of the balancers that are
+  // currently receiving report.
+  // Value: the set of raw pointers to the PerBalancerStores assigned to the LB
+  // ID. Note that the sets in assigned_stores_ form a division of the value set
+  // of per_balancer_stores_.
+  std::unordered_map<grpc::string, std::set<PerBalancerStore*>>
+      assigned_stores_;
+};
+
+// Thread-unsafe two-level bookkeeper of all the load data.
+// Note: We never remove any store objects from this class, as per the
+// current spec. That's because premature removal of the store objects
+// may lead to loss of critical information, e.g., mapping from lb_id to
+// load_key, and the number of in-progress calls. Such loss will cause
+// information inconsistency when the balancer is re-connected. Keeping
+// all the stores should be fine for PerHostStore, since we assume there
+// should only be a few hostnames. But it's a potential problem for
+// PerBalancerStore.
+class LoadDataStore {
+ public:
+  // Returns null if not found. Caller doesn't own the returned store.
+  PerBalancerStore* FindPerBalancerStore(const grpc::string& hostname,
+                                         const grpc::string& lb_id) const;
+
+  // Returns null if hostname or lb_id is not found. The returned pointer points
+  // to the underlying data structure, which is not owned by the caller.
+  const std::set<PerBalancerStore*>* GetAssignedStores(const string& hostname,
+                                                       const string& lb_id);
+
+  // If a PerBalancerStore can be found by the hostname and LB ID in
+  // LoadRecordKey, the load data will be merged to that store. Otherwise,
+  // only track the number of the in-progress calls for this unknown LB ID.
+  void MergeRow(const grpc::string& hostname, const LoadRecordKey& key,
+                const LoadRecordValue& value);
+
+  // Is the given lb_id a tracked unknown LB ID (i.e., the LB ID was associated
+  // with some received load data but unknown to this load data store)?
+  bool IsTrackedUnknownBalancerId(const grpc::string& lb_id) const {
+    return unknown_balancer_id_trackers_.find(lb_id) !=
+           unknown_balancer_id_trackers_.end();
+  }
+
+  // Wrapper around PerHostStore::ReportStreamCreated.
+  void ReportStreamCreated(const grpc::string& hostname,
+                           const grpc::string& lb_id,
+                           const grpc::string& load_key);
+
+  // Wrapper around PerHostStore::ReportStreamClosed.
+  void ReportStreamClosed(const grpc::string& hostname,
+                          const grpc::string& lb_id);
+
+ private:
+  // Buffered data that was fetched from Census but hasn't been sent to
+  // balancer. We need to keep this data ourselves because Census will
+  // delete the data once it's returned.
+  std::unordered_map<grpc::string, PerHostStore> per_host_stores_;
+
+  // Tracks the number of in-progress calls for each unknown LB ID.
+  std::unordered_map<grpc::string, uint64_t> unknown_balancer_id_trackers_;
+};
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_DATA_STORE_H
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporter.cc b/third_party/grpc/src/cpp/server/load_reporter/load_reporter.cc
new file mode 100644
index 0000000..464063a
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporter.cc
@@ -0,0 +1,509 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/impl/codegen/port_platform.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <chrono>
+#include <ctime>
+#include <iterator>
+
+#include "src/cpp/server/load_reporter/constants.h"
+#include "src/cpp/server/load_reporter/get_cpu_stats.h"
+#include "src/cpp/server/load_reporter/load_reporter.h"
+
+#include "opencensus/stats/internal/set_aggregation_window.h"
+
+namespace grpc {
+namespace load_reporter {
+
+CpuStatsProvider::CpuStatsSample CpuStatsProviderDefaultImpl::GetCpuStats() {
+  return GetCpuStatsImpl();
+}
+
+CensusViewProvider::CensusViewProvider()
+    : tag_key_token_(::opencensus::stats::TagKey::Register(kTagKeyToken)),
+      tag_key_host_(::opencensus::stats::TagKey::Register(kTagKeyHost)),
+      tag_key_user_id_(::opencensus::stats::TagKey::Register(kTagKeyUserId)),
+      tag_key_status_(::opencensus::stats::TagKey::Register(kTagKeyStatus)),
+      tag_key_metric_name_(
+          ::opencensus::stats::TagKey::Register(kTagKeyMetricName)) {
+  // One view related to starting a call.
+  auto vd_start_count =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name(kViewStartCount)
+          .set_measure(kMeasureStartCount)
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .set_description(
+              "Delta count of calls started broken down by <token, host, "
+              "user_id>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_start_count);
+  view_descriptor_map_.emplace(kViewStartCount, vd_start_count);
+  // Four views related to ending a call.
+  // If this view is set as Count of kMeasureEndBytesSent (in hope of saving one
+  // measure), it's infeasible to prepare fake data for testing. That's because
+  // the OpenCensus API to make up view data will add the input data as separate
+  // measurements instead of setting the data values directly.
+  auto vd_end_count =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name(kViewEndCount)
+          .set_measure(kMeasureEndCount)
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_status_)
+          .set_description(
+              "Delta count of calls ended broken down by <token, host, "
+              "user_id, status>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_end_count);
+  view_descriptor_map_.emplace(kViewEndCount, vd_end_count);
+  auto vd_end_bytes_sent =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name(kViewEndBytesSent)
+          .set_measure(kMeasureEndBytesSent)
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_status_)
+          .set_description(
+              "Delta sum of bytes sent broken down by <token, host, user_id, "
+              "status>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_end_bytes_sent);
+  view_descriptor_map_.emplace(kViewEndBytesSent, vd_end_bytes_sent);
+  auto vd_end_bytes_received =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name(kViewEndBytesReceived)
+          .set_measure(kMeasureEndBytesReceived)
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_status_)
+          .set_description(
+              "Delta sum of bytes received broken down by <token, host, "
+              "user_id, status>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_end_bytes_received);
+  view_descriptor_map_.emplace(kViewEndBytesReceived, vd_end_bytes_received);
+  auto vd_end_latency_ms =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name(kViewEndLatencyMs)
+          .set_measure(kMeasureEndLatencyMs)
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_status_)
+          .set_description(
+              "Delta sum of latency in ms broken down by <token, host, "
+              "user_id, status>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_end_latency_ms);
+  view_descriptor_map_.emplace(kViewEndLatencyMs, vd_end_latency_ms);
+  // Two views related to other call metrics.
+  auto vd_metric_call_count =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name(kViewOtherCallMetricCount)
+          .set_measure(kMeasureOtherCallMetric)
+          .set_aggregation(::opencensus::stats::Aggregation::Count())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_metric_name_)
+          .set_description(
+              "Delta count of calls broken down by <token, host, user_id, "
+              "metric_name>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_metric_call_count);
+  view_descriptor_map_.emplace(kViewOtherCallMetricCount, vd_metric_call_count);
+  auto vd_metric_value =
+      ::opencensus::stats::ViewDescriptor()
+          .set_name(kViewOtherCallMetricValue)
+          .set_measure(kMeasureOtherCallMetric)
+          .set_aggregation(::opencensus::stats::Aggregation::Sum())
+          .add_column(tag_key_token_)
+          .add_column(tag_key_host_)
+          .add_column(tag_key_user_id_)
+          .add_column(tag_key_metric_name_)
+          .set_description(
+              "Delta sum of call metric value broken down "
+              "by <token, host, user_id, metric_name>.");
+  ::opencensus::stats::SetAggregationWindow(
+      ::opencensus::stats::AggregationWindow::Delta(), &vd_metric_value);
+  view_descriptor_map_.emplace(kViewOtherCallMetricValue, vd_metric_value);
+}
+
+double CensusViewProvider::GetRelatedViewDataRowDouble(
+    const ViewDataMap& view_data_map, const char* view_name,
+    size_t view_name_len, const std::vector<grpc::string>& tag_values) {
+  auto it_vd = view_data_map.find(grpc::string(view_name, view_name_len));
+  GPR_ASSERT(it_vd != view_data_map.end());
+  GPR_ASSERT(it_vd->second.type() ==
+             ::opencensus::stats::ViewData::Type::kDouble);
+  auto it_row = it_vd->second.double_data().find(tag_values);
+  GPR_ASSERT(it_row != it_vd->second.double_data().end());
+  return it_row->second;
+}
+
+uint64_t CensusViewProvider::GetRelatedViewDataRowInt(
+    const ViewDataMap& view_data_map, const char* view_name,
+    size_t view_name_len, const std::vector<grpc::string>& tag_values) {
+  auto it_vd = view_data_map.find(grpc::string(view_name, view_name_len));
+  GPR_ASSERT(it_vd != view_data_map.end());
+  GPR_ASSERT(it_vd->second.type() ==
+             ::opencensus::stats::ViewData::Type::kInt64);
+  auto it_row = it_vd->second.int_data().find(tag_values);
+  GPR_ASSERT(it_row != it_vd->second.int_data().end());
+  GPR_ASSERT(it_row->second >= 0);
+  return it_row->second;
+}
+
+CensusViewProviderDefaultImpl::CensusViewProviderDefaultImpl() {
+  for (const auto& p : view_descriptor_map()) {
+    const grpc::string& view_name = p.first;
+    const ::opencensus::stats::ViewDescriptor& vd = p.second;
+    // We need to use pair's piecewise ctor here, otherwise the deleted copy
+    // ctor of View will be called.
+    view_map_.emplace(std::piecewise_construct,
+                      std::forward_as_tuple(view_name),
+                      std::forward_as_tuple(vd));
+  }
+}
+
+CensusViewProvider::ViewDataMap CensusViewProviderDefaultImpl::FetchViewData() {
+  gpr_log(GPR_DEBUG, "[CVP %p] Starts fetching Census view data.", this);
+  ViewDataMap view_data_map;
+  for (auto& p : view_map_) {
+    const grpc::string& view_name = p.first;
+    ::opencensus::stats::View& view = p.second;
+    if (view.IsValid()) {
+      view_data_map.emplace(view_name, view.GetData());
+      gpr_log(GPR_DEBUG, "[CVP %p] Fetched view data (view: %s).", this,
+              view_name.c_str());
+    } else {
+      gpr_log(
+          GPR_DEBUG,
+          "[CVP %p] Can't fetch view data because view is invalid (view: %s).",
+          this, view_name.c_str());
+    }
+  }
+  return view_data_map;
+}
+
+grpc::string LoadReporter::GenerateLbId() {
+  while (true) {
+    if (next_lb_id_ > UINT32_MAX) {
+      gpr_log(GPR_ERROR, "[LR %p] The LB ID exceeds the max valid value!",
+              this);
+      return "";
+    }
+    int64_t lb_id = next_lb_id_++;
+    // Overflow should never happen.
+    GPR_ASSERT(lb_id >= 0);
+    // Convert to padded hex string for a 32-bit LB ID. E.g, "0000ca5b".
+    char buf[kLbIdLength + 1];
+    snprintf(buf, sizeof(buf), "%08lx", lb_id);
+    grpc::string lb_id_str(buf, kLbIdLength);
+    // The client may send requests with LB ID that has never been allocated
+    // by this load reporter. Those IDs are tracked and will be skipped when
+    // we generate a new ID.
+    if (!load_data_store_.IsTrackedUnknownBalancerId(lb_id_str)) {
+      return lb_id_str;
+    }
+  }
+}
+
+::grpc::lb::v1::LoadBalancingFeedback
+LoadReporter::GenerateLoadBalancingFeedback() {
+  std::unique_lock<std::mutex> lock(feedback_mu_);
+  auto now = std::chrono::system_clock::now();
+  // Discard records outside the window until there is only one record
+  // outside the window, which is used as the base for difference.
+  while (feedback_records_.size() > 1 &&
+         !IsRecordInWindow(feedback_records_[1], now)) {
+    feedback_records_.pop_front();
+  }
+  if (feedback_records_.size() < 2) {
+    return ::grpc::lb::v1::LoadBalancingFeedback::default_instance();
+  }
+  // Find the longest range with valid ends.
+  auto oldest = feedback_records_.begin();
+  auto newest = feedback_records_.end() - 1;
+  while (std::distance(oldest, newest) > 0 &&
+         (newest->cpu_limit == 0 || oldest->cpu_limit == 0)) {
+    // A zero limit means that the system info reading was failed, so these
+    // records can't be used to calculate CPU utilization.
+    if (newest->cpu_limit == 0) --newest;
+    if (oldest->cpu_limit == 0) ++oldest;
+  }
+  if (std::distance(oldest, newest) < 1 ||
+      oldest->end_time == newest->end_time ||
+      newest->cpu_limit == oldest->cpu_limit) {
+    return ::grpc::lb::v1::LoadBalancingFeedback::default_instance();
+  }
+  uint64_t rpcs = 0;
+  uint64_t errors = 0;
+  for (auto p = newest; p != oldest; --p) {
+    // Because these two numbers are counters, the oldest record shouldn't be
+    // included.
+    rpcs += p->rpcs;
+    errors += p->errors;
+  }
+  double cpu_usage = newest->cpu_usage - oldest->cpu_usage;
+  double cpu_limit = newest->cpu_limit - oldest->cpu_limit;
+  std::chrono::duration<double> duration_seconds =
+      newest->end_time - oldest->end_time;
+  lock.unlock();
+  ::grpc::lb::v1::LoadBalancingFeedback feedback;
+  feedback.set_server_utilization(static_cast<float>(cpu_usage / cpu_limit));
+  feedback.set_calls_per_second(
+      static_cast<float>(rpcs / duration_seconds.count()));
+  feedback.set_errors_per_second(
+      static_cast<float>(errors / duration_seconds.count()));
+  return feedback;
+}
+
+::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load>
+LoadReporter::GenerateLoads(const grpc::string& hostname,
+                            const grpc::string& lb_id) {
+  std::lock_guard<std::mutex> lock(store_mu_);
+  auto assigned_stores = load_data_store_.GetAssignedStores(hostname, lb_id);
+  GPR_ASSERT(assigned_stores != nullptr);
+  GPR_ASSERT(!assigned_stores->empty());
+  ::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load> loads;
+  for (PerBalancerStore* per_balancer_store : *assigned_stores) {
+    GPR_ASSERT(!per_balancer_store->IsSuspended());
+    if (!per_balancer_store->load_record_map().empty()) {
+      for (const auto& p : per_balancer_store->load_record_map()) {
+        const auto& key = p.first;
+        const auto& value = p.second;
+        auto load = loads.Add();
+        load->set_load_balance_tag(key.lb_tag());
+        load->set_user_id(key.user_id());
+        load->set_client_ip_address(key.GetClientIpBytes());
+        load->set_num_calls_started(static_cast<int64_t>(value.start_count()));
+        load->set_num_calls_finished_without_error(
+            static_cast<int64_t>(value.ok_count()));
+        load->set_num_calls_finished_with_error(
+            static_cast<int64_t>(value.error_count()));
+        load->set_total_bytes_sent(static_cast<int64_t>(value.bytes_sent()));
+        load->set_total_bytes_received(
+            static_cast<int64_t>(value.bytes_recv()));
+        load->mutable_total_latency()->set_seconds(
+            static_cast<int64_t>(value.latency_ms() / 1000));
+        load->mutable_total_latency()->set_nanos(
+            (static_cast<int32_t>(value.latency_ms()) % 1000) * 1000000);
+        for (const auto& p : value.call_metrics()) {
+          const grpc::string& metric_name = p.first;
+          const CallMetricValue& metric_value = p.second;
+          auto call_metric_data = load->add_metric_data();
+          call_metric_data->set_metric_name(metric_name);
+          call_metric_data->set_num_calls_finished_with_metric(
+              metric_value.num_calls());
+          call_metric_data->set_total_metric_value(
+              metric_value.total_metric_value());
+        }
+        if (per_balancer_store->lb_id() != lb_id) {
+          // This per-balancer store is an orphan assigned to this receiving
+          // balancer.
+          AttachOrphanLoadId(load, *per_balancer_store);
+        }
+      }
+      per_balancer_store->ClearLoadRecordMap();
+    }
+    if (per_balancer_store->IsNumCallsInProgressChangedSinceLastReport()) {
+      auto load = loads.Add();
+      load->set_num_calls_in_progress(
+          per_balancer_store->GetNumCallsInProgressForReport());
+      if (per_balancer_store->lb_id() != lb_id) {
+        // This per-balancer store is an orphan assigned to this receiving
+        // balancer.
+        AttachOrphanLoadId(load, *per_balancer_store);
+      }
+    }
+  }
+  return loads;
+}
+
+void LoadReporter::AttachOrphanLoadId(
+    ::grpc::lb::v1::Load* load, const PerBalancerStore& per_balancer_store) {
+  if (per_balancer_store.lb_id() == kInvalidLbId) {
+    load->set_load_key_unknown(true);
+  } else {
+    // We shouldn't set load_key_unknown to any value in this case because
+    // load_key_unknown and orphaned_load_identifier are under an oneof struct.
+    load->mutable_orphaned_load_identifier()->set_load_key(
+        per_balancer_store.load_key());
+    load->mutable_orphaned_load_identifier()->set_load_balancer_id(
+        per_balancer_store.lb_id());
+  }
+}
+
+void LoadReporter::AppendNewFeedbackRecord(uint64_t rpcs, uint64_t errors) {
+  CpuStatsProvider::CpuStatsSample cpu_stats;
+  if (cpu_stats_provider_ != nullptr) {
+    cpu_stats = cpu_stats_provider_->GetCpuStats();
+  } else {
+    // This will make the load balancing feedback generation a no-op.
+    cpu_stats = {0, 0};
+  }
+  std::unique_lock<std::mutex> lock(feedback_mu_);
+  feedback_records_.emplace_back(std::chrono::system_clock::now(), rpcs, errors,
+                                 cpu_stats.first, cpu_stats.second);
+}
+
+void LoadReporter::ReportStreamCreated(const grpc::string& hostname,
+                                       const grpc::string& lb_id,
+                                       const grpc::string& load_key) {
+  std::lock_guard<std::mutex> lock(store_mu_);
+  load_data_store_.ReportStreamCreated(hostname, lb_id, load_key);
+  gpr_log(GPR_INFO,
+          "[LR %p] Report stream created (host: %s, LB ID: %s, load key: %s).",
+          this, hostname.c_str(), lb_id.c_str(), load_key.c_str());
+}
+
+void LoadReporter::ReportStreamClosed(const grpc::string& hostname,
+                                      const grpc::string& lb_id) {
+  std::lock_guard<std::mutex> lock(store_mu_);
+  load_data_store_.ReportStreamClosed(hostname, lb_id);
+  gpr_log(GPR_INFO, "[LR %p] Report stream closed (host: %s, LB ID: %s).", this,
+          hostname.c_str(), lb_id.c_str());
+}
+
+void LoadReporter::ProcessViewDataCallStart(
+    const CensusViewProvider::ViewDataMap& view_data_map) {
+  auto it = view_data_map.find(kViewStartCount);
+  if (it != view_data_map.end()) {
+    for (const auto& p : it->second.int_data()) {
+      const std::vector<grpc::string>& tag_values = p.first;
+      const uint64_t start_count = static_cast<uint64_t>(p.second);
+      const grpc::string& client_ip_and_token = tag_values[0];
+      const grpc::string& host = tag_values[1];
+      const grpc::string& user_id = tag_values[2];
+      LoadRecordKey key(client_ip_and_token, user_id);
+      LoadRecordValue value = LoadRecordValue(start_count);
+      {
+        std::unique_lock<std::mutex> lock(store_mu_);
+        load_data_store_.MergeRow(host, key, value);
+      }
+    }
+  }
+}
+
+void LoadReporter::ProcessViewDataCallEnd(
+    const CensusViewProvider::ViewDataMap& view_data_map) {
+  uint64_t total_end_count = 0;
+  uint64_t total_error_count = 0;
+  auto it = view_data_map.find(kViewEndCount);
+  if (it != view_data_map.end()) {
+    for (const auto& p : it->second.int_data()) {
+      const std::vector<grpc::string>& tag_values = p.first;
+      const uint64_t end_count = static_cast<uint64_t>(p.second);
+      const grpc::string& client_ip_and_token = tag_values[0];
+      const grpc::string& host = tag_values[1];
+      const grpc::string& user_id = tag_values[2];
+      const grpc::string& status = tag_values[3];
+      // This is due to a bug reported internally of Java server load reporting
+      // implementation.
+      // TODO(juanlishen): Check whether this situation happens in OSS C++.
+      if (client_ip_and_token.size() == 0) {
+        gpr_log(GPR_DEBUG,
+                "Skipping processing Opencensus record with empty "
+                "client_ip_and_token tag.");
+        continue;
+      }
+      LoadRecordKey key(client_ip_and_token, user_id);
+      const uint64_t bytes_sent = CensusViewProvider::GetRelatedViewDataRowInt(
+          view_data_map, kViewEndBytesSent, sizeof(kViewEndBytesSent) - 1,
+          tag_values);
+      const uint64_t bytes_received =
+          CensusViewProvider::GetRelatedViewDataRowInt(
+              view_data_map, kViewEndBytesReceived,
+              sizeof(kViewEndBytesReceived) - 1, tag_values);
+      const uint64_t latency_ms = CensusViewProvider::GetRelatedViewDataRowInt(
+          view_data_map, kViewEndLatencyMs, sizeof(kViewEndLatencyMs) - 1,
+          tag_values);
+      uint64_t ok_count = 0;
+      uint64_t error_count = 0;
+      total_end_count += end_count;
+      if (std::strcmp(status.c_str(), kCallStatusOk) == 0) {
+        ok_count = end_count;
+      } else {
+        error_count = end_count;
+        total_error_count += end_count;
+      }
+      LoadRecordValue value = LoadRecordValue(
+          0, ok_count, error_count, bytes_sent, bytes_received, latency_ms);
+      {
+        std::unique_lock<std::mutex> lock(store_mu_);
+        load_data_store_.MergeRow(host, key, value);
+      }
+    }
+  }
+  AppendNewFeedbackRecord(total_end_count, total_error_count);
+}
+
+void LoadReporter::ProcessViewDataOtherCallMetrics(
+    const CensusViewProvider::ViewDataMap& view_data_map) {
+  auto it = view_data_map.find(kViewOtherCallMetricCount);
+  if (it != view_data_map.end()) {
+    for (const auto& p : it->second.int_data()) {
+      const std::vector<grpc::string>& tag_values = p.first;
+      const int64_t num_calls = p.second;
+      const grpc::string& client_ip_and_token = tag_values[0];
+      const grpc::string& host = tag_values[1];
+      const grpc::string& user_id = tag_values[2];
+      const grpc::string& metric_name = tag_values[3];
+      LoadRecordKey key(client_ip_and_token, user_id);
+      const double total_metric_value =
+          CensusViewProvider::GetRelatedViewDataRowDouble(
+              view_data_map, kViewOtherCallMetricValue,
+              sizeof(kViewOtherCallMetricValue) - 1, tag_values);
+      LoadRecordValue value = LoadRecordValue(
+          metric_name, static_cast<uint64_t>(num_calls), total_metric_value);
+      {
+        std::unique_lock<std::mutex> lock(store_mu_);
+        load_data_store_.MergeRow(host, key, value);
+      }
+    }
+  }
+}
+
+void LoadReporter::FetchAndSample() {
+  gpr_log(GPR_DEBUG,
+          "[LR %p] Starts fetching Census view data and sampling LB feedback "
+          "record.",
+          this);
+  CensusViewProvider::ViewDataMap view_data_map =
+      census_view_provider_->FetchViewData();
+  ProcessViewDataCallStart(view_data_map);
+  ProcessViewDataCallEnd(view_data_map);
+  ProcessViewDataOtherCallMetrics(view_data_map);
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporter.h b/third_party/grpc/src/cpp/server/load_reporter/load_reporter.h
new file mode 100644
index 0000000..b2254d5
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporter.h
@@ -0,0 +1,228 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H
+#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H
+
+#include <grpc/support/port_platform.h>
+
+#include <atomic>
+#include <chrono>
+#include <deque>
+#include <vector>
+
+#include <grpc/support/log.h>
+#include <grpcpp/impl/codegen/config.h>
+
+#include "src/cpp/server/load_reporter/load_data_store.h"
+#include "src/proto/grpc/lb/v1/load_reporter.grpc.pb.h"
+
+#include "opencensus/stats/stats.h"
+
+namespace grpc {
+namespace load_reporter {
+
+// The interface to get the Census stats. Abstracted for mocking.
+class CensusViewProvider {
+ public:
+  // Maps from the view name to the view data.
+  using ViewDataMap =
+      std::unordered_map<grpc::string, ::opencensus::stats::ViewData>;
+  // Maps from the view name to the view descriptor.
+  using ViewDescriptorMap =
+      std::unordered_map<grpc::string, ::opencensus::stats::ViewDescriptor>;
+
+  CensusViewProvider();
+  virtual ~CensusViewProvider() = default;
+
+  // Fetches the view data accumulated since last fetching, and returns it as a
+  // map from the view name to the view data.
+  virtual ViewDataMap FetchViewData() = 0;
+
+  // A helper function that gets a row with the input tag values from the view
+  // data. Only used when we know that row must exist because we have seen a row
+  // with the same tag values in a related view data. Several ViewData's are
+  // considered related if their views are based on the measures that are always
+  // recorded at the same time.
+  static double GetRelatedViewDataRowDouble(
+      const ViewDataMap& view_data_map, const char* view_name,
+      size_t view_name_len, const std::vector<grpc::string>& tag_values);
+  static uint64_t GetRelatedViewDataRowInt(
+      const ViewDataMap& view_data_map, const char* view_name,
+      size_t view_name_len, const std::vector<grpc::string>& tag_values);
+
+ protected:
+  const ViewDescriptorMap& view_descriptor_map() const {
+    return view_descriptor_map_;
+  }
+
+ private:
+  ViewDescriptorMap view_descriptor_map_;
+  // Tag keys.
+  ::opencensus::stats::TagKey tag_key_token_;
+  ::opencensus::stats::TagKey tag_key_host_;
+  ::opencensus::stats::TagKey tag_key_user_id_;
+  ::opencensus::stats::TagKey tag_key_status_;
+  ::opencensus::stats::TagKey tag_key_metric_name_;
+};
+
+// The default implementation fetches the real stats from Census.
+class CensusViewProviderDefaultImpl : public CensusViewProvider {
+ public:
+  CensusViewProviderDefaultImpl();
+
+  ViewDataMap FetchViewData() override;
+
+ private:
+  std::unordered_map<grpc::string, ::opencensus::stats::View> view_map_;
+};
+
+// The interface to get the CPU stats. Abstracted for mocking.
+class CpuStatsProvider {
+ public:
+  // The used and total amounts of CPU usage.
+  using CpuStatsSample = std::pair<uint64_t, uint64_t>;
+
+  virtual ~CpuStatsProvider() = default;
+
+  // Gets the cumulative used CPU and total CPU resource.
+  virtual CpuStatsSample GetCpuStats() = 0;
+};
+
+// The default implementation reads CPU jiffies from the system to calculate CPU
+// utilization.
+class CpuStatsProviderDefaultImpl : public CpuStatsProvider {
+ public:
+  CpuStatsSample GetCpuStats() override;
+};
+
+// Maintains all the load data and load reporting streams.
+class LoadReporter {
+ public:
+  // TODO(juanlishen): Allow config for providers from users.
+  LoadReporter(uint32_t feedback_sample_window_seconds,
+               std::unique_ptr<CensusViewProvider> census_view_provider,
+               std::unique_ptr<CpuStatsProvider> cpu_stats_provider)
+      : feedback_sample_window_seconds_(feedback_sample_window_seconds),
+        census_view_provider_(std::move(census_view_provider)),
+        cpu_stats_provider_(std::move(cpu_stats_provider)) {
+    // Append the initial record so that the next real record can have a base.
+    AppendNewFeedbackRecord(0, 0);
+  }
+
+  // Fetches the latest data from Census and merge it into the data store.
+  // Also adds a new sample to the LB feedback sliding window.
+  // Thread-unsafe. (1). The access to the load data store and feedback records
+  // has locking. (2). The access to the Census view provider and CPU stats
+  // provider lacks locking, but we only access these two members in this method
+  // (in testing, we also access them when setting up expectation). So the
+  // invocations of this method must be serialized.
+  void FetchAndSample();
+
+  // Generates a report for that host and balancer. The report contains
+  // all the stats data accumulated between the last report (i.e., the last
+  // consumption) and the last fetch from Census (i.e., the last production).
+  // Thread-safe.
+  ::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load> GenerateLoads(
+      const grpc::string& hostname, const grpc::string& lb_id);
+
+  // The feedback is calculated from the stats data recorded in the sliding
+  // window. Outdated records are discarded.
+  // Thread-safe.
+  ::grpc::lb::v1::LoadBalancingFeedback GenerateLoadBalancingFeedback();
+
+  // Wrapper around LoadDataStore::ReportStreamCreated.
+  // Thread-safe.
+  void ReportStreamCreated(const grpc::string& hostname,
+                           const grpc::string& lb_id,
+                           const grpc::string& load_key);
+
+  // Wrapper around LoadDataStore::ReportStreamClosed.
+  // Thread-safe.
+  void ReportStreamClosed(const grpc::string& hostname,
+                          const grpc::string& lb_id);
+
+  // Generates a unique LB ID of length kLbIdLength. Returns an empty string
+  // upon failure. Thread-safe.
+  grpc::string GenerateLbId();
+
+  // Accessors only for testing.
+  CensusViewProvider* census_view_provider() {
+    return census_view_provider_.get();
+  }
+  CpuStatsProvider* cpu_stats_provider() { return cpu_stats_provider_.get(); }
+
+ private:
+  struct LoadBalancingFeedbackRecord {
+    std::chrono::system_clock::time_point end_time;
+    uint64_t rpcs;
+    uint64_t errors;
+    uint64_t cpu_usage;
+    uint64_t cpu_limit;
+
+    LoadBalancingFeedbackRecord(
+        const std::chrono::system_clock::time_point& end_time, uint64_t rpcs,
+        uint64_t errors, uint64_t cpu_usage, uint64_t cpu_limit)
+        : end_time(end_time),
+          rpcs(rpcs),
+          errors(errors),
+          cpu_usage(cpu_usage),
+          cpu_limit(cpu_limit) {}
+  };
+
+  // Finds the view data about starting call from the view_data_map and merges
+  // the data to the load data store.
+  void ProcessViewDataCallStart(
+      const CensusViewProvider::ViewDataMap& view_data_map);
+  // Finds the view data about ending call from the view_data_map and merges the
+  // data to the load data store.
+  void ProcessViewDataCallEnd(
+      const CensusViewProvider::ViewDataMap& view_data_map);
+  // Finds the view data about the customized call metrics from the
+  // view_data_map and merges the data to the load data store.
+  void ProcessViewDataOtherCallMetrics(
+      const CensusViewProvider::ViewDataMap& view_data_map);
+
+  bool IsRecordInWindow(const LoadBalancingFeedbackRecord& record,
+                        std::chrono::system_clock::time_point now) {
+    return record.end_time > now - feedback_sample_window_seconds_;
+  }
+
+  void AppendNewFeedbackRecord(uint64_t rpcs, uint64_t errors);
+
+  // Extracts an OrphanedLoadIdentifier from the per-balancer store and attaches
+  // it to the load.
+  void AttachOrphanLoadId(::grpc::lb::v1::Load* load,
+                          const PerBalancerStore& per_balancer_store);
+
+  std::atomic<int64_t> next_lb_id_{0};
+  const std::chrono::seconds feedback_sample_window_seconds_;
+  std::mutex feedback_mu_;
+  std::deque<LoadBalancingFeedbackRecord> feedback_records_;
+  // TODO(juanlishen): Lock in finer grain. Locking the whole store may be
+  // too expensive.
+  std::mutex store_mu_;
+  LoadDataStore load_data_store_;
+  std::unique_ptr<CensusViewProvider> census_view_provider_;
+  std::unique_ptr<CpuStatsProvider> cpu_stats_provider_;
+};
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.cc b/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.cc
new file mode 100644
index 0000000..d001199
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.cc
@@ -0,0 +1,370 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/server/load_reporter/load_reporter_async_service_impl.h"
+
+namespace grpc {
+namespace load_reporter {
+
+void LoadReporterAsyncServiceImpl::CallableTag::Run(bool ok) {
+  GPR_ASSERT(handler_function_ != nullptr);
+  GPR_ASSERT(handler_ != nullptr);
+  handler_function_(std::move(handler_), ok);
+}
+
+LoadReporterAsyncServiceImpl::LoadReporterAsyncServiceImpl(
+    std::unique_ptr<ServerCompletionQueue> cq)
+    : cq_(std::move(cq)) {
+  thread_ = std::unique_ptr<::grpc_core::Thread>(
+      new ::grpc_core::Thread("server_load_reporting", Work, this));
+  std::unique_ptr<CpuStatsProvider> cpu_stats_provider = nullptr;
+#if defined(GPR_LINUX) || defined(GPR_WINDOWS) || defined(GPR_APPLE)
+  cpu_stats_provider.reset(new CpuStatsProviderDefaultImpl());
+#endif
+  load_reporter_ = std::unique_ptr<LoadReporter>(new LoadReporter(
+      kFeedbackSampleWindowSeconds,
+      std::unique_ptr<CensusViewProvider>(new CensusViewProviderDefaultImpl()),
+      std::move(cpu_stats_provider)));
+}
+
+LoadReporterAsyncServiceImpl::~LoadReporterAsyncServiceImpl() {
+  // We will reach here after the server starts shutting down.
+  shutdown_ = true;
+  {
+    std::unique_lock<std::mutex> lock(cq_shutdown_mu_);
+    cq_->Shutdown();
+  }
+  if (next_fetch_and_sample_alarm_ != nullptr)
+    next_fetch_and_sample_alarm_->Cancel();
+  thread_->Join();
+}
+
+void LoadReporterAsyncServiceImpl::ScheduleNextFetchAndSample() {
+  auto next_fetch_and_sample_time =
+      gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                   gpr_time_from_millis(kFetchAndSampleIntervalSeconds * 1000,
+                                        GPR_TIMESPAN));
+  {
+    std::unique_lock<std::mutex> lock(cq_shutdown_mu_);
+    if (shutdown_) return;
+    // TODO(juanlishen): Improve the Alarm implementation to reuse a single
+    // instance for multiple events.
+    next_fetch_and_sample_alarm_.reset(new Alarm);
+    next_fetch_and_sample_alarm_->Set(cq_.get(), next_fetch_and_sample_time,
+                                      this);
+  }
+  gpr_log(GPR_DEBUG, "[LRS %p] Next fetch-and-sample scheduled.", this);
+}
+
+void LoadReporterAsyncServiceImpl::FetchAndSample(bool ok) {
+  if (!ok) {
+    gpr_log(GPR_INFO, "[LRS %p] Fetch-and-sample is stopped.", this);
+    return;
+  }
+  gpr_log(GPR_DEBUG, "[LRS %p] Starting a fetch-and-sample...", this);
+  load_reporter_->FetchAndSample();
+  ScheduleNextFetchAndSample();
+}
+
+void LoadReporterAsyncServiceImpl::Work(void* arg) {
+  LoadReporterAsyncServiceImpl* service =
+      reinterpret_cast<LoadReporterAsyncServiceImpl*>(arg);
+  service->FetchAndSample(true /* ok */);
+  // TODO(juanlishen): This is a workaround to wait for the cq to be ready. Need
+  // to figure out why cq is not ready after service starts.
+  gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                               gpr_time_from_seconds(1, GPR_TIMESPAN)));
+  ReportLoadHandler::CreateAndStart(service->cq_.get(), service,
+                                    service->load_reporter_.get());
+  void* tag;
+  bool ok;
+  while (true) {
+    if (!service->cq_->Next(&tag, &ok)) {
+      // The completion queue is shutting down.
+      GPR_ASSERT(service->shutdown_);
+      break;
+    }
+    if (tag == service) {
+      service->FetchAndSample(ok);
+    } else {
+      auto* next_step = static_cast<CallableTag*>(tag);
+      next_step->Run(ok);
+    }
+  }
+}
+
+void LoadReporterAsyncServiceImpl::StartThread() { thread_->Start(); }
+
+void LoadReporterAsyncServiceImpl::ReportLoadHandler::CreateAndStart(
+    ServerCompletionQueue* cq, LoadReporterAsyncServiceImpl* service,
+    LoadReporter* load_reporter) {
+  std::shared_ptr<ReportLoadHandler> handler =
+      std::make_shared<ReportLoadHandler>(cq, service, load_reporter);
+  ReportLoadHandler* p = handler.get();
+  {
+    std::unique_lock<std::mutex> lock(service->cq_shutdown_mu_);
+    if (service->shutdown_) return;
+    p->on_done_notified_ =
+        CallableTag(std::bind(&ReportLoadHandler::OnDoneNotified, p,
+                              std::placeholders::_1, std::placeholders::_2),
+                    handler);
+    p->next_inbound_ =
+        CallableTag(std::bind(&ReportLoadHandler::OnRequestDelivered, p,
+                              std::placeholders::_1, std::placeholders::_2),
+                    std::move(handler));
+    p->ctx_.AsyncNotifyWhenDone(&p->on_done_notified_);
+    service->RequestReportLoad(&p->ctx_, &p->stream_, cq, cq,
+                               &p->next_inbound_);
+  }
+}
+
+LoadReporterAsyncServiceImpl::ReportLoadHandler::ReportLoadHandler(
+    ServerCompletionQueue* cq, LoadReporterAsyncServiceImpl* service,
+    LoadReporter* load_reporter)
+    : cq_(cq),
+      service_(service),
+      load_reporter_(load_reporter),
+      stream_(&ctx_),
+      call_status_(WAITING_FOR_DELIVERY) {}
+
+void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnRequestDelivered(
+    std::shared_ptr<ReportLoadHandler> self, bool ok) {
+  if (ok) {
+    call_status_ = DELIVERED;
+  } else {
+    // AsyncNotifyWhenDone() needs to be called before the call starts, but the
+    // tag will not pop out if the call never starts (
+    // https://github.com/grpc/grpc/issues/10136). So we need to manually
+    // release the ownership of the handler in this case.
+    GPR_ASSERT(on_done_notified_.ReleaseHandler() != nullptr);
+  }
+  if (!ok || shutdown_) {
+    // The value of ok being false means that the server is shutting down.
+    Shutdown(std::move(self), "OnRequestDelivered");
+    return;
+  }
+  // Spawn a new handler instance to serve the next new client. Every handler
+  // instance will deallocate itself when it's done.
+  CreateAndStart(cq_, service_, load_reporter_);
+  {
+    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    if (service_->shutdown_) {
+      lock.release()->unlock();
+      Shutdown(std::move(self), "OnRequestDelivered");
+      return;
+    }
+    next_inbound_ =
+        CallableTag(std::bind(&ReportLoadHandler::OnReadDone, this,
+                              std::placeholders::_1, std::placeholders::_2),
+                    std::move(self));
+    stream_.Read(&request_, &next_inbound_);
+  }
+  // LB ID is unique for each load reporting stream.
+  lb_id_ = load_reporter_->GenerateLbId();
+  gpr_log(GPR_INFO,
+          "[LRS %p] Call request delivered (lb_id_: %s, handler: %p). "
+          "Start reading the initial request...",
+          service_, lb_id_.c_str(), this);
+}
+
+void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnReadDone(
+    std::shared_ptr<ReportLoadHandler> self, bool ok) {
+  if (!ok || shutdown_) {
+    if (!ok && call_status_ < INITIAL_REQUEST_RECEIVED) {
+      // The client may have half-closed the stream or the stream is broken.
+      gpr_log(GPR_INFO,
+              "[LRS %p] Failed reading the initial request from the stream "
+              "(lb_id_: %s, handler: %p, done_notified: %d, is_cancelled: %d).",
+              service_, lb_id_.c_str(), this, static_cast<int>(done_notified_),
+              static_cast<int>(is_cancelled_));
+    }
+    Shutdown(std::move(self), "OnReadDone");
+    return;
+  }
+  // We only receive one request, which is the initial request.
+  if (call_status_ < INITIAL_REQUEST_RECEIVED) {
+    if (!request_.has_initial_request()) {
+      Shutdown(std::move(self), "OnReadDone+initial_request_not_found");
+    } else {
+      call_status_ = INITIAL_REQUEST_RECEIVED;
+      const auto& initial_request = request_.initial_request();
+      load_balanced_hostname_ = initial_request.load_balanced_hostname();
+      load_key_ = initial_request.load_key();
+      load_reporter_->ReportStreamCreated(load_balanced_hostname_, lb_id_,
+                                          load_key_);
+      const auto& load_report_interval = initial_request.load_report_interval();
+      load_report_interval_ms_ =
+          static_cast<uint64_t>(load_report_interval.seconds() * 1000 +
+                                load_report_interval.nanos() / 1000);
+      gpr_log(
+          GPR_INFO,
+          "[LRS %p] Initial request received. Start load reporting (load "
+          "balanced host: %s, interval: %lu ms, lb_id_: %s, handler: %p)...",
+          service_, load_balanced_hostname_.c_str(), load_report_interval_ms_,
+          lb_id_.c_str(), this);
+      SendReport(self, true /* ok */);
+      // Expect this read to fail.
+      {
+        std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+        if (service_->shutdown_) {
+          lock.release()->unlock();
+          Shutdown(std::move(self), "OnReadDone");
+          return;
+        }
+        next_inbound_ =
+            CallableTag(std::bind(&ReportLoadHandler::OnReadDone, this,
+                                  std::placeholders::_1, std::placeholders::_2),
+                        std::move(self));
+        stream_.Read(&request_, &next_inbound_);
+      }
+    }
+  } else {
+    // Another request received! This violates the spec.
+    gpr_log(GPR_ERROR,
+            "[LRS %p] Another request received (lb_id_: %s, handler: %p).",
+            service_, lb_id_.c_str(), this);
+    Shutdown(std::move(self), "OnReadDone+second_request");
+  }
+}
+
+void LoadReporterAsyncServiceImpl::ReportLoadHandler::ScheduleNextReport(
+    std::shared_ptr<ReportLoadHandler> self, bool ok) {
+  if (!ok || shutdown_) {
+    Shutdown(std::move(self), "ScheduleNextReport");
+    return;
+  }
+  auto next_report_time = gpr_time_add(
+      gpr_now(GPR_CLOCK_MONOTONIC),
+      gpr_time_from_millis(load_report_interval_ms_, GPR_TIMESPAN));
+  {
+    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    if (service_->shutdown_) {
+      lock.release()->unlock();
+      Shutdown(std::move(self), "ScheduleNextReport");
+      return;
+    }
+    next_outbound_ =
+        CallableTag(std::bind(&ReportLoadHandler::SendReport, this,
+                              std::placeholders::_1, std::placeholders::_2),
+                    std::move(self));
+    // TODO(juanlishen): Improve the Alarm implementation to reuse a single
+    // instance for multiple events.
+    next_report_alarm_.reset(new Alarm);
+    next_report_alarm_->Set(cq_, next_report_time, &next_outbound_);
+  }
+  gpr_log(GPR_DEBUG,
+          "[LRS %p] Next load report scheduled (lb_id_: %s, handler: %p).",
+          service_, lb_id_.c_str(), this);
+}
+
+void LoadReporterAsyncServiceImpl::ReportLoadHandler::SendReport(
+    std::shared_ptr<ReportLoadHandler> self, bool ok) {
+  if (!ok || shutdown_) {
+    Shutdown(std::move(self), "SendReport");
+    return;
+  }
+  ::grpc::lb::v1::LoadReportResponse response;
+  auto loads = load_reporter_->GenerateLoads(load_balanced_hostname_, lb_id_);
+  response.mutable_load()->Swap(&loads);
+  auto feedback = load_reporter_->GenerateLoadBalancingFeedback();
+  response.mutable_load_balancing_feedback()->Swap(&feedback);
+  if (call_status_ < INITIAL_RESPONSE_SENT) {
+    auto initial_response = response.mutable_initial_response();
+    initial_response->set_load_balancer_id(lb_id_);
+    initial_response->set_implementation_id(
+        ::grpc::lb::v1::InitialLoadReportResponse::CPP);
+    initial_response->set_server_version(kVersion);
+    call_status_ = INITIAL_RESPONSE_SENT;
+  }
+  {
+    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    if (service_->shutdown_) {
+      lock.release()->unlock();
+      Shutdown(std::move(self), "SendReport");
+      return;
+    }
+    next_outbound_ =
+        CallableTag(std::bind(&ReportLoadHandler::ScheduleNextReport, this,
+                              std::placeholders::_1, std::placeholders::_2),
+                    std::move(self));
+    stream_.Write(response, &next_outbound_);
+    gpr_log(GPR_INFO,
+            "[LRS %p] Sending load report (lb_id_: %s, handler: %p, loads "
+            "count: %d)...",
+            service_, lb_id_.c_str(), this, response.load().size());
+  }
+}
+
+void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnDoneNotified(
+    std::shared_ptr<ReportLoadHandler> self, bool ok) {
+  GPR_ASSERT(ok);
+  done_notified_ = true;
+  if (ctx_.IsCancelled()) {
+    is_cancelled_ = true;
+  }
+  gpr_log(GPR_INFO,
+          "[LRS %p] Load reporting call is notified done (handler: %p, "
+          "is_cancelled: %d).",
+          service_, this, static_cast<int>(is_cancelled_));
+  Shutdown(std::move(self), "OnDoneNotified");
+}
+
+void LoadReporterAsyncServiceImpl::ReportLoadHandler::Shutdown(
+    std::shared_ptr<ReportLoadHandler> self, const char* reason) {
+  if (!shutdown_) {
+    gpr_log(GPR_INFO,
+            "[LRS %p] Shutting down the handler (lb_id_: %s, handler: %p, "
+            "reason: %s).",
+            service_, lb_id_.c_str(), this, reason);
+    shutdown_ = true;
+    if (call_status_ >= INITIAL_REQUEST_RECEIVED) {
+      load_reporter_->ReportStreamClosed(load_balanced_hostname_, lb_id_);
+      next_report_alarm_->Cancel();
+    }
+  }
+  // OnRequestDelivered() may be called after OnDoneNotified(), so we need to
+  // try to Finish() every time we are in Shutdown().
+  if (call_status_ >= DELIVERED && call_status_ < FINISH_CALLED) {
+    std::unique_lock<std::mutex> lock(service_->cq_shutdown_mu_);
+    if (!service_->shutdown_) {
+      on_finish_done_ =
+          CallableTag(std::bind(&ReportLoadHandler::OnFinishDone, this,
+                                std::placeholders::_1, std::placeholders::_2),
+                      std::move(self));
+      // TODO(juanlishen): Maybe add a message proto for the client to
+      // explicitly cancel the stream so that we can return OK status in such
+      // cases.
+      stream_.Finish(Status::CANCELLED, &on_finish_done_);
+      call_status_ = FINISH_CALLED;
+    }
+  }
+}
+
+void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnFinishDone(
+    std::shared_ptr<ReportLoadHandler> self, bool ok) {
+  if (ok) {
+    gpr_log(GPR_INFO,
+            "[LRS %p] Load reporting finished (lb_id_: %s, handler: %p).",
+            service_, lb_id_.c_str(), this);
+  }
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.h b/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.h
new file mode 100644
index 0000000..6fc577f
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.h
@@ -0,0 +1,194 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_ASYNC_SERVICE_IMPL_H
+#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_ASYNC_SERVICE_IMPL_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+#include <grpcpp/alarm.h>
+#include <grpcpp/grpcpp.h>
+
+#include "src/core/lib/gprpp/thd.h"
+#include "src/cpp/server/load_reporter/load_reporter.h"
+
+namespace grpc {
+namespace load_reporter {
+
+// Async load reporting service. It's mainly responsible for controlling the
+// procedure of incoming requests. The real business logic is handed off to the
+// LoadReporter. There should be at most one instance of this service on a
+// server to avoid spreading the load data into multiple places.
+class LoadReporterAsyncServiceImpl
+    : public grpc::lb::v1::LoadReporter::AsyncService {
+ public:
+  explicit LoadReporterAsyncServiceImpl(
+      std::unique_ptr<ServerCompletionQueue> cq);
+  ~LoadReporterAsyncServiceImpl();
+
+  // Starts the working thread.
+  void StartThread();
+
+  // Not copyable nor movable.
+  LoadReporterAsyncServiceImpl(const LoadReporterAsyncServiceImpl&) = delete;
+  LoadReporterAsyncServiceImpl& operator=(const LoadReporterAsyncServiceImpl&) =
+      delete;
+
+ private:
+  class ReportLoadHandler;
+
+  // A tag that can be called with a bool argument. It's tailored for
+  // ReportLoadHandler's use. Before being used, it should be constructed with a
+  // method of ReportLoadHandler and a shared pointer to the handler. The
+  // shared pointer will be moved to the invoked function and the function can
+  // only be invoked once. That makes ref counting of the handler easier,
+  // because the shared pointer is not bound to the function and can be gone
+  // once the invoked function returns (if not used any more).
+  class CallableTag {
+   public:
+    using HandlerFunction =
+        std::function<void(std::shared_ptr<ReportLoadHandler>, bool)>;
+
+    CallableTag() {}
+
+    CallableTag(HandlerFunction func,
+                std::shared_ptr<ReportLoadHandler> handler)
+        : handler_function_(std::move(func)), handler_(std::move(handler)) {
+      GPR_ASSERT(handler_function_ != nullptr);
+      GPR_ASSERT(handler_ != nullptr);
+    }
+
+    // Runs the tag. This should be called only once. The handler is no longer
+    // owned by this tag after this method is invoked.
+    void Run(bool ok);
+
+    // Releases and returns the shared pointer to the handler.
+    std::shared_ptr<ReportLoadHandler> ReleaseHandler() {
+      return std::move(handler_);
+    }
+
+   private:
+    HandlerFunction handler_function_ = nullptr;
+    std::shared_ptr<ReportLoadHandler> handler_;
+  };
+
+  // Each handler takes care of one load reporting stream. It contains
+  // per-stream data and it will access the members of the parent class (i.e.,
+  // LoadReporterAsyncServiceImpl) for service-wide data (e.g., the load data).
+  class ReportLoadHandler {
+   public:
+    // Instantiates a ReportLoadHandler and requests the next load reporting
+    // call. The handler object will manage its own lifetime, so no action is
+    // needed from the caller any more regarding that object.
+    static void CreateAndStart(ServerCompletionQueue* cq,
+                               LoadReporterAsyncServiceImpl* service,
+                               LoadReporter* load_reporter);
+
+    // This ctor is public because we want to use std::make_shared<> in
+    // CreateAndStart(). This ctor shouldn't be used elsewhere.
+    ReportLoadHandler(ServerCompletionQueue* cq,
+                      LoadReporterAsyncServiceImpl* service,
+                      LoadReporter* load_reporter);
+
+   private:
+    // After the handler has a call request delivered, it starts reading the
+    // initial request. Also, a new handler is spawned so that we can keep
+    // servicing future calls.
+    void OnRequestDelivered(std::shared_ptr<ReportLoadHandler> self, bool ok);
+
+    // The first Read() is expected to succeed, after which the handler starts
+    // sending load reports back to the balancer. The second Read() is
+    // expected to fail, which happens when the balancer half-closes the
+    // stream to signal that it's no longer interested in the load reports. For
+    // the latter case, the handler will then close the stream.
+    void OnReadDone(std::shared_ptr<ReportLoadHandler> self, bool ok);
+
+    // The report sending operations are sequential as: send report -> send
+    // done, schedule the next send -> waiting for the alarm to fire -> alarm
+    // fires, send report -> ...
+    void SendReport(std::shared_ptr<ReportLoadHandler> self, bool ok);
+    void ScheduleNextReport(std::shared_ptr<ReportLoadHandler> self, bool ok);
+
+    // Called when Finish() is done.
+    void OnFinishDone(std::shared_ptr<ReportLoadHandler> self, bool ok);
+
+    // Called when AsyncNotifyWhenDone() notifies us.
+    void OnDoneNotified(std::shared_ptr<ReportLoadHandler> self, bool ok);
+
+    void Shutdown(std::shared_ptr<ReportLoadHandler> self, const char* reason);
+
+    // The key fields of the stream.
+    grpc::string lb_id_;
+    grpc::string load_balanced_hostname_;
+    grpc::string load_key_;
+    uint64_t load_report_interval_ms_;
+
+    // The data for RPC communication with the load reportee.
+    ServerContext ctx_;
+    ::grpc::lb::v1::LoadReportRequest request_;
+
+    // The members passed down from LoadReporterAsyncServiceImpl.
+    ServerCompletionQueue* cq_;
+    LoadReporterAsyncServiceImpl* service_;
+    LoadReporter* load_reporter_;
+    ServerAsyncReaderWriter<::grpc::lb::v1::LoadReportResponse,
+                            ::grpc::lb::v1::LoadReportRequest>
+        stream_;
+
+    // The status of the RPC progress.
+    enum CallStatus {
+      WAITING_FOR_DELIVERY,
+      DELIVERED,
+      INITIAL_REQUEST_RECEIVED,
+      INITIAL_RESPONSE_SENT,
+      FINISH_CALLED
+    } call_status_;
+    bool shutdown_{false};
+    bool done_notified_{false};
+    bool is_cancelled_{false};
+    CallableTag on_done_notified_;
+    CallableTag on_finish_done_;
+    CallableTag next_inbound_;
+    CallableTag next_outbound_;
+    std::unique_ptr<Alarm> next_report_alarm_;
+  };
+
+  // Handles the incoming requests and drives the completion queue in a loop.
+  static void Work(void* arg);
+
+  // Schedules the next data fetching from Census and LB feedback sampling.
+  void ScheduleNextFetchAndSample();
+
+  // Fetches data from Census and samples LB feedback.
+  void FetchAndSample(bool ok);
+
+  std::unique_ptr<ServerCompletionQueue> cq_;
+  // To synchronize the operations related to shutdown state of cq_, so that we
+  // don't enqueue new tags into cq_ after it is already shut down.
+  std::mutex cq_shutdown_mu_;
+  std::atomic_bool shutdown_{false};
+  std::unique_ptr<::grpc_core::Thread> thread_;
+  std::unique_ptr<LoadReporter> load_reporter_;
+  std::unique_ptr<Alarm> next_fetch_and_sample_alarm_;
+};
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_ASYNC_SERVICE_IMPL_H
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc
new file mode 100644
index 0000000..81cf6ac
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include <grpcpp/ext/server_load_reporting.h>
+
+#include "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h"
+
+namespace grpc {
+namespace load_reporter {
+namespace experimental {
+
+void LoadReportingServiceServerBuilderOption::UpdateArguments(
+    ::grpc::ChannelArguments* args) {
+  args->SetInt(GRPC_ARG_ENABLE_LOAD_REPORTING, true);
+}
+
+void LoadReportingServiceServerBuilderOption::UpdatePlugins(
+    std::vector<std::unique_ptr<::grpc::ServerBuilderPlugin>>* plugins) {
+  plugins->emplace_back(new LoadReportingServiceServerBuilderPlugin());
+}
+
+}  // namespace experimental
+}  // namespace load_reporter
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc
new file mode 100644
index 0000000..c2c5dd5
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/port_platform.h>
+
+#include "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h"
+
+#include <grpcpp/impl/server_initializer.h>
+
+namespace grpc {
+namespace load_reporter {
+
+bool LoadReportingServiceServerBuilderPlugin::has_sync_methods() const {
+  if (service_ != nullptr) {
+    return service_->has_synchronous_methods();
+  }
+  return false;
+}
+
+bool LoadReportingServiceServerBuilderPlugin::has_async_methods() const {
+  if (service_ != nullptr) {
+    return service_->has_async_methods();
+  }
+  return false;
+}
+
+void LoadReportingServiceServerBuilderPlugin::UpdateServerBuilder(
+    grpc::ServerBuilder* builder) {
+  auto cq = builder->AddCompletionQueue();
+  service_ = std::make_shared<LoadReporterAsyncServiceImpl>(std::move(cq));
+}
+
+void LoadReportingServiceServerBuilderPlugin::InitServer(
+    grpc::ServerInitializer* si) {
+  si->RegisterService(service_);
+}
+
+void LoadReportingServiceServerBuilderPlugin::Finish(
+    grpc::ServerInitializer* si) {
+  service_->StartThread();
+  service_.reset();
+}
+
+}  // namespace load_reporter
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h
new file mode 100644
index 0000000..1f09859
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_SRC_CPP_LOAD_REPORTING_SERVICE_SERVER_BUILDER_PLUGIN_H
+#define GRPC_SRC_CPP_LOAD_REPORTING_SERVICE_SERVER_BUILDER_PLUGIN_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpcpp/impl/server_builder_plugin.h>
+
+#include "src/cpp/server/load_reporter/load_reporter_async_service_impl.h"
+
+namespace grpc {
+namespace load_reporter {
+
+// The plugin that registers and starts load reporting service when starting a
+// server.
+class LoadReportingServiceServerBuilderPlugin : public ServerBuilderPlugin {
+ public:
+  ~LoadReportingServiceServerBuilderPlugin() override = default;
+  grpc::string name() override { return "load_reporting_service"; }
+
+  // Creates a load reporting service.
+  void UpdateServerBuilder(grpc::ServerBuilder* builder) override;
+
+  // Registers the load reporter service.
+  void InitServer(grpc::ServerInitializer* si) override;
+
+  // Starts the load reporter service.
+  void Finish(grpc::ServerInitializer* si) override;
+
+  void ChangeArguments(const grpc::string& name, void* value) override {}
+  void UpdateChannelArguments(grpc::ChannelArguments* args) override {}
+  bool has_sync_methods() const override;
+  bool has_async_methods() const override;
+
+ private:
+  std::shared_ptr<LoadReporterAsyncServiceImpl> service_;
+};
+
+std::unique_ptr<grpc::ServerBuilderPlugin>
+CreateLoadReportingServiceServerBuilderPlugin();
+
+}  // namespace load_reporter
+}  // namespace grpc
+
+#endif  // GRPC_SRC_CPP_LOAD_REPORTING_SERVICE_SERVER_BUILDER_PLUGIN_H
diff --git a/third_party/grpc/src/cpp/server/load_reporter/util.cc b/third_party/grpc/src/cpp/server/load_reporter/util.cc
new file mode 100644
index 0000000..89bdf57
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/load_reporter/util.cc
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed 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 <grpc/impl/codegen/port_platform.h>
+
+#include <grpcpp/ext/server_load_reporting.h>
+
+#include <cmath>
+
+#include <grpc/support/log.h>
+
+namespace grpc {
+namespace load_reporter {
+namespace experimental {
+
+void AddLoadReportingCost(grpc::ServerContext* ctx,
+                          const grpc::string& cost_name, double cost_value) {
+  if (std::isnormal(cost_value)) {
+    grpc::string buf;
+    buf.resize(sizeof(cost_value) + cost_name.size());
+    memcpy(&(*buf.begin()), &cost_value, sizeof(cost_value));
+    memcpy(&(*buf.begin()) + sizeof(cost_value), cost_name.data(),
+           cost_name.size());
+    ctx->AddTrailingMetadata(GRPC_LB_COST_MD_KEY, buf);
+  } else {
+    gpr_log(GPR_ERROR, "Call metric value is not normal.");
+  }
+}
+
+}  // namespace experimental
+}  // namespace load_reporter
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/secure_server_credentials.cc b/third_party/grpc/src/cpp/server/secure_server_credentials.cc
new file mode 100644
index 0000000..453e76e
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/secure_server_credentials.cc
@@ -0,0 +1,150 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <functional>
+#include <map>
+#include <memory>
+
+#include <grpcpp/impl/codegen/slice.h>
+#include <grpcpp/security/auth_metadata_processor.h>
+
+#include "src/cpp/common/secure_auth_context.h"
+#include "src/cpp/server/secure_server_credentials.h"
+
+namespace grpc {
+
+void AuthMetadataProcessorAyncWrapper::Destroy(void* wrapper) {
+  auto* w = static_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
+  delete w;
+}
+
+void AuthMetadataProcessorAyncWrapper::Process(
+    void* wrapper, grpc_auth_context* context, const grpc_metadata* md,
+    size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) {
+  auto* w = static_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
+  if (!w->processor_) {
+    // Early exit.
+    cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_OK, nullptr);
+    return;
+  }
+  if (w->processor_->IsBlocking()) {
+    w->thread_pool_->Add([w, context, md, num_md, cb, user_data] {
+      w->AuthMetadataProcessorAyncWrapper::InvokeProcessor(context, md, num_md,
+                                                           cb, user_data);
+    });
+  } else {
+    // invoke directly.
+    w->InvokeProcessor(context, md, num_md, cb, user_data);
+  }
+}
+
+void AuthMetadataProcessorAyncWrapper::InvokeProcessor(
+    grpc_auth_context* ctx, const grpc_metadata* md, size_t num_md,
+    grpc_process_auth_metadata_done_cb cb, void* user_data) {
+  AuthMetadataProcessor::InputMetadata metadata;
+  for (size_t i = 0; i < num_md; i++) {
+    metadata.insert(std::make_pair(StringRefFromSlice(&md[i].key),
+                                   StringRefFromSlice(&md[i].value)));
+  }
+  SecureAuthContext context(ctx);
+  AuthMetadataProcessor::OutputMetadata consumed_metadata;
+  AuthMetadataProcessor::OutputMetadata response_metadata;
+
+  Status status = processor_->Process(metadata, &context, &consumed_metadata,
+                                      &response_metadata);
+
+  std::vector<grpc_metadata> consumed_md;
+  for (auto it = consumed_metadata.begin(); it != consumed_metadata.end();
+       ++it) {
+    grpc_metadata md_entry;
+    md_entry.key = SliceReferencingString(it->first);
+    md_entry.value = SliceReferencingString(it->second);
+    md_entry.flags = 0;
+    consumed_md.push_back(md_entry);
+  }
+  std::vector<grpc_metadata> response_md;
+  for (auto it = response_metadata.begin(); it != response_metadata.end();
+       ++it) {
+    grpc_metadata md_entry;
+    md_entry.key = SliceReferencingString(it->first);
+    md_entry.value = SliceReferencingString(it->second);
+    md_entry.flags = 0;
+    response_md.push_back(md_entry);
+  }
+  auto consumed_md_data = consumed_md.empty() ? nullptr : &consumed_md[0];
+  auto response_md_data = response_md.empty() ? nullptr : &response_md[0];
+  cb(user_data, consumed_md_data, consumed_md.size(), response_md_data,
+     response_md.size(), static_cast<grpc_status_code>(status.error_code()),
+     status.error_message().c_str());
+}
+
+int SecureServerCredentials::AddPortToServer(const grpc::string& addr,
+                                             grpc_server* server) {
+  return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_);
+}
+
+void SecureServerCredentials::SetAuthMetadataProcessor(
+    const std::shared_ptr<AuthMetadataProcessor>& processor) {
+  auto* wrapper = new AuthMetadataProcessorAyncWrapper(processor);
+  grpc_server_credentials_set_auth_metadata_processor(
+      creds_, {AuthMetadataProcessorAyncWrapper::Process,
+               AuthMetadataProcessorAyncWrapper::Destroy, wrapper});
+}
+
+std::shared_ptr<ServerCredentials> SslServerCredentials(
+    const SslServerCredentialsOptions& options) {
+  std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
+  for (auto key_cert_pair = options.pem_key_cert_pairs.begin();
+       key_cert_pair != options.pem_key_cert_pairs.end(); key_cert_pair++) {
+    grpc_ssl_pem_key_cert_pair p = {key_cert_pair->private_key.c_str(),
+                                    key_cert_pair->cert_chain.c_str()};
+    pem_key_cert_pairs.push_back(p);
+  }
+  grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create_ex(
+      options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
+      pem_key_cert_pairs.empty() ? nullptr : &pem_key_cert_pairs[0],
+      pem_key_cert_pairs.size(),
+      options.force_client_auth
+          ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+          : options.client_certificate_request,
+      nullptr);
+  return std::shared_ptr<ServerCredentials>(
+      new SecureServerCredentials(c_creds));
+}
+
+namespace experimental {
+
+std::shared_ptr<ServerCredentials> AltsServerCredentials(
+    const AltsServerCredentialsOptions& options) {
+  grpc_alts_credentials_options* c_options =
+      grpc_alts_credentials_server_options_create();
+  grpc_server_credentials* c_creds =
+      grpc_alts_server_credentials_create(c_options);
+  grpc_alts_credentials_options_destroy(c_options);
+  return std::shared_ptr<ServerCredentials>(
+      new SecureServerCredentials(c_creds));
+}
+
+std::shared_ptr<ServerCredentials> LocalServerCredentials(
+    grpc_local_connect_type type) {
+  return std::shared_ptr<ServerCredentials>(
+      new SecureServerCredentials(grpc_local_server_credentials_create(type)));
+}
+
+}  // namespace experimental
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/secure_server_credentials.h b/third_party/grpc/src/cpp/server/secure_server_credentials.h
new file mode 100644
index 0000000..8a81af2
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/secure_server_credentials.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
+#define GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
+
+#include <memory>
+
+#include <grpcpp/security/server_credentials.h>
+
+#include <grpc/grpc_security.h>
+
+#include "src/cpp/server/thread_pool_interface.h"
+
+namespace grpc {
+
+class AuthMetadataProcessorAyncWrapper final {
+ public:
+  static void Destroy(void* wrapper);
+
+  static void Process(void* wrapper, grpc_auth_context* context,
+                      const grpc_metadata* md, size_t num_md,
+                      grpc_process_auth_metadata_done_cb cb, void* user_data);
+
+  AuthMetadataProcessorAyncWrapper(
+      const std::shared_ptr<AuthMetadataProcessor>& processor)
+      : thread_pool_(CreateDefaultThreadPool()), processor_(processor) {}
+
+ private:
+  void InvokeProcessor(grpc_auth_context* context, const grpc_metadata* md,
+                       size_t num_md, grpc_process_auth_metadata_done_cb cb,
+                       void* user_data);
+  std::unique_ptr<ThreadPoolInterface> thread_pool_;
+  std::shared_ptr<AuthMetadataProcessor> processor_;
+};
+
+class SecureServerCredentials final : public ServerCredentials {
+ public:
+  explicit SecureServerCredentials(grpc_server_credentials* creds)
+      : creds_(creds) {}
+  ~SecureServerCredentials() override {
+    grpc_server_credentials_release(creds_);
+  }
+
+  int AddPortToServer(const grpc::string& addr, grpc_server* server) override;
+
+  void SetAuthMetadataProcessor(
+      const std::shared_ptr<AuthMetadataProcessor>& processor) override;
+
+ private:
+  grpc_server_credentials* creds_;
+  std::unique_ptr<AuthMetadataProcessorAyncWrapper> processor_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
diff --git a/third_party/grpc/src/cpp/server/server_builder.cc b/third_party/grpc/src/cpp/server/server_builder.cc
new file mode 100644
index 0000000..0dc03b6
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/server_builder.cc
@@ -0,0 +1,396 @@
+/*
+ *
+ * Copyright 2015-2016 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/server_builder.h>
+
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpcpp/impl/service_type.h>
+#include <grpcpp/resource_quota.h>
+#include <grpcpp/server.h>
+
+#include <utility>
+
+#include "src/core/lib/gpr/useful.h"
+#include "src/cpp/server/thread_pool_interface.h"
+
+namespace grpc {
+
+static std::vector<std::unique_ptr<ServerBuilderPlugin> (*)()>*
+    g_plugin_factory_list;
+static gpr_once once_init_plugin_list = GPR_ONCE_INIT;
+
+static void do_plugin_list_init(void) {
+  g_plugin_factory_list =
+      new std::vector<std::unique_ptr<ServerBuilderPlugin> (*)()>();
+}
+
+ServerBuilder::ServerBuilder()
+    : max_receive_message_size_(INT_MIN),
+      max_send_message_size_(INT_MIN),
+      sync_server_settings_(SyncServerSettings()),
+      resource_quota_(nullptr),
+      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++) {
+    auto& factory = *it;
+    plugins_.emplace_back(factory());
+  }
+
+  // all compression algorithms enabled by default.
+  enabled_compression_algorithms_bitset_ =
+      (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
+  memset(&maybe_default_compression_level_, 0,
+         sizeof(maybe_default_compression_level_));
+  memset(&maybe_default_compression_algorithm_, 0,
+         sizeof(maybe_default_compression_algorithm_));
+}
+
+ServerBuilder::~ServerBuilder() {
+  if (resource_quota_ != nullptr) {
+    grpc_resource_quota_unref(resource_quota_);
+  }
+}
+
+std::unique_ptr<ServerCompletionQueue> ServerBuilder::AddCompletionQueue(
+    bool is_frequently_polled) {
+  ServerCompletionQueue* cq = new ServerCompletionQueue(
+      GRPC_CQ_NEXT,
+      is_frequently_polled ? GRPC_CQ_DEFAULT_POLLING : GRPC_CQ_NON_LISTENING,
+      nullptr);
+  cqs_.push_back(cq);
+  return std::unique_ptr<ServerCompletionQueue>(cq);
+}
+
+ServerBuilder& ServerBuilder::RegisterService(Service* service) {
+  services_.emplace_back(new NamedService(service));
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::RegisterService(const grpc::string& addr,
+                                              Service* service) {
+  services_.emplace_back(new NamedService(addr, service));
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::RegisterAsyncGenericService(
+    AsyncGenericService* service) {
+  if (generic_service_) {
+    gpr_log(GPR_ERROR,
+            "Adding multiple AsyncGenericService is unsupported for now. "
+            "Dropping the service %p",
+            (void*)service);
+  } else {
+    generic_service_ = service;
+  }
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::SetOption(
+    std::unique_ptr<ServerBuilderOption> option) {
+  options_.push_back(std::move(option));
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::SetSyncServerOption(
+    ServerBuilder::SyncServerOption option, int val) {
+  switch (option) {
+    case NUM_CQS:
+      sync_server_settings_.num_cqs = val;
+      break;
+    case MIN_POLLERS:
+      sync_server_settings_.min_pollers = val;
+      break;
+    case MAX_POLLERS:
+      sync_server_settings_.max_pollers = val;
+      break;
+    case CQ_TIMEOUT_MSEC:
+      sync_server_settings_.cq_timeout_msec = val;
+      break;
+  }
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::SetCompressionAlgorithmSupportStatus(
+    grpc_compression_algorithm algorithm, bool enabled) {
+  if (enabled) {
+    GPR_BITSET(&enabled_compression_algorithms_bitset_, algorithm);
+  } else {
+    GPR_BITCLEAR(&enabled_compression_algorithms_bitset_, algorithm);
+  }
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::SetDefaultCompressionLevel(
+    grpc_compression_level level) {
+  maybe_default_compression_level_.level = level;
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::SetDefaultCompressionAlgorithm(
+    grpc_compression_algorithm algorithm) {
+  maybe_default_compression_algorithm_.is_set = true;
+  maybe_default_compression_algorithm_.algorithm = algorithm;
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::SetResourceQuota(
+    const grpc::ResourceQuota& resource_quota) {
+  if (resource_quota_ != nullptr) {
+    grpc_resource_quota_unref(resource_quota_);
+  }
+  resource_quota_ = resource_quota.c_resource_quota();
+  grpc_resource_quota_ref(resource_quota_);
+  return *this;
+}
+
+ServerBuilder& ServerBuilder::AddListeningPort(
+    const grpc::string& addr_uri, std::shared_ptr<ServerCredentials> creds,
+    int* selected_port) {
+  const grpc::string uri_scheme = "dns:";
+  grpc::string addr = addr_uri;
+  if (addr_uri.compare(0, uri_scheme.size(), uri_scheme) == 0) {
+    size_t pos = uri_scheme.size();
+    while (addr_uri[pos] == '/') ++pos;  // Skip slashes.
+    addr = addr_uri.substr(pos);
+  }
+  Port port = {addr, std::move(creds), selected_port};
+  ports_.push_back(port);
+  return *this;
+}
+
+std::unique_ptr<Server> ServerBuilder::BuildAndStart() {
+  ChannelArguments args;
+  for (auto option = options_.begin(); option != options_.end(); ++option) {
+    (*option)->UpdateArguments(&args);
+    (*option)->UpdatePlugins(&plugins_);
+  }
+
+  for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) {
+    (*plugin)->UpdateServerBuilder(this);
+    (*plugin)->UpdateChannelArguments(&args);
+  }
+
+  if (max_receive_message_size_ >= -1) {
+    args.SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, max_receive_message_size_);
+  }
+
+  // The default message size is -1 (max), so no need to explicitly set it for
+  // -1.
+  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_);
+  if (maybe_default_compression_level_.is_set) {
+    args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL,
+                maybe_default_compression_level_.level);
+  }
+  if (maybe_default_compression_algorithm_.is_set) {
+    args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM,
+                maybe_default_compression_algorithm_.algorithm);
+  }
+
+  if (resource_quota_ != nullptr) {
+    args.SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA, resource_quota_,
+                              grpc_resource_quota_arg_vtable());
+  }
+
+  // == Determine if the server has any syncrhonous methods ==
+  bool has_sync_methods = false;
+  for (auto it = services_.begin(); it != services_.end(); ++it) {
+    if ((*it)->service->has_synchronous_methods()) {
+      has_sync_methods = true;
+      break;
+    }
+  }
+
+  if (!has_sync_methods) {
+    for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) {
+      if ((*plugin)->has_sync_methods()) {
+        has_sync_methods = true;
+        break;
+      }
+    }
+  }
+
+  // If this is a Sync server, i.e a server expositing sync API, then the server
+  // needs to create some completion queues to listen for incoming requests.
+  // 'sync_server_cqs' are those internal completion queues.
+  //
+  // This is different from the completion queues added to the server via
+  // ServerBuilder's AddCompletionQueue() method (those completion queues
+  // are in 'cqs_' member variable of ServerBuilder object)
+  std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
+      sync_server_cqs(std::make_shared<
+                      std::vector<std::unique_ptr<ServerCompletionQueue>>>());
+
+  int num_frequently_polled_cqs = 0;
+  for (auto it = cqs_.begin(); it != cqs_.end(); ++it) {
+    if ((*it)->IsFrequentlyPolled()) {
+      num_frequently_polled_cqs++;
+    }
+  }
+
+  const bool is_hybrid_server =
+      has_sync_methods && num_frequently_polled_cqs > 0;
+
+  if (has_sync_methods) {
+    grpc_cq_polling_type polling_type =
+        is_hybrid_server ? GRPC_CQ_NON_POLLING : GRPC_CQ_DEFAULT_POLLING;
+
+    // Create completion queues to listen to incoming rpc requests
+    for (int i = 0; i < sync_server_settings_.num_cqs; i++) {
+      sync_server_cqs->emplace_back(
+          new ServerCompletionQueue(GRPC_CQ_NEXT, polling_type, nullptr));
+    }
+  }
+
+  // == Determine if the server has any callback methods ==
+  bool has_callback_methods = false;
+  for (auto it = services_.begin(); it != services_.end(); ++it) {
+    if ((*it)->service->has_callback_methods()) {
+      has_callback_methods = true;
+      break;
+    }
+  }
+
+  // TODO(vjpai): Add a section here for plugins once they can support callback
+  // methods
+
+  if (has_sync_methods) {
+    // This is a Sync server
+    gpr_log(GPR_INFO,
+            "Synchronous server. Num CQs: %d, Min pollers: %d, Max Pollers: "
+            "%d, CQ timeout (msec): %d",
+            sync_server_settings_.num_cqs, sync_server_settings_.min_pollers,
+            sync_server_settings_.max_pollers,
+            sync_server_settings_.cq_timeout_msec);
+  }
+
+  if (has_callback_methods) {
+    gpr_log(GPR_INFO, "Callback server.");
+  }
+
+  std::unique_ptr<Server> server(new Server(
+      max_receive_message_size_, &args, sync_server_cqs,
+      sync_server_settings_.min_pollers, sync_server_settings_.max_pollers,
+      sync_server_settings_.cq_timeout_msec, resource_quota_,
+      std::move(interceptor_creators_)));
+
+  ServerInitializer* initializer = server->initializer();
+
+  // Register all the completion queues with the server. i.e
+  //  1. sync_server_cqs: internal completion queues created IF this is a sync
+  //     server
+  //  2. cqs_: Completion queues added via AddCompletionQueue() call
+
+  for (auto it = sync_server_cqs->begin(); it != sync_server_cqs->end(); ++it) {
+    grpc_server_register_completion_queue(server->server_, (*it)->cq(),
+                                          nullptr);
+    num_frequently_polled_cqs++;
+  }
+
+  if (has_callback_methods) {
+    auto* cq = server->CallbackCQ();
+    grpc_server_register_completion_queue(server->server_, cq->cq(), nullptr);
+    num_frequently_polled_cqs++;
+  }
+
+  // cqs_ contains the completion queue added by calling the ServerBuilder's
+  // AddCompletionQueue() API. Some of them may not be frequently polled (i.e by
+  // calling Next() or AsyncNext()) and hence are not safe to be used for
+  // listening to incoming channels. Such completion queues must be registered
+  // as non-listening queues
+  for (auto it = cqs_.begin(); it != cqs_.end(); ++it) {
+    grpc_server_register_completion_queue(server->server_, (*it)->cq(),
+                                          nullptr);
+  }
+
+  if (num_frequently_polled_cqs == 0) {
+    gpr_log(GPR_ERROR,
+            "At least one of the completion queues must be frequently polled");
+    return nullptr;
+  }
+
+  for (auto service = services_.begin(); service != services_.end();
+       service++) {
+    if (!server->RegisterService((*service)->host.get(), (*service)->service)) {
+      return nullptr;
+    }
+  }
+
+  for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) {
+    (*plugin)->InitServer(initializer);
+  }
+
+  if (generic_service_) {
+    server->RegisterAsyncGenericService(generic_service_);
+  } else {
+    for (auto it = services_.begin(); it != services_.end(); ++it) {
+      if ((*it)->service->has_generic_methods()) {
+        gpr_log(GPR_ERROR,
+                "Some methods were marked generic but there is no "
+                "generic service registered.");
+        return nullptr;
+      }
+    }
+  }
+
+  bool added_port = false;
+  for (auto port = ports_.begin(); port != ports_.end(); port++) {
+    int r = server->AddListeningPort(port->addr, port->creds.get());
+    if (!r) {
+      if (added_port) server->Shutdown();
+      return nullptr;
+    }
+    added_port = true;
+    if (port->selected_port != nullptr) {
+      *port->selected_port = r;
+    }
+  }
+
+  auto cqs_data = cqs_.empty() ? nullptr : &cqs_[0];
+  server->Start(cqs_data, cqs_.size());
+
+  for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) {
+    (*plugin)->Finish(initializer);
+  }
+
+  return server;
+}
+
+void ServerBuilder::InternalAddPluginFactory(
+    std::unique_ptr<ServerBuilderPlugin> (*CreatePlugin)()) {
+  gpr_once_init(&once_init_plugin_list, do_plugin_list_init);
+  (*g_plugin_factory_list).push_back(CreatePlugin);
+}
+
+ServerBuilder& ServerBuilder::EnableWorkaround(grpc_workaround_list id) {
+  switch (id) {
+    case GRPC_WORKAROUND_ID_CRONET_COMPRESSION:
+      return AddChannelArgument(GRPC_ARG_WORKAROUND_CRONET_COMPRESSION, 1);
+    default:
+      gpr_log(GPR_ERROR, "Workaround %u does not exist or is obsolete.", id);
+      return *this;
+  }
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/server_cc.cc b/third_party/grpc/src/cpp/server/server_cc.cc
new file mode 100644
index 0000000..1e3c574
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/server_cc.cc
@@ -0,0 +1,1166 @@
+/*
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/server.h>
+
+#include <cstdlib>
+#include <sstream>
+#include <utility>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/generic/async_generic_service.h>
+#include <grpcpp/impl/codegen/async_unary_call.h>
+#include <grpcpp/impl/codegen/call.h>
+#include <grpcpp/impl/codegen/completion_queue_tag.h>
+#include <grpcpp/impl/codegen/server_interceptor.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/impl/method_handler_impl.h>
+#include <grpcpp/impl/rpc_service_method.h>
+#include <grpcpp/impl/server_initializer.h>
+#include <grpcpp/impl/service_type.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/server_context.h>
+#include <grpcpp/support/time.h>
+
+#include "src/core/ext/transport/inproc/inproc_transport.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/completion_queue.h"
+#include "src/cpp/client/create_channel_internal.h"
+#include "src/cpp/server/health/default_health_check_service.h"
+#include "src/cpp/thread_manager/thread_manager.h"
+
+namespace grpc {
+namespace {
+
+// The default value for maximum number of threads that can be created in the
+// sync server. This value of INT_MAX is chosen to match the default behavior if
+// no ResourceQuota is set. To modify the max number of threads in a sync
+// server, pass a custom ResourceQuota object  (with the desired number of
+// max-threads set) to the server builder.
+#define DEFAULT_MAX_SYNC_SERVER_THREADS INT_MAX
+
+// How many callback requests of each method should we pre-register at start
+#define DEFAULT_CALLBACK_REQS_PER_METHOD 32
+
+class DefaultGlobalCallbacks final : public Server::GlobalCallbacks {
+ public:
+  ~DefaultGlobalCallbacks() override {}
+  void PreSynchronousRequest(ServerContext* context) override {}
+  void PostSynchronousRequest(ServerContext* context) override {}
+};
+
+std::shared_ptr<Server::GlobalCallbacks> g_callbacks = nullptr;
+gpr_once g_once_init_callbacks = GPR_ONCE_INIT;
+
+void InitGlobalCallbacks() {
+  if (!g_callbacks) {
+    g_callbacks.reset(new DefaultGlobalCallbacks());
+  }
+}
+
+class ShutdownTag : public internal::CompletionQueueTag {
+ public:
+  bool FinalizeResult(void** tag, bool* status) { return false; }
+};
+
+class DummyTag : public internal::CompletionQueueTag {
+ public:
+  bool FinalizeResult(void** tag, bool* status) { return true; }
+};
+
+class UnimplementedAsyncRequestContext {
+ protected:
+  UnimplementedAsyncRequestContext() : generic_stream_(&server_context_) {}
+
+  GenericServerContext server_context_;
+  GenericServerAsyncReaderWriter generic_stream_;
+};
+
+}  // namespace
+
+/// Use private inheritance rather than composition only to establish order
+/// of construction, since the public base class should be constructed after the
+/// elements belonging to the private base class are constructed. This is not
+/// possible using true composition.
+class Server::UnimplementedAsyncRequest final
+    : private UnimplementedAsyncRequestContext,
+      public GenericAsyncRequest {
+ public:
+  UnimplementedAsyncRequest(Server* server, ServerCompletionQueue* cq)
+      : GenericAsyncRequest(server, &server_context_, &generic_stream_, cq, cq,
+                            nullptr, false),
+        server_(server),
+        cq_(cq) {}
+
+  bool FinalizeResult(void** tag, bool* status) override;
+
+  ServerContext* context() { return &server_context_; }
+  GenericServerAsyncReaderWriter* stream() { return &generic_stream_; }
+
+ private:
+  Server* const server_;
+  ServerCompletionQueue* const cq_;
+};
+
+/// UnimplementedAsyncResponse should not post user-visible completions to the
+/// C++ completion queue, but is generated as a CQ event by the core
+class Server::UnimplementedAsyncResponse final
+    : public internal::CallOpSet<internal::CallOpSendInitialMetadata,
+                                 internal::CallOpServerSendStatus> {
+ public:
+  UnimplementedAsyncResponse(UnimplementedAsyncRequest* request);
+  ~UnimplementedAsyncResponse() { delete request_; }
+
+  bool FinalizeResult(void** tag, bool* status) override {
+    if (internal::CallOpSet<
+            internal::CallOpSendInitialMetadata,
+            internal::CallOpServerSendStatus>::FinalizeResult(tag, status)) {
+      delete this;
+    } else {
+      // The tag was swallowed due to interception. We will see it again.
+    }
+    return false;
+  }
+
+ private:
+  UnimplementedAsyncRequest* const request_;
+};
+
+class Server::SyncRequest final : public internal::CompletionQueueTag {
+ public:
+  SyncRequest(internal::RpcServiceMethod* method, void* method_tag)
+      : method_(method),
+        method_tag_(method_tag),
+        in_flight_(false),
+        has_request_payload_(
+            method->method_type() == internal::RpcMethod::NORMAL_RPC ||
+            method->method_type() == internal::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_);
+  }
+
+  void SetupRequest() { cq_ = grpc_completion_queue_create_for_pluck(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 (method_tag_) {
+      if (GRPC_CALL_OK !=
+          grpc_server_request_registered_call(
+              server, method_tag_, &call_, &deadline_, &request_metadata_,
+              has_request_payload_ ? &request_payload_ : nullptr, cq_,
+              notify_cq, this)) {
+        TeardownRequest();
+        return;
+      }
+    } else {
+      if (!call_details_) {
+        call_details_ = new grpc_call_details;
+        grpc_call_details_init(call_details_);
+      }
+      if (grpc_server_request_call(server, &call_, call_details_,
+                                   &request_metadata_, cq_, notify_cq,
+                                   this) != GRPC_CALL_OK) {
+        TeardownRequest();
+        return;
+      }
+    }
+  }
+
+  void PostShutdownCleanup() {
+    if (call_) {
+      grpc_call_unref(call_);
+      call_ = nullptr;
+    }
+    if (cq_) {
+      grpc_completion_queue_destroy(cq_);
+      cq_ = nullptr;
+    }
+  }
+
+  bool FinalizeResult(void** tag, bool* status) override {
+    if (!*status) {
+      grpc_completion_queue_destroy(cq_);
+      cq_ = nullptr;
+    }
+    if (call_details_) {
+      deadline_ = call_details_->deadline;
+      grpc_call_details_destroy(call_details_);
+      grpc_call_details_init(call_details_);
+    }
+    return true;
+  }
+
+  // The CallData class represents a call that is "active" as opposed
+  // to just being requested. It wraps and takes ownership of the cq from
+  // the call request
+  class CallData final {
+   public:
+    explicit CallData(Server* server, SyncRequest* mrd)
+        : cq_(mrd->cq_),
+          ctx_(mrd->deadline_, &mrd->request_metadata_),
+          has_request_payload_(mrd->has_request_payload_),
+          request_payload_(has_request_payload_ ? mrd->request_payload_
+                                                : nullptr),
+          request_(nullptr),
+          method_(mrd->method_),
+          call_(
+              mrd->call_, server, &cq_, server->max_receive_message_size(),
+              ctx_.set_server_rpc_info(method_->name(), method_->method_type(),
+                                       server->interceptor_creators_)),
+          server_(server),
+          global_callbacks_(nullptr),
+          resources_(false) {
+      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(const std::shared_ptr<GlobalCallbacks>& global_callbacks,
+             bool resources) {
+      global_callbacks_ = global_callbacks;
+      resources_ = resources;
+
+      interceptor_methods_.SetCall(&call_);
+      interceptor_methods_.SetReverse();
+      // Set interception point for RECV INITIAL METADATA
+      interceptor_methods_.AddInterceptionHookPoint(
+          experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+      interceptor_methods_.SetRecvInitialMetadata(&ctx_.client_metadata_);
+
+      if (has_request_payload_) {
+        // Set interception point for RECV MESSAGE
+        auto* handler = resources_ ? method_->handler()
+                                   : server_->resource_exhausted_handler_.get();
+        request_ = handler->Deserialize(call_.call(), request_payload_,
+                                        &request_status_);
+
+        request_payload_ = nullptr;
+        interceptor_methods_.AddInterceptionHookPoint(
+            experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+        interceptor_methods_.SetRecvMessage(request_);
+      }
+
+      if (interceptor_methods_.RunInterceptors(
+              [this]() { ContinueRunAfterInterception(); })) {
+        ContinueRunAfterInterception();
+      } else {
+        // There were interceptors to be run, so ContinueRunAfterInterception
+        // will be run when interceptors are done.
+      }
+    }
+
+    void ContinueRunAfterInterception() {
+      {
+        ctx_.BeginCompletionOp(&call_, nullptr, nullptr);
+        global_callbacks_->PreSynchronousRequest(&ctx_);
+        auto* handler = resources_ ? method_->handler()
+                                   : server_->resource_exhausted_handler_.get();
+        handler->RunHandler(internal::MethodHandler::HandlerParameter(
+            &call_, &ctx_, request_, request_status_, nullptr));
+        request_ = nullptr;
+        global_callbacks_->PostSynchronousRequest(&ctx_);
+
+        cq_.Shutdown();
+
+        internal::CompletionQueueTag* op_tag = ctx_.GetCompletionOpTag();
+        cq_.TryPluck(op_tag, gpr_inf_future(GPR_CLOCK_REALTIME));
+
+        /* Ensure the cq_ is shutdown */
+        DummyTag ignored_tag;
+        GPR_ASSERT(cq_.Pluck(&ignored_tag) == false);
+      }
+      delete this;
+    }
+
+   private:
+    CompletionQueue cq_;
+    ServerContext ctx_;
+    const bool has_request_payload_;
+    grpc_byte_buffer* request_payload_;
+    void* request_;
+    Status request_status_;
+    internal::RpcServiceMethod* const method_;
+    internal::Call call_;
+    Server* server_;
+    std::shared_ptr<GlobalCallbacks> global_callbacks_;
+    bool resources_;
+    internal::InterceptorBatchMethodsImpl interceptor_methods_;
+  };
+
+ private:
+  internal::RpcServiceMethod* const method_;
+  void* const method_tag_;
+  bool in_flight_;
+  const bool has_request_payload_;
+  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_;
+};
+
+class Server::CallbackRequest final : public internal::CompletionQueueTag {
+ public:
+  CallbackRequest(Server* server, internal::RpcServiceMethod* method,
+                  void* method_tag)
+      : server_(server),
+        method_(method),
+        method_tag_(method_tag),
+        has_request_payload_(
+            method->method_type() == internal::RpcMethod::NORMAL_RPC ||
+            method->method_type() == internal::RpcMethod::SERVER_STREAMING),
+        cq_(server->CallbackCQ()),
+        tag_(this) {
+    Setup();
+  }
+
+  ~CallbackRequest() { Clear(); }
+
+  void Request() {
+    if (method_tag_) {
+      if (GRPC_CALL_OK !=
+          grpc_server_request_registered_call(
+              server_->c_server(), method_tag_, &call_, &deadline_,
+              &request_metadata_,
+              has_request_payload_ ? &request_payload_ : nullptr, cq_->cq(),
+              cq_->cq(), static_cast<void*>(&tag_))) {
+        return;
+      }
+    } else {
+      if (!call_details_) {
+        call_details_ = new grpc_call_details;
+        grpc_call_details_init(call_details_);
+      }
+      if (grpc_server_request_call(server_->c_server(), &call_, call_details_,
+                                   &request_metadata_, cq_->cq(), cq_->cq(),
+                                   static_cast<void*>(&tag_)) != GRPC_CALL_OK) {
+        return;
+      }
+    }
+  }
+
+  bool FinalizeResult(void** tag, bool* status) override { return false; }
+
+ private:
+  class CallbackCallTag : public grpc_experimental_completion_queue_functor {
+   public:
+    CallbackCallTag(Server::CallbackRequest* req) : req_(req) {
+      functor_run = &CallbackCallTag::StaticRun;
+    }
+
+    // force_run can not be performed on a tag if operations using this tag
+    // have been sent to PerformOpsOnCall. It is intended for error conditions
+    // that are detected before the operations are internally processed.
+    void force_run(bool ok) { Run(ok); }
+
+   private:
+    Server::CallbackRequest* req_;
+    internal::Call* call_;
+
+    static void StaticRun(grpc_experimental_completion_queue_functor* cb,
+                          int ok) {
+      static_cast<CallbackCallTag*>(cb)->Run(static_cast<bool>(ok));
+    }
+    void Run(bool ok) {
+      void* ignored = req_;
+      bool new_ok = ok;
+      GPR_ASSERT(!req_->FinalizeResult(&ignored, &new_ok));
+      GPR_ASSERT(ignored == req_);
+
+      if (!ok) {
+        // The call has been shutdown
+        req_->Clear();
+        return;
+      }
+
+      // Bind the call, deadline, and metadata from what we got
+      req_->ctx_.set_call(req_->call_);
+      req_->ctx_.cq_ = req_->cq_;
+      req_->ctx_.BindDeadlineAndMetadata(req_->deadline_,
+                                         &req_->request_metadata_);
+      req_->request_metadata_.count = 0;
+
+      // Create a C++ Call to control the underlying core call
+      call_ = new (grpc_call_arena_alloc(req_->call_, sizeof(internal::Call)))
+          internal::Call(
+              req_->call_, req_->server_, req_->cq_,
+              req_->server_->max_receive_message_size(),
+              req_->ctx_.set_server_rpc_info(
+                  req_->method_->name(), req_->method_->method_type(),
+                  req_->server_->interceptor_creators_));
+
+      req_->interceptor_methods_.SetCall(call_);
+      req_->interceptor_methods_.SetReverse();
+      // Set interception point for RECV INITIAL METADATA
+      req_->interceptor_methods_.AddInterceptionHookPoint(
+          experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+      req_->interceptor_methods_.SetRecvInitialMetadata(
+          &req_->ctx_.client_metadata_);
+
+      if (req_->has_request_payload_) {
+        // Set interception point for RECV MESSAGE
+        req_->request_ = req_->method_->handler()->Deserialize(
+            req_->call_, req_->request_payload_, &req_->request_status_);
+        req_->request_payload_ = nullptr;
+        req_->interceptor_methods_.AddInterceptionHookPoint(
+            experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
+        req_->interceptor_methods_.SetRecvMessage(req_->request_);
+      }
+
+      if (req_->interceptor_methods_.RunInterceptors(
+              [this] { ContinueRunAfterInterception(); })) {
+        ContinueRunAfterInterception();
+      } else {
+        // There were interceptors to be run, so ContinueRunAfterInterception
+        // will be run when interceptors are done.
+      }
+    }
+    void ContinueRunAfterInterception() {
+      req_->method_->handler()->RunHandler(
+          internal::MethodHandler::HandlerParameter(
+              call_, &req_->ctx_, req_->request_, req_->request_status_,
+              [this] {
+                req_->Reset();
+                req_->Request();
+              }));
+    }
+  };
+
+  void Reset() {
+    Clear();
+    Setup();
+  }
+
+  void Clear() {
+    if (call_details_) {
+      delete call_details_;
+      call_details_ = nullptr;
+    }
+    grpc_metadata_array_destroy(&request_metadata_);
+    if (has_request_payload_ && request_payload_) {
+      grpc_byte_buffer_destroy(request_payload_);
+    }
+    ctx_.Clear();
+    interceptor_methods_.ClearState();
+  }
+
+  void Setup() {
+    grpc_metadata_array_init(&request_metadata_);
+    ctx_.Setup(gpr_inf_future(GPR_CLOCK_REALTIME));
+    request_payload_ = nullptr;
+    request_ = nullptr;
+    request_status_ = Status();
+  }
+
+  Server* const server_;
+  internal::RpcServiceMethod* const method_;
+  void* const method_tag_;
+  const bool has_request_payload_;
+  grpc_byte_buffer* request_payload_;
+  void* request_;
+  Status request_status_;
+  grpc_call_details* call_details_ = nullptr;
+  grpc_call* call_;
+  gpr_timespec deadline_;
+  grpc_metadata_array request_metadata_;
+  CompletionQueue* cq_;
+  CallbackCallTag tag_;
+  ServerContext ctx_;
+  internal::InterceptorBatchMethodsImpl interceptor_methods_;
+};
+
+// Implementation of ThreadManager. Each instance of SyncRequestThreadManager
+// manages a pool of threads that poll for incoming Sync RPCs and call the
+// appropriate RPC handlers
+class Server::SyncRequestThreadManager : public ThreadManager {
+ public:
+  SyncRequestThreadManager(Server* server, CompletionQueue* server_cq,
+                           std::shared_ptr<GlobalCallbacks> global_callbacks,
+                           grpc_resource_quota* rq, int min_pollers,
+                           int max_pollers, int cq_timeout_msec)
+      : ThreadManager("SyncServer", rq, min_pollers, max_pollers),
+        server_(server),
+        server_cq_(server_cq),
+        cq_timeout_msec_(cq_timeout_msec),
+        global_callbacks_(std::move(global_callbacks)) {}
+
+  WorkStatus PollForWork(void** tag, bool* ok) override {
+    *tag = nullptr;
+    // TODO(ctiller): workaround for GPR_TIMESPAN based deadlines not working
+    // right now
+    gpr_timespec deadline =
+        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                     gpr_time_from_millis(cq_timeout_msec_, GPR_TIMESPAN));
+
+    switch (server_cq_->AsyncNext(tag, ok, deadline)) {
+      case CompletionQueue::TIMEOUT:
+        return TIMEOUT;
+      case CompletionQueue::SHUTDOWN:
+        return SHUTDOWN;
+      case CompletionQueue::GOT_EVENT:
+        return WORK_FOUND;
+    }
+
+    GPR_UNREACHABLE_CODE(return TIMEOUT);
+  }
+
+  void DoWork(void* tag, bool ok, bool resources) override {
+    SyncRequest* sync_req = static_cast<SyncRequest*>(tag);
+
+    if (!sync_req) {
+      // No tag. Nothing to work on. This is an unlikley scenario and possibly a
+      // bug in RPC Manager implementation.
+      gpr_log(GPR_ERROR, "Sync server. DoWork() was called with NULL tag");
+      return;
+    }
+
+    if (ok) {
+      // Calldata takes ownership of the completion queue and interceptors
+      // inside sync_req
+      auto* cd = new SyncRequest::CallData(server_, sync_req);
+      // Prepare for the next request
+      if (!IsShutdown()) {
+        sync_req->SetupRequest();  // Create new completion queue for sync_req
+        sync_req->Request(server_->c_server(), server_cq_->cq());
+      }
+
+      GPR_TIMER_SCOPE("cd.Run()", 0);
+      cd->Run(global_callbacks_, resources);
+    }
+    // TODO (sreek) If ok is false here (which it isn't in case of
+    // grpc_request_registered_call), we should still re-queue the request
+    // object
+  }
+
+  void AddSyncMethod(internal::RpcServiceMethod* method, void* tag) {
+    sync_requests_.emplace_back(new SyncRequest(method, tag));
+  }
+
+  void AddUnknownSyncMethod() {
+    if (!sync_requests_.empty()) {
+      unknown_method_.reset(new internal::RpcServiceMethod(
+          "unknown", internal::RpcMethod::BIDI_STREAMING,
+          new internal::UnknownMethodHandler));
+      sync_requests_.emplace_back(
+          new SyncRequest(unknown_method_.get(), nullptr));
+    }
+  }
+
+  void Shutdown() override {
+    ThreadManager::Shutdown();
+    server_cq_->Shutdown();
+  }
+
+  void Wait() override {
+    ThreadManager::Wait();
+    // Drain any pending items from the queue
+    void* tag;
+    bool ok;
+    while (server_cq_->Next(&tag, &ok)) {
+      if (ok) {
+        // If a request was pulled off the queue, it means that the thread
+        // handling the request added it to the completion queue after shutdown
+        // was called - because the thread had already started and checked the
+        // shutdown flag before shutdown was called. In this case, we simply
+        // clean it up here, *after* calling wait on all the worker threads, at
+        // which point we are certain no in-flight requests will add more to the
+        // queue. This fixes an intermittent memory leak on shutdown.
+        SyncRequest* sync_req = static_cast<SyncRequest*>(tag);
+        sync_req->PostShutdownCleanup();
+      }
+    }
+  }
+
+  void Start() {
+    if (!sync_requests_.empty()) {
+      for (auto m = sync_requests_.begin(); m != sync_requests_.end(); m++) {
+        (*m)->SetupRequest();
+        (*m)->Request(server_->c_server(), server_cq_->cq());
+      }
+
+      Initialize();  // ThreadManager's Initialize()
+    }
+  }
+
+ private:
+  Server* server_;
+  CompletionQueue* server_cq_;
+  int cq_timeout_msec_;
+  std::vector<std::unique_ptr<SyncRequest>> sync_requests_;
+  std::unique_ptr<internal::RpcServiceMethod> unknown_method_;
+  std::shared_ptr<Server::GlobalCallbacks> global_callbacks_;
+};
+
+static internal::GrpcLibraryInitializer g_gli_initializer;
+Server::Server(
+    int max_receive_message_size, ChannelArguments* args,
+    std::shared_ptr<std::vector<std::unique_ptr<ServerCompletionQueue>>>
+        sync_server_cqs,
+    int min_pollers, int max_pollers, int sync_cq_timeout_msec,
+    grpc_resource_quota* server_rq,
+    std::vector<
+        std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>
+        interceptor_creators)
+    : interceptor_creators_(std::move(interceptor_creators)),
+      max_receive_message_size_(max_receive_message_size),
+      sync_server_cqs_(std::move(sync_server_cqs)),
+      started_(false),
+      shutdown_(false),
+      shutdown_notified_(false),
+      has_generic_service_(false),
+      server_(nullptr),
+      server_initializer_(new ServerInitializer(this)),
+      health_check_service_disabled_(false) {
+  g_gli_initializer.summon();
+  gpr_once_init(&g_once_init_callbacks, InitGlobalCallbacks);
+  global_callbacks_ = g_callbacks;
+  global_callbacks_->UpdateArguments(args);
+
+  if (sync_server_cqs_ != nullptr) {
+    bool default_rq_created = false;
+    if (server_rq == nullptr) {
+      server_rq = grpc_resource_quota_create("SyncServer-default-rq");
+      grpc_resource_quota_set_max_threads(server_rq,
+                                          DEFAULT_MAX_SYNC_SERVER_THREADS);
+      default_rq_created = true;
+    }
+
+    for (const auto& it : *sync_server_cqs_) {
+      sync_req_mgrs_.emplace_back(new SyncRequestThreadManager(
+          this, it.get(), global_callbacks_, server_rq, min_pollers,
+          max_pollers, sync_cq_timeout_msec));
+    }
+
+    if (default_rq_created) {
+      grpc_resource_quota_unref(server_rq);
+    }
+  }
+
+  grpc_channel_args channel_args;
+  args->SetChannelArgs(&channel_args);
+
+  for (size_t i = 0; i < channel_args.num_args; i++) {
+    if (0 ==
+        strcmp(channel_args.args[i].key, kHealthCheckServiceInterfaceArg)) {
+      if (channel_args.args[i].value.pointer.p == nullptr) {
+        health_check_service_disabled_ = true;
+      } else {
+        health_check_service_.reset(static_cast<HealthCheckServiceInterface*>(
+            channel_args.args[i].value.pointer.p));
+      }
+      break;
+    }
+  }
+
+  server_ = grpc_server_create(&channel_args, nullptr);
+}
+
+Server::~Server() {
+  {
+    std::unique_lock<std::mutex> lock(mu_);
+    if (callback_cq_ != nullptr) {
+      callback_cq_->Shutdown();
+    }
+    if (started_ && !shutdown_) {
+      lock.unlock();
+      Shutdown();
+    } else if (!started_) {
+      // Shutdown the completion queues
+      for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
+        (*it)->Shutdown();
+      }
+    }
+  }
+
+  grpc_server_destroy(server_);
+}
+
+void Server::SetGlobalCallbacks(GlobalCallbacks* callbacks) {
+  GPR_ASSERT(!g_callbacks);
+  GPR_ASSERT(callbacks);
+  g_callbacks.reset(callbacks);
+}
+
+grpc_server* Server::c_server() { return server_; }
+
+std::shared_ptr<Channel> Server::InProcessChannel(
+    const ChannelArguments& args) {
+  grpc_channel_args channel_args = args.c_channel_args();
+  return CreateChannelInternal(
+      "inproc", grpc_inproc_channel_create(server_, &channel_args, nullptr),
+      std::vector<
+          std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
+}
+
+std::shared_ptr<Channel>
+Server::experimental_type::InProcessChannelWithInterceptors(
+    const ChannelArguments& args,
+    std::vector<
+        std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>
+        interceptor_creators) {
+  grpc_channel_args channel_args = args.c_channel_args();
+  return CreateChannelInternal(
+      "inproc",
+      grpc_inproc_channel_create(server_->server_, &channel_args, nullptr),
+      std::move(interceptor_creators));
+}
+
+static grpc_server_register_method_payload_handling PayloadHandlingForMethod(
+    internal::RpcServiceMethod* method) {
+  switch (method->method_type()) {
+    case internal::RpcMethod::NORMAL_RPC:
+    case internal::RpcMethod::SERVER_STREAMING:
+      return GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER;
+    case internal::RpcMethod::CLIENT_STREAMING:
+    case internal::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;
+    }
+
+    internal::RpcServiceMethod* method = it->get();
+    void* method_registration_tag = grpc_server_register_method(
+        server_, method->name(), host ? host->c_str() : nullptr,
+        PayloadHandlingForMethod(method), 0);
+    if (method_registration_tag == nullptr) {
+      gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
+              method->name());
+      return false;
+    }
+
+    if (method->handler() == nullptr) {  // Async method without handler
+      method->set_server_tag(method_registration_tag);
+    } else if (method->api_type() ==
+               internal::RpcServiceMethod::ApiType::SYNC) {
+      for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
+        (*it)->AddSyncMethod(method, method_registration_tag);
+      }
+    } else {
+      // a callback method. Register at least some callback requests
+      // TODO(vjpai): Register these dynamically based on need
+      for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) {
+        auto* req = new CallbackRequest(this, method, method_registration_tag);
+        callback_reqs_.emplace_back(req);
+      }
+      // Enqueue it so that it will be Request'ed later once
+      // all request matchers are created at core server startup
+    }
+
+    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_);
+  int port = creds->AddPortToServer(addr, server_);
+  global_callbacks_->AddPort(this, addr, creds, port);
+  return port;
+}
+
+void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
+  GPR_ASSERT(!started_);
+  global_callbacks_->PreServerStart(this);
+  started_ = true;
+
+  // Only create default health check service when user did not provide an
+  // explicit one.
+  ServerCompletionQueue* health_check_cq = nullptr;
+  DefaultHealthCheckService::HealthCheckServiceImpl*
+      default_health_check_service_impl = nullptr;
+  if (health_check_service_ == nullptr && !health_check_service_disabled_ &&
+      DefaultHealthCheckServiceEnabled()) {
+    auto* default_hc_service = new DefaultHealthCheckService;
+    health_check_service_.reset(default_hc_service);
+    // We create a non-polling CQ to avoid impacting application
+    // performance.  This ensures that we don't introduce thread hops
+    // for application requests that wind up on this CQ, which is polled
+    // in its own thread.
+    health_check_cq =
+        new ServerCompletionQueue(GRPC_CQ_NEXT, GRPC_CQ_NON_POLLING, nullptr);
+    grpc_server_register_completion_queue(server_, health_check_cq->cq(),
+                                          nullptr);
+    default_health_check_service_impl =
+        default_hc_service->GetHealthCheckService(
+            std::unique_ptr<ServerCompletionQueue>(health_check_cq));
+    RegisterService(nullptr, default_health_check_service_impl);
+  }
+
+  grpc_server_start(server_);
+
+  if (!has_generic_service_) {
+    for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
+      (*it)->AddUnknownSyncMethod();
+    }
+
+    for (size_t i = 0; i < num_cqs; i++) {
+      if (cqs[i]->IsFrequentlyPolled()) {
+        new UnimplementedAsyncRequest(this, cqs[i]);
+      }
+    }
+    if (health_check_cq != nullptr) {
+      new UnimplementedAsyncRequest(this, health_check_cq);
+    }
+  }
+
+  // If this server has any support for synchronous methods (has any sync
+  // server CQs), make sure that we have a ResourceExhausted handler
+  // to deal with the case of thread exhaustion
+  if (sync_server_cqs_ != nullptr && !sync_server_cqs_->empty()) {
+    resource_exhausted_handler_.reset(new internal::ResourceExhaustedHandler);
+  }
+
+  for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
+    (*it)->Start();
+  }
+
+  for (auto& cbreq : callback_reqs_) {
+    cbreq->Request();
+  }
+
+  if (default_health_check_service_impl != nullptr) {
+    default_health_check_service_impl->StartServingThread();
+  }
+}
+
+void Server::ShutdownInternal(gpr_timespec deadline) {
+  std::unique_lock<std::mutex> lock(mu_);
+  if (!shutdown_) {
+    shutdown_ = true;
+
+    /// The completion queue to use for server shutdown completion notification
+    CompletionQueue shutdown_cq;
+    ShutdownTag shutdown_tag;  // Dummy shutdown tag
+    grpc_server_shutdown_and_notify(server_, shutdown_cq.cq(), &shutdown_tag);
+
+    shutdown_cq.Shutdown();
+
+    void* tag;
+    bool ok;
+    CompletionQueue::NextStatus status =
+        shutdown_cq.AsyncNext(&tag, &ok, deadline);
+
+    // If this timed out, it means we are done with the grace period for a clean
+    // shutdown. We should force a shutdown now by cancelling all inflight calls
+    if (status == CompletionQueue::NextStatus::TIMEOUT) {
+      grpc_server_cancel_all_calls(server_);
+    }
+    // Else in case of SHUTDOWN or GOT_EVENT, it means that the server has
+    // successfully shutdown
+
+    // Shutdown all ThreadManagers. This will try to gracefully stop all the
+    // threads in the ThreadManagers (once they process any inflight requests)
+    for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
+      (*it)->Shutdown();  // ThreadManager's Shutdown()
+    }
+
+    // Wait for threads in all ThreadManagers to terminate
+    for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
+      (*it)->Wait();
+    }
+
+    // Drain the shutdown queue (if the previous call to AsyncNext() timed out
+    // and we didn't remove the tag from the queue yet)
+    while (shutdown_cq.Next(&tag, &ok)) {
+      // Nothing to be done here. Just ignore ok and tag values
+    }
+
+    shutdown_notified_ = true;
+    shutdown_cv_.notify_all();
+  }
+}
+
+void Server::Wait() {
+  std::unique_lock<std::mutex> lock(mu_);
+  while (started_ && !shutdown_notified_) {
+    shutdown_cv_.wait(lock);
+  }
+}
+
+void Server::PerformOpsOnCall(internal::CallOpSetInterface* ops,
+                              internal::Call* call) {
+  ops->FillOps(call);
+}
+
+ServerInterface::BaseAsyncRequest::BaseAsyncRequest(
+    ServerInterface* server, ServerContext* context,
+    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+    ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
+    : server_(server),
+      context_(context),
+      stream_(stream),
+      call_cq_(call_cq),
+      notification_cq_(notification_cq),
+      tag_(tag),
+      delete_on_finalize_(delete_on_finalize),
+      call_(nullptr),
+      done_intercepting_(false) {
+  /* Set up interception state partially for the receive ops. call_wrapper_ is
+   * not filled at this point, but it will be filled before the interceptors are
+   * run. */
+  interceptor_methods_.SetCall(&call_wrapper_);
+  interceptor_methods_.SetReverse();
+  call_cq_->RegisterAvalanching();  // This op will trigger more ops
+}
+
+ServerInterface::BaseAsyncRequest::~BaseAsyncRequest() {
+  call_cq_->CompleteAvalanching();
+}
+
+bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
+                                                       bool* status) {
+  if (done_intercepting_) {
+    *tag = tag_;
+    if (delete_on_finalize_) {
+      delete this;
+    }
+    return true;
+  }
+  context_->set_call(call_);
+  context_->cq_ = call_cq_;
+  if (call_wrapper_.call() == nullptr) {
+    // Fill it since it is empty.
+    call_wrapper_ = internal::Call(
+        call_, server_, call_cq_, server_->max_receive_message_size(), nullptr);
+  }
+
+  // just the pointers inside call are copied here
+  stream_->BindCall(&call_wrapper_);
+
+  if (*status && call_ && call_wrapper_.server_rpc_info()) {
+    done_intercepting_ = true;
+    // Set interception point for RECV INITIAL METADATA
+    interceptor_methods_.AddInterceptionHookPoint(
+        experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
+    interceptor_methods_.SetRecvInitialMetadata(&context_->client_metadata_);
+    if (interceptor_methods_.RunInterceptors(
+            [this]() { ContinueFinalizeResultAfterInterception(); })) {
+      // There are no interceptors to run. Continue
+    } else {
+      // There were interceptors to be run, so
+      // ContinueFinalizeResultAfterInterception will be run when interceptors
+      // are done.
+      return false;
+    }
+  }
+  if (*status && call_) {
+    context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
+  }
+  *tag = tag_;
+  if (delete_on_finalize_) {
+    delete this;
+  }
+  return true;
+}
+
+void ServerInterface::BaseAsyncRequest::
+    ContinueFinalizeResultAfterInterception() {
+  context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr);
+  // Queue a tag which will be returned immediately
+  grpc_core::ExecCtx exec_ctx;
+  grpc_cq_begin_op(notification_cq_->cq(), this);
+  grpc_cq_end_op(
+      notification_cq_->cq(), this, GRPC_ERROR_NONE,
+      [](void* arg, grpc_cq_completion* completion) { delete completion; },
+      nullptr, new grpc_cq_completion());
+}
+
+ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest(
+    ServerInterface* server, ServerContext* context,
+    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+    ServerCompletionQueue* notification_cq, void* tag, const char* name,
+    internal::RpcMethod::RpcType type)
+    : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
+                       true),
+      name_(name),
+      type_(type) {}
+
+void ServerInterface::RegisteredAsyncRequest::IssueRequest(
+    void* registered_method, grpc_byte_buffer** payload,
+    ServerCompletionQueue* notification_cq) {
+  GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_registered_call(
+                                 server_->server(), registered_method, &call_,
+                                 &context_->deadline_,
+                                 context_->client_metadata_.arr(), payload,
+                                 call_cq_->cq(), notification_cq->cq(), this));
+}
+
+ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
+    ServerInterface* server, GenericServerContext* context,
+    internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq,
+    ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize)
+    : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag,
+                       delete_on_finalize) {
+  grpc_call_details_init(&call_details_);
+  GPR_ASSERT(notification_cq);
+  GPR_ASSERT(call_cq);
+  GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
+                                 server->server(), &call_, &call_details_,
+                                 context->client_metadata_.arr(), call_cq->cq(),
+                                 notification_cq->cq(), this));
+}
+
+bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
+                                                          bool* status) {
+  // If we are done intercepting, there is nothing more for us to do
+  if (done_intercepting_) {
+    return BaseAsyncRequest::FinalizeResult(tag, status);
+  }
+  // TODO(yangg) remove the copy here.
+  if (*status) {
+    static_cast<GenericServerContext*>(context_)->method_ =
+        StringFromCopiedSlice(call_details_.method);
+    static_cast<GenericServerContext*>(context_)->host_ =
+        StringFromCopiedSlice(call_details_.host);
+    context_->deadline_ = call_details_.deadline;
+  }
+  grpc_slice_unref(call_details_.method);
+  grpc_slice_unref(call_details_.host);
+  call_wrapper_ = internal::Call(
+      call_, server_, call_cq_, server_->max_receive_message_size(),
+      context_->set_server_rpc_info(
+          static_cast<GenericServerContext*>(context_)->method_.c_str(),
+          internal::RpcMethod::BIDI_STREAMING,
+          *server_->interceptor_creators()));
+  return BaseAsyncRequest::FinalizeResult(tag, status);
+}
+
+bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag,
+                                                       bool* status) {
+  if (GenericAsyncRequest::FinalizeResult(tag, status)) {
+    // We either had no interceptors run or we are done intercepting
+    if (*status) {
+      new UnimplementedAsyncRequest(server_, cq_);
+      new UnimplementedAsyncResponse(this);
+    } else {
+      delete this;
+    }
+  } else {
+    // The tag was swallowed due to interception. We will see it again.
+  }
+  return false;
+}
+
+Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse(
+    UnimplementedAsyncRequest* request)
+    : request_(request) {
+  Status status(StatusCode::UNIMPLEMENTED, "");
+  internal::UnknownMethodHandler::FillOps(request_->context(), this);
+  request_->stream()->call_.PerformOps(this);
+}
+
+ServerInitializer* Server::initializer() { return server_initializer_.get(); }
+
+namespace {
+class ShutdownCallback : public grpc_experimental_completion_queue_functor {
+ public:
+  ShutdownCallback() { functor_run = &ShutdownCallback::Run; }
+  // TakeCQ takes ownership of the cq into the shutdown callback
+  // so that the shutdown callback will be responsible for destroying it
+  void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
+
+  // The Run function will get invoked by the completion queue library
+  // when the shutdown is actually complete
+  static void Run(grpc_experimental_completion_queue_functor* cb, int) {
+    auto* callback = static_cast<ShutdownCallback*>(cb);
+    delete callback->cq_;
+    delete callback;
+  }
+
+ private:
+  CompletionQueue* cq_ = nullptr;
+};
+}  // namespace
+
+CompletionQueue* Server::CallbackCQ() {
+  // TODO(vjpai): Consider using a single global CQ for the default CQ
+  // if there is no explicit per-server CQ registered
+  std::lock_guard<std::mutex> l(mu_);
+  if (callback_cq_ == nullptr) {
+    auto* shutdown_callback = new ShutdownCallback;
+    callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{
+        GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
+        shutdown_callback});
+
+    // Transfer ownership of the new cq to its own shutdown callback
+    shutdown_callback->TakeCQ(callback_cq_);
+  }
+  return callback_cq_;
+};
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/server_context.cc b/third_party/grpc/src/cpp/server/server_context.cc
new file mode 100644
index 0000000..1b524bc
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/server_context.cc
@@ -0,0 +1,380 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/server_context.h>
+#include <grpcpp/support/server_callback.h>
+
+#include <algorithm>
+#include <mutex>
+#include <utility>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpc/load_reporting.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpcpp/completion_queue.h>
+#include <grpcpp/impl/call.h>
+#include <grpcpp/support/time.h>
+
+#include "src/core/lib/surface/call.h"
+
+namespace grpc {
+
+// CompletionOp
+
+class ServerContext::CompletionOp final : public internal::CallOpSetInterface {
+ public:
+  // initial refs: one in the server context, one in the cq
+  // must ref the call before calling constructor and after deleting this
+  CompletionOp(internal::Call* call, internal::ServerReactor* reactor)
+      : call_(*call),
+        reactor_(reactor),
+        has_tag_(false),
+        tag_(nullptr),
+        core_cq_tag_(this),
+        refs_(2),
+        finalized_(false),
+        cancelled_(0),
+        done_intercepting_(false) {}
+
+  // CompletionOp isn't copyable or movable
+  CompletionOp(const CompletionOp&) = delete;
+  CompletionOp& operator=(const CompletionOp&) = delete;
+  CompletionOp(CompletionOp&&) = delete;
+  CompletionOp& operator=(CompletionOp&&) = delete;
+
+  ~CompletionOp() {
+    if (call_.server_rpc_info()) {
+      call_.server_rpc_info()->Unref();
+    }
+  }
+
+  void FillOps(internal::Call* call) override;
+
+  // This should always be arena allocated in the call, so override delete.
+  // But this class is not trivially destructible, so must actually call delete
+  // before allowing the arena to be freed
+  static void operator delete(void* ptr, std::size_t size) {
+    assert(size == sizeof(CompletionOp));
+  }
+
+  // This operator should never be called as the memory should be freed as part
+  // of the arena destruction. It only exists to provide a matching operator
+  // delete to the operator new so that some compilers will not complain (see
+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
+  // there are no tests catching the compiler warning.
+  static void operator delete(void*, void*) { assert(0); }
+
+  bool FinalizeResult(void** tag, bool* status) override;
+
+  bool CheckCancelled(CompletionQueue* cq) {
+    cq->TryPluck(this);
+    return CheckCancelledNoPluck();
+  }
+  bool CheckCancelledAsync() { return CheckCancelledNoPluck(); }
+
+  void set_tag(void* tag) {
+    has_tag_ = true;
+    tag_ = tag;
+  }
+
+  void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; }
+
+  void* core_cq_tag() override { return core_cq_tag_; }
+
+  void Unref();
+
+  // This will be called while interceptors are run if the RPC is a hijacked
+  // RPC. This should set hijacking state for each of the ops.
+  void SetHijackingState() override {
+    /* Servers don't allow hijacking */
+    GPR_CODEGEN_ASSERT(false);
+  }
+
+  /* Should be called after interceptors are done running */
+  void ContinueFillOpsAfterInterception() override {}
+
+  /* Should be called after interceptors are done running on the finalize result
+   * path */
+  void ContinueFinalizeResultAfterInterception() override {
+    done_intercepting_ = true;
+    if (!has_tag_) {
+      /* We don't have a tag to return. */
+      std::unique_lock<std::mutex> lock(mu_);
+      if (--refs_ == 0) {
+        lock.unlock();
+        grpc_call* call = call_.call();
+        delete this;
+        grpc_call_unref(call);
+      }
+      return;
+    }
+    /* Start a dummy op so that we can return the tag */
+    GPR_CODEGEN_ASSERT(
+        GRPC_CALL_OK ==
+        grpc_call_start_batch(call_.call(), nullptr, 0, core_cq_tag_, nullptr));
+  }
+
+ private:
+  bool CheckCancelledNoPluck() {
+    std::lock_guard<std::mutex> g(mu_);
+    return finalized_ ? (cancelled_ != 0) : false;
+  }
+
+  internal::Call call_;
+  internal::ServerReactor* reactor_;
+  bool has_tag_;
+  void* tag_;
+  void* core_cq_tag_;
+  std::mutex mu_;
+  int refs_;
+  bool finalized_;
+  int cancelled_;  // This is an int (not bool) because it is passed to core
+  bool done_intercepting_;
+  internal::InterceptorBatchMethodsImpl interceptor_methods_;
+};
+
+void ServerContext::CompletionOp::Unref() {
+  std::unique_lock<std::mutex> lock(mu_);
+  if (--refs_ == 0) {
+    lock.unlock();
+    grpc_call* call = call_.call();
+    delete this;
+    grpc_call_unref(call);
+  }
+}
+
+void ServerContext::CompletionOp::FillOps(internal::Call* call) {
+  grpc_op ops;
+  ops.op = GRPC_OP_RECV_CLOSE_ON_SERVER;
+  ops.data.recv_close_on_server.cancelled = &cancelled_;
+  ops.flags = 0;
+  ops.reserved = nullptr;
+  interceptor_methods_.SetCall(&call_);
+  interceptor_methods_.SetReverse();
+  interceptor_methods_.SetCallOpSetInterface(this);
+  GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call->call(), &ops, 1,
+                                                   core_cq_tag_, nullptr));
+  /* No interceptors to run here */
+}
+
+bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) {
+  bool ret = false;
+  std::unique_lock<std::mutex> lock(mu_);
+  if (done_intercepting_) {
+    /* We are done intercepting. */
+    if (has_tag_) {
+      *tag = tag_;
+      ret = true;
+    }
+    if (--refs_ == 0) {
+      lock.unlock();
+      grpc_call* call = call_.call();
+      delete this;
+      grpc_call_unref(call);
+    }
+    return ret;
+  }
+  finalized_ = true;
+
+  // If for some reason the incoming status is false, mark that as a
+  // cancellation.
+  // TODO(vjpai): does this ever happen?
+  if (!*status) {
+    cancelled_ = 1;
+  }
+
+  if (cancelled_ && (reactor_ != nullptr)) {
+    reactor_->OnCancel();
+  }
+  /* Release the lock since we are going to be running through interceptors now
+   */
+  lock.unlock();
+  /* Add interception point and run through interceptors */
+  interceptor_methods_.AddInterceptionHookPoint(
+      experimental::InterceptionHookPoints::POST_RECV_CLOSE);
+  if (interceptor_methods_.RunInterceptors()) {
+    /* No interceptors were run */
+    if (has_tag_) {
+      *tag = tag_;
+      ret = true;
+    }
+    lock.lock();
+    if (--refs_ == 0) {
+      lock.unlock();
+      grpc_call* call = call_.call();
+      delete this;
+      grpc_call_unref(call);
+    }
+    return ret;
+  }
+  /* There are interceptors to be run. Return false for now */
+  return false;
+}
+
+// ServerContext body
+
+ServerContext::ServerContext() { Setup(gpr_inf_future(GPR_CLOCK_REALTIME)); }
+
+ServerContext::ServerContext(gpr_timespec deadline, grpc_metadata_array* arr) {
+  Setup(deadline);
+  std::swap(*client_metadata_.arr(), *arr);
+}
+
+void ServerContext::Setup(gpr_timespec deadline) {
+  completion_op_ = nullptr;
+  has_notify_when_done_tag_ = false;
+  async_notify_when_done_tag_ = nullptr;
+  deadline_ = deadline;
+  call_ = nullptr;
+  cq_ = nullptr;
+  sent_initial_metadata_ = false;
+  compression_level_set_ = false;
+  has_pending_ops_ = false;
+  rpc_info_ = nullptr;
+}
+
+void ServerContext::BindDeadlineAndMetadata(gpr_timespec deadline,
+                                            grpc_metadata_array* arr) {
+  deadline_ = deadline;
+  std::swap(*client_metadata_.arr(), *arr);
+}
+
+ServerContext::~ServerContext() { Clear(); }
+
+void ServerContext::Clear() {
+  auth_context_.reset();
+  initial_metadata_.clear();
+  trailing_metadata_.clear();
+  client_metadata_.Reset();
+  if (completion_op_) {
+    completion_op_->Unref();
+    completion_op_ = nullptr;
+    completion_tag_.Clear();
+  }
+  if (rpc_info_) {
+    rpc_info_->Unref();
+    rpc_info_ = nullptr;
+  }
+  if (call_) {
+    auto* call = call_;
+    call_ = nullptr;
+    grpc_call_unref(call);
+  }
+}
+
+void ServerContext::BeginCompletionOp(internal::Call* call,
+                                      std::function<void(bool)> callback,
+                                      internal::ServerReactor* reactor) {
+  GPR_ASSERT(!completion_op_);
+  if (rpc_info_) {
+    rpc_info_->Ref();
+  }
+  grpc_call_ref(call->call());
+  completion_op_ =
+      new (grpc_call_arena_alloc(call->call(), sizeof(CompletionOp)))
+          CompletionOp(call, reactor);
+  if (callback != nullptr) {
+    completion_tag_.Set(call->call(), std::move(callback), completion_op_);
+    completion_op_->set_core_cq_tag(&completion_tag_);
+    completion_op_->set_tag(completion_op_);
+  } else if (has_notify_when_done_tag_) {
+    completion_op_->set_tag(async_notify_when_done_tag_);
+  }
+  call->PerformOps(completion_op_);
+}
+
+internal::CompletionQueueTag* ServerContext::GetCompletionOpTag() {
+  return static_cast<internal::CompletionQueueTag*>(completion_op_);
+}
+
+void ServerContext::AddInitialMetadata(const grpc::string& key,
+                                       const grpc::string& value) {
+  initial_metadata_.insert(std::make_pair(key, value));
+}
+
+void ServerContext::AddTrailingMetadata(const grpc::string& key,
+                                        const grpc::string& value) {
+  trailing_metadata_.insert(std::make_pair(key, value));
+}
+
+void ServerContext::TryCancel() const {
+  internal::CancelInterceptorBatchMethods cancel_methods;
+  if (rpc_info_) {
+    for (size_t i = 0; i < rpc_info_->interceptors_.size(); i++) {
+      rpc_info_->RunInterceptor(&cancel_methods, i);
+    }
+  }
+  grpc_call_error err = grpc_call_cancel_with_status(
+      call_, GRPC_STATUS_CANCELLED, "Cancelled on the server side", nullptr);
+  if (err != GRPC_CALL_OK) {
+    gpr_log(GPR_ERROR, "TryCancel failed with: %d", err);
+  }
+}
+
+bool ServerContext::IsCancelled() const {
+  if (completion_tag_) {
+    // When using callback API, this result is always valid.
+    return completion_op_->CheckCancelledAsync();
+  } else if (has_notify_when_done_tag_) {
+    // When using async API, the result is only valid
+    // if the tag has already been delivered at the completion queue
+    return completion_op_ && completion_op_->CheckCancelledAsync();
+  } else {
+    // when using sync API, the result is always valid
+    return completion_op_ && completion_op_->CheckCancelled(cq_);
+  }
+}
+
+void ServerContext::set_compression_algorithm(
+    grpc_compression_algorithm algorithm) {
+  compression_algorithm_ = algorithm;
+  const char* algorithm_name = nullptr;
+  if (!grpc_compression_algorithm_name(algorithm, &algorithm_name)) {
+    gpr_log(GPR_ERROR, "Name for compression algorithm '%d' unknown.",
+            algorithm);
+    abort();
+  }
+  GPR_ASSERT(algorithm_name != nullptr);
+  AddInitialMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name);
+}
+
+grpc::string ServerContext::peer() const {
+  grpc::string peer;
+  if (call_) {
+    char* c_peer = grpc_call_get_peer(call_);
+    peer = c_peer;
+    gpr_free(c_peer);
+  }
+  return peer;
+}
+
+const struct census_context* ServerContext::census_context() const {
+  return grpc_census_call_get_context(call_);
+}
+
+void ServerContext::SetLoadReportingCosts(
+    const std::vector<grpc::string>& cost_data) {
+  if (call_ == nullptr) return;
+  for (const auto& cost_datum : cost_data) {
+    AddTrailingMetadata(GRPC_LB_COST_MD_KEY, cost_datum);
+  }
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/server_credentials.cc b/third_party/grpc/src/cpp/server/server_credentials.cc
new file mode 100644
index 0000000..c3b3a8b
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/server_credentials.cc
@@ -0,0 +1,25 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/security/server_credentials.h>
+
+namespace grpc {
+
+ServerCredentials::~ServerCredentials() {}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/server_posix.cc b/third_party/grpc/src/cpp/server/server_posix.cc
new file mode 100644
index 0000000..7c221ed
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/server_posix.cc
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/server_posix.h>
+
+#include <grpc/grpc_posix.h>
+
+namespace grpc {
+
+#ifdef GPR_SUPPORT_CHANNELS_FROM_FD
+
+void AddInsecureChannelFromFd(Server* server, int fd) {
+  grpc_server_add_insecure_channel_from_fd(server->c_server(), nullptr, fd);
+}
+
+#endif  // GPR_SUPPORT_CHANNELS_FROM_FD
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/server/thread_pool_interface.h b/third_party/grpc/src/cpp/server/thread_pool_interface.h
new file mode 100644
index 0000000..028842a
--- /dev/null
+++ b/third_party/grpc/src/cpp/server/thread_pool_interface.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_THREAD_POOL_INTERFACE_H
+#define GRPC_INTERNAL_CPP_THREAD_POOL_INTERFACE_H
+
+#include <functional>
+
+namespace grpc {
+
+// A thread pool interface for running callbacks.
+class ThreadPoolInterface {
+ public:
+  virtual ~ThreadPoolInterface() {}
+
+  // Schedule the given callback for execution.
+  virtual void Add(const std::function<void()>& callback) = 0;
+};
+
+// Allows different codebases to use their own thread pool impls
+typedef ThreadPoolInterface* (*CreateThreadPoolFunc)(void);
+void SetCreateThreadPool(CreateThreadPoolFunc func);
+
+ThreadPoolInterface* CreateDefaultThreadPool();
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_THREAD_POOL_INTERFACE_H
diff --git a/third_party/grpc/src/cpp/thread_manager/thread_manager.cc b/third_party/grpc/src/cpp/thread_manager/thread_manager.cc
new file mode 100644
index 0000000..3e8606a
--- /dev/null
+++ b/third_party/grpc/src/cpp/thread_manager/thread_manager.cc
@@ -0,0 +1,252 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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 "src/cpp/thread_manager/thread_manager.h"
+
+#include <climits>
+#include <mutex>
+
+#include <grpc/support/log.h>
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+namespace grpc {
+
+ThreadManager::WorkerThread::WorkerThread(ThreadManager* thd_mgr)
+    : thd_mgr_(thd_mgr) {
+  // Make thread creation exclusive with respect to its join happening in
+  // ~WorkerThread().
+  thd_ = grpc_core::Thread(
+      "grpcpp_sync_server",
+      [](void* th) { static_cast<ThreadManager::WorkerThread*>(th)->Run(); },
+      this);
+  thd_.Start();
+}
+
+void ThreadManager::WorkerThread::Run() {
+  thd_mgr_->MainWorkLoop();
+  thd_mgr_->MarkAsCompleted(this);
+}
+
+ThreadManager::WorkerThread::~WorkerThread() {
+  // Don't join until the thread is fully constructed.
+  thd_.Join();
+}
+
+ThreadManager::ThreadManager(const char* name,
+                             grpc_resource_quota* resource_quota,
+                             int min_pollers, int max_pollers)
+    : shutdown_(false),
+      num_pollers_(0),
+      min_pollers_(min_pollers),
+      max_pollers_(max_pollers == -1 ? INT_MAX : max_pollers),
+      num_threads_(0),
+      max_active_threads_sofar_(0) {
+  resource_user_ = grpc_resource_user_create(resource_quota, name);
+}
+
+ThreadManager::~ThreadManager() {
+  {
+    std::lock_guard<std::mutex> lock(mu_);
+    GPR_ASSERT(num_threads_ == 0);
+  }
+
+  grpc_core::ExecCtx exec_ctx;  // grpc_resource_user_unref needs an exec_ctx
+  grpc_resource_user_unref(resource_user_);
+  CleanupCompletedThreads();
+}
+
+void ThreadManager::Wait() {
+  std::unique_lock<std::mutex> lock(mu_);
+  while (num_threads_ != 0) {
+    shutdown_cv_.wait(lock);
+  }
+}
+
+void ThreadManager::Shutdown() {
+  std::lock_guard<std::mutex> lock(mu_);
+  shutdown_ = true;
+}
+
+bool ThreadManager::IsShutdown() {
+  std::lock_guard<std::mutex> lock(mu_);
+  return shutdown_;
+}
+
+int ThreadManager::GetMaxActiveThreadsSoFar() {
+  std::lock_guard<std::mutex> list_lock(list_mu_);
+  return max_active_threads_sofar_;
+}
+
+void ThreadManager::MarkAsCompleted(WorkerThread* thd) {
+  {
+    std::lock_guard<std::mutex> list_lock(list_mu_);
+    completed_threads_.push_back(thd);
+  }
+
+  {
+    std::lock_guard<std::mutex> lock(mu_);
+    num_threads_--;
+    if (num_threads_ == 0) {
+      shutdown_cv_.notify_one();
+    }
+  }
+
+  // Give a thread back to the resource quota
+  grpc_resource_user_free_threads(resource_user_, 1);
+}
+
+void ThreadManager::CleanupCompletedThreads() {
+  std::list<WorkerThread*> completed_threads;
+  {
+    // swap out the completed threads list: allows other threads to clean up
+    // more quickly
+    std::unique_lock<std::mutex> lock(list_mu_);
+    completed_threads.swap(completed_threads_);
+  }
+  for (auto thd : completed_threads) delete thd;
+}
+
+void ThreadManager::Initialize() {
+  if (!grpc_resource_user_allocate_threads(resource_user_, min_pollers_)) {
+    gpr_log(GPR_ERROR,
+            "No thread quota available to even create the minimum required "
+            "polling threads (i.e %d). Unable to start the thread manager",
+            min_pollers_);
+    abort();
+  }
+
+  {
+    std::unique_lock<std::mutex> lock(mu_);
+    num_pollers_ = min_pollers_;
+    num_threads_ = min_pollers_;
+    max_active_threads_sofar_ = min_pollers_;
+  }
+
+  for (int i = 0; i < min_pollers_; i++) {
+    new WorkerThread(this);
+  }
+}
+
+void ThreadManager::MainWorkLoop() {
+  while (true) {
+    void* tag;
+    bool ok;
+    WorkStatus work_status = PollForWork(&tag, &ok);
+
+    std::unique_lock<std::mutex> lock(mu_);
+    // Reduce the number of pollers by 1 and check what happened with the poll
+    num_pollers_--;
+    bool done = false;
+    switch (work_status) {
+      case TIMEOUT:
+        // If we timed out and we have more pollers than we need (or we are
+        // shutdown), finish this thread
+        if (shutdown_ || num_pollers_ > max_pollers_) done = true;
+        break;
+      case SHUTDOWN:
+        // If the thread manager is shutdown, finish this thread
+        done = true;
+        break;
+      case WORK_FOUND:
+        // If we got work and there are now insufficient pollers and there is
+        // quota available to create a new thread, start a new poller thread
+        bool resource_exhausted = false;
+        if (!shutdown_ && num_pollers_ < min_pollers_) {
+          if (grpc_resource_user_allocate_threads(resource_user_, 1)) {
+            // We can allocate a new poller thread
+            num_pollers_++;
+            num_threads_++;
+            if (num_threads_ > max_active_threads_sofar_) {
+              max_active_threads_sofar_ = num_threads_;
+            }
+            // Drop lock before spawning thread to avoid contention
+            lock.unlock();
+            new WorkerThread(this);
+          } else if (num_pollers_ > 0) {
+            // There is still at least some thread polling, so we can go on
+            // even though we are below the number of pollers that we would
+            // like to have (min_pollers_)
+            lock.unlock();
+          } else {
+            // There are no pollers to spare and we couldn't allocate
+            // a new thread, so resources are exhausted!
+            lock.unlock();
+            resource_exhausted = true;
+          }
+        } else {
+          // There are a sufficient number of pollers available so we can do
+          // the work and continue polling with our existing poller threads
+          lock.unlock();
+        }
+        // Lock is always released at this point - do the application work
+        // or return resource exhausted if there is new work but we couldn't
+        // get a thread in which to do it.
+        DoWork(tag, ok, !resource_exhausted);
+        // Take the lock again to check post conditions
+        lock.lock();
+        // If we're shutdown, we should finish at this point.
+        if (shutdown_) done = true;
+        break;
+    }
+    // If we decided to finish the thread, break out of the while loop
+    if (done) break;
+
+    // Otherwise go back to polling as long as it doesn't exceed max_pollers_
+    //
+    // **WARNING**:
+    // There is a possibility of threads thrashing here (i.e excessive thread
+    // shutdowns and creations than the ideal case). This happens if max_poller_
+    // count is small and the rate of incoming requests is also small. In such
+    // scenarios we can possibly configure max_pollers_ to a higher value and/or
+    // increase the cq timeout.
+    //
+    // However, not doing this check here and unconditionally incrementing
+    // num_pollers (and hoping that the system will eventually settle down) has
+    // far worse consequences i.e huge number of threads getting created to the
+    // point of thread-exhaustion. For example: if the incoming request rate is
+    // very high, all the polling threads will return very quickly from
+    // PollForWork() with WORK_FOUND. They all briefly decrement num_pollers_
+    // counter thereby possibly - and briefly - making it go below min_pollers;
+    // This will most likely result in the creation of a new poller since
+    // num_pollers_ dipped below min_pollers_.
+    //
+    // Now, If we didn't do the max_poller_ check here, all these threads will
+    // go back to doing PollForWork() and the whole cycle repeats (with a new
+    // thread being added in each cycle). Once the total number of threads in
+    // the system crosses a certain threshold (around ~1500), there is heavy
+    // contention on mutexes (the mu_ here or the mutexes in gRPC core like the
+    // pollset mutex) that makes DoWork() take longer to finish thereby causing
+    // new poller threads to be created even faster. This results in a thread
+    // avalanche.
+    if (num_pollers_ < max_pollers_) {
+      num_pollers_++;
+    } else {
+      break;
+    }
+  };
+
+  // This thread is exiting. Do some cleanup work i.e delete already completed
+  // worker threads
+  CleanupCompletedThreads();
+
+  // If we are here, either ThreadManager is shutting down or it already has
+  // enough threads.
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/thread_manager/thread_manager.h b/third_party/grpc/src/cpp/thread_manager/thread_manager.h
new file mode 100644
index 0000000..6f0bd17
--- /dev/null
+++ b/third_party/grpc/src/cpp/thread_manager/thread_manager.h
@@ -0,0 +1,178 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_THREAD_MANAGER_H
+#define GRPC_INTERNAL_CPP_THREAD_MANAGER_H
+
+#include <condition_variable>
+#include <list>
+#include <memory>
+#include <mutex>
+
+#include <grpcpp/support/config.h>
+
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/resource_quota.h"
+
+namespace grpc {
+
+class ThreadManager {
+ public:
+  explicit ThreadManager(const char* name, grpc_resource_quota* resource_quota,
+                         int min_pollers, int max_pollers);
+  virtual ~ThreadManager();
+
+  // Initializes and Starts the Rpc Manager threads
+  void Initialize();
+
+  // The return type of PollForWork() function
+  enum WorkStatus { WORK_FOUND, SHUTDOWN, TIMEOUT };
+
+  // "Polls" for new work.
+  // If the return value is WORK_FOUND:
+  //  - The implementaion of PollForWork() MAY set some opaque identifier to
+  //    (identify the work item found) via the '*tag' parameter
+  //  - The implementaion MUST set the value of 'ok' to 'true' or 'false'. A
+  //    value of 'false' indicates some implemenation specific error (that is
+  //    neither SHUTDOWN nor TIMEOUT)
+  //  - ThreadManager does not interpret the values of 'tag' and 'ok'
+  //  - ThreadManager WILL call DoWork() and pass '*tag' and 'ok' as input to
+  //    DoWork()
+  //
+  // If the return value is SHUTDOWN:,
+  //  - ThreadManager WILL NOT call DoWork() and terminates the thead
+  //
+  // If the return value is TIMEOUT:,
+  //  - ThreadManager WILL NOT call DoWork()
+  //  - ThreadManager MAY terminate the thread depending on the current number
+  //    of active poller threads and mix_pollers/max_pollers settings
+  //  - Also, the value of timeout is specific to the derived class
+  //    implementation
+  virtual WorkStatus PollForWork(void** tag, bool* ok) = 0;
+
+  // The implementation of DoWork() is supposed to perform the work found by
+  // PollForWork(). The tag and ok parameters are the same as returned by
+  // PollForWork(). The resources parameter indicates that the call actually
+  // has the resources available for performing the RPC's work. If it doesn't,
+  // the implementation should fail it appropriately.
+  //
+  // The implementation of DoWork() should also do any setup needed to ensure
+  // that the next call to PollForWork() (not necessarily by the current thread)
+  // actually finds some work
+  virtual void DoWork(void* tag, bool ok, bool resources) = 0;
+
+  // Mark the ThreadManager as shutdown and begin draining the work. This is a
+  // non-blocking call and the caller should call Wait(), a blocking call which
+  // returns only once the shutdown is complete
+  virtual void Shutdown();
+
+  // Has Shutdown() been called
+  bool IsShutdown();
+
+  // A blocking call that returns only after the ThreadManager has shutdown and
+  // all the threads have drained all the outstanding work
+  virtual void Wait();
+
+  // Max number of concurrent threads that were ever active in this thread
+  // manager so far. This is useful for debugging purposes (and in unit tests)
+  // to check if resource_quota is properly being enforced.
+  int GetMaxActiveThreadsSoFar();
+
+ private:
+  // Helper wrapper class around grpc_core::Thread. Takes a ThreadManager object
+  // and starts a new grpc_core::Thread to calls the Run() function.
+  //
+  // The Run() function calls ThreadManager::MainWorkLoop() function and once
+  // that completes, it marks the WorkerThread completed by calling
+  // ThreadManager::MarkAsCompleted()
+  //
+  // WHY IS THIS NEEDED?:
+  // When a thread terminates, some other thread *must* call Join() on that
+  // thread so that the resources are released. Having a WorkerThread wrapper
+  // will make this easier. Once Run() completes, each thread calls the
+  // following two functions:
+  //    ThreadManager::CleanupCompletedThreads()
+  //    ThreadManager::MarkAsCompleted()
+  //
+  //  - MarkAsCompleted() puts the WorkerThread object in the ThreadManger's
+  //    completed_threads_ list
+  //  - CleanupCompletedThreads() calls "Join()" on the threads that are already
+  //    in the completed_threads_ list  (since a thread cannot call Join() on
+  //    itself, it calls CleanupCompletedThreads() *before* calling
+  //    MarkAsCompleted())
+  //
+  // TODO(sreek): Consider creating the threads 'detached' so that Join() need
+  // not be called (and the need for this WorkerThread class is eliminated)
+  class WorkerThread {
+   public:
+    WorkerThread(ThreadManager* thd_mgr);
+    ~WorkerThread();
+
+   private:
+    // Calls thd_mgr_->MainWorkLoop() and once that completes, calls
+    // thd_mgr_>MarkAsCompleted(this) to mark the thread as completed
+    void Run();
+
+    ThreadManager* const thd_mgr_;
+    grpc_core::Thread thd_;
+  };
+
+  // The main funtion in ThreadManager
+  void MainWorkLoop();
+
+  void MarkAsCompleted(WorkerThread* thd);
+  void CleanupCompletedThreads();
+
+  // Protects shutdown_, num_pollers_, num_threads_ and
+  // max_active_threads_sofar_
+  std::mutex mu_;
+
+  bool shutdown_;
+  std::condition_variable shutdown_cv_;
+
+  // The resource user object to use when requesting quota to create threads
+  //
+  // Note: The user of this ThreadManager object must create grpc_resource_quota
+  // object (that contains the actual max thread quota) and a grpc_resource_user
+  // object through which quota is requested whenver new threads need to be
+  // created
+  grpc_resource_user* resource_user_;
+
+  // Number of threads doing polling
+  int num_pollers_;
+
+  // The minimum and maximum number of threads that should be doing polling
+  int min_pollers_;
+  int max_pollers_;
+
+  // The total number of threads currently active (includes threads includes the
+  // threads that are currently polling i.e num_pollers_)
+  int num_threads_;
+
+  // See GetMaxActiveThreadsSoFar()'s description.
+  // To be more specific, this variable tracks the max value num_threads_ was
+  // ever set so far
+  int max_active_threads_sofar_;
+
+  std::mutex list_mu_;
+  std::list<WorkerThread*> completed_threads_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_THREAD_MANAGER_H
diff --git a/third_party/grpc/src/cpp/util/byte_buffer_cc.cc b/third_party/grpc/src/cpp/util/byte_buffer_cc.cc
new file mode 100644
index 0000000..a7e1645
--- /dev/null
+++ b/third_party/grpc/src/cpp/util/byte_buffer_cc.cc
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/byte_buffer.h>
+#include <grpc/byte_buffer_reader.h>
+#include <grpcpp/impl/grpc_library.h>
+#include <grpcpp/support/byte_buffer.h>
+
+namespace grpc {
+
+static internal::GrpcLibraryInitializer g_gli_initializer;
+
+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");
+  }
+  grpc_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;
+}
+
+ByteBuffer::ByteBuffer(const ByteBuffer& buf) : buffer_(nullptr) {
+  operator=(buf);
+}
+
+ByteBuffer& ByteBuffer::operator=(const ByteBuffer& buf) {
+  if (this != &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/third_party/grpc/src/cpp/util/core_stats.cc b/third_party/grpc/src/cpp/util/core_stats.cc
new file mode 100644
index 0000000..edf0b1b
--- /dev/null
+++ b/third_party/grpc/src/cpp/util/core_stats.cc
@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 "src/cpp/util/core_stats.h"
+
+#include <grpc/support/log.h>
+
+using grpc::core::Bucket;
+using grpc::core::Histogram;
+using grpc::core::Metric;
+using grpc::core::Stats;
+
+namespace grpc {
+
+void CoreStatsToProto(const grpc_stats_data& core, Stats* proto) {
+  for (int i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
+    Metric* m = proto->add_metrics();
+    m->set_name(grpc_stats_counter_name[i]);
+    m->set_count(core.counters[i]);
+  }
+  for (int i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) {
+    Metric* m = proto->add_metrics();
+    m->set_name(grpc_stats_histogram_name[i]);
+    Histogram* h = m->mutable_histogram();
+    for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) {
+      Bucket* b = h->add_buckets();
+      b->set_start(grpc_stats_histo_bucket_boundaries[i][j]);
+      b->set_count(core.histograms[grpc_stats_histo_start[i] + j]);
+    }
+  }
+}
+
+void ProtoToCoreStats(const grpc::core::Stats& proto, grpc_stats_data* core) {
+  memset(core, 0, sizeof(*core));
+  for (const auto& m : proto.metrics()) {
+    switch (m.value_case()) {
+      case Metric::VALUE_NOT_SET:
+        break;
+      case Metric::kCount:
+        for (int i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) {
+          if (m.name() == grpc_stats_counter_name[i]) {
+            core->counters[i] = m.count();
+            break;
+          }
+        }
+        break;
+      case Metric::kHistogram:
+        for (int i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) {
+          if (m.name() == grpc_stats_histogram_name[i]) {
+            const auto& h = m.histogram();
+            bool valid = true;
+            if (grpc_stats_histo_buckets[i] != h.buckets_size()) valid = false;
+            for (int j = 0; valid && j < h.buckets_size(); j++) {
+              if (grpc_stats_histo_bucket_boundaries[i][j] !=
+                  h.buckets(j).start()) {
+                valid = false;
+              }
+            }
+            if (!valid) {
+              gpr_log(GPR_ERROR,
+                      "Found histogram %s but shape is different from proto",
+                      m.name().c_str());
+            }
+            for (int j = 0; valid && j < h.buckets_size(); j++) {
+              core->histograms[grpc_stats_histo_start[i] + j] =
+                  h.buckets(j).count();
+            }
+          }
+        }
+        break;
+    }
+  }
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/util/core_stats.h b/third_party/grpc/src/cpp/util/core_stats.h
new file mode 100644
index 0000000..6366d7d
--- /dev/null
+++ b/third_party/grpc/src/cpp/util/core_stats.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed 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.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CPP_UTIL_CORE_STATS_H
+#define GRPC_INTERNAL_CPP_UTIL_CORE_STATS_H
+
+#include "src/proto/grpc/core/stats.pb.h"
+
+#include "src/core/lib/debug/stats.h"
+
+namespace grpc {
+
+void CoreStatsToProto(const grpc_stats_data& core, grpc::core::Stats* proto);
+void ProtoToCoreStats(const grpc::core::Stats& proto, grpc_stats_data* core);
+
+}  // namespace grpc
+
+#endif  // GRPC_INTERNAL_CPP_UTIL_CORE_STATS_H
diff --git a/third_party/grpc/src/cpp/util/error_details.cc b/third_party/grpc/src/cpp/util/error_details.cc
new file mode 100644
index 0000000..42c887a
--- /dev/null
+++ b/third_party/grpc/src/cpp/util/error_details.cc
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/support/error_details.h>
+
+#include "src/proto/grpc/status/status.pb.h"
+
+namespace grpc {
+
+Status ExtractErrorDetails(const Status& from, ::google::rpc::Status* to) {
+  if (to == nullptr) {
+    return Status(StatusCode::FAILED_PRECONDITION, "");
+  }
+  if (!to->ParseFromString(from.error_details())) {
+    return Status(StatusCode::INVALID_ARGUMENT, "");
+  }
+  return Status::OK;
+}
+
+Status SetErrorDetails(const ::google::rpc::Status& from, Status* to) {
+  if (to == nullptr) {
+    return Status(StatusCode::FAILED_PRECONDITION, "");
+  }
+  StatusCode code = StatusCode::UNKNOWN;
+  if (from.code() >= StatusCode::OK &&
+      from.code() <= StatusCode::UNAUTHENTICATED) {
+    code = static_cast<StatusCode>(from.code());
+  }
+  *to = Status(code, from.message(), from.SerializeAsString());
+  return Status::OK;
+}
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/util/status.cc b/third_party/grpc/src/cpp/util/status.cc
new file mode 100644
index 0000000..93696d8
--- /dev/null
+++ b/third_party/grpc/src/cpp/util/status.cc
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/support/status.h>
+
+namespace grpc {
+
+const Status& Status::OK = Status();
+const Status& Status::CANCELLED = Status(StatusCode::CANCELLED, "");
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/util/string_ref.cc b/third_party/grpc/src/cpp/util/string_ref.cc
new file mode 100644
index 0000000..8b09a82
--- /dev/null
+++ b/third_party/grpc/src/cpp/util/string_ref.cc
@@ -0,0 +1,25 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpcpp/support/string_ref.h>
+
+namespace grpc {
+
+const size_t string_ref::npos = size_t(-1);
+
+}  // namespace grpc
diff --git a/third_party/grpc/src/cpp/util/time_cc.cc b/third_party/grpc/src/cpp/util/time_cc.cc
new file mode 100644
index 0000000..6c9c228
--- /dev/null
+++ b/third_party/grpc/src/cpp/util/time_cc.cc
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed 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 <grpc/support/time.h>
+#include <grpcpp/support/config.h>
+#include <grpcpp/support/time.h>
+
+using std::chrono::duration_cast;
+using std::chrono::high_resolution_clock;
+using std::chrono::nanoseconds;
+using std::chrono::seconds;
+using std::chrono::system_clock;
+
+namespace grpc {
+
+void Timepoint2Timespec(const system_clock::time_point& from,
+                        gpr_timespec* to) {
+  system_clock::duration deadline = from.time_since_epoch();
+  seconds secs = duration_cast<seconds>(deadline);
+  if (from == system_clock::time_point::max() ||
+      secs.count() >= gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec ||
+      secs.count() < 0) {
+    *to = gpr_inf_future(GPR_CLOCK_REALTIME);
+    return;
+  }
+  nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs);
+  to->tv_sec = static_cast<int64_t>(secs.count());
+  to->tv_nsec = static_cast<int32_t>(nsecs.count());
+  to->clock_type = GPR_CLOCK_REALTIME;
+}
+
+void TimepointHR2Timespec(const high_resolution_clock::time_point& from,
+                          gpr_timespec* to) {
+  high_resolution_clock::duration deadline = from.time_since_epoch();
+  seconds secs = duration_cast<seconds>(deadline);
+  if (from == high_resolution_clock::time_point::max() ||
+      secs.count() >= gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec ||
+      secs.count() < 0) {
+    *to = gpr_inf_future(GPR_CLOCK_REALTIME);
+    return;
+  }
+  nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs);
+  to->tv_sec = static_cast<int64_t>(secs.count());
+  to->tv_nsec = static_cast<int32_t>(nsecs.count());
+  to->clock_type = GPR_CLOCK_REALTIME;
+}
+
+system_clock::time_point Timespec2Timepoint(gpr_timespec t) {
+  if (gpr_time_cmp(t, gpr_inf_future(t.clock_type)) == 0) {
+    return system_clock::time_point::max();
+  }
+  t = gpr_convert_clock_type(t, GPR_CLOCK_REALTIME);
+  system_clock::time_point tp;
+  tp += duration_cast<system_clock::time_point::duration>(seconds(t.tv_sec));
+  tp +=
+      duration_cast<system_clock::time_point::duration>(nanoseconds(t.tv_nsec));
+  return tp;
+}
+
+}  // namespace grpc
diff --git a/third_party/nanopb/BUILD b/third_party/nanopb/BUILD
new file mode 100644
index 0000000..e0d16b6
--- /dev/null
+++ b/third_party/nanopb/BUILD
@@ -0,0 +1,15 @@
+licenses(["unencumbered"])
+
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+    visibility = ["//third_party:__pkg__"],
+)
+
+cc_library(
+    name = "nanopb",
+    srcs = glob(["*.c"]),
+    hdrs = glob(["*.h"]),
+    includes = ["."],
+    visibility = ["//visibility:public"],
+)
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.bazel.md b/third_party/nanopb/README.bazel.md
new file mode 100644
index 0000000..03ec26d
--- /dev/null
+++ b/third_party/nanopb/README.bazel.md
@@ -0,0 +1,9 @@
+To reproduce this tree, to:
+
+1. `git clone https://github.com/nanopb/nanopb.git` in a convenient directory
+2. `git checkout <tag>` (current is `0.3.6`, commithash  `eb0e73ca`)
+   Note that we want the same nanopb release used by gRPC upstream for the
+   version in third_party/grpc.
+3. `rm -fr <nanopb git tree>/tests`
+4. `cp -R <nanopb git tree>/* third_party/nanopb
+5. Update BUILD and README.bazel.md if necessary.
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..b4f657e
--- /dev/null
+++ b/third_party/nanopb/docs/concepts.rst
@@ -0,0 +1,390 @@
+======================
+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 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;
+                                                                                |    uint8_t bytes[40];
+                                                                                | } Person_data_t;
+                                                                                | Person_data_t data;
+=============================================================================== =======================
+
+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..2432857
--- /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.
+
+__ http://code.google.com/apis/protocolbuffers/
+
+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..7d27a51
--- /dev/null
+++ b/third_party/nanopb/docs/reference.rst
@@ -0,0 +1,765 @@
+=====================
+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* or *FT_IGNORE* to force a callback
+                               field, a dynamically allocated field, a static
+                               field or to completely ignore the 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_SVARINT     0x01  Integer, zigzag encoded.
+PB_LTYPE_FIXED32     0x02  32-bit integer or floating point.
+PB_LTYPE_FIXED64     0x03  64-bit integer or floating point.
+PB_LTYPE_BYTES       0x04  Structure with *size_t* field and byte array.
+PB_LTYPE_STRING      0x05  Null-terminated string.
+PB_LTYPE_SUBMESSAGE  0x06  Submessage structure.
+==================== ===== ================================================
+
+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, SVARINT           PB_WT_VARINT
+FIXED64                   PB_WT_64BIT  
+STRING, BYTES, SUBMESSAGE 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..46a5f38d
--- /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..2f1d92f
--- /dev/null
+++ b/third_party/nanopb/generator/nanopb_generator.py
@@ -0,0 +1,1576 @@
+#!/usr/bin/env python
+
+from __future__ import unicode_literals
+
+'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
+nanopb_version = "nanopb-0.3.6"
+
+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
+
+        # 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':
+                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':
+            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':
+                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:
+                    inner_init = '{0, {0}}'
+                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 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
+        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
+
+    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
+
+    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())
+
+        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..9b2f0fb
--- /dev/null
+++ b/third_party/nanopb/generator/proto/nanopb.proto
@@ -0,0 +1,97 @@
+// 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.
+}
+
+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..5e5d07b
--- /dev/null
+++ b/third_party/nanopb/pb.h
@@ -0,0 +1,556 @@
+/* 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.6
+
+/* 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
+
+/* Number of declared LTYPES */
+#define PB_LTYPES_COUNT 9
+#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}
+
+/* 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)
+
+#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 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}
+
+#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..78911e7
--- /dev/null
+++ b/third_party/nanopb/pb_decode.c
@@ -0,0 +1,1340 @@
+/* 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_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 (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..9f91c9d
--- /dev/null
+++ b/third_party/nanopb/pb_encode.c
@@ -0,0 +1,689 @@
+/* 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_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:
+            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 = (const pb_bytes_array_t*)src;
+    
+    if (src == NULL)
+    {
+        /* Threat 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; /* Threat 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/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/opencensus/opencensus-api-0.19.2.jar b/third_party/opencensus/opencensus-api-0.19.2.jar
new file mode 100644
index 0000000..c9427f9
--- /dev/null
+++ b/third_party/opencensus/opencensus-api-0.19.2.jar
Binary files differ
diff --git a/third_party/opencensus/opencensus-api-0.24.0.jar b/third_party/opencensus/opencensus-api-0.24.0.jar
deleted file mode 100644
index dcace99..0000000
--- a/third_party/opencensus/opencensus-api-0.24.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/opencensus/opencensus-contrib-grpc-metrics-0.19.2.jar b/third_party/opencensus/opencensus-contrib-grpc-metrics-0.19.2.jar
new file mode 100644
index 0000000..dfb7080
--- /dev/null
+++ b/third_party/opencensus/opencensus-contrib-grpc-metrics-0.19.2.jar
Binary files differ
diff --git a/third_party/opencensus/opencensus-contrib-grpc-metrics-0.24.0.jar b/third_party/opencensus/opencensus-contrib-grpc-metrics-0.24.0.jar
deleted file mode 100644
index db5ad3d..0000000
--- a/third_party/opencensus/opencensus-contrib-grpc-metrics-0.24.0.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/perfmark/LICENSE b/third_party/perfmark/LICENSE
deleted file mode 100644
index 261eeb9..0000000
--- a/third_party/perfmark/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed 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.
diff --git a/third_party/perfmark/README.md b/third_party/perfmark/README.md
deleted file mode 100644
index ad401c0..0000000
--- a/third_party/perfmark/README.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# PerfMark
-
-![PerfMark Hummingbird](doc/perfmark.png "PerfMark")
-
-PerfMark is a low-overhead, manually-instrumented, tracing library for Java.  Users can add the
-tracing function calls to their code to see how long each part takes.
-
-## Features
-
-*   **Very Low Overhead**:  When enabled, tracing a function call adds about **70ns**.   Tracing is
-    done in a lock-free, wait-free, thread local buffer, which avoids interfering with your 
-    latency-sensitive code.
-    
-*   **Dynamically Enabled**: PerfMark can be enabled or disabled at runtime.  When disabled, 
-    PerfMark has *zero overhead*, taking advantage of the JIT compiler to remove the tracing.
-    
-*   **Inter-thread Communication**: Existing profilers have difficulty expressing which thread 
-    wakes up and executes work on another thread.  PerfMark allows users to express this 
-    relationship explicitly, making for a clear picture of how code flows.
-
-*   **Small Library Size**: The PerfMark tracing API is only *5 KB* in size, and has minimal 
-    dependencies making it easy to include in other projects.  If no backend for recording the trace
-    is present, the library safely disables itself.
-
-*   **Multiple Java Versions**: The PerfMark API supports Java 6, making it easy to include on 
-    older or constrained environments.  Additionally, PerfMark includes optimized backends for 
-    Java 6, Java 7, and Java 9.  Each of these backends is automatically loaded at runtime 
-    (if possible) and uses advanced JVM features for maximum speed. 
-
-*   **Chrome Trace Viewer Integration**: PerfMark can export to the Chrome Trace Event Format, 
-    making it easy to view in your Web Browser.
-    
-## Usage
-
-To use PerfMark, add the following dependencies to your `build.gradle`:
-- `io.perfmark:perfmark-api:0.21.0`
-- `io.perfmark:perfmark-traceviewer:0.21.0`
-
-In your code, add the PerfMark tracing calls like so:
-
-```java
-Map<String, Header> parseHeaders(List<String> rawHeaders) {
-  PerfMark.startTask("Parse HTTP headers");
-  try {
-    Map<String, String> headers = new HashMap<>();
-    for (String rawHeader : rawHeaders) {
-      Header header = parseHeader(rawHeader);
-      headers.put(header.name(), header);
-    }
-    return headers;
-  } finally {
-    PerfMark.stopTask("Parse HTTP headers");
-  }
-}
-
-```
-
-PerfMark can also be used to record asynchronous work:
-
-```java
-Future<Response> buildResponse() {
-  PerfMark.startTask("Build Response");
-  final Link link = PerfMark.linkOut();
-  try {
-    return executor.submit(() -> {
-      PerfMark.startTask("Async Response");
-      PerfMark.linkIn(link);
-      try {
-        return new Response(/* ... */);
-      } finally {
-        PerfMark.stopTask("Async Response");
-      }
-    });
-  } finally {
-    PerfMark.stopTask("Build Response");
-  }
-}
-```
-
-To view the traces in your browser, generate the HTML:
-
-```java
-  PerfMark.setEnabled(true);
-  PerfMark.startTask("My Task");
-  } finally {
-    PerfMark.stopTask("My Task");
-  }
-  TraceEventViewer.writeTraceHtml();
-}
-```
-
-The output looks like:
-
-![PerfMark Hummingbird](doc/screenshot.png "PerfMark")
-
-
-
-## Versioning and API Stability
-
-PerfMark uses Semantic Versioning, and thus will not break existing APIs within a minor version 
-update.  PerfMark may need to disable some functionality, and thus may need to make some tracing 
-calls become No-ops.  In such cases, it will remain safe to call these functions being recorded.
-
-## Users
-
-PerfMark was designed originally for [gRPC](https://github.com/grpc/grpc-java).
diff --git a/third_party/perfmark/perfmark-api-0.19.0.jar b/third_party/perfmark/perfmark-api-0.19.0.jar
deleted file mode 100644
index 557a056..0000000
--- a/third_party/perfmark/perfmark-api-0.19.0.jar
+++ /dev/null
Binary files differ