Merge branch 'master' into pollset_set_test
diff --git a/.gitignore b/.gitignore
index 618c9ba..4098106 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,7 +32,7 @@
 # python compiled objects
 *.pyc
 
-#eclipse project files
+# eclipse project files
 .cproject
 .project
 .settings
@@ -111,3 +111,6 @@
 bazel-grpc
 bazel-out
 bazel-testlogs
+
+# Debug output
+gdb.txt
diff --git a/BUILD b/BUILD
index 5f0d50c..5053860 100644
--- a/BUILD
+++ b/BUILD
@@ -499,6 +499,8 @@
         "src/core/lib/slice/percent_encoding.c",
         "src/core/lib/slice/slice.c",
         "src/core/lib/slice/slice_buffer.c",
+        "src/core/lib/slice/slice_hash_table.c",
+        "src/core/lib/slice/slice_intern.c",
         "src/core/lib/slice/slice_string_helpers.c",
         "src/core/lib/surface/alarm.c",
         "src/core/lib/surface/api_trace.c",
@@ -520,12 +522,13 @@
         "src/core/lib/surface/version.c",
         "src/core/lib/transport/byte_stream.c",
         "src/core/lib/transport/connectivity_state.c",
-        "src/core/lib/transport/mdstr_hash_table.c",
+        "src/core/lib/transport/error_utils.c",
         "src/core/lib/transport/metadata.c",
         "src/core/lib/transport/metadata_batch.c",
         "src/core/lib/transport/pid_controller.c",
         "src/core/lib/transport/service_config.c",
         "src/core/lib/transport/static_metadata.c",
+        "src/core/lib/transport/status_conversion.c",
         "src/core/lib/transport/timeout_encoding.c",
         "src/core/lib/transport/transport.c",
         "src/core/lib/transport/transport_op_string.c",
@@ -555,6 +558,7 @@
         "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_epoll_linux.h",
         "src/core/lib/iomgr/ev_poll_posix.h",
         "src/core/lib/iomgr/ev_posix.h",
@@ -607,6 +611,7 @@
         "src/core/lib/json/json_reader.h",
         "src/core/lib/json/json_writer.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/surface/api_trace.h",
@@ -618,16 +623,19 @@
         "src/core/lib/surface/completion_queue.h",
         "src/core/lib/surface/event_string.h",
         "src/core/lib/surface/init.h",
+        "src/core/lib/surface/validate_metadata.h",
         "src/core/lib/surface/lame_client.h",
         "src/core/lib/surface/server.h",
         "src/core/lib/transport/byte_stream.h",
         "src/core/lib/transport/connectivity_state.h",
-        "src/core/lib/transport/mdstr_hash_table.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/timeout_encoding.h",
         "src/core/lib/transport/transport.h",
         "src/core/lib/transport/transport_impl.h",
@@ -886,7 +894,6 @@
         "src/core/ext/transport/chttp2/transport/huffsyms.c",
         "src/core/ext/transport/chttp2/transport/incoming_metadata.c",
         "src/core/ext/transport/chttp2/transport/parsing.c",
-        "src/core/ext/transport/chttp2/transport/status_conversion.c",
         "src/core/ext/transport/chttp2/transport/stream_lists.c",
         "src/core/ext/transport/chttp2/transport/stream_map.c",
         "src/core/ext/transport/chttp2/transport/varint.c",
@@ -906,11 +913,9 @@
         "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_errors.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/status_conversion.h",
         "src/core/ext/transport/chttp2/transport/stream_map.h",
         "src/core/ext/transport/chttp2/transport/varint.h",
     ],
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d083f0d..5ba5911 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -482,6 +482,8 @@
   src/core/lib/slice/percent_encoding.c
   src/core/lib/slice/slice.c
   src/core/lib/slice/slice_buffer.c
+  src/core/lib/slice/slice_hash_table.c
+  src/core/lib/slice/slice_intern.c
   src/core/lib/slice/slice_string_helpers.c
   src/core/lib/surface/alarm.c
   src/core/lib/surface/api_trace.c
@@ -503,12 +505,13 @@
   src/core/lib/surface/version.c
   src/core/lib/transport/byte_stream.c
   src/core/lib/transport/connectivity_state.c
-  src/core/lib/transport/mdstr_hash_table.c
+  src/core/lib/transport/error_utils.c
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
   src/core/lib/transport/pid_controller.c
   src/core/lib/transport/service_config.c
   src/core/lib/transport/static_metadata.c
+  src/core/lib/transport/status_conversion.c
   src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
   src/core/lib/transport/transport_op_string.c
@@ -529,7 +532,6 @@
   src/core/ext/transport/chttp2/transport/huffsyms.c
   src/core/ext/transport/chttp2/transport/incoming_metadata.c
   src/core/ext/transport/chttp2/transport/parsing.c
-  src/core/ext/transport/chttp2/transport/status_conversion.c
   src/core/ext/transport/chttp2/transport/stream_lists.c
   src/core/ext/transport/chttp2/transport/stream_map.c
   src/core/ext/transport/chttp2/transport/varint.c
@@ -772,6 +774,8 @@
   src/core/lib/slice/percent_encoding.c
   src/core/lib/slice/slice.c
   src/core/lib/slice/slice_buffer.c
+  src/core/lib/slice/slice_hash_table.c
+  src/core/lib/slice/slice_intern.c
   src/core/lib/slice/slice_string_helpers.c
   src/core/lib/surface/alarm.c
   src/core/lib/surface/api_trace.c
@@ -793,12 +797,13 @@
   src/core/lib/surface/version.c
   src/core/lib/transport/byte_stream.c
   src/core/lib/transport/connectivity_state.c
-  src/core/lib/transport/mdstr_hash_table.c
+  src/core/lib/transport/error_utils.c
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
   src/core/lib/transport/pid_controller.c
   src/core/lib/transport/service_config.c
   src/core/lib/transport/static_metadata.c
+  src/core/lib/transport/status_conversion.c
   src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
   src/core/lib/transport/transport_op_string.c
@@ -822,7 +827,6 @@
   src/core/ext/transport/chttp2/transport/huffsyms.c
   src/core/ext/transport/chttp2/transport/incoming_metadata.c
   src/core/ext/transport/chttp2/transport/parsing.c
-  src/core/ext/transport/chttp2/transport/status_conversion.c
   src/core/ext/transport/chttp2/transport/stream_lists.c
   src/core/ext/transport/chttp2/transport/stream_map.c
   src/core/ext/transport/chttp2/transport/varint.c
@@ -1052,6 +1056,8 @@
   src/core/lib/slice/percent_encoding.c
   src/core/lib/slice/slice.c
   src/core/lib/slice/slice_buffer.c
+  src/core/lib/slice/slice_hash_table.c
+  src/core/lib/slice/slice_intern.c
   src/core/lib/slice/slice_string_helpers.c
   src/core/lib/surface/alarm.c
   src/core/lib/surface/api_trace.c
@@ -1073,12 +1079,13 @@
   src/core/lib/surface/version.c
   src/core/lib/transport/byte_stream.c
   src/core/lib/transport/connectivity_state.c
-  src/core/lib/transport/mdstr_hash_table.c
+  src/core/lib/transport/error_utils.c
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
   src/core/lib/transport/pid_controller.c
   src/core/lib/transport/service_config.c
   src/core/lib/transport/static_metadata.c
+  src/core/lib/transport/status_conversion.c
   src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
   src/core/lib/transport/transport_op_string.c
@@ -1267,6 +1274,8 @@
   src/core/lib/slice/percent_encoding.c
   src/core/lib/slice/slice.c
   src/core/lib/slice/slice_buffer.c
+  src/core/lib/slice/slice_hash_table.c
+  src/core/lib/slice/slice_intern.c
   src/core/lib/slice/slice_string_helpers.c
   src/core/lib/surface/alarm.c
   src/core/lib/surface/api_trace.c
@@ -1288,12 +1297,13 @@
   src/core/lib/surface/version.c
   src/core/lib/transport/byte_stream.c
   src/core/lib/transport/connectivity_state.c
-  src/core/lib/transport/mdstr_hash_table.c
+  src/core/lib/transport/error_utils.c
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
   src/core/lib/transport/pid_controller.c
   src/core/lib/transport/service_config.c
   src/core/lib/transport/static_metadata.c
+  src/core/lib/transport/status_conversion.c
   src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
   src/core/lib/transport/transport_op_string.c
@@ -1315,7 +1325,6 @@
   src/core/ext/transport/chttp2/transport/huffsyms.c
   src/core/ext/transport/chttp2/transport/incoming_metadata.c
   src/core/ext/transport/chttp2/transport/parsing.c
-  src/core/ext/transport/chttp2/transport/status_conversion.c
   src/core/ext/transport/chttp2/transport/stream_lists.c
   src/core/ext/transport/chttp2/transport/stream_map.c
   src/core/ext/transport/chttp2/transport/varint.c
@@ -1614,6 +1623,7 @@
   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/status_helper.h
@@ -1711,7 +1721,6 @@
   src/core/ext/transport/chttp2/transport/huffsyms.c
   src/core/ext/transport/chttp2/transport/incoming_metadata.c
   src/core/ext/transport/chttp2/transport/parsing.c
-  src/core/ext/transport/chttp2/transport/status_conversion.c
   src/core/ext/transport/chttp2/transport/stream_lists.c
   src/core/ext/transport/chttp2/transport/stream_map.c
   src/core/ext/transport/chttp2/transport/varint.c
@@ -1800,6 +1809,8 @@
   src/core/lib/slice/percent_encoding.c
   src/core/lib/slice/slice.c
   src/core/lib/slice/slice_buffer.c
+  src/core/lib/slice/slice_hash_table.c
+  src/core/lib/slice/slice_intern.c
   src/core/lib/slice/slice_string_helpers.c
   src/core/lib/surface/alarm.c
   src/core/lib/surface/api_trace.c
@@ -1821,12 +1832,13 @@
   src/core/lib/surface/version.c
   src/core/lib/transport/byte_stream.c
   src/core/lib/transport/connectivity_state.c
-  src/core/lib/transport/mdstr_hash_table.c
+  src/core/lib/transport/error_utils.c
   src/core/lib/transport/metadata.c
   src/core/lib/transport/metadata_batch.c
   src/core/lib/transport/pid_controller.c
   src/core/lib/transport/service_config.c
   src/core/lib/transport/static_metadata.c
+  src/core/lib/transport/status_conversion.c
   src/core/lib/transport/timeout_encoding.c
   src/core/lib/transport/transport.c
   src/core/lib/transport/transport_op_string.c
@@ -1956,6 +1968,7 @@
   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/status_helper.h
@@ -2220,6 +2233,7 @@
   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/status_helper.h
@@ -2377,6 +2391,7 @@
   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/status_helper.h
@@ -4974,30 +4989,6 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
-add_executable(chttp2_status_conversion_test
-  test/core/transport/chttp2/status_conversion_test.c
-)
-
-target_include_directories(chttp2_status_conversion_test
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
-  PRIVATE ${BORINGSSL_ROOT_DIR}/include
-  PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${ZLIB_ROOT_DIR}
-  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
-  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
-)
-
-target_link_libraries(chttp2_status_conversion_test
-  grpc_test_util
-  grpc
-  gpr_test_util
-  gpr
-)
-
-endif (gRPC_BUILD_TESTS)
-if (gRPC_BUILD_TESTS)
-
 add_executable(chttp2_stream_map_test
   test/core/transport/chttp2/stream_map_test.c
 )
@@ -7292,6 +7283,30 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(status_conversion_test
+  test/core/transport/status_conversion_test.c
+)
+
+target_include_directories(status_conversion_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${BORINGSSL_ROOT_DIR}/include
+  PRIVATE ${PROTOBUF_ROOT_DIR}/src
+  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+)
+
+target_link_libraries(status_conversion_test
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(tcp_client_posix_test
   test/core/iomgr/tcp_client_posix_test.c
 )
diff --git a/Makefile b/Makefile
index 9caafd2..9d818e8 100644
--- a/Makefile
+++ b/Makefile
@@ -95,56 +95,6 @@
 CPPFLAGS_opt = -O2
 DEFINES_opt = NDEBUG
 
-VALID_CONFIG_asan-trace-cmp = 1
-REQUIRE_CUSTOM_LIBRARIES_asan-trace-cmp = 1
-CC_asan-trace-cmp = clang
-CXX_asan-trace-cmp = clang++
-LD_asan-trace-cmp = clang
-LDXX_asan-trace-cmp = clang++
-CPPFLAGS_asan-trace-cmp = -O0 -fsanitize-coverage=edge -fsanitize-coverage=trace-cmp -fsanitize=address -fno-omit-frame-pointer -Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS
-LDFLAGS_asan-trace-cmp = -fsanitize=address
-DEFINES_asan-trace-cmp += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
-
-VALID_CONFIG_dbg = 1
-CC_dbg = $(DEFAULT_CC)
-CXX_dbg = $(DEFAULT_CXX)
-LD_dbg = $(DEFAULT_CC)
-LDXX_dbg = $(DEFAULT_CXX)
-CPPFLAGS_dbg = -O0
-DEFINES_dbg = _DEBUG DEBUG
-
-VALID_CONFIG_easan = 1
-REQUIRE_CUSTOM_LIBRARIES_easan = 1
-CC_easan = clang
-CXX_easan = clang++
-LD_easan = clang
-LDXX_easan = clang++
-CPPFLAGS_easan = -O0 -fsanitize-coverage=edge -fsanitize=address -fno-omit-frame-pointer -Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS
-LDFLAGS_easan = -fsanitize=address
-DEFINES_easan = _DEBUG DEBUG GRPC_EXECUTION_CONTEXT_SANITIZER
-DEFINES_easan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
-
-VALID_CONFIG_asan = 1
-REQUIRE_CUSTOM_LIBRARIES_asan = 1
-CC_asan = clang
-CXX_asan = clang++
-LD_asan = clang
-LDXX_asan = clang++
-CPPFLAGS_asan = -O0 -fsanitize-coverage=edge -fsanitize=address -fno-omit-frame-pointer -Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS
-LDFLAGS_asan = -fsanitize=address
-DEFINES_asan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
-
-VALID_CONFIG_msan = 1
-REQUIRE_CUSTOM_LIBRARIES_msan = 1
-CC_msan = clang
-CXX_msan = clang++
-LD_msan = clang
-LDXX_msan = clang++
-CPPFLAGS_msan = -O0 -fsanitize-coverage=edge -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1 -Wno-unused-command-line-argument -fPIE -pie -DGPR_NO_DIRECT_SYSCALLS
-LDFLAGS_msan = -fsanitize=memory -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1 -fPIE -pie $(if $(JENKINS_BUILD),-Wl$(comma)-Ttext-segment=0x7e0000000000,)
-DEFINES_msan = NDEBUG
-DEFINES_msan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=4
-
 VALID_CONFIG_basicprof = 1
 CC_basicprof = $(DEFAULT_CC)
 CXX_basicprof = $(DEFAULT_CXX)
@@ -173,24 +123,60 @@
 LDFLAGS_asan-noleaks = -fsanitize=address
 DEFINES_asan-noleaks += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
 
-VALID_CONFIG_edbg = 1
-CC_edbg = $(DEFAULT_CC)
-CXX_edbg = $(DEFAULT_CXX)
-LD_edbg = $(DEFAULT_CC)
-LDXX_edbg = $(DEFAULT_CXX)
-CPPFLAGS_edbg = -O0
-DEFINES_edbg = _DEBUG DEBUG GRPC_EXECUTION_CONTEXT_SANITIZER
+VALID_CONFIG_asan-trace-cmp = 1
+REQUIRE_CUSTOM_LIBRARIES_asan-trace-cmp = 1
+CC_asan-trace-cmp = clang
+CXX_asan-trace-cmp = clang++
+LD_asan-trace-cmp = clang
+LDXX_asan-trace-cmp = clang++
+CPPFLAGS_asan-trace-cmp = -O0 -fsanitize-coverage=edge -fsanitize-coverage=trace-cmp -fsanitize=address -fno-omit-frame-pointer -Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS
+LDFLAGS_asan-trace-cmp = -fsanitize=address
+DEFINES_asan-trace-cmp += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
 
-VALID_CONFIG_ubsan = 1
-REQUIRE_CUSTOM_LIBRARIES_ubsan = 1
-CC_ubsan = clang
-CXX_ubsan = clang++
-LD_ubsan = clang
-LDXX_ubsan = clang++
-CPPFLAGS_ubsan = -O0 -fsanitize-coverage=edge -fsanitize=undefined,unsigned-integer-overflow -fno-omit-frame-pointer -Wno-unused-command-line-argument -Wvarargs
-LDFLAGS_ubsan = -fsanitize=undefined,unsigned-integer-overflow
-DEFINES_ubsan = NDEBUG
-DEFINES_ubsan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=1.5
+VALID_CONFIG_dbg = 1
+CC_dbg = $(DEFAULT_CC)
+CXX_dbg = $(DEFAULT_CXX)
+LD_dbg = $(DEFAULT_CC)
+LDXX_dbg = $(DEFAULT_CXX)
+CPPFLAGS_dbg = -O0
+DEFINES_dbg = _DEBUG DEBUG
+
+VALID_CONFIG_stapprof = 1
+CC_stapprof = $(DEFAULT_CC)
+CXX_stapprof = $(DEFAULT_CXX)
+LD_stapprof = $(DEFAULT_CC)
+LDXX_stapprof = $(DEFAULT_CXX)
+CPPFLAGS_stapprof = -O2 -DGRPC_STAP_PROFILER
+DEFINES_stapprof = NDEBUG
+
+VALID_CONFIG_gcov = 1
+CC_gcov = gcc
+CXX_gcov = g++
+LD_gcov = gcc
+LDXX_gcov = g++
+CPPFLAGS_gcov = -O0 -fprofile-arcs -ftest-coverage -Wno-return-type
+LDFLAGS_gcov = -fprofile-arcs -ftest-coverage -rdynamic
+DEFINES_gcov = _DEBUG DEBUG GPR_GCOV
+
+VALID_CONFIG_memcheck = 1
+CC_memcheck = $(DEFAULT_CC)
+CXX_memcheck = $(DEFAULT_CXX)
+LD_memcheck = $(DEFAULT_CC)
+LDXX_memcheck = $(DEFAULT_CXX)
+CPPFLAGS_memcheck = -O0
+LDFLAGS_memcheck = -rdynamic
+DEFINES_memcheck = _DEBUG DEBUG
+DEFINES_memcheck += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=10
+
+VALID_CONFIG_asan = 1
+REQUIRE_CUSTOM_LIBRARIES_asan = 1
+CC_asan = clang
+CXX_asan = clang++
+LD_asan = clang
+LDXX_asan = clang++
+CPPFLAGS_asan = -O0 -fsanitize-coverage=edge -fsanitize=address -fno-omit-frame-pointer -Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS
+LDFLAGS_asan = -fsanitize=address
+DEFINES_asan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=3
 
 VALID_CONFIG_tsan = 1
 REQUIRE_CUSTOM_LIBRARIES_tsan = 1
@@ -203,13 +189,27 @@
 DEFINES_tsan = GRPC_TSAN
 DEFINES_tsan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=5
 
-VALID_CONFIG_stapprof = 1
-CC_stapprof = $(DEFAULT_CC)
-CXX_stapprof = $(DEFAULT_CXX)
-LD_stapprof = $(DEFAULT_CC)
-LDXX_stapprof = $(DEFAULT_CXX)
-CPPFLAGS_stapprof = -O2 -DGRPC_STAP_PROFILER
-DEFINES_stapprof = NDEBUG
+VALID_CONFIG_ubsan = 1
+REQUIRE_CUSTOM_LIBRARIES_ubsan = 1
+CC_ubsan = clang
+CXX_ubsan = clang++
+LD_ubsan = clang
+LDXX_ubsan = clang++
+CPPFLAGS_ubsan = -O0 -fsanitize-coverage=edge -fsanitize=undefined,unsigned-integer-overflow -fno-omit-frame-pointer -Wno-unused-command-line-argument -Wvarargs
+LDFLAGS_ubsan = -fsanitize=undefined,unsigned-integer-overflow
+DEFINES_ubsan = NDEBUG
+DEFINES_ubsan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=1.5
+
+VALID_CONFIG_msan = 1
+REQUIRE_CUSTOM_LIBRARIES_msan = 1
+CC_msan = clang
+CXX_msan = clang++
+LD_msan = clang
+LDXX_msan = clang++
+CPPFLAGS_msan = -O0 -fsanitize-coverage=edge -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1 -Wno-unused-command-line-argument -fPIE -pie -DGPR_NO_DIRECT_SYSCALLS
+LDFLAGS_msan = -fsanitize=memory -DGTEST_HAS_TR1_TUPLE=0 -DGTEST_USE_OWN_TR1_TUPLE=1 -fPIE -pie $(if $(JENKINS_BUILD),-Wl$(comma)-Ttext-segment=0x7e0000000000,)
+DEFINES_msan = NDEBUG
+DEFINES_msan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=4
 
 VALID_CONFIG_mutrace = 1
 CC_mutrace = $(DEFAULT_CC)
@@ -220,36 +220,6 @@
 LDFLAGS_mutrace = -rdynamic
 DEFINES_mutrace = NDEBUG
 
-VALID_CONFIG_memcheck = 1
-CC_memcheck = $(DEFAULT_CC)
-CXX_memcheck = $(DEFAULT_CXX)
-LD_memcheck = $(DEFAULT_CC)
-LDXX_memcheck = $(DEFAULT_CXX)
-CPPFLAGS_memcheck = -O0
-LDFLAGS_memcheck = -rdynamic
-DEFINES_memcheck = _DEBUG DEBUG
-DEFINES_memcheck += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=10
-
-VALID_CONFIG_etsan = 1
-REQUIRE_CUSTOM_LIBRARIES_etsan = 1
-CC_etsan = clang
-CXX_etsan = clang++
-LD_etsan = clang
-LDXX_etsan = clang++
-CPPFLAGS_etsan = -O0 -fsanitize=thread -fno-omit-frame-pointer -Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS
-LDFLAGS_etsan = -fsanitize=thread
-DEFINES_etsan = _DEBUG DEBUG GRPC_EXECUTION_CONTEXT_SANITIZER
-DEFINES_etsan += GRPC_TEST_SLOWDOWN_BUILD_FACTOR=5
-
-VALID_CONFIG_gcov = 1
-CC_gcov = gcc
-CXX_gcov = g++
-LD_gcov = gcc
-LDXX_gcov = g++
-CPPFLAGS_gcov = -O0 -fprofile-arcs -ftest-coverage -Wno-return-type
-LDFLAGS_gcov = -fprofile-arcs -ftest-coverage -rdynamic
-DEFINES_gcov = _DEBUG DEBUG GPR_GCOV
-
 
 
 # General settings.
@@ -944,7 +914,6 @@
 census_trace_context_test: $(BINDIR)/$(CONFIG)/census_trace_context_test
 channel_create_test: $(BINDIR)/$(CONFIG)/channel_create_test
 chttp2_hpack_encoder_test: $(BINDIR)/$(CONFIG)/chttp2_hpack_encoder_test
-chttp2_status_conversion_test: $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test
 chttp2_stream_map_test: $(BINDIR)/$(CONFIG)/chttp2_stream_map_test
 chttp2_varint_test: $(BINDIR)/$(CONFIG)/chttp2_varint_test
 client_fuzzer: $(BINDIR)/$(CONFIG)/client_fuzzer
@@ -1053,6 +1022,7 @@
 sockaddr_utils_test: $(BINDIR)/$(CONFIG)/sockaddr_utils_test
 socket_utils_test: $(BINDIR)/$(CONFIG)/socket_utils_test
 ssl_server_fuzzer: $(BINDIR)/$(CONFIG)/ssl_server_fuzzer
+status_conversion_test: $(BINDIR)/$(CONFIG)/status_conversion_test
 tcp_client_posix_test: $(BINDIR)/$(CONFIG)/tcp_client_posix_test
 tcp_posix_test: $(BINDIR)/$(CONFIG)/tcp_posix_test
 tcp_server_posix_test: $(BINDIR)/$(CONFIG)/tcp_server_posix_test
@@ -1304,7 +1274,6 @@
   $(BINDIR)/$(CONFIG)/census_trace_context_test \
   $(BINDIR)/$(CONFIG)/channel_create_test \
   $(BINDIR)/$(CONFIG)/chttp2_hpack_encoder_test \
-  $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test \
   $(BINDIR)/$(CONFIG)/chttp2_stream_map_test \
   $(BINDIR)/$(CONFIG)/chttp2_varint_test \
   $(BINDIR)/$(CONFIG)/combiner_test \
@@ -1395,6 +1364,7 @@
   $(BINDIR)/$(CONFIG)/sockaddr_resolver_test \
   $(BINDIR)/$(CONFIG)/sockaddr_utils_test \
   $(BINDIR)/$(CONFIG)/socket_utils_test \
+  $(BINDIR)/$(CONFIG)/status_conversion_test \
   $(BINDIR)/$(CONFIG)/tcp_client_posix_test \
   $(BINDIR)/$(CONFIG)/tcp_posix_test \
   $(BINDIR)/$(CONFIG)/tcp_server_posix_test \
@@ -1658,8 +1628,6 @@
 	$(Q) $(BINDIR)/$(CONFIG)/channel_create_test || ( echo test channel_create_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_hpack_encoder_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_hpack_encoder_test || ( echo test chttp2_hpack_encoder_test failed ; exit 1 )
-	$(E) "[RUN]     Testing chttp2_status_conversion_test"
-	$(Q) $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test || ( echo test chttp2_status_conversion_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_stream_map_test"
 	$(Q) $(BINDIR)/$(CONFIG)/chttp2_stream_map_test || ( echo test chttp2_stream_map_test failed ; exit 1 )
 	$(E) "[RUN]     Testing chttp2_varint_test"
@@ -1818,6 +1786,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/sockaddr_utils_test || ( echo test sockaddr_utils_test failed ; exit 1 )
 	$(E) "[RUN]     Testing socket_utils_test"
 	$(Q) $(BINDIR)/$(CONFIG)/socket_utils_test || ( echo test socket_utils_test failed ; exit 1 )
+	$(E) "[RUN]     Testing status_conversion_test"
+	$(Q) $(BINDIR)/$(CONFIG)/status_conversion_test || ( echo test status_conversion_test failed ; exit 1 )
 	$(E) "[RUN]     Testing tcp_client_posix_test"
 	$(Q) $(BINDIR)/$(CONFIG)/tcp_client_posix_test || ( echo test tcp_client_posix_test failed ; exit 1 )
 	$(E) "[RUN]     Testing tcp_posix_test"
@@ -2746,6 +2716,8 @@
     src/core/lib/slice/percent_encoding.c \
     src/core/lib/slice/slice.c \
     src/core/lib/slice/slice_buffer.c \
+    src/core/lib/slice/slice_hash_table.c \
+    src/core/lib/slice/slice_intern.c \
     src/core/lib/slice/slice_string_helpers.c \
     src/core/lib/surface/alarm.c \
     src/core/lib/surface/api_trace.c \
@@ -2767,12 +2739,13 @@
     src/core/lib/surface/version.c \
     src/core/lib/transport/byte_stream.c \
     src/core/lib/transport/connectivity_state.c \
-    src/core/lib/transport/mdstr_hash_table.c \
+    src/core/lib/transport/error_utils.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/pid_controller.c \
     src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/status_conversion.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
@@ -2793,7 +2766,6 @@
     src/core/ext/transport/chttp2/transport/huffsyms.c \
     src/core/ext/transport/chttp2/transport/incoming_metadata.c \
     src/core/ext/transport/chttp2/transport/parsing.c \
-    src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
     src/core/ext/transport/chttp2/transport/varint.c \
@@ -3052,6 +3024,8 @@
     src/core/lib/slice/percent_encoding.c \
     src/core/lib/slice/slice.c \
     src/core/lib/slice/slice_buffer.c \
+    src/core/lib/slice/slice_hash_table.c \
+    src/core/lib/slice/slice_intern.c \
     src/core/lib/slice/slice_string_helpers.c \
     src/core/lib/surface/alarm.c \
     src/core/lib/surface/api_trace.c \
@@ -3073,12 +3047,13 @@
     src/core/lib/surface/version.c \
     src/core/lib/transport/byte_stream.c \
     src/core/lib/transport/connectivity_state.c \
-    src/core/lib/transport/mdstr_hash_table.c \
+    src/core/lib/transport/error_utils.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/pid_controller.c \
     src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/status_conversion.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
@@ -3102,7 +3077,6 @@
     src/core/ext/transport/chttp2/transport/huffsyms.c \
     src/core/ext/transport/chttp2/transport/incoming_metadata.c \
     src/core/ext/transport/chttp2/transport/parsing.c \
-    src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
     src/core/ext/transport/chttp2/transport/varint.c \
@@ -3348,6 +3322,8 @@
     src/core/lib/slice/percent_encoding.c \
     src/core/lib/slice/slice.c \
     src/core/lib/slice/slice_buffer.c \
+    src/core/lib/slice/slice_hash_table.c \
+    src/core/lib/slice/slice_intern.c \
     src/core/lib/slice/slice_string_helpers.c \
     src/core/lib/surface/alarm.c \
     src/core/lib/surface/api_trace.c \
@@ -3369,12 +3345,13 @@
     src/core/lib/surface/version.c \
     src/core/lib/transport/byte_stream.c \
     src/core/lib/transport/connectivity_state.c \
-    src/core/lib/transport/mdstr_hash_table.c \
+    src/core/lib/transport/error_utils.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/pid_controller.c \
     src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/status_conversion.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
@@ -3569,6 +3546,8 @@
     src/core/lib/slice/percent_encoding.c \
     src/core/lib/slice/slice.c \
     src/core/lib/slice/slice_buffer.c \
+    src/core/lib/slice/slice_hash_table.c \
+    src/core/lib/slice/slice_intern.c \
     src/core/lib/slice/slice_string_helpers.c \
     src/core/lib/surface/alarm.c \
     src/core/lib/surface/api_trace.c \
@@ -3590,12 +3569,13 @@
     src/core/lib/surface/version.c \
     src/core/lib/transport/byte_stream.c \
     src/core/lib/transport/connectivity_state.c \
-    src/core/lib/transport/mdstr_hash_table.c \
+    src/core/lib/transport/error_utils.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/pid_controller.c \
     src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/status_conversion.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
@@ -3617,7 +3597,6 @@
     src/core/ext/transport/chttp2/transport/huffsyms.c \
     src/core/ext/transport/chttp2/transport/incoming_metadata.c \
     src/core/ext/transport/chttp2/transport/parsing.c \
-    src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
     src/core/ext/transport/chttp2/transport/varint.c \
@@ -3925,6 +3904,7 @@
     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/status_helper.h \
@@ -4068,7 +4048,6 @@
     src/core/ext/transport/chttp2/transport/huffsyms.c \
     src/core/ext/transport/chttp2/transport/incoming_metadata.c \
     src/core/ext/transport/chttp2/transport/parsing.c \
-    src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
     src/core/ext/transport/chttp2/transport/varint.c \
@@ -4157,6 +4136,8 @@
     src/core/lib/slice/percent_encoding.c \
     src/core/lib/slice/slice.c \
     src/core/lib/slice/slice_buffer.c \
+    src/core/lib/slice/slice_hash_table.c \
+    src/core/lib/slice/slice_intern.c \
     src/core/lib/slice/slice_string_helpers.c \
     src/core/lib/surface/alarm.c \
     src/core/lib/surface/api_trace.c \
@@ -4178,12 +4159,13 @@
     src/core/lib/surface/version.c \
     src/core/lib/transport/byte_stream.c \
     src/core/lib/transport/connectivity_state.c \
-    src/core/lib/transport/mdstr_hash_table.c \
+    src/core/lib/transport/error_utils.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/pid_controller.c \
     src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/status_conversion.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
@@ -4294,6 +4276,7 @@
     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/status_helper.h \
@@ -4651,6 +4634,7 @@
     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/status_helper.h \
@@ -4831,6 +4815,7 @@
     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/status_helper.h \
@@ -8208,38 +8193,6 @@
 endif
 
 
-CHTTP2_STATUS_CONVERSION_TEST_SRC = \
-    test/core/transport/chttp2/status_conversion_test.c \
-
-CHTTP2_STATUS_CONVERSION_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_STATUS_CONVERSION_TEST_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/chttp2_status_conversion_test: openssl_dep_error
-
-else
-
-
-
-$(BINDIR)/$(CONFIG)/chttp2_status_conversion_test: $(CHTTP2_STATUS_CONVERSION_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(CHTTP2_STATUS_CONVERSION_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/chttp2_status_conversion_test
-
-endif
-
-$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/status_conversion_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_chttp2_status_conversion_test: $(CHTTP2_STATUS_CONVERSION_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(CHTTP2_STATUS_CONVERSION_TEST_OBJS:.o=.dep)
-endif
-endif
-
-
 CHTTP2_STREAM_MAP_TEST_SRC = \
     test/core/transport/chttp2/stream_map_test.c \
 
@@ -11696,6 +11649,38 @@
 endif
 
 
+STATUS_CONVERSION_TEST_SRC = \
+    test/core/transport/status_conversion_test.c \
+
+STATUS_CONVERSION_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(STATUS_CONVERSION_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/status_conversion_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/status_conversion_test: $(STATUS_CONVERSION_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(STATUS_CONVERSION_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/status_conversion_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/transport/status_conversion_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_status_conversion_test: $(STATUS_CONVERSION_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(STATUS_CONVERSION_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 TCP_CLIENT_POSIX_TEST_SRC = \
     test/core/iomgr/tcp_client_posix_test.c \
 
diff --git a/binding.gyp b/binding.gyp
index 9328a30..fe9c425 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -686,6 +686,8 @@
         'src/core/lib/slice/percent_encoding.c',
         'src/core/lib/slice/slice.c',
         'src/core/lib/slice/slice_buffer.c',
+        'src/core/lib/slice/slice_hash_table.c',
+        'src/core/lib/slice/slice_intern.c',
         'src/core/lib/slice/slice_string_helpers.c',
         'src/core/lib/surface/alarm.c',
         'src/core/lib/surface/api_trace.c',
@@ -707,12 +709,13 @@
         'src/core/lib/surface/version.c',
         'src/core/lib/transport/byte_stream.c',
         'src/core/lib/transport/connectivity_state.c',
-        'src/core/lib/transport/mdstr_hash_table.c',
+        'src/core/lib/transport/error_utils.c',
         'src/core/lib/transport/metadata.c',
         'src/core/lib/transport/metadata_batch.c',
         'src/core/lib/transport/pid_controller.c',
         'src/core/lib/transport/service_config.c',
         'src/core/lib/transport/static_metadata.c',
+        'src/core/lib/transport/status_conversion.c',
         'src/core/lib/transport/timeout_encoding.c',
         'src/core/lib/transport/transport.c',
         'src/core/lib/transport/transport_op_string.c',
@@ -733,7 +736,6 @@
         'src/core/ext/transport/chttp2/transport/huffsyms.c',
         'src/core/ext/transport/chttp2/transport/incoming_metadata.c',
         'src/core/ext/transport/chttp2/transport/parsing.c',
-        'src/core/ext/transport/chttp2/transport/status_conversion.c',
         'src/core/ext/transport/chttp2/transport/stream_lists.c',
         'src/core/ext/transport/chttp2/transport/stream_map.c',
         'src/core/ext/transport/chttp2/transport/varint.c',
@@ -884,6 +886,7 @@
         "src/node/ext/node_grpc.cc",
         "src/node/ext/server.cc",
         "src/node/ext/server_credentials.cc",
+        "src/node/ext/slice.cc",
         "src/node/ext/timeval.cc",
       ],
       "dependencies": [
diff --git a/build.yaml b/build.yaml
index 5ccbd0a..28d823b 100644
--- a/build.yaml
+++ b/build.yaml
@@ -188,6 +188,7 @@
   - 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_epoll_linux.h
   - src/core/lib/iomgr/ev_poll_posix.h
   - src/core/lib/iomgr/ev_posix.h
@@ -240,6 +241,7 @@
   - src/core/lib/json/json_reader.h
   - src/core/lib/json/json_writer.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/surface/api_trace.h
@@ -253,14 +255,17 @@
   - 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/byte_stream.h
   - src/core/lib/transport/connectivity_state.h
-  - src/core/lib/transport/mdstr_hash_table.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/timeout_encoding.h
   - src/core/lib/transport/transport.h
   - src/core/lib/transport/transport_impl.h
@@ -349,6 +354,8 @@
   - src/core/lib/slice/percent_encoding.c
   - src/core/lib/slice/slice.c
   - src/core/lib/slice/slice_buffer.c
+  - src/core/lib/slice/slice_hash_table.c
+  - src/core/lib/slice/slice_intern.c
   - src/core/lib/slice/slice_string_helpers.c
   - src/core/lib/surface/alarm.c
   - src/core/lib/surface/api_trace.c
@@ -370,12 +377,13 @@
   - src/core/lib/surface/version.c
   - src/core/lib/transport/byte_stream.c
   - src/core/lib/transport/connectivity_state.c
-  - src/core/lib/transport/mdstr_hash_table.c
+  - src/core/lib/transport/error_utils.c
   - src/core/lib/transport/metadata.c
   - src/core/lib/transport/metadata_batch.c
   - src/core/lib/transport/pid_controller.c
   - src/core/lib/transport/service_config.c
   - src/core/lib/transport/static_metadata.c
+  - src/core/lib/transport/status_conversion.c
   - src/core/lib/transport/timeout_encoding.c
   - src/core/lib/transport/transport.c
   - src/core/lib/transport/transport_op_string.c
@@ -594,11 +602,9 @@
   - 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_errors.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/status_conversion.h
   - src/core/ext/transport/chttp2/transport/stream_map.h
   - src/core/ext/transport/chttp2/transport/varint.h
   src:
@@ -618,7 +624,6 @@
   - src/core/ext/transport/chttp2/transport/huffsyms.c
   - src/core/ext/transport/chttp2/transport/incoming_metadata.c
   - src/core/ext/transport/chttp2/transport/parsing.c
-  - src/core/ext/transport/chttp2/transport/status_conversion.c
   - src/core/ext/transport/chttp2/transport/stream_lists.c
   - src/core/ext/transport/chttp2/transport/stream_map.c
   - src/core/ext/transport/chttp2/transport/varint.c
@@ -828,6 +833,7 @@
   - 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/status_helper.h
@@ -1499,16 +1505,6 @@
   - grpc
   - gpr_test_util
   - gpr
-- name: chttp2_status_conversion_test
-  build: test
-  language: c
-  src:
-  - test/core/transport/chttp2/status_conversion_test.c
-  deps:
-  - grpc_test_util
-  - grpc
-  - gpr_test_util
-  - gpr
 - name: chttp2_stream_map_test
   build: test
   language: c
@@ -2691,6 +2687,16 @@
   corpus_dirs:
   - test/core/security/corpus/ssl_server_corpus
   maxlen: 2048
+- name: status_conversion_test
+  build: test
+  language: c
+  src:
+  - test/core/transport/status_conversion_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: tcp_client_posix_test
   cpu_cost: 0.5
   build: test
@@ -3782,36 +3788,6 @@
   dbg:
     CPPFLAGS: -O0
     DEFINES: _DEBUG DEBUG
-  easan:
-    CC: clang
-    CPPFLAGS: -O0 -fsanitize-coverage=edge -fsanitize=address -fno-omit-frame-pointer
-      -Wno-unused-command-line-argument -DGPR_NO_DIRECT_SYSCALLS
-    CXX: clang++
-    DEFINES: _DEBUG DEBUG GRPC_EXECUTION_CONTEXT_SANITIZER
-    LD: clang
-    LDFLAGS: -fsanitize=address
-    LDXX: clang++
-    compile_the_world: true
-    test_environ:
-      ASAN_OPTIONS: detect_leaks=1:color=always
-      LSAN_OPTIONS: suppressions=tools/lsan_suppressions.txt:report_objects=1
-    timeout_multiplier: 3
-  edbg:
-    CPPFLAGS: -O0
-    DEFINES: _DEBUG DEBUG GRPC_EXECUTION_CONTEXT_SANITIZER
-  etsan:
-    CC: clang
-    CPPFLAGS: -O0 -fsanitize=thread -fno-omit-frame-pointer -Wno-unused-command-line-argument
-      -DGPR_NO_DIRECT_SYSCALLS
-    CXX: clang++
-    DEFINES: _DEBUG DEBUG GRPC_EXECUTION_CONTEXT_SANITIZER
-    LD: clang
-    LDFLAGS: -fsanitize=thread
-    LDXX: clang++
-    compile_the_world: true
-    test_environ:
-      TSAN_OPTIONS: suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1
-    timeout_multiplier: 5
   gcov:
     CC: gcc
     CPPFLAGS: -O0 -fprofile-arcs -ftest-coverage -Wno-return-type
@@ -3910,6 +3886,7 @@
   - src/node/ext/completion_queue.h
   - src/node/ext/server.h
   - src/node/ext/server_credentials.h
+  - src/node/ext/slice.h
   - src/node/ext/timeval.h
   js:
   - src/node/index.js
@@ -3931,6 +3908,7 @@
   - src/node/ext/node_grpc.cc
   - src/node/ext/server.cc
   - src/node/ext/server_credentials.cc
+  - src/node/ext/slice.cc
   - src/node/ext/timeval.cc
 openssl_fallback:
   base_uri: https://openssl.org/source/old/1.0.2/
diff --git a/config.m4 b/config.m4
index 9b2d819..2a3ba55 100644
--- a/config.m4
+++ b/config.m4
@@ -165,6 +165,8 @@
     src/core/lib/slice/percent_encoding.c \
     src/core/lib/slice/slice.c \
     src/core/lib/slice/slice_buffer.c \
+    src/core/lib/slice/slice_hash_table.c \
+    src/core/lib/slice/slice_intern.c \
     src/core/lib/slice/slice_string_helpers.c \
     src/core/lib/surface/alarm.c \
     src/core/lib/surface/api_trace.c \
@@ -186,12 +188,13 @@
     src/core/lib/surface/version.c \
     src/core/lib/transport/byte_stream.c \
     src/core/lib/transport/connectivity_state.c \
-    src/core/lib/transport/mdstr_hash_table.c \
+    src/core/lib/transport/error_utils.c \
     src/core/lib/transport/metadata.c \
     src/core/lib/transport/metadata_batch.c \
     src/core/lib/transport/pid_controller.c \
     src/core/lib/transport/service_config.c \
     src/core/lib/transport/static_metadata.c \
+    src/core/lib/transport/status_conversion.c \
     src/core/lib/transport/timeout_encoding.c \
     src/core/lib/transport/transport.c \
     src/core/lib/transport/transport_op_string.c \
@@ -212,7 +215,6 @@
     src/core/ext/transport/chttp2/transport/huffsyms.c \
     src/core/ext/transport/chttp2/transport/incoming_metadata.c \
     src/core/ext/transport/chttp2/transport/parsing.c \
-    src/core/ext/transport/chttp2/transport/status_conversion.c \
     src/core/ext/transport/chttp2/transport/stream_lists.c \
     src/core/ext/transport/chttp2/transport/stream_map.c \
     src/core/ext/transport/chttp2/transport/varint.c \
diff --git a/doc/service_config.md b/doc/service_config.md
index 7318b69..2dabb83 100644
--- a/doc/service_config.md
+++ b/doc/service_config.md
@@ -95,10 +95,7 @@
       # will not be sent and the client will see an error.
       # Note that 0 is a valid value, meaning that the request message must
       # be empty.
-      #
-      # The format of the value is that of the 'uint64' type defined here:
-      # https://developers.google.com/protocol-buffers/docs/proto3#json
-      'maxRequestMessageBytes': string,
+      'maxRequestMessageBytes': number,
 
       # The maximum allowed payload size for an individual response or object
       # in a stream (server->client) in bytes. The size which is measured is
@@ -114,10 +111,7 @@
       # will not be sent, and the client will see an error.
       # Note that 0 is a valid value, meaning that the response message must
       # be empty.
-      #
-      # The format of the value is that of the 'uint64' type defined here:
-      # https://developers.google.com/protocol-buffers/docs/proto3#json
-      'maxResponseMessageBytes': string
+      'maxResponseMessageBytes': number
     }
   ]
 }
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index d9d0f10..39e0c34 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -274,6 +274,7 @@
                       '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_epoll_linux.h',
                       'src/core/lib/iomgr/ev_poll_posix.h',
                       'src/core/lib/iomgr/ev_posix.h',
@@ -326,6 +327,7 @@
                       'src/core/lib/json/json_reader.h',
                       'src/core/lib/json/json_writer.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/surface/api_trace.h',
@@ -339,14 +341,17 @@
                       '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/byte_stream.h',
                       'src/core/lib/transport/connectivity_state.h',
-                      'src/core/lib/transport/mdstr_hash_table.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/timeout_encoding.h',
                       'src/core/lib/transport/transport.h',
                       'src/core/lib/transport/transport_impl.h',
@@ -363,11 +368,9 @@
                       '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_errors.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/status_conversion.h',
                       'src/core/ext/transport/chttp2/transport/stream_map.h',
                       'src/core/ext/transport/chttp2/transport/varint.h',
                       'src/core/ext/transport/chttp2/alpn/alpn.h',
@@ -520,6 +523,8 @@
                       'src/core/lib/slice/percent_encoding.c',
                       'src/core/lib/slice/slice.c',
                       'src/core/lib/slice/slice_buffer.c',
+                      'src/core/lib/slice/slice_hash_table.c',
+                      'src/core/lib/slice/slice_intern.c',
                       'src/core/lib/slice/slice_string_helpers.c',
                       'src/core/lib/surface/alarm.c',
                       'src/core/lib/surface/api_trace.c',
@@ -541,12 +546,13 @@
                       'src/core/lib/surface/version.c',
                       'src/core/lib/transport/byte_stream.c',
                       'src/core/lib/transport/connectivity_state.c',
-                      'src/core/lib/transport/mdstr_hash_table.c',
+                      'src/core/lib/transport/error_utils.c',
                       'src/core/lib/transport/metadata.c',
                       'src/core/lib/transport/metadata_batch.c',
                       'src/core/lib/transport/pid_controller.c',
                       'src/core/lib/transport/service_config.c',
                       'src/core/lib/transport/static_metadata.c',
+                      'src/core/lib/transport/status_conversion.c',
                       'src/core/lib/transport/timeout_encoding.c',
                       'src/core/lib/transport/transport.c',
                       'src/core/lib/transport/transport_op_string.c',
@@ -567,7 +573,6 @@
                       'src/core/ext/transport/chttp2/transport/huffsyms.c',
                       'src/core/ext/transport/chttp2/transport/incoming_metadata.c',
                       'src/core/ext/transport/chttp2/transport/parsing.c',
-                      'src/core/ext/transport/chttp2/transport/status_conversion.c',
                       'src/core/ext/transport/chttp2/transport/stream_lists.c',
                       'src/core/ext/transport/chttp2/transport/stream_map.c',
                       'src/core/ext/transport/chttp2/transport/varint.c',
@@ -692,6 +697,7 @@
                               '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_epoll_linux.h',
                               'src/core/lib/iomgr/ev_poll_posix.h',
                               'src/core/lib/iomgr/ev_posix.h',
@@ -744,6 +750,7 @@
                               'src/core/lib/json/json_reader.h',
                               'src/core/lib/json/json_writer.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/surface/api_trace.h',
@@ -757,14 +764,17 @@
                               '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/byte_stream.h',
                               'src/core/lib/transport/connectivity_state.h',
-                              'src/core/lib/transport/mdstr_hash_table.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/timeout_encoding.h',
                               'src/core/lib/transport/transport.h',
                               'src/core/lib/transport/transport_impl.h',
@@ -781,11 +791,9 @@
                               '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_errors.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/status_conversion.h',
                               'src/core/ext/transport/chttp2/transport/stream_map.h',
                               'src/core/ext/transport/chttp2/transport/varint.h',
                               'src/core/ext/transport/chttp2/alpn/alpn.h',
diff --git a/grpc.def b/grpc.def
index ff856e7..5ef59ab 100644
--- a/grpc.def
+++ b/grpc.def
@@ -141,17 +141,30 @@
     grpc_slice_new_with_user_data
     grpc_slice_new_with_len
     grpc_slice_malloc
+    grpc_slice_intern
     grpc_slice_from_copied_string
     grpc_slice_from_copied_buffer
     grpc_slice_from_static_string
+    grpc_slice_from_static_buffer
     grpc_slice_sub
     grpc_slice_sub_no_ref
     grpc_slice_split_tail
     grpc_slice_split_head
-    gpr_empty_slice
+    grpc_empty_slice
+    grpc_slice_default_hash_impl
+    grpc_slice_default_eq_impl
+    grpc_slice_eq
     grpc_slice_cmp
     grpc_slice_str_cmp
+    grpc_slice_buf_cmp
+    grpc_slice_buf_start_eq
+    grpc_slice_rchr
+    grpc_slice_chr
+    grpc_slice_slice
+    grpc_slice_hash
     grpc_slice_is_equivalent
+    grpc_slice_dup
+    grpc_slice_to_c_string
     grpc_slice_buffer_init
     grpc_slice_buffer_destroy
     grpc_slice_buffer_add
diff --git a/grpc.gemspec b/grpc.gemspec
index 2d84682..874af6b 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -191,6 +191,7 @@
   s.files += %w( src/core/lib/iomgr/endpoint.h )
   s.files += %w( src/core/lib/iomgr/endpoint_pair.h )
   s.files += %w( src/core/lib/iomgr/error.h )
+  s.files += %w( src/core/lib/iomgr/error_internal.h )
   s.files += %w( src/core/lib/iomgr/ev_epoll_linux.h )
   s.files += %w( src/core/lib/iomgr/ev_poll_posix.h )
   s.files += %w( src/core/lib/iomgr/ev_posix.h )
@@ -243,6 +244,7 @@
   s.files += %w( src/core/lib/json/json_reader.h )
   s.files += %w( src/core/lib/json/json_writer.h )
   s.files += %w( src/core/lib/slice/percent_encoding.h )
+  s.files += %w( src/core/lib/slice/slice_hash_table.h )
   s.files += %w( src/core/lib/slice/slice_internal.h )
   s.files += %w( src/core/lib/slice/slice_string_helpers.h )
   s.files += %w( src/core/lib/surface/api_trace.h )
@@ -256,14 +258,17 @@
   s.files += %w( src/core/lib/surface/init.h )
   s.files += %w( src/core/lib/surface/lame_client.h )
   s.files += %w( src/core/lib/surface/server.h )
+  s.files += %w( src/core/lib/surface/validate_metadata.h )
   s.files += %w( src/core/lib/transport/byte_stream.h )
   s.files += %w( src/core/lib/transport/connectivity_state.h )
-  s.files += %w( src/core/lib/transport/mdstr_hash_table.h )
+  s.files += %w( src/core/lib/transport/error_utils.h )
+  s.files += %w( src/core/lib/transport/http2_errors.h )
   s.files += %w( src/core/lib/transport/metadata.h )
   s.files += %w( src/core/lib/transport/metadata_batch.h )
   s.files += %w( src/core/lib/transport/pid_controller.h )
   s.files += %w( src/core/lib/transport/service_config.h )
   s.files += %w( src/core/lib/transport/static_metadata.h )
+  s.files += %w( src/core/lib/transport/status_conversion.h )
   s.files += %w( src/core/lib/transport/timeout_encoding.h )
   s.files += %w( src/core/lib/transport/transport.h )
   s.files += %w( src/core/lib/transport/transport_impl.h )
@@ -280,11 +285,9 @@
   s.files += %w( src/core/ext/transport/chttp2/transport/hpack_encoder.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/hpack_parser.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/hpack_table.h )
-  s.files += %w( src/core/ext/transport/chttp2/transport/http2_errors.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/huffsyms.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/incoming_metadata.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/internal.h )
-  s.files += %w( src/core/ext/transport/chttp2/transport/status_conversion.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/stream_map.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/varint.h )
   s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.h )
@@ -437,6 +440,8 @@
   s.files += %w( src/core/lib/slice/percent_encoding.c )
   s.files += %w( src/core/lib/slice/slice.c )
   s.files += %w( src/core/lib/slice/slice_buffer.c )
+  s.files += %w( src/core/lib/slice/slice_hash_table.c )
+  s.files += %w( src/core/lib/slice/slice_intern.c )
   s.files += %w( src/core/lib/slice/slice_string_helpers.c )
   s.files += %w( src/core/lib/surface/alarm.c )
   s.files += %w( src/core/lib/surface/api_trace.c )
@@ -458,12 +463,13 @@
   s.files += %w( src/core/lib/surface/version.c )
   s.files += %w( src/core/lib/transport/byte_stream.c )
   s.files += %w( src/core/lib/transport/connectivity_state.c )
-  s.files += %w( src/core/lib/transport/mdstr_hash_table.c )
+  s.files += %w( src/core/lib/transport/error_utils.c )
   s.files += %w( src/core/lib/transport/metadata.c )
   s.files += %w( src/core/lib/transport/metadata_batch.c )
   s.files += %w( src/core/lib/transport/pid_controller.c )
   s.files += %w( src/core/lib/transport/service_config.c )
   s.files += %w( src/core/lib/transport/static_metadata.c )
+  s.files += %w( src/core/lib/transport/status_conversion.c )
   s.files += %w( src/core/lib/transport/timeout_encoding.c )
   s.files += %w( src/core/lib/transport/transport.c )
   s.files += %w( src/core/lib/transport/transport_op_string.c )
@@ -484,7 +490,6 @@
   s.files += %w( src/core/ext/transport/chttp2/transport/huffsyms.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/incoming_metadata.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/parsing.c )
-  s.files += %w( src/core/ext/transport/chttp2/transport/status_conversion.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/stream_lists.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/stream_map.c )
   s.files += %w( src/core/ext/transport/chttp2/transport/varint.c )
diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h
index 6ab0061..a17cdf9 100644
--- a/include/grpc++/impl/codegen/call.h
+++ b/include/grpc++/impl/codegen/call.h
@@ -45,6 +45,7 @@
 #include <grpc++/impl/codegen/config.h>
 #include <grpc++/impl/codegen/core_codegen_interface.h>
 #include <grpc++/impl/codegen/serialization_traits.h>
+#include <grpc++/impl/codegen/slice.h>
 #include <grpc++/impl/codegen/status.h>
 #include <grpc++/impl/codegen/status_helper.h>
 #include <grpc++/impl/codegen/string_ref.h>
@@ -62,19 +63,6 @@
 class CompletionQueue;
 extern CoreCodegenInterface* g_core_codegen_interface;
 
-inline void FillMetadataMap(
-    grpc_metadata_array* arr,
-    std::multimap<grpc::string_ref, grpc::string_ref>* metadata) {
-  for (size_t i = 0; i < arr->count; i++) {
-    // TODO(yangg) handle duplicates?
-    metadata->insert(std::pair<grpc::string_ref, grpc::string_ref>(
-        arr->metadata[i].key, grpc::string_ref(arr->metadata[i].value,
-                                               arr->metadata[i].value_length)));
-  }
-  g_core_codegen_interface->grpc_metadata_array_destroy(arr);
-  g_core_codegen_interface->grpc_metadata_array_init(arr);
-}
-
 // 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(
@@ -87,9 +75,8 @@
           metadata.size() * sizeof(grpc_metadata)));
   size_t i = 0;
   for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) {
-    metadata_array[i].key = iter->first.c_str();
-    metadata_array[i].value = iter->second.c_str();
-    metadata_array[i].value_length = iter->second.size();
+    metadata_array[i].key = SliceReferencingString(iter->first);
+    metadata_array[i].value = SliceReferencingString(iter->second);
   }
   return metadata_array;
 }
@@ -175,7 +162,7 @@
 class CallNoOp {
  protected:
   void AddOp(grpc_op* ops, size_t* nops) {}
-  void FinishOp(bool* status, int max_receive_message_size) {}
+  void FinishOp(bool* status) {}
 };
 
 class CallOpSendInitialMetadata {
@@ -213,7 +200,7 @@
     op->data.send_initial_metadata.maybe_compression_level.level =
         maybe_compression_level_.level;
   }
-  void FinishOp(bool* status, int max_receive_message_size) {
+  void FinishOp(bool* status) {
     if (!send_) return;
     g_core_codegen_interface->gpr_free(initial_metadata_);
     send_ = false;
@@ -249,11 +236,11 @@
     op->op = GRPC_OP_SEND_MESSAGE;
     op->flags = write_options_.flags();
     op->reserved = NULL;
-    op->data.send_message = send_buf_;
+    op->data.send_message.send_message = send_buf_;
     // Flags are per-message: clear them after use.
     write_options_.Clear();
   }
-  void FinishOp(bool* status, int max_receive_message_size) {
+  void FinishOp(bool* status) {
     if (own_buf_) g_core_codegen_interface->grpc_byte_buffer_destroy(send_buf_);
     send_buf_ = nullptr;
   }
@@ -298,17 +285,15 @@
     op->op = GRPC_OP_RECV_MESSAGE;
     op->flags = 0;
     op->reserved = NULL;
-    op->data.recv_message = &recv_buf_;
+    op->data.recv_message.recv_message = &recv_buf_;
   }
 
-  void FinishOp(bool* status, int max_receive_message_size) {
+  void FinishOp(bool* status) {
     if (message_ == nullptr) return;
     if (recv_buf_) {
       if (*status) {
         got_message = *status =
-            SerializationTraits<R>::Deserialize(recv_buf_, message_,
-                                                max_receive_message_size)
-                .ok();
+            SerializationTraits<R>::Deserialize(recv_buf_, message_).ok();
       } else {
         got_message = false;
         g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@@ -331,8 +316,7 @@
 namespace CallOpGenericRecvMessageHelper {
 class DeserializeFunc {
  public:
-  virtual Status Deserialize(grpc_byte_buffer* buf,
-                             int max_receive_message_size) = 0;
+  virtual Status Deserialize(grpc_byte_buffer* buf) = 0;
   virtual ~DeserializeFunc() {}
 };
 
@@ -340,10 +324,8 @@
 class DeserializeFuncType final : public DeserializeFunc {
  public:
   DeserializeFuncType(R* message) : message_(message) {}
-  Status Deserialize(grpc_byte_buffer* buf,
-                     int max_receive_message_size) override {
-    return SerializationTraits<R>::Deserialize(buf, message_,
-                                               max_receive_message_size);
+  Status Deserialize(grpc_byte_buffer* buf) override {
+    return SerializationTraits<R>::Deserialize(buf, message_);
   }
 
   ~DeserializeFuncType() override {}
@@ -379,16 +361,15 @@
     op->op = GRPC_OP_RECV_MESSAGE;
     op->flags = 0;
     op->reserved = NULL;
-    op->data.recv_message = &recv_buf_;
+    op->data.recv_message.recv_message = &recv_buf_;
   }
 
-  void FinishOp(bool* status, int max_receive_message_size) {
+  void FinishOp(bool* status) {
     if (!deserialize_) return;
     if (recv_buf_) {
       if (*status) {
         got_message = true;
-        *status =
-            deserialize_->Deserialize(recv_buf_, max_receive_message_size).ok();
+        *status = deserialize_->Deserialize(recv_buf_).ok();
       } else {
         got_message = false;
         g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
@@ -422,7 +403,7 @@
     op->flags = 0;
     op->reserved = NULL;
   }
-  void FinishOp(bool* status, int max_receive_message_size) { send_ = false; }
+  void FinishOp(bool* status) { send_ = false; }
 
  private:
   bool send_;
@@ -451,13 +432,14 @@
         trailing_metadata_count_;
     op->data.send_status_from_server.trailing_metadata = trailing_metadata_;
     op->data.send_status_from_server.status = send_status_code_;
+    status_details_slice_ = SliceReferencingString(send_status_details_);
     op->data.send_status_from_server.status_details =
-        send_status_details_.empty() ? nullptr : send_status_details_.c_str();
+        send_status_details_.empty() ? nullptr : &status_details_slice_;
     op->flags = 0;
     op->reserved = NULL;
   }
 
-  void FinishOp(bool* status, int max_receive_message_size) {
+  void FinishOp(bool* status) {
     if (!send_status_available_) return;
     g_core_codegen_interface->gpr_free(trailing_metadata_);
     send_status_available_ = false;
@@ -469,36 +451,36 @@
   grpc::string send_status_details_;
   size_t trailing_metadata_count_;
   grpc_metadata* trailing_metadata_;
+  grpc_slice status_details_slice_;
 };
 
 class CallOpRecvInitialMetadata {
  public:
-  CallOpRecvInitialMetadata() : recv_initial_metadata_(nullptr) {}
+  CallOpRecvInitialMetadata() : metadata_map_(nullptr) {}
 
   void RecvInitialMetadata(ClientContext* context) {
     context->initial_metadata_received_ = true;
-    recv_initial_metadata_ = &context->recv_initial_metadata_;
+    metadata_map_ = &context->recv_initial_metadata_;
   }
 
  protected:
   void AddOp(grpc_op* ops, size_t* nops) {
-    if (!recv_initial_metadata_) return;
-    memset(&recv_initial_metadata_arr_, 0, sizeof(recv_initial_metadata_arr_));
+    if (metadata_map_ == nullptr) return;
     grpc_op* op = &ops[(*nops)++];
     op->op = GRPC_OP_RECV_INITIAL_METADATA;
-    op->data.recv_initial_metadata = &recv_initial_metadata_arr_;
+    op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr();
     op->flags = 0;
     op->reserved = NULL;
   }
-  void FinishOp(bool* status, int max_receive_message_size) {
-    if (recv_initial_metadata_ == nullptr) return;
-    FillMetadataMap(&recv_initial_metadata_arr_, recv_initial_metadata_);
-    recv_initial_metadata_ = nullptr;
+
+  void FinishOp(bool* status) {
+    if (metadata_map_ == nullptr) return;
+    metadata_map_->FillMap();
+    metadata_map_ = nullptr;
   }
 
  private:
-  std::multimap<grpc::string_ref, grpc::string_ref>* recv_initial_metadata_;
-  grpc_metadata_array recv_initial_metadata_arr_;
+  MetadataMap* metadata_map_;
 };
 
 class CallOpClientRecvStatus {
@@ -506,46 +488,37 @@
   CallOpClientRecvStatus() : recv_status_(nullptr) {}
 
   void ClientRecvStatus(ClientContext* context, Status* status) {
-    recv_trailing_metadata_ = &context->trailing_metadata_;
+    metadata_map_ = &context->trailing_metadata_;
     recv_status_ = status;
   }
 
  protected:
   void AddOp(grpc_op* ops, size_t* nops) {
     if (recv_status_ == nullptr) return;
-    memset(&recv_trailing_metadata_arr_, 0,
-           sizeof(recv_trailing_metadata_arr_));
-    status_details_ = nullptr;
-    status_details_capacity_ = 0;
     grpc_op* op = &ops[(*nops)++];
     op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
-    op->data.recv_status_on_client.trailing_metadata =
-        &recv_trailing_metadata_arr_;
+    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 = &status_details_;
-    op->data.recv_status_on_client.status_details_capacity =
-        &status_details_capacity_;
     op->flags = 0;
     op->reserved = NULL;
   }
 
-  void FinishOp(bool* status, int max_receive_message_size) {
+  void FinishOp(bool* status) {
     if (recv_status_ == nullptr) return;
-    FillMetadataMap(&recv_trailing_metadata_arr_, recv_trailing_metadata_);
-    *recv_status_ = Status(
-        static_cast<StatusCode>(status_code_),
-        status_details_ ? grpc::string(status_details_) : grpc::string());
-    g_core_codegen_interface->gpr_free(status_details_);
+    metadata_map_->FillMap();
+    *recv_status_ = Status(static_cast<StatusCode>(status_code_),
+                           grpc::string(GRPC_SLICE_START_PTR(status_details_),
+                                        GRPC_SLICE_END_PTR(status_details_)));
+    g_core_codegen_interface->grpc_slice_unref(status_details_);
     recv_status_ = nullptr;
   }
 
  private:
-  std::multimap<grpc::string_ref, grpc::string_ref>* recv_trailing_metadata_;
+  MetadataMap* metadata_map_;
   Status* recv_status_;
-  grpc_metadata_array recv_trailing_metadata_arr_;
   grpc_status_code status_code_;
-  char* status_details_;
-  size_t status_details_capacity_;
+  grpc_slice status_details_;
 };
 
 /// An abstract collection of CallOpSet's, to be used whenever
@@ -566,22 +539,17 @@
 /// API.
 class CallOpSetInterface : public CompletionQueueTag {
  public:
-  CallOpSetInterface() : max_receive_message_size_(0) {}
+  CallOpSetInterface() {}
   /// Fills in grpc_op, starting from ops[*nops] and moving
   /// upwards.
   virtual void FillOps(grpc_op* ops, size_t* nops) = 0;
 
-  void set_max_receive_message_size(int max_receive_message_size) {
-    max_receive_message_size_ = max_receive_message_size;
-  }
-
   /// Mark this as belonging to a collection if needed
   void SetCollection(std::shared_ptr<CallOpSetCollectionInterface> collection) {
     collection_ = collection;
   }
 
  protected:
-  int max_receive_message_size_;
   std::shared_ptr<CallOpSetCollectionInterface> collection_;
 };
 
@@ -613,12 +581,12 @@
   }
 
   bool FinalizeResult(void** tag, bool* status) override {
-    this->Op1::FinishOp(status, max_receive_message_size_);
-    this->Op2::FinishOp(status, max_receive_message_size_);
-    this->Op3::FinishOp(status, max_receive_message_size_);
-    this->Op4::FinishOp(status, max_receive_message_size_);
-    this->Op5::FinishOp(status, max_receive_message_size_);
-    this->Op6::FinishOp(status, max_receive_message_size_);
+    this->Op1::FinishOp(status);
+    this->Op2::FinishOp(status);
+    this->Op3::FinishOp(status);
+    this->Op4::FinishOp(status);
+    this->Op5::FinishOp(status);
+    this->Op6::FinishOp(status);
     *tag = return_tag_;
     collection_.reset();  // drop the ref at this point
     return true;
@@ -650,35 +618,19 @@
  public:
   /* call is owned by the caller */
   Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
-      : call_hook_(call_hook),
-        cq_(cq),
-        call_(call),
-        max_receive_message_size_(-1) {}
-
-  Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq,
-       int max_receive_message_size)
-      : call_hook_(call_hook),
-        cq_(cq),
-        call_(call),
-        max_receive_message_size_(max_receive_message_size) {}
+      : call_hook_(call_hook), cq_(cq), call_(call) {}
 
   void PerformOps(CallOpSetInterface* ops) {
-    if (max_receive_message_size_ > 0) {
-      ops->set_max_receive_message_size(max_receive_message_size_);
-    }
     call_hook_->PerformOpsOnCall(ops, this);
   }
 
   grpc_call* call() const { return call_; }
   CompletionQueue* cq() const { return cq_; }
 
-  int max_receive_message_size() { return max_receive_message_size_; }
-
  private:
   CallHook* call_hook_;
   CompletionQueue* cq_;
   grpc_call* call_;
-  int max_receive_message_size_;
 };
 
 }  // namespace grpc
diff --git a/include/grpc++/impl/codegen/client_context.h b/include/grpc++/impl/codegen/client_context.h
index 777b2f8..b91c7f6 100644
--- a/include/grpc++/impl/codegen/client_context.h
+++ b/include/grpc++/impl/codegen/client_context.h
@@ -57,7 +57,9 @@
 #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/metadata_map.h>
 #include <grpc++/impl/codegen/security/auth_context.h>
+#include <grpc++/impl/codegen/slice.h>
 #include <grpc++/impl/codegen/status.h>
 #include <grpc++/impl/codegen/string_ref.h>
 #include <grpc++/impl/codegen/time.h>
@@ -193,7 +195,7 @@
   const std::multimap<grpc::string_ref, grpc::string_ref>&
   GetServerInitialMetadata() const {
     GPR_CODEGEN_ASSERT(initial_metadata_received_);
-    return recv_initial_metadata_;
+    return *recv_initial_metadata_.map();
   }
 
   /// Return a collection of trailing metadata key-value pairs. Note that keys
@@ -205,7 +207,7 @@
   const std::multimap<grpc::string_ref, grpc::string_ref>&
   GetServerTrailingMetadata() const {
     // TODO(yangg) check finished
-    return trailing_metadata_;
+    return *trailing_metadata_.map();
   }
 
   /// Set the deadline for the client call.
@@ -375,8 +377,8 @@
   mutable std::shared_ptr<const AuthContext> auth_context_;
   struct census_context* census_context_;
   std::multimap<grpc::string, grpc::string> send_initial_metadata_;
-  std::multimap<grpc::string_ref, grpc::string_ref> recv_initial_metadata_;
-  std::multimap<grpc::string_ref, grpc::string_ref> trailing_metadata_;
+  MetadataMap recv_initial_metadata_;
+  MetadataMap trailing_metadata_;
 
   grpc_call* propagate_from_call_;
   PropagationOptions propagation_options_;
diff --git a/include/grpc++/impl/codegen/client_unary_call.h b/include/grpc++/impl/codegen/client_unary_call.h
index 70d6554..201e52a 100644
--- a/include/grpc++/impl/codegen/client_unary_call.h
+++ b/include/grpc++/impl/codegen/client_unary_call.h
@@ -69,7 +69,14 @@
   ops.ClientSendClose();
   ops.ClientRecvStatus(context, &status);
   call.PerformOps(&ops);
-  GPR_CODEGEN_ASSERT((cq.Pluck(&ops) && ops.got_message) || !status.ok());
+  if (cq.Pluck(&ops)) {
+    if (!ops.got_message && status.ok()) {
+      return Status(StatusCode::UNIMPLEMENTED,
+                    "No message returned for unary request");
+    }
+  } else {
+    GPR_CODEGEN_ASSERT(!status.ok());
+  }
   return status;
 }
 
diff --git a/include/grpc++/impl/codegen/core_codegen.h b/include/grpc++/impl/codegen/core_codegen.h
index 6b5e637..754bf14 100644
--- a/include/grpc++/impl/codegen/core_codegen.h
+++ b/include/grpc++/impl/codegen/core_codegen.h
@@ -81,7 +81,10 @@
   grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) 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;
 
@@ -91,7 +94,8 @@
   virtual const Status& ok() override;
   virtual const Status& cancelled() override;
 
-  void assert_fail(const char* failed_assertion) override;
+  void assert_fail(const char* failed_assertion, const char* file,
+                   int line) override;
 };
 
 }  // namespace grpc
diff --git a/include/grpc++/impl/codegen/core_codegen_interface.h b/include/grpc++/impl/codegen/core_codegen_interface.h
index 4783a43..45ea040 100644
--- a/include/grpc++/impl/codegen/core_codegen_interface.h
+++ b/include/grpc++/impl/codegen/core_codegen_interface.h
@@ -56,7 +56,8 @@
 class CoreCodegenInterface {
  public:
   /// Upon a failed assertion, log the error.
-  virtual void assert_fail(const char* failed_assertion) = 0;
+  virtual void assert_fail(const char* failed_assertion, const char* file,
+                           int line) = 0;
 
   virtual grpc_completion_queue* grpc_completion_queue_create(
       void* reserved) = 0;
@@ -99,6 +100,10 @@
   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;
@@ -113,11 +118,11 @@
 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); \
-    }                                                  \
+#define GPR_CODEGEN_ASSERT(x)                                              \
+  do {                                                                     \
+    if (!(x)) {                                                            \
+      grpc::g_core_codegen_interface->assert_fail(#x, __FILE__, __LINE__); \
+    }                                                                      \
   } while (0)
 
 }  // namespace grpc
diff --git a/include/grpc++/impl/codegen/metadata_map.h b/include/grpc++/impl/codegen/metadata_map.h
new file mode 100644
index 0000000..53b9d62
--- /dev/null
+++ b/include/grpc++/impl/codegen/metadata_map.h
@@ -0,0 +1,71 @@
+/*
+*
+* Copyright 2015, Google Inc.
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+*     * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*     * Neither the name of Google Inc. nor the names of its
+* contributors may be used to endorse or promote products derived from
+* this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+*/
+
+#ifndef GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
+#define GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
+
+#include <grpc++/impl/codegen/slice.h>
+
+namespace grpc {
+
+class MetadataMap {
+ public:
+  MetadataMap() { memset(&arr_, 0, sizeof(arr_)); }
+
+  ~MetadataMap() {
+    g_core_codegen_interface->grpc_metadata_array_destroy(&arr_);
+  }
+
+  void FillMap() {
+    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)));
+    }
+  }
+
+  std::multimap<grpc::string_ref, grpc::string_ref> *map() { return &map_; }
+  const std::multimap<grpc::string_ref, grpc::string_ref> *map() const {
+    return &map_;
+  }
+  grpc_metadata_array *arr() { return &arr_; }
+
+ private:
+  grpc_metadata_array arr_;
+  std::multimap<grpc::string_ref, grpc::string_ref> map_;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCXX_IMPL_CODEGEN_METADATA_MAP_H
diff --git a/include/grpc++/impl/codegen/method_handler_impl.h b/include/grpc++/impl/codegen/method_handler_impl.h
index d5d27e1..83b569c 100644
--- a/include/grpc++/impl/codegen/method_handler_impl.h
+++ b/include/grpc++/impl/codegen/method_handler_impl.h
@@ -52,8 +52,8 @@
 
   void RunHandler(const HandlerParameter& param) final {
     RequestType req;
-    Status status = SerializationTraits<RequestType>::Deserialize(
-        param.request, &req, param.max_receive_message_size);
+    Status status =
+        SerializationTraits<RequestType>::Deserialize(param.request, &req);
     ResponseType rsp;
     if (status.ok()) {
       status = func_(service_, param.server_context, &req, &rsp);
@@ -138,8 +138,8 @@
 
   void RunHandler(const HandlerParameter& param) final {
     RequestType req;
-    Status status = SerializationTraits<RequestType>::Deserialize(
-        param.request, &req, param.max_receive_message_size);
+    Status status =
+        SerializationTraits<RequestType>::Deserialize(param.request, &req);
 
     if (status.ok()) {
       ServerWriter<ResponseType> writer(param.call, param.server_context);
diff --git a/include/grpc++/impl/codegen/proto_utils.h b/include/grpc++/impl/codegen/proto_utils.h
index 2f15487..2123b62 100644
--- a/include/grpc++/impl/codegen/proto_utils.h
+++ b/include/grpc++/impl/codegen/proto_utils.h
@@ -203,8 +203,7 @@
   }
 
   static Status Deserialize(grpc_byte_buffer* buffer,
-                            grpc::protobuf::Message* msg,
-                            int max_receive_message_size) {
+                            grpc::protobuf::Message* msg) {
     if (buffer == nullptr) {
       return Status(StatusCode::INTERNAL, "No payload");
     }
@@ -215,10 +214,7 @@
         return reader.status();
       }
       ::grpc::protobuf::io::CodedInputStream decoder(&reader);
-      if (max_receive_message_size > 0) {
-        decoder.SetTotalBytesLimit(max_receive_message_size,
-                                   max_receive_message_size);
-      }
+      decoder.SetTotalBytesLimit(INT_MAX, INT_MAX);
       if (!msg->ParseFromCodedStream(&decoder)) {
         result = Status(StatusCode::INTERNAL, msg->InitializationErrorString());
       }
diff --git a/include/grpc++/impl/codegen/rpc_service_method.h b/include/grpc++/impl/codegen/rpc_service_method.h
index 78c54e3..eb8f9a1 100644
--- a/include/grpc++/impl/codegen/rpc_service_method.h
+++ b/include/grpc++/impl/codegen/rpc_service_method.h
@@ -57,17 +57,12 @@
  public:
   virtual ~MethodHandler() {}
   struct HandlerParameter {
-    HandlerParameter(Call* c, ServerContext* context, grpc_byte_buffer* req,
-                     int max_size)
-        : call(c),
-          server_context(context),
-          request(req),
-          max_receive_message_size(max_size) {}
+    HandlerParameter(Call* c, ServerContext* context, grpc_byte_buffer* req)
+        : call(c), server_context(context), request(req) {}
     Call* call;
     ServerContext* server_context;
     // Handler required to grpc_byte_buffer_destroy this
     grpc_byte_buffer* request;
-    int max_receive_message_size;
   };
   virtual void RunHandler(const HandlerParameter& param) = 0;
 };
diff --git a/include/grpc++/impl/codegen/server_context.h b/include/grpc++/impl/codegen/server_context.h
index dd30576..8c7fe08 100644
--- a/include/grpc++/impl/codegen/server_context.h
+++ b/include/grpc++/impl/codegen/server_context.h
@@ -39,6 +39,7 @@
 
 #include <grpc++/impl/codegen/config.h>
 #include <grpc++/impl/codegen/create_auth_context.h>
+#include <grpc++/impl/codegen/metadata_map.h>
 #include <grpc++/impl/codegen/security/auth_context.h>
 #include <grpc++/impl/codegen/string_ref.h>
 #include <grpc++/impl/codegen/time.h>
@@ -123,7 +124,7 @@
 
   const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata()
       const {
-    return client_metadata_;
+    return *client_metadata_.map();
   }
 
   grpc_compression_level compression_level() const {
@@ -223,7 +224,7 @@
   CompletionQueue* cq_;
   bool sent_initial_metadata_;
   mutable std::shared_ptr<const AuthContext> auth_context_;
-  std::multimap<grpc::string_ref, grpc::string_ref> client_metadata_;
+  MetadataMap client_metadata_;
   std::multimap<grpc::string, grpc::string> initial_metadata_;
   std::multimap<grpc::string, grpc::string> trailing_metadata_;
 
diff --git a/include/grpc++/impl/codegen/server_interface.h b/include/grpc++/impl/codegen/server_interface.h
index 666b9ff..bd1b36e 100644
--- a/include/grpc++/impl/codegen/server_interface.h
+++ b/include/grpc++/impl/codegen/server_interface.h
@@ -152,7 +152,6 @@
     void* const tag_;
     const bool delete_on_finalize_;
     grpc_call* call_;
-    grpc_metadata_array initial_metadata_array_;
   };
 
   class RegisteredAsyncRequest : public BaseAsyncRequest {
@@ -199,9 +198,7 @@
     bool FinalizeResult(void** tag, bool* status) override {
       bool serialization_status =
           *status && payload_ &&
-          SerializationTraits<Message>::Deserialize(
-              payload_, request_, server_->max_receive_message_size())
-              .ok();
+          SerializationTraits<Message>::Deserialize(payload_, request_).ok();
       bool ret = RegisteredAsyncRequest::FinalizeResult(tag, status);
       *status = serialization_status && *status;
       return ret;
diff --git a/include/grpc++/impl/codegen/slice.h b/include/grpc++/impl/codegen/slice.h
new file mode 100644
index 0000000..04b2f9a
--- /dev/null
+++ b/include/grpc++/impl/codegen/slice.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPCXX_IMPL_CODEGEN_SLICE_H
+#define GRPCXX_IMPL_CODEGEN_SLICE_H
+
+#include <grpc++/impl/codegen/core_codegen_interface.h>
+#include <grpc++/impl/codegen/string_ref.h>
+
+namespace grpc {
+
+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  // GRPCXX_IMPL_CODEGEN_SLICE_H
diff --git a/include/grpc++/impl/codegen/sync_stream.h b/include/grpc++/impl/codegen/sync_stream.h
index 4d9b074..1f7708b 100644
--- a/include/grpc++/impl/codegen/sync_stream.h
+++ b/include/grpc++/impl/codegen/sync_stream.h
@@ -160,7 +160,7 @@
   }
 
   bool NextMessageSize(uint32_t* sz) override {
-    *sz = call_.max_receive_message_size();
+    *sz = INT_MAX;
     return true;
   }
 
@@ -310,7 +310,7 @@
   }
 
   bool NextMessageSize(uint32_t* sz) override {
-    *sz = call_.max_receive_message_size();
+    *sz = INT_MAX;
     return true;
   }
 
@@ -382,7 +382,7 @@
   }
 
   bool NextMessageSize(uint32_t* sz) override {
-    *sz = call_->max_receive_message_size();
+    *sz = INT_MAX;
     return true;
   }
 
@@ -474,7 +474,7 @@
   }
 
   bool NextMessageSize(uint32_t* sz) {
-    *sz = call_->max_receive_message_size();
+    *sz = INT_MAX;
     return true;
   }
 
diff --git a/include/grpc++/support/byte_buffer.h b/include/grpc++/support/byte_buffer.h
index 1f317df..064a03b 100644
--- a/include/grpc++/support/byte_buffer.h
+++ b/include/grpc++/support/byte_buffer.h
@@ -95,8 +95,7 @@
 template <>
 class SerializationTraits<ByteBuffer, void> {
  public:
-  static Status Deserialize(grpc_byte_buffer* byte_buffer, ByteBuffer* dest,
-                            int max_receive_message_size) {
+  static Status Deserialize(grpc_byte_buffer* byte_buffer, ByteBuffer* dest) {
     dest->set_buffer(byte_buffer);
     return Status::OK;
   }
diff --git a/include/grpc/compression.h b/include/grpc/compression.h
index 5f285cd..659d6fe 100644
--- a/include/grpc/compression.h
+++ b/include/grpc/compression.h
@@ -34,11 +34,12 @@
 #ifndef GRPC_COMPRESSION_H
 #define GRPC_COMPRESSION_H
 
-#include <stdlib.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" {
@@ -48,8 +49,7 @@
  * grpc_compression_algorithm instance, updating \a algorithm. Returns 1 upon
  * success, 0 otherwise. */
 GRPCAPI int grpc_compression_algorithm_parse(
-    const char *name, size_t name_length,
-    grpc_compression_algorithm *algorithm);
+    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.
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 898f4d5..37b823a 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -178,8 +178,8 @@
     possible values). */
 GRPCAPI grpc_call *grpc_channel_create_call(
     grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
-    grpc_completion_queue *completion_queue, const char *method,
-    const char *host, gpr_timespec deadline, void *reserved);
+    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. */
@@ -402,14 +402,14 @@
 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(const char *key, size_t length);
+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(const char *value, size_t length);
+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(const char *key, size_t length);
+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);
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index ee8101a..442c400 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -294,9 +294,11 @@
 
 /** A single metadata element */
 typedef struct grpc_metadata {
-  const char *key;
-  const char *value;
-  size_t value_length;
+  /* 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.
@@ -338,10 +340,8 @@
 } grpc_metadata_array;
 
 typedef struct {
-  char *method;
-  size_t method_capacity;
-  char *host;
-  size_t host_capacity;
+  grpc_slice method;
+  grpc_slice host;
   gpr_timespec deadline;
   uint32_t flags;
   void *reserved;
@@ -418,23 +418,32 @@
         grpc_compression_level level;
       } maybe_compression_level;
     } send_initial_metadata;
-    struct grpc_byte_buffer *send_message;
+    struct {
+      struct grpc_byte_buffer *send_message;
+    } send_message;
     struct {
       size_t trailing_metadata_count;
       grpc_metadata *trailing_metadata;
       grpc_status_code status;
-      const char *status_details;
+      /* 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. */
-    grpc_metadata_array *recv_initial_metadata;
+    struct {
+      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.
        */
-    struct grpc_byte_buffer **recv_message;
+    struct {
+      struct grpc_byte_buffer **recv_message;
+    } recv_message;
     struct {
       /** ownership of the array is with the caller, but ownership of the
           elements stays with the call object (ie key, value members are owned
@@ -444,28 +453,7 @@
           value, or reuse it in a future op. */
       grpc_metadata_array *trailing_metadata;
       grpc_status_code *status;
-      /** status_details is a buffer owned by the application before the op
-          completes and after the op has completed. During the operation
-          status_details may be reallocated to a size larger than
-          *status_details_capacity, in which case *status_details_capacity will
-          be updated with the new array capacity.
-
-          Pre-allocating space:
-          size_t my_capacity = 8;
-          char *my_details = gpr_malloc(my_capacity);
-          x.status_details = &my_details;
-          x.status_details_capacity = &my_capacity;
-
-          Not pre-allocating space:
-          size_t my_capacity = 0;
-          char *my_details = NULL;
-          x.status_details = &my_details;
-          x.status_details_capacity = &my_capacity;
-
-          After the call:
-          gpr_free(my_details); */
-      char **status_details;
-      size_t *status_details_capacity;
+      grpc_slice *status_details;
     } recv_status_on_client;
     struct {
       /** out argument, set to 1 if the call failed in any way (seen as a
diff --git a/include/grpc/impl/codegen/slice.h b/include/grpc/impl/codegen/slice.h
index 0352604..0b09a0b 100644
--- a/include/grpc/impl/codegen/slice.h
+++ b/include/grpc/impl/codegen/slice.h
@@ -40,6 +40,8 @@
 #include <grpc/impl/codegen/exec_ctx_fwd.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.
@@ -53,14 +55,25 @@
    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)(grpc_exec_ctx *exec_ctx, 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 {
-  void (*ref)(void *);
-  void (*unref)(grpc_exec_ctx *exec_ctx, void *);
+  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;
 
 #define GRPC_SLICE_INLINED_SIZE (sizeof(size_t) + sizeof(uint8_t *) - 1)
@@ -74,7 +87,7 @@
 
    If the slice does not have a refcount, it represents an inlined small piece
    of data that is copied by value. */
-typedef struct grpc_slice {
+struct grpc_slice {
   struct grpc_slice_refcount *refcount;
   union {
     struct {
@@ -86,7 +99,7 @@
       uint8_t bytes[GRPC_SLICE_INLINED_SIZE];
     } inlined;
   } data;
-} grpc_slice;
+};
 
 #define GRPC_SLICE_BUFFER_INLINE_ELEMENTS 8
 
diff --git a/include/grpc/slice.h b/include/grpc/slice.h
index 1f181aa..ea66e09 100644
--- a/include/grpc/slice.h
+++ b/include/grpc/slice.h
@@ -76,6 +76,12 @@
    Aborts if malloc() fails. */
 GPRAPI grpc_slice grpc_slice_malloc(size_t length);
 
+/* 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:
@@ -93,6 +99,9 @@
 /* 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 s, where
    result.data==s.data+begin, and result.length==end-begin.
    The ref count of s is increased by one.
@@ -113,18 +122,45 @@
    Requires s intialized, split <= s.length */
 GPRAPI grpc_slice grpc_slice_split_head(grpc_slice *s, size_t split);
 
-GPRAPI grpc_slice gpr_empty_slice(void);
+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);
+GPRAPI int grpc_slice_buf_cmp(grpc_slice a, const void *b, size_t blen);
+
+/* 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
diff --git a/package.xml b/package.xml
index 06ce67a..d369aba 100644
--- a/package.xml
+++ b/package.xml
@@ -200,6 +200,7 @@
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/error.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/error_internal.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_epoll_linux.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_poll_posix.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_posix.h" role="src" />
@@ -252,6 +253,7 @@
     <file baseinstalldir="/" name="src/core/lib/json/json_reader.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/json/json_writer.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/percent_encoding.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/slice/slice_hash_table.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_internal.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_string_helpers.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/api_trace.h" role="src" />
@@ -265,14 +267,17 @@
     <file baseinstalldir="/" name="src/core/lib/surface/init.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/lame_client.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/server.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/surface/validate_metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/byte_stream.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/connectivity_state.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/transport/mdstr_hash_table.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/error_utils.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/http2_errors.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata_batch.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/pid_controller.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/service_config.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/static_metadata.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/status_conversion.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport_impl.h" role="src" />
@@ -289,11 +294,9 @@
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_encoder.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_parser.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/hpack_table.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/http2_errors.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/huffsyms.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/incoming_metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/internal.h" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/status_conversion.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/stream_map.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/varint.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/alpn/alpn.h" role="src" />
@@ -446,6 +449,8 @@
     <file baseinstalldir="/" name="src/core/lib/slice/percent_encoding.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_buffer.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/slice/slice_hash_table.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/slice/slice_intern.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/slice/slice_string_helpers.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/alarm.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/api_trace.c" role="src" />
@@ -467,12 +472,13 @@
     <file baseinstalldir="/" name="src/core/lib/surface/version.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/byte_stream.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/connectivity_state.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/transport/mdstr_hash_table.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/error_utils.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/metadata_batch.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/pid_controller.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/service_config.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/static_metadata.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/transport/status_conversion.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/timeout_encoding.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/transport/transport_op_string.c" role="src" />
@@ -493,7 +499,6 @@
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/huffsyms.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/incoming_metadata.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/parsing.c" role="src" />
-    <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/status_conversion.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/stream_lists.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/stream_map.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/varint.c" role="src" />
diff --git a/src/core/ext/census/gen/census.pb.h b/src/core/ext/census/gen/census.pb.h
index dae583f..c8546ea 100644
--- a/src/core/ext/census/gen/census.pb.h
+++ b/src/core/ext/census/gen/census.pb.h
@@ -292,4 +292,4 @@
 } /* extern "C" */
 #endif
 
-#endif
+#endif /* GRPC_CORE_EXT_CENSUS_GEN_CENSUS_PB_H */
diff --git a/src/core/ext/census/gen/trace_context.pb.h b/src/core/ext/census/gen/trace_context.pb.h
index 263c4c5..cfb2f04 100644
--- a/src/core/ext/census/gen/trace_context.pb.h
+++ b/src/core/ext/census/gen/trace_context.pb.h
@@ -96,4 +96,4 @@
 } /* extern "C" */
 #endif
 
-#endif
+#endif /* GRPC_CORE_EXT_CENSUS_GEN_TRACE_CONTEXT_PB_H */
diff --git a/src/core/ext/census/grpc_filter.c b/src/core/ext/census/grpc_filter.c
index 8e4d473..65cfe1f 100644
--- a/src/core/ext/census/grpc_filter.c
+++ b/src/core/ext/census/grpc_filter.c
@@ -67,9 +67,7 @@
                                             channel_data *chand) {
   grpc_linked_mdelem *m;
   for (m = md->list.head; m != NULL; m = m->next) {
-    if (m->md->key == GRPC_MDSTR_PATH) {
-      gpr_log(GPR_DEBUG, "%s",
-              (const char *)GRPC_SLICE_START_PTR(m->md->value->slice));
+    if (grpc_slice_eq(GRPC_MDKEY(m->md), GRPC_MDSTR_PATH)) {
       /* Add method tag here */
     }
   }
diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c
index 865e91a..07eb68a 100644
--- a/src/core/ext/client_channel/client_channel.c
+++ b/src/core/ext/client_channel/client_channel.c
@@ -54,6 +54,7 @@
 #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/support/string.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/transport/connectivity_state.h"
@@ -89,7 +90,7 @@
   gpr_free(p);
 }
 
-static const grpc_mdstr_hash_table_vtable method_parameters_vtable = {
+static const grpc_slice_hash_table_vtable method_parameters_vtable = {
     method_parameters_free, method_parameters_copy};
 
 static void *method_parameters_create_from_json(const grpc_json *json) {
@@ -171,7 +172,7 @@
   /** service config in JSON form */
   char *service_config_json;
   /** maps method names to method_parameters structs */
-  grpc_mdstr_hash_table *method_params_table;
+  grpc_slice_hash_table *method_params_table;
   /** incoming resolver result - set by resolver.next() */
   grpc_channel_args *resolver_result;
   /** a list of closures that are all waiting for config to come in */
@@ -273,7 +274,7 @@
   char *lb_policy_name = NULL;
   grpc_lb_policy *lb_policy = NULL;
   grpc_lb_policy *old_lb_policy;
-  grpc_mdstr_hash_table *method_params_table = NULL;
+  grpc_slice_hash_table *method_params_table = NULL;
   grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE;
   bool exit_idle = false;
   grpc_error *state_error = GRPC_ERROR_CREATE("No load balancing policy");
@@ -379,7 +380,7 @@
     chand->service_config_json = service_config_json;
   }
   if (chand->method_params_table != NULL) {
-    grpc_mdstr_hash_table_unref(exec_ctx, chand->method_params_table);
+    grpc_slice_hash_table_unref(exec_ctx, chand->method_params_table);
   }
   chand->method_params_table = method_params_table;
   if (lb_policy != NULL) {
@@ -579,7 +580,7 @@
   gpr_free(chand->lb_policy_name);
   gpr_free(chand->service_config_json);
   if (chand->method_params_table != NULL) {
-    grpc_mdstr_hash_table_unref(exec_ctx, chand->method_params_table);
+    grpc_slice_hash_table_unref(exec_ctx, chand->method_params_table);
   }
   grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker);
   grpc_pollset_set_destroy(chand->interested_parties);
@@ -614,7 +615,7 @@
   // to avoid this without breaking the grpc_deadline_state abstraction.
   grpc_deadline_state deadline_state;
 
-  grpc_mdstr *path;  // Request path.
+  grpc_slice path;  // Request path.
   gpr_timespec call_start_time;
   gpr_timespec deadline;
   wait_for_ready_value wait_for_ready_from_service_config;
@@ -1018,10 +1019,10 @@
   if (error == GRPC_ERROR_NONE) {
     // Get the method config table from channel data.
     gpr_mu_lock(&chand->mu);
-    grpc_mdstr_hash_table *method_params_table = NULL;
+    grpc_slice_hash_table *method_params_table = NULL;
     if (chand->method_params_table != NULL) {
       method_params_table =
-          grpc_mdstr_hash_table_ref(chand->method_params_table);
+          grpc_slice_hash_table_ref(chand->method_params_table);
     }
     gpr_mu_unlock(&chand->mu);
     // If the method config table was present, use it.
@@ -1050,7 +1051,7 @@
           gpr_mu_unlock(&calld->mu);
         }
       }
-      grpc_mdstr_hash_table_unref(exec_ctx, method_params_table);
+      grpc_slice_hash_table_unref(exec_ctx, method_params_table);
     }
   }
   GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "read_service_config");
@@ -1064,7 +1065,7 @@
   call_data *calld = elem->call_data;
   // Initialize data members.
   grpc_deadline_state_init(exec_ctx, elem, args->call_stack);
-  calld->path = GRPC_MDSTR_REF(args->path);
+  calld->path = grpc_slice_ref_internal(args->path);
   calld->call_start_time = args->start_time;
   calld->deadline = gpr_convert_clock_type(args->deadline, GPR_CLOCK_MONOTONIC);
   calld->wait_for_ready_from_service_config = WAIT_FOR_READY_UNSET;
@@ -1088,8 +1089,8 @@
   if (chand->lb_policy != NULL) {
     // We already have a resolver result, so check for service config.
     if (chand->method_params_table != NULL) {
-      grpc_mdstr_hash_table *method_params_table =
-          grpc_mdstr_hash_table_ref(chand->method_params_table);
+      grpc_slice_hash_table *method_params_table =
+          grpc_slice_hash_table_ref(chand->method_params_table);
       gpr_mu_unlock(&chand->mu);
       method_parameters *method_params = grpc_method_config_table_get(
           exec_ctx, method_params_table, args->path);
@@ -1105,7 +1106,7 @@
               method_params->wait_for_ready;
         }
       }
-      grpc_mdstr_hash_table_unref(exec_ctx, method_params_table);
+      grpc_slice_hash_table_unref(exec_ctx, method_params_table);
     } else {
       gpr_mu_unlock(&chand->mu);
     }
@@ -1134,7 +1135,7 @@
                                  void *and_free_memory) {
   call_data *calld = elem->call_data;
   grpc_deadline_state_destroy(exec_ctx, elem);
-  GRPC_MDSTR_UNREF(exec_ctx, calld->path);
+  grpc_slice_unref_internal(exec_ctx, calld->path);
   GRPC_ERROR_UNREF(calld->cancel_error);
   grpc_subchannel_call *call = GET_CALL(calld);
   if (call != NULL && call != CANCELLED_CALL) {
diff --git a/src/core/ext/client_channel/subchannel.c b/src/core/ext/client_channel/subchannel.c
index 05b0882..c5041f6 100644
--- a/src/core/ext/client_channel/subchannel.c
+++ b/src/core/ext/client_channel/subchannel.c
@@ -637,9 +637,8 @@
   grpc_error *error = grpc_channel_stack_builder_finish(
       exec_ctx, builder, 0, 1, connection_destroy, NULL, (void **)&con);
   if (error != GRPC_ERROR_NONE) {
-    const char *msg = grpc_error_string(error);
-    gpr_log(GPR_ERROR, "error initializing subchannel stack: %s", msg);
-    grpc_error_free_string(msg);
+    gpr_log(GPR_ERROR, "error initializing subchannel stack: %s",
+            grpc_error_string(error));
     GRPC_ERROR_UNREF(error);
     abort(); /* TODO(ctiller): what to do here? */
   }
@@ -704,7 +703,6 @@
 
     const char *errmsg = grpc_error_string(error);
     gpr_log(GPR_INFO, "Connect failed: %s", errmsg);
-    grpc_error_free_string(errmsg);
 
     maybe_start_connecting_locked(exec_ctx, c);
     GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting");
@@ -763,7 +761,7 @@
 
 grpc_error *grpc_connected_subchannel_create_call(
     grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con,
-    grpc_polling_entity *pollent, grpc_mdstr *path, gpr_timespec start_time,
+    grpc_polling_entity *pollent, grpc_slice path, gpr_timespec start_time,
     gpr_timespec deadline, grpc_subchannel_call **call) {
   grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con);
   *call = gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size);
@@ -775,7 +773,7 @@
   if (error != GRPC_ERROR_NONE) {
     const char *error_string = grpc_error_string(error);
     gpr_log(GPR_ERROR, "error: %s", error_string);
-    grpc_error_free_string(error_string);
+
     gpr_free(*call);
     return error;
   }
diff --git a/src/core/ext/client_channel/subchannel.h b/src/core/ext/client_channel/subchannel.h
index 684675e..9bd35a7 100644
--- a/src/core/ext/client_channel/subchannel.h
+++ b/src/core/ext/client_channel/subchannel.h
@@ -114,7 +114,7 @@
 /** construct a subchannel call */
 grpc_error *grpc_connected_subchannel_create_call(
     grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *connected_subchannel,
-    grpc_polling_entity *pollent, grpc_mdstr *path, gpr_timespec start_time,
+    grpc_polling_entity *pollent, grpc_slice path, gpr_timespec start_time,
     gpr_timespec deadline, grpc_subchannel_call **subchannel_call);
 
 /** process a transport level op */
diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c
index 97f98df..308facb 100644
--- a/src/core/ext/lb_policy/grpclb/grpclb.c
+++ b/src/core/ext/lb_policy/grpclb/grpclb.c
@@ -135,13 +135,13 @@
 
 /* add lb_token of selected subchannel (address) to the call's initial
  * metadata */
-static void initial_metadata_add_lb_token(
-    grpc_metadata_batch *initial_metadata,
-    grpc_linked_mdelem *lb_token_mdelem_storage, grpc_mdelem *lb_token) {
+static grpc_error *initial_metadata_add_lb_token(
+    grpc_exec_ctx *exec_ctx, grpc_metadata_batch *initial_metadata,
+    grpc_linked_mdelem *lb_token_mdelem_storage, grpc_mdelem lb_token) {
   GPR_ASSERT(lb_token_mdelem_storage != NULL);
-  GPR_ASSERT(lb_token != NULL);
-  grpc_metadata_batch_add_tail(initial_metadata, lb_token_mdelem_storage,
-                               lb_token);
+  GPR_ASSERT(!GRPC_MDISNULL(lb_token));
+  return grpc_metadata_batch_add_tail(exec_ctx, initial_metadata,
+                                      lb_token_mdelem_storage, lb_token);
 }
 
 typedef struct wrapped_rr_closure_arg {
@@ -161,7 +161,7 @@
   grpc_connected_subchannel **target;
 
   /* the LB token associated with the pick */
-  grpc_mdelem *lb_token;
+  grpc_mdelem lb_token;
 
   /* storage for the lb token initial metadata mdelem */
   grpc_linked_mdelem *lb_token_mdelem_storage;
@@ -188,8 +188,8 @@
      * addresses failed to connect). There won't be any user_data/token
      * available */
     if (*wc_arg->target != NULL) {
-      if (wc_arg->lb_token != NULL) {
-        initial_metadata_add_lb_token(wc_arg->initial_metadata,
+      if (!GRPC_MDISNULL(wc_arg->lb_token)) {
+        initial_metadata_add_lb_token(exec_ctx, wc_arg->initial_metadata,
                                       wc_arg->lb_token_mdelem_storage,
                                       GRPC_MDELEM_REF(wc_arg->lb_token));
       } else {
@@ -345,8 +345,7 @@
 
   /* call status code and details, set in lb_on_server_status_received() */
   grpc_status_code lb_call_status;
-  char *lb_call_status_details;
-  size_t lb_call_status_details_capacity;
+  grpc_slice lb_call_status_details;
 
   /** LB call retry backoff state */
   gpr_backoff lb_call_backoff_state;
@@ -388,10 +387,14 @@
 
 /* vtable for LB tokens in grpc_lb_addresses. */
 static void *lb_token_copy(void *token) {
-  return token == NULL ? NULL : GRPC_MDELEM_REF(token);
+  return token == NULL
+             ? NULL
+             : (void *)GRPC_MDELEM_REF((grpc_mdelem){(uintptr_t)token}).payload;
 }
 static void lb_token_destroy(grpc_exec_ctx *exec_ctx, void *token) {
-  if (token != NULL) GRPC_MDELEM_UNREF(exec_ctx, token);
+  if (token != NULL) {
+    GRPC_MDELEM_UNREF(exec_ctx, (grpc_mdelem){(uintptr_t)token});
+  }
 }
 static int lb_token_cmp(void *token1, void *token2) {
   if (token1 > token2) return 1;
@@ -459,10 +462,11 @@
           GPR_ARRAY_SIZE(server->load_balance_token);
       const size_t lb_token_length =
           strnlen(server->load_balance_token, lb_token_max_length);
-      grpc_mdstr *lb_token_mdstr = grpc_mdstr_from_buffer(
-          (uint8_t *)server->load_balance_token, lb_token_length);
-      user_data = grpc_mdelem_from_metadata_strings(
-          exec_ctx, GRPC_MDSTR_LB_TOKEN, lb_token_mdstr);
+      grpc_slice lb_token_mdstr = grpc_slice_from_copied_buffer(
+          server->load_balance_token, lb_token_length);
+      user_data = (void *)grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_LB_TOKEN,
+                                                  lb_token_mdstr)
+                      .payload;
     } else {
       char *uri = grpc_sockaddr_to_uri(&addr);
       gpr_log(GPR_INFO,
@@ -470,7 +474,7 @@
               "be used instead",
               uri);
       gpr_free(uri);
-      user_data = GRPC_MDELEM_LB_TOKEN_EMPTY;
+      user_data = (void *)GRPC_MDELEM_LB_TOKEN_EMPTY.payload;
     }
 
     grpc_lb_addresses_set_address(lb_addresses, addr_idx, &addr.addr, addr.len,
@@ -564,7 +568,7 @@
     GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync");
 
     /* add the load reporting initial metadata */
-    initial_metadata_add_lb_token(pick_args->initial_metadata,
+    initial_metadata_add_lb_token(exec_ctx, pick_args->initial_metadata,
                                   pick_args->lb_token_mdelem_storage,
                                   GRPC_MDELEM_REF(wc_arg->lb_token));
 
@@ -1103,11 +1107,12 @@
   /* Note the following LB call progresses every time there's activity in \a
    * glb_policy->base.interested_parties, which is comprised of the polling
    * entities from \a client_channel. */
+  grpc_slice host = grpc_slice_from_copied_string(glb_policy->server_name);
   glb_policy->lb_call = grpc_channel_create_pollset_set_call(
       exec_ctx, glb_policy->lb_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
       glb_policy->base.interested_parties,
-      "/grpc.lb.v1.LoadBalancer/BalanceLoad", glb_policy->server_name,
-      glb_policy->deadline, NULL);
+      GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD,
+      &host, glb_policy->deadline, NULL);
 
   grpc_metadata_array_init(&glb_policy->lb_initial_metadata_recv);
   grpc_metadata_array_init(&glb_policy->lb_trailing_metadata_recv);
@@ -1120,9 +1125,6 @@
   grpc_slice_unref_internal(exec_ctx, request_payload_slice);
   grpc_grpclb_request_destroy(request);
 
-  glb_policy->lb_call_status_details = NULL;
-  glb_policy->lb_call_status_details_capacity = 0;
-
   grpc_closure_init(&glb_policy->lb_on_server_status_received,
                     lb_on_server_status_received, glb_policy,
                     grpc_schedule_on_exec_ctx);
@@ -1138,7 +1140,8 @@
                    GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
 }
 
-static void lb_call_destroy_locked(glb_lb_policy *glb_policy) {
+static void lb_call_destroy_locked(grpc_exec_ctx *exec_ctx,
+                                   glb_lb_policy *glb_policy) {
   GPR_ASSERT(glb_policy->lb_call != NULL);
   grpc_call_destroy(glb_policy->lb_call);
   glb_policy->lb_call = NULL;
@@ -1147,7 +1150,7 @@
   grpc_metadata_array_destroy(&glb_policy->lb_trailing_metadata_recv);
 
   grpc_byte_buffer_destroy(glb_policy->lb_request_payload);
-  gpr_free(glb_policy->lb_call_status_details);
+  grpc_slice_unref_internal(exec_ctx, glb_policy->lb_call_status_details);
 }
 
 /*
@@ -1178,14 +1181,15 @@
   op++;
 
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &glb_policy->lb_initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &glb_policy->lb_initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
 
   GPR_ASSERT(glb_policy->lb_request_payload != NULL);
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = glb_policy->lb_request_payload;
+  op->data.send_message.send_message = glb_policy->lb_request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -1196,8 +1200,6 @@
   op->data.recv_status_on_client.status = &glb_policy->lb_call_status;
   op->data.recv_status_on_client.status_details =
       &glb_policy->lb_call_status_details;
-  op->data.recv_status_on_client.status_details_capacity =
-      &glb_policy->lb_call_status_details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -1211,7 +1213,7 @@
 
   op = ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &glb_policy->lb_response_payload;
+  op->data.recv_message.recv_message = &glb_policy->lb_response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -1293,7 +1295,7 @@
     if (!glb_policy->shutting_down) {
       /* keep listening for serverlist updates */
       op->op = GRPC_OP_RECV_MESSAGE;
-      op->data.recv_message = &glb_policy->lb_response_payload;
+      op->data.recv_message.recv_message = &glb_policy->lb_response_payload;
       op->flags = 0;
       op->reserved = NULL;
       op++;
@@ -1340,15 +1342,18 @@
   GPR_ASSERT(glb_policy->lb_call != NULL);
 
   if (grpc_lb_glb_trace) {
+    char *status_details =
+        grpc_slice_to_c_string(glb_policy->lb_call_status_details);
     gpr_log(GPR_DEBUG,
             "Status from LB server received. Status = %d, Details = '%s', "
             "(call: %p)",
-            glb_policy->lb_call_status, glb_policy->lb_call_status_details,
+            glb_policy->lb_call_status, status_details,
             (void *)glb_policy->lb_call);
+    gpr_free(status_details);
   }
 
-  /* We need to performe cleanups no matter what. */
-  lb_call_destroy_locked(glb_policy);
+  /* We need to perform cleanups no matter what. */
+  lb_call_destroy_locked(exec_ctx, glb_policy);
 
   if (!glb_policy->shutting_down) {
     /* if we aren't shutting down, restart the LB client call after some time */
diff --git a/src/core/ext/load_reporting/load_reporting_filter.c b/src/core/ext/load_reporting/load_reporting_filter.c
index 07ef10e..8af6191 100644
--- a/src/core/ext/load_reporting/load_reporting_filter.c
+++ b/src/core/ext/load_reporting/load_reporting_filter.c
@@ -41,13 +41,17 @@
 #include "src/core/ext/load_reporting/load_reporting_filter.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/static_metadata.h"
 
 typedef struct call_data {
   intptr_t id; /**< an id unique to the call */
-  char *trailing_md_string;
-  char *initial_md_string;
-  const char *service_method;
+  bool have_trailing_md_string;
+  grpc_slice trailing_md_string;
+  bool have_initial_md_string;
+  grpc_slice initial_md_string;
+  bool have_service_method;
+  grpc_slice service_method;
 
   /* stores the recv_initial_metadata op's ready closure, which we wrap with our
    * own (on_initial_md_ready) in order to capture the incoming initial metadata
@@ -63,42 +67,28 @@
   intptr_t id; /**< an id unique to the channel */
 } channel_data;
 
-typedef struct {
-  grpc_call_element *elem;
-  grpc_exec_ctx *exec_ctx;
-} recv_md_filter_args;
-
-static grpc_mdelem *recv_md_filter(grpc_exec_ctx *exec_ctx, void *user_data,
-                                   grpc_mdelem *md) {
-  recv_md_filter_args *a = user_data;
-  grpc_call_element *elem = a->elem;
-  call_data *calld = elem->call_data;
-
-  if (md->key == GRPC_MDSTR_PATH) {
-    calld->service_method = grpc_mdstr_as_c_string(md->value);
-  } else if (md->key == GRPC_MDSTR_LB_TOKEN) {
-    calld->initial_md_string = gpr_strdup(grpc_mdstr_as_c_string(md->value));
-    return NULL;
-  }
-
-  return md;
-}
-
 static void on_initial_md_ready(grpc_exec_ctx *exec_ctx, void *user_data,
                                 grpc_error *err) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
 
   if (err == GRPC_ERROR_NONE) {
-    recv_md_filter_args a;
-    a.elem = elem;
-    a.exec_ctx = exec_ctx;
-    grpc_metadata_batch_filter(exec_ctx, calld->recv_initial_metadata,
-                               recv_md_filter, &a);
-    if (calld->service_method == NULL) {
+    if (calld->recv_initial_metadata->idx.named.path != NULL) {
+      calld->service_method = grpc_slice_ref_internal(
+          GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.path->md));
+      calld->have_service_method = true;
+    } else {
       err =
           grpc_error_add_child(err, GRPC_ERROR_CREATE("Missing :path header"));
     }
+    if (calld->recv_initial_metadata->idx.named.lb_token != NULL) {
+      calld->initial_md_string = grpc_slice_ref_internal(
+          GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.lb_token->md));
+      calld->have_initial_md_string = true;
+      grpc_metadata_batch_remove(
+          exec_ctx, calld->recv_initial_metadata,
+          calld->recv_initial_metadata->idx.named.lb_token);
+    }
   } else {
     GRPC_ERROR_REF(err);
   }
@@ -149,8 +139,15 @@
                                                 calld->service_method};
   */
 
-  gpr_free(calld->initial_md_string);
-  gpr_free(calld->trailing_md_string);
+  if (calld->have_initial_md_string) {
+    grpc_slice_unref_internal(exec_ctx, calld->initial_md_string);
+  }
+  if (calld->have_trailing_md_string) {
+    grpc_slice_unref_internal(exec_ctx, calld->trailing_md_string);
+  }
+  if (calld->have_service_method) {
+    grpc_slice_unref_internal(exec_ctx, calld->service_method);
+  }
 }
 
 /* Constructor for channel_data */
@@ -193,19 +190,6 @@
   */
 }
 
-static grpc_mdelem *lr_trailing_md_filter(grpc_exec_ctx *exec_ctx,
-                                          void *user_data, grpc_mdelem *md) {
-  grpc_call_element *elem = user_data;
-  call_data *calld = elem->call_data;
-
-  if (md->key == GRPC_MDSTR_LB_COST_BIN) {
-    calld->trailing_md_string = gpr_strdup(grpc_mdstr_as_c_string(md->value));
-    return NULL;
-  }
-
-  return md;
-}
-
 static void lr_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
                                          grpc_call_element *elem,
                                          grpc_transport_stream_op *op) {
@@ -218,8 +202,14 @@
     calld->ops_recv_initial_metadata_ready = op->recv_initial_metadata_ready;
     op->recv_initial_metadata_ready = &calld->on_initial_md_ready;
   } else if (op->send_trailing_metadata) {
-    grpc_metadata_batch_filter(exec_ctx, op->send_trailing_metadata,
-                               lr_trailing_md_filter, elem);
+    if (op->send_trailing_metadata->idx.named.lb_cost_bin != NULL) {
+      calld->trailing_md_string = grpc_slice_ref_internal(
+          GRPC_MDVALUE(op->send_trailing_metadata->idx.named.lb_cost_bin->md));
+      calld->have_trailing_md_string = true;
+      grpc_metadata_batch_remove(
+          exec_ctx, op->send_trailing_metadata,
+          op->send_trailing_metadata->idx.named.lb_cost_bin);
+    }
   }
   grpc_call_next_op(exec_ctx, elem, op);
 
diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c
index bf2f4e5..2c96232 100644
--- a/src/core/ext/resolver/dns/native/dns_resolver.c
+++ b/src/core/ext/resolver/dns/native/dns_resolver.c
@@ -187,9 +187,8 @@
     gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
     gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now);
     gpr_timespec timeout = gpr_time_sub(next_try, now);
-    const char *msg = grpc_error_string(error);
-    gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", msg);
-    grpc_error_free_string(msg);
+    gpr_log(GPR_INFO, "dns resolution failed (will retry): %s",
+            grpc_error_string(error));
     GPR_ASSERT(!r->have_retry_timer);
     r->have_retry_timer = true;
     GRPC_RESOLVER_REF(&r->base, "retry-timer");
diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
index c146a62..a1365f6 100644
--- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
+++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
@@ -182,7 +182,7 @@
   bool errors_found = false;
   for (size_t i = 0; i < addresses->num_addresses; i++) {
     grpc_uri ith_uri = *args->uri;
-    char *part_str = grpc_dump_slice(path_parts.slices[i], GPR_DUMP_ASCII);
+    char *part_str = grpc_slice_to_c_string(path_parts.slices[i]);
     ith_uri.path = part_str;
     if (!parse(&ith_uri, &addresses->addresses[i].address)) {
       errors_found = true; /* GPR_TRUE */
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.c b/src/core/ext/transport/chttp2/server/chttp2_server.c
index 574d1a7..56a1a0d 100644
--- a/src/core/ext/transport/chttp2/server/chttp2_server.c
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.c
@@ -121,7 +121,7 @@
   if (error != GRPC_ERROR_NONE || connection_state->server_state->shutdown) {
     const char *error_str = grpc_error_string(error);
     gpr_log(GPR_ERROR, "Handshaking failed: %s", error_str);
-    grpc_error_free_string(error_str);
+
     if (error == GRPC_ERROR_NONE && args->endpoint != NULL) {
       // We were shut down after handshaking completed successfully, so
       // destroy the endpoint here.
@@ -307,7 +307,7 @@
 
     const char *warning_message = grpc_error_string(err);
     gpr_log(GPR_INFO, "WARNING: %s", warning_message);
-    grpc_error_free_string(warning_message);
+
     /* we managed to bind some addresses: continue */
   }
   grpc_resolved_addresses_destroy(resolved);
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
index bf5026b..c219a7d 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
@@ -51,7 +51,7 @@
   if (err != GRPC_ERROR_NONE) {
     const char *msg = grpc_error_string(err);
     gpr_log(GPR_ERROR, "%s", msg);
-    grpc_error_free_string(msg);
+
     GRPC_ERROR_UNREF(err);
   }
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
index 395c79a..cb2b3f5 100644
--- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
@@ -94,7 +94,7 @@
   if (err != GRPC_ERROR_NONE) {
     const char *msg = grpc_error_string(err);
     gpr_log(GPR_ERROR, "%s", msg);
-    grpc_error_free_string(msg);
+
     GRPC_ERROR_UNREF(err);
   }
   return port_num;
diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.c b/src/core/ext/transport/chttp2/transport/bin_decoder.c
index 8db36e4..8c87de1 100644
--- a/src/core/ext/transport/chttp2/transport/bin_decoder.c
+++ b/src/core/ext/transport/chttp2/transport/bin_decoder.c
@@ -157,7 +157,7 @@
             "grpc_chttp2_base64_decode has a length of %d, which is not a "
             "multiple of 4.\n",
             (int)input_length);
-    return gpr_empty_slice();
+    return grpc_empty_slice();
   }
 
   if (input_length > 0) {
@@ -178,11 +178,11 @@
   ctx.contains_tail = false;
 
   if (!grpc_base64_decode_partial(&ctx)) {
-    char *s = grpc_dump_slice(input, GPR_DUMP_ASCII);
+    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(exec_ctx, output);
-    return gpr_empty_slice();
+    return grpc_empty_slice();
   }
   GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output));
   GPR_ASSERT(ctx.input_cur == GRPC_SLICE_END_PTR(input));
@@ -204,7 +204,7 @@
             "has a tail of 1 byte.\n",
             (int)input_length);
     grpc_slice_unref_internal(exec_ctx, output);
-    return gpr_empty_slice();
+    return grpc_empty_slice();
   }
 
   if (output_length > input_length / 4 * 3 + tail_xtra[input_length % 4]) {
@@ -214,7 +214,7 @@
             (int)output_length,
             (int)(input_length / 4 * 3 + tail_xtra[input_length % 4]));
     grpc_slice_unref_internal(exec_ctx, output);
-    return gpr_empty_slice();
+    return grpc_empty_slice();
   }
 
   ctx.input_cur = GRPC_SLICE_START_PTR(input);
@@ -224,11 +224,11 @@
   ctx.contains_tail = true;
 
   if (!grpc_base64_decode_partial(&ctx)) {
-    char *s = grpc_dump_slice(input, GPR_DUMP_ASCII);
+    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(exec_ctx, output);
-    return gpr_empty_slice();
+    return grpc_empty_slice();
   }
   GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output));
   GPR_ASSERT(ctx.input_cur <= GRPC_SLICE_END_PTR(input));
diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.c b/src/core/ext/transport/chttp2/transport/bin_encoder.c
index af25a43..e301c07 100644
--- a/src/core/ext/transport/chttp2/transport/bin_encoder.c
+++ b/src/core/ext/transport/chttp2/transport/bin_encoder.c
@@ -177,8 +177,7 @@
   enc_flush_some(out);
 }
 
-grpc_slice grpc_chttp2_base64_encode_and_huffman_compress_impl(
-    grpc_slice input) {
+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;
diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.h b/src/core/ext/transport/chttp2/transport/bin_encoder.h
index 477559d..0f899c8 100644
--- a/src/core/ext/transport/chttp2/transport/bin_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/bin_encoder.h
@@ -49,7 +49,6 @@
    grpc_slice y = grpc_chttp2_huffman_compress(x);
    grpc_slice_unref_internal(exec_ctx, x);
    return y; */
-grpc_slice grpc_chttp2_base64_encode_and_huffman_compress_impl(
-    grpc_slice input);
+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/src/core/ext/transport/chttp2/transport/chttp2_plugin.c b/src/core/ext/transport/chttp2/transport/chttp2_plugin.c
index bd87253..59b21e3 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_plugin.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_plugin.c
@@ -31,14 +31,11 @@
  *
  */
 
-#include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/transport/metadata.h"
 
 void grpc_chttp2_plugin_init(void) {
-  grpc_chttp2_base64_encode_and_huffman_compress =
-      grpc_chttp2_base64_encode_and_huffman_compress_impl;
   grpc_register_tracer("http", &grpc_http_trace);
   grpc_register_tracer("flowctl", &grpc_flowctl_trace);
 }
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 68a6a21..2004bc6 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -44,9 +44,7 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
 
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
-#include "src/core/ext/transport/chttp2/transport/status_conversion.h"
 #include "src/core/ext/transport/chttp2/transport/varint.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/http/parser.h"
@@ -55,7 +53,10 @@
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.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_impl.h"
 
@@ -409,7 +410,7 @@
           grpc_error_add_child(t->close_transport_on_writes_finished, error);
       return;
     }
-    if (!grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) {
+    if (!grpc_error_has_clear_grpc_status(error)) {
       error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS,
                                  GRPC_STATUS_UNAVAILABLE);
     }
@@ -866,7 +867,6 @@
             (int)(closure->next_data.scratch / CLOSURE_BARRIER_FIRST_REF_BIT),
             (int)(closure->next_data.scratch % CLOSURE_BARRIER_FIRST_REF_BIT),
             desc, errstr);
-    grpc_error_free_string(errstr);
   }
   if (error != GRPC_ERROR_NONE) {
     if (closure->error_data.error == GRPC_ERROR_NONE) {
@@ -895,12 +895,9 @@
 }
 
 static bool contains_non_ok_status(grpc_metadata_batch *batch) {
-  grpc_linked_mdelem *l;
-  for (l = batch->list.head; l; l = l->next) {
-    if (l->md->key == GRPC_MDSTR_GRPC_STATUS &&
-        l->md != GRPC_MDELEM_GRPC_STATUS_0) {
-      return true;
-    }
+  if (batch->idx.named.grpc_status != NULL) {
+    return !grpc_mdelem_eq(batch->idx.named.grpc_status->md,
+                           GRPC_MDELEM_GRPC_STATUS_0);
   }
   return false;
 }
@@ -980,9 +977,12 @@
                          bool is_client, bool is_initial) {
   for (grpc_linked_mdelem *md = md_batch->list.head; md != md_batch->list.tail;
        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", grpc_mdstr_as_c_string(md->md->key),
-            grpc_mdstr_as_c_string(md->md->value));
+            is_client ? "CLI" : "SVR", key, value);
+    gpr_free(key);
+    gpr_free(value);
   }
 }
 
@@ -1025,11 +1025,7 @@
   }
 
   if (op->cancel_error != GRPC_ERROR_NONE) {
-    grpc_chttp2_cancel_stream(exec_ctx, t, s, GRPC_ERROR_REF(op->cancel_error));
-  }
-
-  if (op->close_error != GRPC_ERROR_NONE) {
-    close_from_api(exec_ctx, t, s, GRPC_ERROR_REF(op->close_error));
+    grpc_chttp2_cancel_stream(exec_ctx, t, s, op->cancel_error);
   }
 
   if (op->send_initial_metadata != NULL) {
@@ -1080,8 +1076,9 @@
         s->send_initial_metadata = NULL;
         grpc_chttp2_complete_closure_step(
             exec_ctx, t, s, &s->send_initial_metadata_finished,
-            GRPC_ERROR_CREATE(
-                "Attempt to send initial metadata after stream was closed"),
+            GRPC_ERROR_CREATE_REFERENCING(
+                "Attempt to send initial metadata after stream was closed",
+                &s->write_closed_error, 1),
             "send_initial_metadata_finished");
       }
     }
@@ -1093,7 +1090,9 @@
     if (s->write_closed) {
       grpc_chttp2_complete_closure_step(
           exec_ctx, t, s, &s->fetching_send_message_finished,
-          GRPC_ERROR_CREATE("Attempt to send message after stream was closed"),
+          GRPC_ERROR_CREATE_REFERENCING(
+              "Attempt to send message after stream was closed",
+              &s->write_closed_error, 1),
           "fetching_send_message_finished");
     } else {
       GPR_ASSERT(s->fetching_send_message == NULL);
@@ -1265,11 +1264,16 @@
 }
 
 static void send_goaway(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                        grpc_chttp2_error_code error, grpc_slice data) {
+                        grpc_error *error) {
   t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED;
-  grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)error, data,
-                            &t->qbuf);
+  grpc_http2_error_code http_error;
+  const char *msg;
+  grpc_error_get_status(error, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL, &msg,
+                        &http_error);
+  grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)http_error,
+                            grpc_slice_from_copied_string(msg), &t->qbuf);
   grpc_chttp2_initiate_write(exec_ctx, t, false, "goaway_sent");
+  GRPC_ERROR_UNREF(error);
 }
 
 static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx,
@@ -1285,10 +1289,8 @@
         op->on_connectivity_state_change);
   }
 
-  if (op->send_goaway) {
-    send_goaway(exec_ctx, t,
-                grpc_chttp2_grpc_status_to_http2_error(op->goaway_status),
-                grpc_slice_ref_internal(*op->goaway_message));
+  if (op->goaway_error) {
+    send_goaway(exec_ctx, t, op->goaway_error);
   }
 
   if (op->set_accept_stream) {
@@ -1348,8 +1350,8 @@
         incoming_byte_stream_destroy_locked(exec_ctx, bs, GRPC_ERROR_NONE);
       }
     }
-    grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[0],
-                                                 s->recv_initial_metadata);
+    grpc_chttp2_incoming_metadata_buffer_publish(
+        exec_ctx, &s->metadata_buffer[0], s->recv_initial_metadata);
     null_then_run_closure(exec_ctx, &s->recv_initial_metadata_ready,
                           GRPC_ERROR_NONE);
   }
@@ -1392,8 +1394,8 @@
     }
     if (s->all_incoming_byte_streams_finished &&
         s->recv_trailing_metadata_finished != NULL) {
-      grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[1],
-                                                   s->recv_trailing_metadata);
+      grpc_chttp2_incoming_metadata_buffer_publish(
+          exec_ctx, &s->metadata_buffer[1], s->recv_trailing_metadata);
       grpc_chttp2_complete_closure_step(
           exec_ctx, t, s, &s->recv_trailing_metadata_finished, GRPC_ERROR_NONE,
           "recv_trailing_metadata_finished");
@@ -1441,70 +1443,37 @@
   maybe_start_some_streams(exec_ctx, t);
 }
 
-static void status_codes_from_error(grpc_error *error, gpr_timespec deadline,
-                                    grpc_chttp2_error_code *http2_error,
-                                    grpc_status_code *grpc_status) {
-  intptr_t ip_http;
-  intptr_t ip_grpc;
-  bool have_http =
-      grpc_error_get_int(error, GRPC_ERROR_INT_HTTP2_ERROR, &ip_http);
-  bool have_grpc =
-      grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &ip_grpc);
-  if (have_http) {
-    *http2_error = (grpc_chttp2_error_code)ip_http;
-  } else if (have_grpc) {
-    *http2_error =
-        grpc_chttp2_grpc_status_to_http2_error((grpc_status_code)ip_grpc);
-  } else {
-    *http2_error = GRPC_CHTTP2_INTERNAL_ERROR;
-  }
-  if (have_grpc) {
-    *grpc_status = (grpc_status_code)ip_grpc;
-  } else if (have_http) {
-    *grpc_status = grpc_chttp2_http2_error_to_grpc_status(
-        (grpc_chttp2_error_code)ip_http, deadline);
-  } else {
-    *grpc_status = GRPC_STATUS_INTERNAL;
-  }
-}
-
 void grpc_chttp2_cancel_stream(grpc_exec_ctx *exec_ctx,
                                grpc_chttp2_transport *t, grpc_chttp2_stream *s,
                                grpc_error *due_to_error) {
-  if (!s->read_closed || !s->write_closed) {
-    grpc_status_code grpc_status;
-    grpc_chttp2_error_code http_error;
-    status_codes_from_error(due_to_error, s->deadline, &http_error,
-                            &grpc_status);
+  if (!t->is_client && !s->sent_trailing_metadata &&
+      grpc_error_has_clear_grpc_status(due_to_error)) {
+    close_from_api(exec_ctx, 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, NULL, NULL, &http_error);
       grpc_slice_buffer_add(
           &t->qbuf, grpc_chttp2_rst_stream_create(s->id, (uint32_t)http_error,
                                                   &s->stats.outgoing));
       grpc_chttp2_initiate_write(exec_ctx, t, false, "rst_stream");
     }
-
-    const char *msg =
-        grpc_error_get_str(due_to_error, GRPC_ERROR_STR_GRPC_MESSAGE);
-    bool free_msg = false;
-    if (msg == NULL) {
-      free_msg = true;
-      msg = grpc_error_string(due_to_error);
-    }
-    grpc_slice msg_slice = grpc_slice_from_copied_string(msg);
-    grpc_chttp2_fake_status(exec_ctx, t, s, grpc_status, &msg_slice);
-    if (free_msg) grpc_error_free_string(msg);
   }
   if (due_to_error != GRPC_ERROR_NONE && !s->seen_error) {
     s->seen_error = true;
-    grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s);
   }
   grpc_chttp2_mark_stream_closed(exec_ctx, t, s, 1, 1, due_to_error);
 }
 
 void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                             grpc_chttp2_stream *s, grpc_status_code status,
-                             grpc_slice *slice) {
+                             grpc_chttp2_stream *s, grpc_error *error) {
+  grpc_status_code status;
+  const char *msg;
+  grpc_error_get_status(error, s->deadline, &status, &msg, NULL);
+
   if (status != GRPC_STATUS_OK) {
     s->seen_error = true;
   }
@@ -1518,24 +1487,21 @@
       s->recv_trailing_metadata_finished != NULL) {
     char status_string[GPR_LTOA_MIN_BUFSIZE];
     gpr_ltoa(status, status_string);
-    grpc_chttp2_incoming_metadata_buffer_add(
-        &s->metadata_buffer[1], grpc_mdelem_from_metadata_strings(
-                                    exec_ctx, GRPC_MDSTR_GRPC_STATUS,
-                                    grpc_mdstr_from_string(status_string)));
-    if (slice) {
-      grpc_chttp2_incoming_metadata_buffer_add(
-          &s->metadata_buffer[1],
-          grpc_mdelem_from_metadata_strings(
-              exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
-              grpc_mdstr_from_slice(exec_ctx,
-                                    grpc_slice_ref_internal(*slice))));
+    grpc_chttp2_incoming_metadata_buffer_replace_or_add(
+        exec_ctx, &s->metadata_buffer[1],
+        grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_STATUS,
+                                grpc_slice_from_copied_string(status_string)));
+    if (msg != NULL) {
+      grpc_chttp2_incoming_metadata_buffer_replace_or_add(
+          exec_ctx, &s->metadata_buffer[1],
+          grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
+                                  grpc_slice_from_copied_string(msg)));
     }
     s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE;
     grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s);
   }
-  if (slice) {
-    grpc_slice_unref_internal(exec_ctx, *slice);
-  }
+
+  GRPC_ERROR_UNREF(error);
 }
 
 static void add_error(grpc_error *error, grpc_error **refs, size_t *nrefs) {
@@ -1601,12 +1567,37 @@
                                     int close_writes, grpc_error *error) {
   if (s->read_closed && s->write_closed) {
     /* already closed */
+    grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, 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(exec_ctx, 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(exec_ctx, 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(exec_ctx, 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;
@@ -1615,22 +1606,9 @@
     decrement_active_streams_locked(exec_ctx, t, s);
     grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s);
     grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s);
-    grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s);
   }
-  if (close_writes && !s->write_closed) {
-    s->write_closed_error = GRPC_ERROR_REF(error);
-    s->write_closed = true;
-    grpc_chttp2_fail_pending_writes(exec_ctx, t, s, GRPC_ERROR_REF(error));
+  if (became_closed) {
     grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s);
-  }
-  if (s->read_closed && s->write_closed) {
-    if (s->id != 0) {
-      remove_stream(exec_ctx, t, s->id,
-                    removal_error(GRPC_ERROR_REF(error), s, "Stream removed"));
-    } else {
-      /* Purge streams waiting on concurrency still waiting for id assignment */
-      grpc_chttp2_list_remove_waiting_for_concurrency(t, s);
-    }
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2");
   }
   GRPC_ERROR_UNREF(error);
@@ -1644,112 +1622,92 @@
   uint8_t *p;
   uint32_t len = 0;
   grpc_status_code grpc_status;
-  grpc_chttp2_error_code http_error;
-  status_codes_from_error(error, s->deadline, &http_error, &grpc_status);
+  const char *msg;
+  grpc_error_get_status(error, s->deadline, &grpc_status, &msg, NULL);
 
   GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100);
 
-  if (s->id != 0 && !t->is_client) {
-    /* 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. */
-    status_hdr = grpc_slice_malloc(15 + (grpc_status >= 10));
-    p = GRPC_SLICE_START_PTR(status_hdr);
-    *p++ = 0x40; /* literal header */
-    *p++ = 11;   /* len(grpc-status) */
+  /* 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. */
+  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++ = (uint8_t)('0' + grpc_status);
+  } else {
+    *p++ = 2;
+    *p++ = (uint8_t)('0' + (grpc_status / 10));
+    *p++ = (uint8_t)('0' + (grpc_status % 10));
+  }
+  GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr));
+  len += (uint32_t)GRPC_SLICE_LENGTH(status_hdr);
+
+  if (msg != NULL) {
+    size_t msg_len = strlen(msg);
+    GPR_ASSERT(msg_len <= UINT32_MAX);
+    uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 0);
+    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++ = 't';
+    *p++ = 's';
     *p++ = 'a';
-    *p++ = 't';
-    *p++ = 'u';
-    *p++ = 's';
-    if (grpc_status < 10) {
-      *p++ = 1;
-      *p++ = (uint8_t)('0' + grpc_status);
-    } else {
-      *p++ = 2;
-      *p++ = (uint8_t)('0' + (grpc_status / 10));
-      *p++ = (uint8_t)('0' + (grpc_status % 10));
-    }
-    GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr));
-    len += (uint32_t)GRPC_SLICE_LENGTH(status_hdr);
-
-    const char *optional_message =
-        grpc_error_get_str(error, GRPC_ERROR_STR_GRPC_MESSAGE);
-
-    if (optional_message != NULL) {
-      size_t msg_len = strlen(optional_message);
-      GPR_ASSERT(msg_len <= UINT32_MAX);
-      uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 0);
-      message_pfx = grpc_slice_malloc(14 + msg_len_len);
-      p = GRPC_SLICE_START_PTR(message_pfx);
-      *p++ = 0x40;
-      *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, 0, 0, p,
-                               (uint32_t)msg_len_len);
-      p += msg_len_len;
-      GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx));
-      len += (uint32_t)GRPC_SLICE_LENGTH(message_pfx);
-      len += (uint32_t)msg_len;
-    }
-
-    hdr = grpc_slice_malloc(9);
-    p = GRPC_SLICE_START_PTR(hdr);
-    *p++ = (uint8_t)(len >> 16);
-    *p++ = (uint8_t)(len >> 8);
-    *p++ = (uint8_t)(len);
-    *p++ = GRPC_CHTTP2_FRAME_HEADER;
-    *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS;
-    *p++ = (uint8_t)(s->id >> 24);
-    *p++ = (uint8_t)(s->id >> 16);
-    *p++ = (uint8_t)(s->id >> 8);
-    *p++ = (uint8_t)(s->id);
-    GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr));
-
-    grpc_slice_buffer_add(&t->qbuf, hdr);
-    grpc_slice_buffer_add(&t->qbuf, status_hdr);
-    if (optional_message) {
-      grpc_slice_buffer_add(&t->qbuf, message_pfx);
-      grpc_slice_buffer_add(&t->qbuf,
-                            grpc_slice_from_copied_string(optional_message));
-    }
-    grpc_slice_buffer_add(
-        &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_CHTTP2_NO_ERROR,
-                                                &s->stats.outgoing));
+    *p++ = 'g';
+    *p++ = 'e';
+    GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 0, 0, p, (uint32_t)msg_len_len);
+    p += msg_len_len;
+    GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx));
+    len += (uint32_t)GRPC_SLICE_LENGTH(message_pfx);
+    len += (uint32_t)msg_len;
   }
 
-  const char *msg = grpc_error_get_str(error, GRPC_ERROR_STR_GRPC_MESSAGE);
-  bool free_msg = false;
-  if (msg == NULL) {
-    free_msg = true;
-    msg = grpc_error_string(error);
+  hdr = grpc_slice_malloc(9);
+  p = GRPC_SLICE_START_PTR(hdr);
+  *p++ = (uint8_t)(len >> 16);
+  *p++ = (uint8_t)(len >> 8);
+  *p++ = (uint8_t)(len);
+  *p++ = GRPC_CHTTP2_FRAME_HEADER;
+  *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS;
+  *p++ = (uint8_t)(s->id >> 24);
+  *p++ = (uint8_t)(s->id >> 16);
+  *p++ = (uint8_t)(s->id >> 8);
+  *p++ = (uint8_t)(s->id);
+  GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr));
+
+  grpc_slice_buffer_add(&t->qbuf, hdr);
+  grpc_slice_buffer_add(&t->qbuf, status_hdr);
+  if (msg != NULL) {
+    grpc_slice_buffer_add(&t->qbuf, message_pfx);
+    grpc_slice_buffer_add(&t->qbuf, grpc_slice_from_copied_string(msg));
   }
-  grpc_slice msg_slice = grpc_slice_from_copied_string(msg);
-  grpc_chttp2_fake_status(exec_ctx, t, s, grpc_status, &msg_slice);
-  if (free_msg) grpc_error_free_string(msg);
+  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(exec_ctx, t, s, 1, 1, error);
   grpc_chttp2_initiate_write(exec_ctx, t, false, "close_from_api");
@@ -1827,8 +1785,10 @@
   if (parse_error == GRPC_ERROR_NONE &&
       (parse_error = grpc_http_parser_eof(&parser)) == GRPC_ERROR_NONE) {
     error = grpc_error_set_int(
-        GRPC_ERROR_CREATE("Trying to connect an http1.x server"),
-        GRPC_ERROR_INT_HTTP_STATUS, response.status);
+        grpc_error_set_int(
+            GRPC_ERROR_CREATE("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);
 
@@ -2089,6 +2049,8 @@
   grpc_closure_sched(exec_ctx, bs->on_next, GRPC_ERROR_REF(error));
   bs->on_next = NULL;
   GRPC_ERROR_UNREF(bs->error);
+  grpc_chttp2_cancel_stream(exec_ctx, bs->transport, bs->stream,
+                            GRPC_ERROR_REF(error));
   bs->error = error;
 }
 
@@ -2197,8 +2159,10 @@
       gpr_log(GPR_DEBUG, "HTTP2: %s - send goaway to free memory",
               t->peer_string);
     }
-    send_goaway(exec_ctx, t, GRPC_CHTTP2_ENHANCE_YOUR_CALM,
-                grpc_slice_from_static_string("Buffers full"));
+    send_goaway(exec_ctx, t,
+                grpc_error_set_int(GRPC_ERROR_CREATE("Buffers full"),
+                                   GRPC_ERROR_INT_HTTP2_ERROR,
+                                   GRPC_HTTP2_ENHANCE_YOUR_CALM));
   } else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace) {
     gpr_log(GPR_DEBUG,
             "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR
@@ -2227,7 +2191,7 @@
     grpc_chttp2_cancel_stream(
         exec_ctx, t, s, grpc_error_set_int(GRPC_ERROR_CREATE("Buffers full"),
                                            GRPC_ERROR_INT_HTTP2_ERROR,
-                                           GRPC_CHTTP2_ENHANCE_YOUR_CALM));
+                                           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
diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
index 20043f5..7d5beed 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
@@ -39,8 +39,7 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/transport/chttp2/transport/frame.h"
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
-#include "src/core/ext/transport/chttp2/transport/status_conversion.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) {
@@ -109,17 +108,9 @@
                       (((uint32_t)p->reason_bytes[2]) << 8) |
                       (((uint32_t)p->reason_bytes[3]));
     grpc_error *error = GRPC_ERROR_NONE;
-    if (reason != GRPC_CHTTP2_NO_ERROR || s->header_frames_received < 2) {
+    if (reason != GRPC_HTTP2_NO_ERROR || s->header_frames_received < 2) {
       error = grpc_error_set_int(GRPC_ERROR_CREATE("RST_STREAM"),
                                  GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)reason);
-      grpc_status_code status_code = grpc_chttp2_http2_error_to_grpc_status(
-          (grpc_chttp2_error_code)reason, s->deadline);
-      char *status_details;
-      gpr_asprintf(&status_details, "Received RST_STREAM with error code %d",
-                   reason);
-      grpc_slice slice_details = grpc_slice_from_copied_string(status_details);
-      gpr_free(status_details);
-      grpc_chttp2_fake_status(exec_ctx, t, s, status_code, &slice_details);
     }
     grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, true, error);
   }
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.c b/src/core/ext/transport/chttp2/transport/frame_settings.c
index 98facae..be9b663 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.c
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.c
@@ -43,8 +43,8 @@
 
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/ext/transport/chttp2/transport/frame.h"
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/transport/http2_errors.h"
 
 #define MAX_MAX_HEADER_LIST_SIZE (1024 * 1024 * 1024)
 
@@ -52,21 +52,21 @@
 const grpc_chttp2_setting_parameters
     grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = {
         {NULL, 0, 0, 0, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
-         GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_HTTP2_PROTOCOL_ERROR},
         {"HEADER_TABLE_SIZE", 4096, 0, 0xffffffff,
-         GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
         {"ENABLE_PUSH", 1, 0, 1, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
-         GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_HTTP2_PROTOCOL_ERROR},
         {"MAX_CONCURRENT_STREAMS", 0xffffffffu, 0, 0xffffffffu,
-         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
         {"INITIAL_WINDOW_SIZE", 65535, 0, 0x7fffffffu,
          GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
-         GRPC_CHTTP2_FLOW_CONTROL_ERROR},
+         GRPC_HTTP2_FLOW_CONTROL_ERROR},
         {"MAX_FRAME_SIZE", 16384, 16384, 16777215,
-         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
         {"MAX_HEADER_LIST_SIZE", MAX_MAX_HEADER_LIST_SIZE, 0,
          MAX_MAX_HEADER_LIST_SIZE, GRPC_CHTTP2_CLAMP_INVALID_VALUE,
-         GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_HTTP2_PROTOCOL_ERROR},
 };
 
 static uint8_t *fill_header(uint8_t *out, uint32_t length, uint8_t flags) {
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
index 49a8326..63df8e1 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
@@ -49,6 +49,7 @@
 #include "src/core/ext/transport/chttp2/transport/hpack_table.h"
 #include "src/core/ext/transport/chttp2/transport/varint.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"
@@ -64,6 +65,10 @@
 /* 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 = {NULL, NULL};
+static const grpc_slice terminal_slice = {&terminal_slice_refcount,
+                                          .data.refcounted = {0, 0}};
+
 extern int grpc_http_trace;
 
 typedef struct {
@@ -185,9 +190,12 @@
 
 /* add an element to the decoder table */
 static void add_elem(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c,
-                     grpc_mdelem *elem) {
-  uint32_t key_hash = elem->key->hash;
-  uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash);
+                     grpc_mdelem elem) {
+  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);
   uint32_t new_index = c->tail_remote_index + c->table_elems + 1;
   size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem);
 
@@ -212,17 +220,18 @@
   c->table_elems++;
 
   /* Store this element into {entries,indices}_elem */
-  if (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == 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 (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == elem) {
+  } 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 (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == NULL) {
+  } 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 (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == NULL) {
+  } 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;
@@ -241,24 +250,34 @@
 
   /* do exactly the same for the key (so we can find by that again too) */
 
-  if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == elem->key) {
+  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 (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key) {
+  } 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)] == NULL) {
-    c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key);
+  } 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)] == NULL) {
-    c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key);
+  } 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_MDSTR_UNREF(exec_ctx, c->entries_keys[HASH_FRAGMENT_2(key_hash)]);
-    c->entries_keys[HASH_FRAGMENT_2(key_hash)] = GRPC_MDSTR_REF(elem->key);
+    grpc_slice_unref_internal(exec_ctx,
+                              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_MDSTR_UNREF(exec_ctx, c->entries_keys[HASH_FRAGMENT_3(key_hash)]);
-    c->entries_keys[HASH_FRAGMENT_3(key_hash)] = GRPC_MDSTR_REF(elem->key);
+    grpc_slice_unref_internal(exec_ctx,
+                              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;
   }
 }
@@ -270,20 +289,18 @@
                            len);
 }
 
-static grpc_slice get_wire_value(grpc_mdelem *elem, uint8_t *huffman_prefix) {
-  if (grpc_is_binary_header(
-          (const char *)GRPC_SLICE_START_PTR(elem->key->slice),
-          GRPC_SLICE_LENGTH(elem->key->slice))) {
+static grpc_slice get_wire_value(grpc_mdelem elem, uint8_t *huffman_prefix) {
+  if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
     *huffman_prefix = 0x80;
-    return grpc_mdstr_as_base64_encoded_and_huffman_compressed(elem->value);
+    return grpc_chttp2_base64_encode_and_huffman_compress(GRPC_MDVALUE(elem));
   }
   /* TODO(ctiller): opportunistically compress non-binary headers */
   *huffman_prefix = 0x00;
-  return elem->value->slice;
+  return grpc_slice_ref_internal(GRPC_MDVALUE(elem));
 }
 
 static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor *c,
-                               uint32_t key_index, grpc_mdelem *elem,
+                               uint32_t key_index, grpc_mdelem elem,
                                framer_state *st) {
   uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2);
   uint8_t huffman_prefix;
@@ -296,11 +313,11 @@
                            add_tiny_header_data(st, len_pfx), len_pfx);
   GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, huffman_prefix,
                            add_tiny_header_data(st, len_val_len), len_val_len);
-  add_header_data(st, grpc_slice_ref_internal(value_slice));
+  add_header_data(st, value_slice);
 }
 
 static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor *c,
-                              uint32_t key_index, grpc_mdelem *elem,
+                              uint32_t key_index, grpc_mdelem elem,
                               framer_state *st) {
   uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4);
   uint8_t huffman_prefix;
@@ -313,12 +330,12 @@
                            add_tiny_header_data(st, len_pfx), len_pfx);
   GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, huffman_prefix,
                            add_tiny_header_data(st, len_val_len), len_val_len);
-  add_header_data(st, grpc_slice_ref_internal(value_slice));
+  add_header_data(st, value_slice);
 }
 
 static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c,
-                                 grpc_mdelem *elem, framer_state *st) {
-  uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(elem->key->slice);
+                                 grpc_mdelem elem, framer_state *st) {
+  uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
   uint8_t huffman_prefix;
   grpc_slice value_slice = get_wire_value(elem, &huffman_prefix);
   uint32_t len_val = (uint32_t)GRPC_SLICE_LENGTH(value_slice);
@@ -329,15 +346,15 @@
   *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(elem->key->slice));
+  add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem)));
   GRPC_CHTTP2_WRITE_VARINT(len_val, 1, huffman_prefix,
                            add_tiny_header_data(st, len_val_len), len_val_len);
-  add_header_data(st, grpc_slice_ref_internal(value_slice));
+  add_header_data(st, value_slice);
 }
 
 static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c,
-                                grpc_mdelem *elem, framer_state *st) {
-  uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(elem->key->slice);
+                                grpc_mdelem elem, framer_state *st) {
+  uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
   uint8_t huffman_prefix;
   grpc_slice value_slice = get_wire_value(elem, &huffman_prefix);
   uint32_t len_val = (uint32_t)GRPC_SLICE_LENGTH(value_slice);
@@ -348,10 +365,10 @@
   *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(elem->key->slice));
+  add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem)));
   GRPC_CHTTP2_WRITE_VARINT(len_val, 1, huffman_prefix,
                            add_tiny_header_data(st, len_val_len), len_val_len);
-  add_header_data(st, grpc_slice_ref_internal(value_slice));
+  add_header_data(st, value_slice);
 }
 
 static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor *c,
@@ -369,15 +386,9 @@
 
 /* encode an mdelem */
 static void hpack_enc(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c,
-                      grpc_mdelem *elem, framer_state *st) {
-  uint32_t key_hash = elem->key->hash;
-  uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash);
-  size_t decoder_space_usage;
-  uint32_t indices_key;
-  int should_add_elem;
-
-  GPR_ASSERT(GRPC_SLICE_LENGTH(elem->key->slice) > 0);
-  if (GRPC_SLICE_START_PTR(elem->key->slice)[0] != ':') { /* regular header */
+                      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(
@@ -385,11 +396,39 @@
         "Reserved header (colon-prefixed) happening after regular ones.");
   }
 
+  if (grpc_http_trace && !GRPC_MDELEM_IS_INTERNED(elem)) {
+    char *k = grpc_slice_to_c_string(GRPC_MDKEY(elem));
+    char *v = grpc_slice_to_c_string(GRPC_MDVALUE(elem));
+    gpr_log(
+        GPR_DEBUG,
+        "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);
+  }
+  if (!GRPC_MDELEM_IS_INTERNED(elem)) {
+    emit_lithdr_noidx_v(c, elem, st);
+    return;
+  }
+
+  uint32_t key_hash;
+  uint32_t value_hash;
+  uint32_t elem_hash;
+  size_t decoder_space_usage;
+  uint32_t indices_key;
+  int should_add_elem;
+
+  key_hash = grpc_slice_hash(GRPC_MDKEY(elem));
+  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 (c->entries_elems[HASH_FRAGMENT_2(elem_hash)] == elem &&
+  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)]),
@@ -397,7 +436,7 @@
     return;
   }
 
-  if (c->entries_elems[HASH_FRAGMENT_3(elem_hash)] == elem &&
+  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)]),
@@ -414,7 +453,8 @@
   /* no hits for the elem... maybe there's a key? */
 
   indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)];
-  if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == elem->key &&
+  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) */
     if (should_add_elem) {
@@ -429,7 +469,8 @@
   }
 
   indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)];
-  if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key &&
+  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) */
     if (should_add_elem) {
@@ -463,11 +504,11 @@
                          grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
                          framer_state *st) {
   char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
-  grpc_mdelem *mdelem;
+  grpc_mdelem mdelem;
   grpc_http2_encode_timeout(
       gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str);
-  mdelem = grpc_mdelem_from_metadata_strings(
-      exec_ctx, GRPC_MDSTR_GRPC_TIMEOUT, grpc_mdstr_from_string(timeout_str));
+  mdelem = grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_TIMEOUT,
+                                   grpc_slice_from_copied_string(timeout_str));
   hpack_enc(exec_ctx, c, mdelem, st);
   GRPC_MDELEM_UNREF(exec_ctx, mdelem);
 }
@@ -484,14 +525,19 @@
       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_exec_ctx *exec_ctx,
                                           grpc_chttp2_hpack_compressor *c) {
   int i;
   for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) {
-    if (c->entries_keys[i]) GRPC_MDSTR_UNREF(exec_ctx, c->entries_keys[i]);
-    if (c->entries_elems[i]) GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[i]);
+    if (c->entries_keys[i].refcount != &terminal_slice_refcount) {
+      grpc_slice_unref_internal(exec_ctx, c->entries_keys[i]);
+    }
+    GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[i]);
   }
   gpr_free(c->table_elem_size);
 }
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
index 3a35496..83ba5b1 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
@@ -74,8 +74,8 @@
 
   /* entry tables for keys & elems: these tables track values that have been
      seen and *may* be in the decompressor table */
-  grpc_mdstr *entries_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES];
-  grpc_mdelem *entries_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES];
+  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];
 
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.c b/src/core/ext/transport/chttp2/transport/hpack_parser.c
index 8b91cc7..40f5120 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.c
@@ -50,9 +50,13 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/http2_errors.h"
+
+/* TODO(ctiller): remove before submission */
+#include "src/core/lib/slice/slice_string_helpers.h"
 
 extern int grpc_http_trace;
 
@@ -668,8 +672,22 @@
 
 /* emission helpers */
 static grpc_error *on_hdr(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p,
-                          grpc_mdelem *md, int add_to_table) {
+                          grpc_mdelem md, int add_to_table) {
+  if (grpc_http_trace && !GRPC_MDELEM_IS_INTERNED(md)) {
+    char *k = grpc_slice_to_c_string(GRPC_MDKEY(md));
+    char *v = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+    gpr_log(
+        GPR_DEBUG,
+        "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(exec_ctx, &p->table, md);
     if (err != GRPC_ERROR_NONE) return err;
   }
@@ -681,10 +699,28 @@
   return GRPC_ERROR_NONE;
 }
 
-static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p,
-                               grpc_chttp2_hpack_parser_string *str) {
-  grpc_mdstr *s = grpc_mdstr_from_buffer((uint8_t *)str->str, str->length);
-  str->length = 0;
+static grpc_slice take_string(grpc_exec_ctx *exec_ctx,
+                              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(exec_ctx, 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;
 }
 
@@ -771,8 +807,8 @@
                                         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 (md == NULL) {
+  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("Invalid HPACK index received"),
                            GRPC_ERROR_INT_INDEX, (intptr_t)p->index),
@@ -813,12 +849,13 @@
                                         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(md != NULL); /* handled in string parsing */
-  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
-                                            exec_ctx, GRPC_MDSTR_REF(md->key),
-                                            take_string(p, &p->value)),
-                           1);
+  grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+  GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  grpc_error *err = on_hdr(
+      exec_ctx, p,
+      grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
+                              take_string(exec_ctx, p, &p->value, true)),
+      1);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -828,10 +865,11 @@
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
-                                            exec_ctx, take_string(p, &p->key),
-                                            take_string(p, &p->value)),
-                           1);
+  grpc_error *err = on_hdr(
+      exec_ctx, p,
+      grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true),
+                              take_string(exec_ctx, p, &p->value, true)),
+      1);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -881,12 +919,13 @@
                                         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(md != NULL); /* handled in string parsing */
-  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
-                                            exec_ctx, GRPC_MDSTR_REF(md->key),
-                                            take_string(p, &p->value)),
-                           0);
+  grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+  GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  grpc_error *err = on_hdr(
+      exec_ctx, p,
+      grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
+                              take_string(exec_ctx, p, &p->value, false)),
+      0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -896,10 +935,11 @@
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
-                                            exec_ctx, take_string(p, &p->key),
-                                            take_string(p, &p->value)),
-                           0);
+  grpc_error *err = on_hdr(
+      exec_ctx, p,
+      grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true),
+                              take_string(exec_ctx, p, &p->value, false)),
+      0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -949,12 +989,13 @@
                                         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(md != NULL); /* handled in string parsing */
-  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
-                                            exec_ctx, GRPC_MDSTR_REF(md->key),
-                                            take_string(p, &p->value)),
-                           0);
+  grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+  GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  grpc_error *err = on_hdr(
+      exec_ctx, p,
+      grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
+                              take_string(exec_ctx, p, &p->value, false)),
+      0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -964,10 +1005,11 @@
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
-  grpc_error *err = on_hdr(exec_ctx, p, grpc_mdelem_from_metadata_strings(
-                                            exec_ctx, take_string(p, &p->key),
-                                            take_string(p, &p->value)),
-                           0);
+  grpc_error *err = on_hdr(
+      exec_ctx, p,
+      grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true),
+                              take_string(exec_ctx, p, &p->value, false)),
+      0);
   if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err);
   return parse_begin(exec_ctx, p, cur, end);
 }
@@ -1261,14 +1303,15 @@
 static void append_bytes(grpc_chttp2_hpack_parser_string *str,
                          const uint8_t *data, size_t length) {
   if (length == 0) return;
-  if (length + str->length > str->capacity) {
-    GPR_ASSERT(str->length + length <= UINT32_MAX);
-    str->capacity = (uint32_t)(str->length + length);
-    str->str = gpr_realloc(str->str, str->capacity);
+  if (length + str->data.copied.length > str->data.copied.capacity) {
+    GPR_ASSERT(str->data.copied.length + length <= UINT32_MAX);
+    str->data.copied.capacity = (uint32_t)(str->data.copied.length + length);
+    str->data.copied.str =
+        gpr_realloc(str->data.copied.str, str->data.copied.capacity);
   }
-  memcpy(str->str + str->length, data, length);
-  GPR_ASSERT(length <= UINT32_MAX - str->length);
-  str->length += (uint32_t)length;
+  memcpy(str->data.copied.str + str->data.copied.length, data, length);
+  GPR_ASSERT(length <= UINT32_MAX - str->data.copied.length);
+  str->data.copied.length += (uint32_t)length;
 }
 
 static grpc_error *append_string(grpc_exec_ctx *exec_ctx,
@@ -1351,11 +1394,9 @@
       exec_ctx, p, cur, end, GRPC_ERROR_CREATE("Should never reach here")));
 }
 
-/* append a null terminator to a string */
 static grpc_error *finish_str(grpc_exec_ctx *exec_ctx,
                               grpc_chttp2_hpack_parser *p, const uint8_t *cur,
                               const uint8_t *end) {
-  uint8_t terminator = 0;
   uint8_t decoded[2];
   uint32_t bits;
   grpc_chttp2_hpack_parser_string *str = p->parsing.str;
@@ -1396,8 +1437,6 @@
       append_bytes(str, decoded, 2);
       break;
   }
-  append_bytes(str, &terminator, 1);
-  p->parsing.str->length--; /* don't actually count the null terminator */
   return GRPC_ERROR_NONE;
 }
 
@@ -1472,8 +1511,18 @@
                                       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) >= (intptr_t)p->strlen &&
+      p->current_slice_refcount != NULL) {
+    str->copied = false;
+    str->data.referenced.refcount = p->current_slice_refcount;
+    str->data.referenced.data.refcounted.bytes = (uint8_t *)cur;
+    str->data.referenced.data.refcounted.length = p->strlen;
+    grpc_slice_ref_internal(str->data.referenced);
+    return parse_next(exec_ctx, p, cur + p->strlen, end);
+  }
   p->strgot = 0;
-  str->length = 0;
+  str->copied = true;
+  str->data.copied.length = 0;
   p->parsing.str = str;
   p->huff_state = 0;
   p->binary = binary;
@@ -1490,21 +1539,22 @@
 /* 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.str, p->key.length);
+  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 (!elem) {
+  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("Invalid HPACK index received"),
                            GRPC_ERROR_INT_INDEX, (intptr_t)p->index),
         GRPC_ERROR_INT_SIZE, (intptr_t)p->table.num_ents);
   }
-  *is = grpc_is_binary_header(
-      (const char *)GRPC_SLICE_START_PTR(elem->key->slice),
-      GRPC_SLICE_LENGTH(elem->key->slice));
+  *is = grpc_is_binary_header(GRPC_MDKEY(elem));
   return GRPC_ERROR_NONE;
 }
 
@@ -1539,12 +1589,14 @@
   p->on_header = NULL;
   p->on_header_user_data = NULL;
   p->state = parse_begin;
-  p->key.str = NULL;
-  p->key.capacity = 0;
-  p->key.length = 0;
-  p->value.str = NULL;
-  p->value.capacity = 0;
-  p->value.length = 0;
+  p->key.data.referenced = grpc_empty_slice();
+  p->key.data.copied.str = NULL;
+  p->key.data.copied.capacity = 0;
+  p->key.data.copied.length = 0;
+  p->value.data.referenced = grpc_empty_slice();
+  p->value.data.copied.str = NULL;
+  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(exec_ctx, &p->table);
@@ -1559,19 +1611,24 @@
                                       grpc_chttp2_hpack_parser *p) {
   grpc_chttp2_hptbl_destroy(exec_ctx, &p->table);
   GRPC_ERROR_UNREF(p->last_error);
-  gpr_free(p->key.str);
-  gpr_free(p->value.str);
+  grpc_slice_unref_internal(exec_ctx, p->key.data.referenced);
+  grpc_slice_unref_internal(exec_ctx, 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_exec_ctx *exec_ctx,
                                            grpc_chttp2_hpack_parser *p,
-                                           const uint8_t *beg,
-                                           const uint8_t *end) {
+                                           grpc_slice slice) {
   /* TODO(ctiller): limit the distance of end from beg, and perform multiple
      steps in the event of a large chunk of data to limit
      stack space usage when no tail call optimization is
      available */
-  return p->state(exec_ctx, p, beg, end);
+  p->current_slice_refcount = slice.refcount;
+  grpc_error *error = p->state(exec_ctx, p, GRPC_SLICE_START_PTR(slice),
+                               GRPC_SLICE_END_PTR(slice));
+  p->current_slice_refcount = NULL;
+  return error;
 }
 
 typedef void (*maybe_complete_func_type)(grpc_exec_ctx *exec_ctx,
@@ -1587,7 +1644,7 @@
   grpc_chttp2_transport *t = s->t;
   if (!s->write_closed) {
     grpc_slice_buffer_add(
-        &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_CHTTP2_NO_ERROR,
+        &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR,
                                                 &s->stats.outgoing));
     grpc_chttp2_initiate_write(exec_ctx, t, false, "force_rst_stream");
     grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, true, GRPC_ERROR_NONE);
@@ -1605,8 +1662,7 @@
   if (s != NULL) {
     s->stats.incoming.header_bytes += GRPC_SLICE_LENGTH(slice);
   }
-  grpc_error *error = grpc_chttp2_hpack_parser_parse(
-      exec_ctx, parser, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_END_PTR(slice));
+  grpc_error *error = grpc_chttp2_hpack_parser_parse(exec_ctx, parser, slice);
   if (error != GRPC_ERROR_NONE) {
     GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
     return error;
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.h b/src/core/ext/transport/chttp2/transport/hpack_parser.h
index 52ccf1e..a817183 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.h
@@ -49,14 +49,20 @@
     const uint8_t *end);
 
 typedef struct {
-  char *str;
-  uint32_t length;
-  uint32_t capacity;
+  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)(grpc_exec_ctx *exec_ctx, void *user_data, grpc_mdelem *md);
+  void (*on_header)(grpc_exec_ctx *exec_ctx, void *user_data, grpc_mdelem md);
   void *on_header_user_data;
 
   grpc_error *last_error;
@@ -67,6 +73,8 @@
   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;
@@ -106,11 +114,9 @@
 
 void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p);
 
-/* returns 1 on success, 0 on error */
 grpc_error *grpc_chttp2_hpack_parser_parse(grpc_exec_ctx *exec_ctx,
                                            grpc_chttp2_hpack_parser *p,
-                                           const uint8_t *beg,
-                                           const uint8_t *end);
+                                           grpc_slice slice);
 
 /* wraps grpc_chttp2_hpack_parser_parse to provide a frame level parser for
    the transport */
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.c b/src/core/ext/transport/chttp2/transport/hpack_table.c
index 26d4036..62dd1b8 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.c
@@ -190,8 +190,11 @@
   tbl->ents = 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_strings(
-        exec_ctx, static_table[i].key, static_table[i].value);
+    tbl->static_ents[i - 1] = grpc_mdelem_from_slices(
+        exec_ctx,
+        grpc_slice_intern(grpc_slice_from_static_string(static_table[i].key)),
+        grpc_slice_intern(
+            grpc_slice_from_static_string(static_table[i].value)));
   }
 }
 
@@ -208,8 +211,8 @@
   gpr_free(tbl->ents);
 }
 
-grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
-                                      uint32_t tbl_index) {
+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];
@@ -222,14 +225,14 @@
     return tbl->ents[offset];
   }
   /* Invalid entry: return error */
-  return NULL;
+  return GRPC_MDNULL;
 }
 
 /* Evict one element from the table */
 static void evict1(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl) {
-  grpc_mdelem *first_ent = tbl->ents[tbl->first_ent];
-  size_t elem_bytes = GRPC_SLICE_LENGTH(first_ent->key->slice) +
-                      GRPC_SLICE_LENGTH(first_ent->value->slice) +
+  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 -= (uint32_t)elem_bytes;
@@ -239,7 +242,7 @@
 }
 
 static void rebuild_ents(grpc_chttp2_hptbl *tbl, uint32_t new_cap) {
-  grpc_mdelem **ents = gpr_malloc(sizeof(*ents) * new_cap);
+  grpc_mdelem *ents = gpr_malloc(sizeof(*ents) * new_cap);
   uint32_t i;
 
   for (i = 0; i < tbl->num_ents; i++) {
@@ -301,10 +304,10 @@
 }
 
 grpc_error *grpc_chttp2_hptbl_add(grpc_exec_ctx *exec_ctx,
-                                  grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
+                                  grpc_chttp2_hptbl *tbl, grpc_mdelem md) {
   /* determine how many bytes of buffer this entry represents */
-  size_t elem_bytes = GRPC_SLICE_LENGTH(md->key->slice) +
-                      GRPC_SLICE_LENGTH(md->value->slice) +
+  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) {
@@ -352,16 +355,16 @@
 }
 
 grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
-    const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
+    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 (md->key != ent->key) continue;
+    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 = md->value == ent->value;
+    r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent));
     if (r.has_value) return r;
   }
 
@@ -369,10 +372,10 @@
   for (i = 0; i < tbl->num_ents; i++) {
     uint32_t idx =
         (uint32_t)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY);
-    grpc_mdelem *ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
-    if (md->key != ent->key) continue;
+    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 = md->value == ent->value;
+    r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent));
     if (r.has_value) return r;
   }
 
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.h b/src/core/ext/transport/chttp2/transport/hpack_table.h
index 144574e..32a0380 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.h
@@ -79,8 +79,8 @@
   /* 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_mdelem *ents;
+  grpc_mdelem static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY];
 } grpc_chttp2_hptbl;
 
 /* initialize a hpack table */
@@ -94,12 +94,12 @@
                                                      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);
+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_exec_ctx *exec_ctx,
                                   grpc_chttp2_hptbl *tbl,
-                                  grpc_mdelem *md) GRPC_MUST_USE_RESULT;
+                                  grpc_mdelem md) GRPC_MUST_USE_RESULT;
 /* 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 {
@@ -107,6 +107,6 @@
   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);
+    const grpc_chttp2_hptbl *tbl, grpc_mdelem md);
 
 #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H */
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.c b/src/core/ext/transport/chttp2/transport/incoming_metadata.c
index 5d10949..c91b019 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.c
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.c
@@ -57,7 +57,7 @@
 }
 
 void grpc_chttp2_incoming_metadata_buffer_add(
-    grpc_chttp2_incoming_metadata_buffer *buffer, grpc_mdelem *elem) {
+    grpc_chttp2_incoming_metadata_buffer *buffer, grpc_mdelem elem) {
   GPR_ASSERT(!buffer->published);
   if (buffer->capacity == buffer->count) {
     buffer->capacity = GPR_MAX(8, 2 * buffer->capacity);
@@ -68,6 +68,19 @@
   buffer->size += GRPC_MDELEM_LENGTH(elem);
 }
 
+void grpc_chttp2_incoming_metadata_buffer_replace_or_add(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer,
+    grpc_mdelem elem) {
+  for (size_t i = 0; i < buffer->count; i++) {
+    if (grpc_slice_eq(GRPC_MDKEY(buffer->elems[i].md), GRPC_MDKEY(elem))) {
+      GRPC_MDELEM_UNREF(exec_ctx, buffer->elems[i].md);
+      buffer->elems[i].md = elem;
+      return;
+    }
+  }
+  grpc_chttp2_incoming_metadata_buffer_add(buffer, elem);
+}
+
 void grpc_chttp2_incoming_metadata_buffer_set_deadline(
     grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline) {
   GPR_ASSERT(!buffer->published);
@@ -75,21 +88,20 @@
 }
 
 void grpc_chttp2_incoming_metadata_buffer_publish(
-    grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch) {
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer,
+    grpc_metadata_batch *batch) {
   GPR_ASSERT(!buffer->published);
   buffer->published = 1;
   if (buffer->count > 0) {
     size_t i;
-    for (i = 1; i < buffer->count; i++) {
-      buffer->elems[i].prev = &buffer->elems[i - 1];
+    for (i = 0; i < buffer->count; i++) {
+      /* TODO(ctiller): do something better here */
+      if (!GRPC_LOG_IF_ERROR("grpc_chttp2_incoming_metadata_buffer_publish",
+                             grpc_metadata_batch_link_tail(
+                                 exec_ctx, batch, &buffer->elems[i]))) {
+        GRPC_MDELEM_UNREF(exec_ctx, buffer->elems[i].md);
+      }
     }
-    for (i = 0; i < buffer->count - 1; i++) {
-      buffer->elems[i].next = &buffer->elems[i + 1];
-    }
-    buffer->elems[0].prev = NULL;
-    buffer->elems[buffer->count - 1].next = NULL;
-    batch->list.head = &buffer->elems[0];
-    batch->list.tail = &buffer->elems[buffer->count - 1];
   } else {
     batch->list.head = batch->list.tail = NULL;
   }
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
index 7a0c4da..1eac6fc 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.h
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
@@ -51,10 +51,14 @@
 void grpc_chttp2_incoming_metadata_buffer_destroy(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer);
 void grpc_chttp2_incoming_metadata_buffer_publish(
-    grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch);
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer,
+    grpc_metadata_batch *batch);
 
 void grpc_chttp2_incoming_metadata_buffer_add(
-    grpc_chttp2_incoming_metadata_buffer *buffer, grpc_mdelem *elem);
+    grpc_chttp2_incoming_metadata_buffer *buffer, grpc_mdelem elem);
+void grpc_chttp2_incoming_metadata_buffer_replace_or_add(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer,
+    grpc_mdelem elem);
 void grpc_chttp2_incoming_metadata_buffer_set_deadline(
     grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline);
 
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index ea7beb4..ee5edc9 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -618,8 +618,7 @@
                                uint32_t stream_id, int64_t val1, int64_t val2);
 
 void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                             grpc_chttp2_stream *stream,
-                             grpc_status_code status, grpc_slice *details);
+                             grpc_chttp2_stream *stream, grpc_error *error);
 void grpc_chttp2_mark_stream_closed(grpc_exec_ctx *exec_ctx,
                                     grpc_chttp2_transport *t,
                                     grpc_chttp2_stream *s, int close_reads,
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index 4fb5dc7..f58cd69 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -39,10 +39,11 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
-#include "src/core/ext/transport/chttp2/transport/status_conversion.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_exec_ctx *exec_ctx,
@@ -200,7 +201,7 @@
         return err;
       }
       if (t->incoming_frame_size == 0) {
-        err = parse_frame_slice(exec_ctx, t, gpr_empty_slice(), 1);
+        err = parse_frame_slice(exec_ctx, t, grpc_empty_slice(), 1);
         if (err != GRPC_ERROR_NONE) {
           return err;
         }
@@ -335,7 +336,7 @@
   return GRPC_ERROR_NONE;
 }
 
-static void skip_header(grpc_exec_ctx *exec_ctx, void *tp, grpc_mdelem *md) {
+static void skip_header(grpc_exec_ctx *exec_ctx, void *tp, grpc_mdelem md) {
   GRPC_MDELEM_UNREF(exec_ctx, md);
 }
 
@@ -432,7 +433,7 @@
     }
     grpc_slice_buffer_add(
         &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id,
-                                                GRPC_CHTTP2_PROTOCOL_ERROR,
+                                                GRPC_HTTP2_PROTOCOL_ERROR,
                                                 &s->stats.outgoing));
     return init_skip_frame_parser(exec_ctx, t, 0);
   } else {
@@ -443,7 +444,7 @@
 static void free_timeout(void *p) { gpr_free(p); }
 
 static void on_initial_header(grpc_exec_ctx *exec_ctx, void *tp,
-                              grpc_mdelem *md) {
+                              grpc_mdelem md) {
   grpc_chttp2_transport *t = tp;
   grpc_chttp2_stream *s = t->incoming_stream;
 
@@ -451,32 +452,42 @@
 
   GPR_ASSERT(s != NULL);
 
-  GRPC_CHTTP2_IF_TRACING(gpr_log(
-      GPR_INFO, "HTTP:%d:HDR:%s: %s: %s", s->id, t->is_client ? "CLI" : "SVR",
-      grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)));
+  if (grpc_http_trace) {
+    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 (md->key == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) {
+  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 (md->key == GRPC_MDSTR_GRPC_TIMEOUT) {
+  if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) {
     gpr_timespec *cached_timeout = grpc_mdelem_get_user_data(md, free_timeout);
-    if (!cached_timeout) {
+    gpr_timespec timeout;
+    if (cached_timeout == NULL) {
       /* not already parsed: parse it now, and store the result away */
       cached_timeout = gpr_malloc(sizeof(gpr_timespec));
-      if (!grpc_http2_decode_timeout(grpc_mdstr_as_c_string(md->value),
-                                     cached_timeout)) {
-        gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'",
-                grpc_mdstr_as_c_string(md->value));
+      if (!grpc_http2_decode_timeout(GRPC_MDVALUE(md), cached_timeout)) {
+        char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+        gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val);
+        gpr_free(val);
         *cached_timeout = gpr_inf_future(GPR_TIMESPAN);
       }
-      cached_timeout =
-          grpc_mdelem_set_user_data(md, free_timeout, cached_timeout);
+      timeout = *cached_timeout;
+      grpc_mdelem_set_user_data(md, free_timeout, cached_timeout);
+    } else {
+      timeout = *cached_timeout;
     }
     grpc_chttp2_incoming_metadata_buffer_set_deadline(
         &s->metadata_buffer[0],
-        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), *cached_timeout));
+        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), timeout));
     GRPC_MDELEM_UNREF(exec_ctx, md);
   } else {
     const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md);
@@ -505,7 +516,7 @@
 }
 
 static void on_trailing_header(grpc_exec_ctx *exec_ctx, void *tp,
-                               grpc_mdelem *md) {
+                               grpc_mdelem md) {
   grpc_chttp2_transport *t = tp;
   grpc_chttp2_stream *s = t->incoming_stream;
 
@@ -513,11 +524,18 @@
 
   GPR_ASSERT(s != NULL);
 
-  GRPC_CHTTP2_IF_TRACING(gpr_log(
-      GPR_INFO, "HTTP:%d:TRL:%s: %s: %s", s->id, t->is_client ? "CLI" : "SVR",
-      grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)));
+  if (grpc_http_trace) {
+    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 (md->key == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) {
+  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;
   }
@@ -733,14 +751,13 @@
     if (grpc_http_trace) {
       const char *msg = grpc_error_string(err);
       gpr_log(GPR_ERROR, "%s", msg);
-      grpc_error_free_string(msg);
     }
     grpc_chttp2_parsing_become_skip_parser(exec_ctx, t);
     if (s) {
       s->forced_close_error = err;
       grpc_slice_buffer_add(
           &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id,
-                                                  GRPC_CHTTP2_PROTOCOL_ERROR,
+                                                  GRPC_HTTP2_PROTOCOL_ERROR,
                                                   &s->stats.outgoing));
     } else {
       GRPC_ERROR_UNREF(err);
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index ef2010a..148e384 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -37,9 +37,9 @@
 
 #include <grpc/support/log.h>
 
-#include "src/core/ext/transport/chttp2/transport/http2_errors.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) {
@@ -74,6 +74,15 @@
   GRPC_ERROR_UNREF(error);
 }
 
+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;
+}
+
 bool grpc_chttp2_begin_write(grpc_exec_ctx *exec_ctx,
                              grpc_chttp2_transport *t) {
   grpc_chttp2_stream *s;
@@ -101,8 +110,11 @@
 
   if (t->outgoing_window > 0) {
     while (grpc_chttp2_list_pop_stalled_by_transport(t, &s)) {
-      grpc_chttp2_become_writable(exec_ctx, t, s, false,
-                                  "transport.read_flow_control");
+      if (!t->closed && grpc_chttp2_list_add_writable_stream(t, s) &&
+          stream_ref_if_not_destroyed(&s->refcount->refs)) {
+        grpc_chttp2_initiate_write(exec_ctx, t, false,
+                                   "transport.read_flow_control");
+      }
     }
   }
 
@@ -164,7 +176,7 @@
             s->sent_trailing_metadata = true;
             if (!t->is_client && !s->read_closed) {
               grpc_slice_buffer_add(&t->outbuf, grpc_chttp2_rst_stream_create(
-                                                    s->id, GRPC_CHTTP2_NO_ERROR,
+                                                    s->id, GRPC_HTTP2_NO_ERROR,
                                                     &s->stats.outgoing));
             }
           }
@@ -197,7 +209,7 @@
         if (!t->is_client && !s->read_closed) {
           grpc_slice_buffer_add(
               &t->outbuf, grpc_chttp2_rst_stream_create(
-                              s->id, GRPC_CHTTP2_NO_ERROR, &s->stats.outgoing));
+                              s->id, GRPC_HTTP2_NO_ERROR, &s->stats.outgoing));
         }
         now_writing = true;
       }
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c
index 6f58163..1275a4b 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.c
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.c
@@ -44,6 +44,8 @@
 #include "src/core/ext/transport/chttp2/transport/incoming_metadata.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/support/string.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/transport/metadata_batch.h"
@@ -437,9 +439,11 @@
   for (size_t i = 0; i < headers->count; i++) {
     grpc_chttp2_incoming_metadata_buffer_add(
         &s->state.rs.initial_metadata,
-        grpc_mdelem_from_metadata_strings(
-            &exec_ctx, grpc_mdstr_from_string(headers->headers[i].key),
-            grpc_mdstr_from_string(headers->headers[i].value)));
+        grpc_mdelem_from_slices(
+            &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string(
+                           headers->headers[i].key)),
+            grpc_slice_intern(
+                grpc_slice_from_static_string(headers->headers[i].value))));
   }
   s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true;
   if (!(s->state.state_op_done[OP_CANCEL_ERROR] ||
@@ -534,9 +538,11 @@
                trailers->headers[i].value);
     grpc_chttp2_incoming_metadata_buffer_add(
         &s->state.rs.trailing_metadata,
-        grpc_mdelem_from_metadata_strings(
-            &exec_ctx, grpc_mdstr_from_string(trailers->headers[i].key),
-            grpc_mdstr_from_string(trailers->headers[i].value)));
+        grpc_mdelem_from_slices(
+            &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string(
+                           trailers->headers[i].key)),
+            grpc_slice_intern(
+                grpc_slice_from_static_string(trailers->headers[i].value))));
     s->state.rs.trailing_metadata_valid = true;
     if (0 == strcmp(trailers->headers[i].key, "grpc-status") &&
         0 != strcmp(trailers->headers[i].value, "0")) {
@@ -616,33 +622,41 @@
   curr = head;
   size_t num_headers = 0;
   while (num_headers < num_headers_available) {
-    grpc_mdelem *mdelem = curr->md;
+    grpc_mdelem mdelem = curr->md;
     curr = curr->next;
-    const char *key = grpc_mdstr_as_c_string(mdelem->key);
-    const char *value = grpc_mdstr_as_c_string(mdelem->value);
-    if (mdelem->key == GRPC_MDSTR_SCHEME ||
-        mdelem->key == GRPC_MDSTR_AUTHORITY) {
+    char *key = grpc_slice_to_c_string(GRPC_MDKEY(mdelem));
+    char *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 (mdelem->key == GRPC_MDSTR_METHOD) {
-      if (mdelem->value == GRPC_MDSTR_PUT) {
+    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 (mdelem->key == GRPC_MDSTR_PATH) {
+    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++;
+    gpr_free(key);
+    gpr_free(value);
     if (curr == NULL) {
       break;
     }
@@ -662,7 +676,7 @@
 
 static bool header_has_authority(grpc_linked_mdelem *head) {
   while (head != NULL) {
-    if (head->md->key == GRPC_MDSTR_AUTHORITY) {
+    if (grpc_slice_eq(GRPC_MDKEY(head->md), GRPC_MDSTR_AUTHORITY)) {
       return true;
     }
     head = head->next;
@@ -843,6 +857,12 @@
     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);
+    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;
     result = ACTION_TAKEN_WITH_CALLBACK;
   } else if (stream_op->recv_initial_metadata &&
@@ -857,7 +877,8 @@
                          GRPC_ERROR_NONE);
     } else {
       grpc_chttp2_incoming_metadata_buffer_publish(
-          &oas->s->state.rs.initial_metadata, stream_op->recv_initial_metadata);
+          exec_ctx, &oas->s->state.rs.initial_metadata,
+          stream_op->recv_initial_metadata);
       grpc_closure_sched(exec_ctx, stream_op->recv_initial_metadata_ready,
                          GRPC_ERROR_NONE);
     }
@@ -1013,7 +1034,7 @@
     CRONET_LOG(GPR_DEBUG, "running: %p  OP_RECV_TRAILING_METADATA", oas);
     if (oas->s->state.rs.trailing_metadata_valid) {
       grpc_chttp2_incoming_metadata_buffer_publish(
-          &oas->s->state.rs.trailing_metadata,
+          exec_ctx, &oas->s->state.rs.trailing_metadata,
           stream_op->recv_trailing_metadata);
       stream_state->rs.trailing_metadata_valid = false;
     }
diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c
index 8f08b42..ec973d4 100644
--- a/src/core/lib/channel/channel_stack.c
+++ b/src/core/lib/channel/channel_stack.c
@@ -170,7 +170,7 @@
     grpc_exec_ctx *exec_ctx, grpc_channel_stack *channel_stack,
     int initial_refs, grpc_iomgr_cb_func destroy, void *destroy_arg,
     grpc_call_context_element *context, const void *transport_server_data,
-    grpc_mdstr *path, gpr_timespec start_time, gpr_timespec deadline,
+    grpc_slice path, gpr_timespec start_time, gpr_timespec deadline,
     grpc_call_stack *call_stack) {
   grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack);
   grpc_call_element_args args;
@@ -288,41 +288,10 @@
       sizeof(grpc_call_stack)));
 }
 
-static void destroy_op(grpc_exec_ctx *exec_ctx, void *op, grpc_error *error) {
-  gpr_free(op);
-}
-
-void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx,
-                                   grpc_call_element *elem) {
-  grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
-  memset(op, 0, sizeof(*op));
-  op->cancel_error = GRPC_ERROR_CANCELLED;
-  op->on_complete =
-      grpc_closure_create(destroy_op, op, grpc_schedule_on_exec_ctx);
-  elem->filter->start_transport_stream_op(exec_ctx, elem, op);
-}
-
-void grpc_call_element_send_cancel_with_message(grpc_exec_ctx *exec_ctx,
-                                                grpc_call_element *elem,
-                                                grpc_status_code status,
-                                                grpc_slice *optional_message) {
-  grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
-  memset(op, 0, sizeof(*op));
-  op->on_complete =
-      grpc_closure_create(destroy_op, op, grpc_schedule_on_exec_ctx);
-  grpc_transport_stream_op_add_cancellation_with_message(exec_ctx, op, status,
-                                                         optional_message);
-  elem->filter->start_transport_stream_op(exec_ctx, elem, op);
-}
-
-void grpc_call_element_send_close_with_message(grpc_exec_ctx *exec_ctx,
-                                               grpc_call_element *elem,
-                                               grpc_status_code status,
-                                               grpc_slice *optional_message) {
-  grpc_transport_stream_op *op = gpr_malloc(sizeof(*op));
-  memset(op, 0, sizeof(*op));
-  op->on_complete =
-      grpc_closure_create(destroy_op, op, grpc_schedule_on_exec_ctx);
-  grpc_transport_stream_op_add_close(exec_ctx, op, status, optional_message);
+void grpc_call_element_signal_error(grpc_exec_ctx *exec_ctx,
+                                    grpc_call_element *elem,
+                                    grpc_error *error) {
+  grpc_transport_stream_op *op = grpc_make_transport_stream_op(NULL);
+  op->cancel_error = error;
   elem->filter->start_transport_stream_op(exec_ctx, elem, op);
 }
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index d9d3a85..1cf07d4 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -81,7 +81,7 @@
   grpc_call_stack *call_stack;
   const void *server_transport_data;
   grpc_call_context_element *context;
-  grpc_mdstr *path;
+  grpc_slice path;
   gpr_timespec start_time;
   gpr_timespec deadline;
 } grpc_call_element_args;
@@ -238,7 +238,7 @@
     grpc_exec_ctx *exec_ctx, grpc_channel_stack *channel_stack,
     int initial_refs, grpc_iomgr_cb_func destroy, void *destroy_arg,
     grpc_call_context_element *context, const void *transport_server_data,
-    grpc_mdstr *path, gpr_timespec start_time, gpr_timespec deadline,
+    grpc_slice path, gpr_timespec start_time, gpr_timespec deadline,
     grpc_call_stack *call_stack);
 /* Set a pollset or a pollset_set for a call stack: must occur before the first
  * op is started */
@@ -299,18 +299,9 @@
 void grpc_call_log_op(char *file, int line, gpr_log_severity severity,
                       grpc_call_element *elem, grpc_transport_stream_op *op);
 
-void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx,
-                                   grpc_call_element *cur_elem);
-
-void grpc_call_element_send_cancel_with_message(grpc_exec_ctx *exec_ctx,
-                                                grpc_call_element *cur_elem,
-                                                grpc_status_code status,
-                                                grpc_slice *optional_message);
-
-void grpc_call_element_send_close_with_message(grpc_exec_ctx *exec_ctx,
-                                               grpc_call_element *cur_elem,
-                                               grpc_status_code status,
-                                               grpc_slice *optional_message);
+void grpc_call_element_signal_error(grpc_exec_ctx *exec_ctx,
+                                    grpc_call_element *cur_elem,
+                                    grpc_error *error);
 
 extern int grpc_trace_channel;
 
diff --git a/src/core/lib/channel/compress_filter.c b/src/core/lib/channel/compress_filter.c
index 337c194..c860d60 100644
--- a/src/core/lib/channel/compress_filter.c
+++ b/src/core/lib/channel/compress_filter.c
@@ -45,6 +45,7 @@
 #include "src/core/lib/compression/message_compress.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/support/string.h"
 #include "src/core/lib/transport/static_metadata.h"
 
@@ -80,39 +81,6 @@
   uint32_t supported_compression_algorithms;
 } channel_data;
 
-/** For each \a md element from the incoming metadata, filter out the entry for
- * "grpc-encoding", using its value to populate the call data's
- * compression_algorithm field. */
-static grpc_mdelem *compression_md_filter(grpc_exec_ctx *exec_ctx,
-                                          void *user_data, grpc_mdelem *md) {
-  grpc_call_element *elem = user_data;
-  call_data *calld = elem->call_data;
-  channel_data *channeld = elem->channel_data;
-
-  if (md->key == GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST) {
-    const char *md_c_str = grpc_mdstr_as_c_string(md->value);
-    if (!grpc_compression_algorithm_parse(md_c_str, strlen(md_c_str),
-                                          &calld->compression_algorithm)) {
-      gpr_log(GPR_ERROR,
-              "Invalid compression algorithm: '%s' (unknown). Ignoring.",
-              md_c_str);
-      calld->compression_algorithm = GRPC_COMPRESS_NONE;
-    }
-    if (!GPR_BITGET(channeld->enabled_algorithms_bitset,
-                    calld->compression_algorithm)) {
-      gpr_log(GPR_ERROR,
-              "Invalid compression algorithm: '%s' (previously disabled). "
-              "Ignoring.",
-              md_c_str);
-      calld->compression_algorithm = GRPC_COMPRESS_NONE;
-    }
-    calld->has_compression_algorithm = 1;
-    return NULL;
-  }
-
-  return md;
-}
-
 static int skip_compression(grpc_call_element *elem, uint32_t flags) {
   call_data *calld = elem->call_data;
   channel_data *channeld = elem->channel_data;
@@ -131,32 +99,65 @@
 }
 
 /** Filter initial metadata */
-static void process_send_initial_metadata(
+static grpc_error *process_send_initial_metadata(
+    grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+    grpc_metadata_batch *initial_metadata) GRPC_MUST_USE_RESULT;
+static grpc_error *process_send_initial_metadata(
     grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     grpc_metadata_batch *initial_metadata) {
+  grpc_error *error;
   call_data *calld = elem->call_data;
   channel_data *channeld = elem->channel_data;
   /* Parse incoming request for compression. If any, it'll be available
    * at calld->compression_algorithm */
-  grpc_metadata_batch_filter(exec_ctx, initial_metadata, compression_md_filter,
-                             elem);
-  if (!calld->has_compression_algorithm) {
+  if (initial_metadata->idx.named.grpc_internal_encoding_request != NULL) {
+    grpc_mdelem md =
+        initial_metadata->idx.named.grpc_internal_encoding_request->md;
+    if (!grpc_compression_algorithm_parse(GRPC_MDVALUE(md),
+                                          &calld->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->compression_algorithm = GRPC_COMPRESS_NONE;
+    }
+    if (!GPR_BITGET(channeld->enabled_algorithms_bitset,
+                    calld->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->compression_algorithm = GRPC_COMPRESS_NONE;
+    }
+    calld->has_compression_algorithm = 1;
+
+    grpc_metadata_batch_remove(
+        exec_ctx, initial_metadata,
+        initial_metadata->idx.named.grpc_internal_encoding_request);
+  } else {
     /* If no algorithm was found in the metadata and we aren't
      * exceptionally skipping compression, fall back to the channel
      * default */
     calld->compression_algorithm = channeld->default_compression_algorithm;
     calld->has_compression_algorithm = 1; /* GPR_TRUE */
   }
+
   /* hint compression algorithm */
-  grpc_metadata_batch_add_tail(
-      initial_metadata, &calld->compression_algorithm_storage,
+  error = grpc_metadata_batch_add_tail(
+      exec_ctx, initial_metadata, &calld->compression_algorithm_storage,
       grpc_compression_encoding_mdelem(calld->compression_algorithm));
 
+  if (error != GRPC_ERROR_NONE) return error;
+
   /* convey supported compression algorithms */
-  grpc_metadata_batch_add_tail(initial_metadata,
-                               &calld->accept_encoding_storage,
-                               GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(
-                                   channeld->supported_compression_algorithms));
+  error = grpc_metadata_batch_add_tail(
+      exec_ctx, initial_metadata, &calld->accept_encoding_storage,
+      GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(
+          channeld->supported_compression_algorithms));
+
+  return error;
 }
 
 static void continue_send_message(grpc_exec_ctx *exec_ctx,
@@ -247,7 +248,12 @@
   GPR_TIMER_BEGIN("compress_start_transport_stream_op", 0);
 
   if (op->send_initial_metadata) {
-    process_send_initial_metadata(exec_ctx, elem, op->send_initial_metadata);
+    grpc_error *error = process_send_initial_metadata(
+        exec_ctx, elem, op->send_initial_metadata);
+    if (error != GRPC_ERROR_NONE) {
+      grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error);
+      return;
+    }
   }
   if (op->send_message != NULL &&
       !skip_compression(elem, op->send_message->flags)) {
diff --git a/src/core/lib/channel/deadline_filter.c b/src/core/lib/channel/deadline_filter.c
index a45a4d4..bc9a2ef 100644
--- a/src/core/lib/channel/deadline_filter.c
+++ b/src/core/lib/channel/deadline_filter.c
@@ -56,10 +56,11 @@
   deadline_state->timer_pending = false;
   gpr_mu_unlock(&deadline_state->timer_mu);
   if (error != GRPC_ERROR_CANCELLED) {
-    grpc_slice msg = grpc_slice_from_static_string("Deadline Exceeded");
-    grpc_call_element_send_cancel_with_message(
-        exec_ctx, elem, GRPC_STATUS_DEADLINE_EXCEEDED, &msg);
-    grpc_slice_unref_internal(exec_ctx, msg);
+    grpc_call_element_signal_error(
+        exec_ctx, elem,
+        grpc_error_set_int(GRPC_ERROR_CREATE("Deadline Exceeded"),
+                           GRPC_ERROR_INT_GRPC_STATUS,
+                           GRPC_STATUS_DEADLINE_EXCEEDED));
   }
   GRPC_CALL_STACK_UNREF(exec_ctx, deadline_state->call_stack, "deadline_timer");
 }
@@ -196,8 +197,7 @@
     grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
     grpc_transport_stream_op* op) {
   grpc_deadline_state* deadline_state = elem->call_data;
-  if (op->cancel_error != GRPC_ERROR_NONE ||
-      op->close_error != GRPC_ERROR_NONE) {
+  if (op->cancel_error != GRPC_ERROR_NONE) {
     cancel_timer_if_needed(exec_ctx, deadline_state);
   } else {
     // Make sure we know when the call is complete, so that we can cancel
@@ -285,8 +285,7 @@
                                              grpc_call_element* elem,
                                              grpc_transport_stream_op* op) {
   server_call_data* calld = elem->call_data;
-  if (op->cancel_error != GRPC_ERROR_NONE ||
-      op->close_error != GRPC_ERROR_NONE) {
+  if (op->cancel_error != GRPC_ERROR_NONE) {
     cancel_timer_if_needed(exec_ctx, &calld->base.deadline_state);
   } else {
     // If we're receiving initial metadata, we need to get the deadline
diff --git a/src/core/lib/channel/http_client_filter.c b/src/core/lib/channel/http_client_filter.c
index d154450..49a2a98 100644
--- a/src/core/lib/channel/http_client_filter.c
+++ b/src/core/lib/channel/http_client_filter.c
@@ -38,6 +38,7 @@
 #include "src/core/lib/profiling/timers.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/support/string.h"
 #include "src/core/lib/transport/static_metadata.h"
 #include "src/core/lib/transport/transport_impl.h"
@@ -88,77 +89,104 @@
 } call_data;
 
 typedef struct channel_data {
-  grpc_mdelem *static_scheme;
-  grpc_mdelem *user_agent;
+  grpc_mdelem static_scheme;
+  grpc_mdelem user_agent;
   size_t max_payload_size_for_get;
 } channel_data;
 
-static grpc_mdelem *client_recv_filter(grpc_exec_ctx *exec_ctx, void *user_data,
-                                       grpc_mdelem *md) {
-  grpc_call_element *elem = user_data;
-  if (md == GRPC_MDELEM_STATUS_200) {
-    return NULL;
-  } else if (md->key == GRPC_MDSTR_STATUS) {
-    char *message_string;
-    gpr_asprintf(&message_string, "Received http2 header with status: %s",
-                 grpc_mdstr_as_c_string(md->value));
-    grpc_slice message = grpc_slice_from_copied_string(message_string);
-    gpr_free(message_string);
-    grpc_call_element_send_close_with_message(exec_ctx, elem,
-                                              GRPC_STATUS_CANCELLED, &message);
-    return NULL;
-  } else if (md->key == GRPC_MDSTR_GRPC_MESSAGE) {
-    grpc_slice pct_decoded_msg =
-        grpc_permissive_percent_decode_slice(md->value->slice);
-    if (grpc_slice_is_equivalent(pct_decoded_msg, md->value->slice)) {
-      grpc_slice_unref_internal(exec_ctx, pct_decoded_msg);
-      return md;
+static grpc_error *client_filter_incoming_metadata(grpc_exec_ctx *exec_ctx,
+                                                   grpc_call_element *elem,
+                                                   grpc_metadata_batch *b) {
+  if (b->idx.named.status != NULL) {
+    if (grpc_mdelem_eq(b->idx.named.status->md, GRPC_MDELEM_STATUS_200)) {
+      grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.status);
     } else {
-      return grpc_mdelem_from_metadata_strings(
-          exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
-          grpc_mdstr_from_slice(exec_ctx, pct_decoded_msg));
+      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(
+                      "Received http2 :status header with non-200 OK status"),
+                  GRPC_ERROR_STR_VALUE, val),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED),
+          GRPC_ERROR_STR_GRPC_MESSAGE, msg);
+      gpr_free(val);
+      gpr_free(msg);
+      return e;
     }
-  } else if (md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
-    return NULL;
-  } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
-    const char *value_str = grpc_mdstr_as_c_string(md->value);
-    if (strncmp(value_str, EXPECTED_CONTENT_TYPE,
-                EXPECTED_CONTENT_TYPE_LENGTH) == 0 &&
-        (value_str[EXPECTED_CONTENT_TYPE_LENGTH] == '+' ||
-         value_str[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. */
-      gpr_log(GPR_INFO, "Unexpected content-type '%s'", value_str);
-    }
-    return NULL;
   }
-  return md;
+
+  if (b->idx.named.grpc_message != NULL) {
+    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(exec_ctx, pct_decoded_msg);
+    } else {
+      grpc_metadata_batch_set_value(exec_ctx, b->idx.named.grpc_message,
+                                    pct_decoded_msg);
+    }
+  }
+
+  if (b->idx.named.content_type != NULL) {
+    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(exec_ctx, b, b->idx.named.content_type);
+  }
+
+  return GRPC_ERROR_NONE;
 }
 
 static void hc_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx,
                                         void *user_data, grpc_error *error) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
-  grpc_metadata_batch_filter(exec_ctx, calld->recv_initial_metadata,
-                             client_recv_filter, elem);
-  grpc_closure_run(exec_ctx, calld->on_done_recv_initial_metadata,
-                   GRPC_ERROR_REF(error));
+  if (error == GRPC_ERROR_NONE) {
+    error = client_filter_incoming_metadata(exec_ctx, elem,
+                                            calld->recv_initial_metadata);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  grpc_closure_run(exec_ctx, calld->on_done_recv_initial_metadata, error);
 }
 
 static void hc_on_recv_trailing_metadata(grpc_exec_ctx *exec_ctx,
                                          void *user_data, grpc_error *error) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
-  grpc_metadata_batch_filter(exec_ctx, calld->recv_trailing_metadata,
-                             client_recv_filter, elem);
-  grpc_closure_run(exec_ctx, calld->on_done_recv_trailing_metadata,
-                   GRPC_ERROR_REF(error));
+  if (error == GRPC_ERROR_NONE) {
+    error = client_filter_incoming_metadata(exec_ctx, elem,
+                                            calld->recv_trailing_metadata);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  grpc_closure_run(exec_ctx, calld->on_done_recv_trailing_metadata, error);
 }
 
 static void hc_on_complete(grpc_exec_ctx *exec_ctx, void *user_data,
@@ -179,15 +207,12 @@
   calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, error);
 }
 
-static grpc_mdelem *client_strip_filter(grpc_exec_ctx *exec_ctx,
-                                        void *user_data, grpc_mdelem *md) {
-  /* eat the things we'd like to set ourselves */
-  if (md->key == GRPC_MDSTR_METHOD) return NULL;
-  if (md->key == GRPC_MDSTR_SCHEME) return NULL;
-  if (md->key == GRPC_MDSTR_TE) return NULL;
-  if (md->key == GRPC_MDSTR_CONTENT_TYPE) return NULL;
-  if (md->key == GRPC_MDSTR_USER_AGENT) return NULL;
-  return md;
+static void remove_if_present(grpc_exec_ctx *exec_ctx,
+                              grpc_metadata_batch *batch,
+                              grpc_metadata_batch_callouts_index idx) {
+  if (batch->idx.array[idx] != NULL) {
+    grpc_metadata_batch_remove(exec_ctx, batch, batch->idx.array[idx]);
+  }
 }
 
 static void continue_send_message(grpc_exec_ctx *exec_ctx,
@@ -226,18 +251,20 @@
   }
 }
 
-static void hc_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                         grpc_transport_stream_op *op) {
+static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx,
+                                grpc_call_element *elem,
+                                grpc_transport_stream_op *op) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
   channel_data *channeld = elem->channel_data;
+  grpc_error *error;
 
   if (op->send_initial_metadata != NULL) {
     /* Decide which HTTP VERB to use. We use GET if the request is marked
     cacheable, and the operation contains both initial metadata and send
     message, and the payload is below the size threshold, and all the data
     for this request is immediately available. */
-    grpc_mdelem *method = GRPC_MDELEM_METHOD_POST;
+    grpc_mdelem method = GRPC_MDELEM_METHOD_POST;
     if ((op->send_initial_metadata_flags &
          GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) &&
         op->send_message != NULL &&
@@ -254,7 +281,7 @@
     }
 
     /* Attempt to read the data from send_message and create a header field. */
-    if (method == GRPC_MDELEM_METHOD_GET) {
+    if (grpc_mdelem_eq(method, GRPC_MDELEM_METHOD_GET)) {
       /* allocate memory to hold the entire payload */
       calld->payload_bytes = gpr_malloc(op->send_message->length);
 
@@ -267,12 +294,14 @@
       if (calld->send_message_blocked == false) {
         /* when all the send_message data is available, then create a MDELEM and
         append to headers */
-        grpc_mdelem *payload_bin = grpc_mdelem_from_metadata_strings(
+        grpc_mdelem payload_bin = grpc_mdelem_from_slices(
             exec_ctx, GRPC_MDSTR_GRPC_PAYLOAD_BIN,
-            grpc_mdstr_from_buffer(calld->payload_bytes,
-                                   op->send_message->length));
-        grpc_metadata_batch_add_tail(op->send_initial_metadata,
-                                     &calld->payload_bin, payload_bin);
+            grpc_slice_from_copied_buffer((const char *)calld->payload_bytes,
+                                          op->send_message->length));
+        error =
+            grpc_metadata_batch_add_tail(exec_ctx, op->send_initial_metadata,
+                                         &calld->payload_bin, payload_bin);
+        if (error != GRPC_ERROR_NONE) return error;
         calld->on_complete = op->on_complete;
         op->on_complete = &calld->hc_on_complete;
         op->send_message = NULL;
@@ -285,21 +314,35 @@
       }
     }
 
-    grpc_metadata_batch_filter(exec_ctx, op->send_initial_metadata,
-                               client_strip_filter, elem);
+    remove_if_present(exec_ctx, op->send_initial_metadata, GRPC_BATCH_METHOD);
+    remove_if_present(exec_ctx, op->send_initial_metadata, GRPC_BATCH_SCHEME);
+    remove_if_present(exec_ctx, op->send_initial_metadata, GRPC_BATCH_TE);
+    remove_if_present(exec_ctx, op->send_initial_metadata,
+                      GRPC_BATCH_CONTENT_TYPE);
+    remove_if_present(exec_ctx, op->send_initial_metadata,
+                      GRPC_BATCH_USER_AGENT);
+
     /* Send : prefixed headers, which have to be before any application
        layer headers. */
-    grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
-                                 method);
-    grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
-                                 channeld->static_scheme);
-    grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
-                                 GRPC_MDELEM_TE_TRAILERS);
-    grpc_metadata_batch_add_tail(
-        op->send_initial_metadata, &calld->content_type,
+    error = grpc_metadata_batch_add_head(exec_ctx, op->send_initial_metadata,
+                                         &calld->method, method);
+    if (error != GRPC_ERROR_NONE) return error;
+    error =
+        grpc_metadata_batch_add_head(exec_ctx, op->send_initial_metadata,
+                                     &calld->scheme, channeld->static_scheme);
+    if (error != GRPC_ERROR_NONE) return error;
+    error = grpc_metadata_batch_add_tail(exec_ctx, op->send_initial_metadata,
+                                         &calld->te_trailers,
+                                         GRPC_MDELEM_TE_TRAILERS);
+    if (error != GRPC_ERROR_NONE) return error;
+    error = grpc_metadata_batch_add_tail(
+        exec_ctx, op->send_initial_metadata, &calld->content_type,
         GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
-    grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent,
-                                 GRPC_MDELEM_REF(channeld->user_agent));
+    if (error != GRPC_ERROR_NONE) return error;
+    error = grpc_metadata_batch_add_tail(exec_ctx, op->send_initial_metadata,
+                                         &calld->user_agent,
+                                         GRPC_MDELEM_REF(channeld->user_agent));
+    if (error != GRPC_ERROR_NONE) return error;
   }
 
   if (op->recv_initial_metadata != NULL) {
@@ -315,6 +358,8 @@
     calld->on_done_recv_trailing_metadata = op->on_complete;
     op->on_complete = &calld->hc_on_recv_trailing_metadata;
   }
+
+  return GRPC_ERROR_NONE;
 }
 
 static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
@@ -322,15 +367,20 @@
                                   grpc_transport_stream_op *op) {
   GPR_TIMER_BEGIN("hc_start_transport_op", 0);
   GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
-  hc_mutate_op(exec_ctx, elem, op);
-  GPR_TIMER_END("hc_start_transport_op", 0);
-  call_data *calld = elem->call_data;
-  if (op->send_message != NULL && calld->send_message_blocked) {
-    /* Don't forward the op. send_message contains slices that aren't ready
-    yet. The call will be forwarded by the op_complete of slice read call. */
+  grpc_error *error = hc_mutate_op(exec_ctx, elem, op);
+  if (error != GRPC_ERROR_NONE) {
+    grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error);
   } else {
-    grpc_call_next_op(exec_ctx, elem, op);
+    call_data *calld = elem->call_data;
+    if (op->send_message != NULL && calld->send_message_blocked) {
+      /* Don't forward the op. send_message contains slices that aren't ready
+      yet. The call will be forwarded by the op_complete of slice read call.
+      */
+    } else {
+      grpc_call_next_op(exec_ctx, elem, op);
+    }
   }
+  GPR_TIMER_END("hc_start_transport_op", 0);
 }
 
 /* Constructor for call_data */
@@ -367,18 +417,18 @@
   grpc_slice_buffer_destroy_internal(exec_ctx, &calld->slices);
 }
 
-static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
+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};
+  grpc_mdelem valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP,
+                                 GRPC_MDELEM_SCHEME_HTTPS};
   if (args != NULL) {
     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 == strcmp(grpc_mdstr_as_c_string(valid_schemes[j]->value),
-                          args->args[i].value.string)) {
+          if (0 == grpc_slice_str_cmp(GRPC_MDVALUE(valid_schemes[j]),
+                                      args->args[i].value.string)) {
             return valid_schemes[j];
           }
         }
@@ -404,13 +454,13 @@
   return kMaxPayloadSizeForGet;
 }
 
-static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args,
-                                        const char *transport_name) {
+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_mdstr *result;
+  grpc_slice result;
 
   gpr_strvec_init(&v);
 
@@ -448,7 +498,7 @@
 
   tmp = gpr_strvec_flatten(&v, NULL);
   gpr_strvec_destroy(&v);
-  result = grpc_mdstr_from_string(tmp);
+  result = grpc_slice_intern(grpc_slice_from_static_string(tmp));
   gpr_free(tmp);
 
   return result;
@@ -464,7 +514,7 @@
   chand->static_scheme = scheme_from_args(args->channel_args);
   chand->max_payload_size_for_get =
       max_payload_size_from_args(args->channel_args);
-  chand->user_agent = grpc_mdelem_from_metadata_strings(
+  chand->user_agent = grpc_mdelem_from_slices(
       exec_ctx, GRPC_MDSTR_USER_AGENT,
       user_agent_from_args(args->channel_args,
                            args->optional_transport->vtable->name));
diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c
index f508231..3f99297 100644
--- a/src/core/lib/channel/http_server_filter.c
+++ b/src/core/lib/channel/http_server_filter.c
@@ -39,6 +39,7 @@
 #include "src/core/lib/profiling/timers.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"
@@ -47,18 +48,13 @@
 extern int grpc_http_trace;
 
 typedef struct call_data {
-  uint8_t seen_path;
-  uint8_t seen_method;
-  uint8_t sent_status;
-  uint8_t seen_scheme;
-  uint8_t seen_te_trailers;
-  uint8_t seen_authority;
-  uint8_t seen_payload_bin;
   grpc_linked_mdelem status;
   grpc_linked_mdelem content_type;
 
+  /* did this request come with payload-bin */
+  bool seen_payload_bin;
   /* flag to ensure payload_bin is delivered only once */
-  uint8_t payload_bin_delivered;
+  bool payload_bin_delivered;
 
   grpc_metadata_batch *recv_initial_metadata;
   bool *recv_idempotent_request;
@@ -83,109 +79,152 @@
 
 typedef struct channel_data { uint8_t unused; } channel_data;
 
-static grpc_mdelem *server_filter_outgoing_metadata(grpc_exec_ctx *exec_ctx,
-                                                    void *user_data,
-                                                    grpc_mdelem *md) {
-  if (md->key == GRPC_MDSTR_GRPC_MESSAGE) {
+static grpc_error *server_filter_outgoing_metadata(grpc_exec_ctx *exec_ctx,
+                                                   grpc_call_element *elem,
+                                                   grpc_metadata_batch *b) {
+  if (b->idx.named.grpc_message != NULL) {
     grpc_slice pct_encoded_msg = grpc_percent_encode_slice(
-        md->value->slice, grpc_compatible_percent_encoding_unreserved_bytes);
-    if (grpc_slice_is_equivalent(pct_encoded_msg, md->value->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(exec_ctx, pct_encoded_msg);
-      return md;
     } else {
-      return grpc_mdelem_from_metadata_strings(
-          exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
-          grpc_mdstr_from_slice(exec_ctx, pct_encoded_msg));
+      grpc_metadata_batch_set_value(exec_ctx, b->idx.named.grpc_message,
+                                    pct_encoded_msg);
     }
-  } else {
-    return md;
   }
+  return GRPC_ERROR_NONE;
 }
 
-static grpc_mdelem *server_filter(grpc_exec_ctx *exec_ctx, void *user_data,
-                                  grpc_mdelem *md) {
-  grpc_call_element *elem = user_data;
-  call_data *calld = elem->call_data;
+static void add_error(const char *error_name, grpc_error **cumulative,
+                      grpc_error *new) {
+  if (new == GRPC_ERROR_NONE) return;
+  if (*cumulative == GRPC_ERROR_NONE) {
+    *cumulative = GRPC_ERROR_CREATE(error_name);
+  }
+  *cumulative = grpc_error_add_child(*cumulative, new);
+}
 
-  /* Check if it is one of the headers we care about. */
-  if (md == GRPC_MDELEM_TE_TRAILERS || md == GRPC_MDELEM_METHOD_POST ||
-      md == GRPC_MDELEM_METHOD_PUT || md == GRPC_MDELEM_METHOD_GET ||
-      md == GRPC_MDELEM_SCHEME_HTTP || md == GRPC_MDELEM_SCHEME_HTTPS ||
-      md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
-    /* swallow it */
-    if (md == GRPC_MDELEM_METHOD_POST) {
-      calld->seen_method = 1;
+static grpc_error *server_filter_incoming_metadata(grpc_exec_ctx *exec_ctx,
+                                                   grpc_call_element *elem,
+                                                   grpc_metadata_batch *b) {
+  call_data *calld = elem->call_data;
+  grpc_error *error = GRPC_ERROR_NONE;
+  static const char *error_name = "Failed processing incoming headers";
+
+  if (b->idx.named.method != NULL) {
+    if (grpc_mdelem_eq(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) {
       *calld->recv_idempotent_request = false;
       *calld->recv_cacheable_request = false;
-    } else if (md == GRPC_MDELEM_METHOD_PUT) {
-      calld->seen_method = 1;
+    } else if (grpc_mdelem_eq(b->idx.named.method->md,
+                              GRPC_MDELEM_METHOD_PUT)) {
       *calld->recv_idempotent_request = true;
-    } else if (md == GRPC_MDELEM_METHOD_GET) {
-      calld->seen_method = 1;
+    } else if (grpc_mdelem_eq(b->idx.named.method->md,
+                              GRPC_MDELEM_METHOD_GET)) {
       *calld->recv_cacheable_request = true;
-    } else if (md->key == GRPC_MDSTR_SCHEME) {
-      calld->seen_scheme = 1;
-    } else if (md == GRPC_MDELEM_TE_TRAILERS) {
-      calld->seen_te_trailers = 1;
-    }
-    /* TODO(klempner): Track that we've seen all the headers we should
-       require */
-    return NULL;
-  } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
-    const char *value_str = grpc_mdstr_as_c_string(md->value);
-    if (strncmp(value_str, EXPECTED_CONTENT_TYPE,
-                EXPECTED_CONTENT_TYPE_LENGTH) == 0 &&
-        (value_str[EXPECTED_CONTENT_TYPE_LENGTH] == '+' ||
-         value_str[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. */
-      gpr_log(GPR_INFO, "Unexpected content-type '%s'", value_str);
+      add_error(error_name, &error,
+                grpc_attach_md_to_error(GRPC_ERROR_CREATE("Bad header"),
+                                        b->idx.named.method->md));
     }
-    return NULL;
-  } else if (md->key == GRPC_MDSTR_TE || md->key == GRPC_MDSTR_METHOD ||
-             md->key == GRPC_MDSTR_SCHEME) {
-    gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
-            grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value));
-    /* swallow it and error everything out. */
-    /* TODO(klempner): We ought to generate more descriptive error messages
-       on the wire here. */
-    grpc_call_element_send_cancel(exec_ctx, elem);
-    return NULL;
-  } else if (md->key == GRPC_MDSTR_PATH) {
-    if (calld->seen_path) {
-      gpr_log(GPR_ERROR, "Received :path twice");
-      return NULL;
+    grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.method);
+  } else {
+    add_error(error_name, &error,
+              grpc_error_set_str(GRPC_ERROR_CREATE("Missing header"),
+                                 GRPC_ERROR_STR_KEY, ":method"));
+  }
+
+  if (b->idx.named.te != NULL) {
+    if (!grpc_mdelem_eq(b->idx.named.te->md, GRPC_MDELEM_TE_TRAILERS)) {
+      add_error(error_name, &error,
+                grpc_attach_md_to_error(GRPC_ERROR_CREATE("Bad header"),
+                                        b->idx.named.te->md));
     }
-    calld->seen_path = 1;
-    return md;
-  } else if (md->key == GRPC_MDSTR_AUTHORITY) {
-    calld->seen_authority = 1;
-    return md;
-  } else if (md->key == GRPC_MDSTR_HOST) {
-    /* translate host to :authority since :authority may be
-       omitted */
-    grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
-        exec_ctx, GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
-    calld->seen_authority = 1;
-    return authority;
-  } else if (md->key == GRPC_MDSTR_GRPC_PAYLOAD_BIN) {
-    /* Retrieve the payload from the value of the 'grpc-internal-payload-bin'
-       header field */
-    calld->seen_payload_bin = 1;
+    grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.te);
+  } else {
+    add_error(error_name, &error,
+              grpc_error_set_str(GRPC_ERROR_CREATE("Missing header"),
+                                 GRPC_ERROR_STR_KEY, "te"));
+  }
+
+  if (b->idx.named.scheme != NULL) {
+    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)) {
+      add_error(error_name, &error,
+                grpc_attach_md_to_error(GRPC_ERROR_CREATE("Bad header"),
+                                        b->idx.named.scheme->md));
+    }
+    grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.scheme);
+  } else {
+    add_error(error_name, &error,
+              grpc_error_set_str(GRPC_ERROR_CREATE("Missing header"),
+                                 GRPC_ERROR_STR_KEY, ":scheme"));
+  }
+
+  if (b->idx.named.content_type != NULL) {
+    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(exec_ctx, b, b->idx.named.content_type);
+  }
+
+  if (b->idx.named.path == NULL) {
+    add_error(error_name, &error,
+              grpc_error_set_str(GRPC_ERROR_CREATE("Missing header"),
+                                 GRPC_ERROR_STR_KEY, ":path"));
+  }
+
+  if (b->idx.named.host != NULL) {
+    add_error(
+        error_name, &error,
+        grpc_metadata_batch_substitute(
+            exec_ctx, b, b->idx.named.host,
+            grpc_mdelem_from_slices(
+                exec_ctx, GRPC_MDSTR_AUTHORITY,
+                grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.host->md)))));
+  }
+
+  if (b->idx.named.authority == NULL) {
+    add_error(error_name, &error,
+              grpc_error_set_str(GRPC_ERROR_CREATE("Missing header"),
+                                 GRPC_ERROR_STR_KEY, ":authority"));
+  }
+
+  if (b->idx.named.grpc_payload_bin != NULL) {
+    calld->seen_payload_bin = true;
     grpc_slice_buffer_add(&calld->read_slice_buffer,
-                          grpc_slice_ref_internal(md->value->slice));
+                          grpc_slice_ref_internal(
+                              GRPC_MDVALUE(b->idx.named.grpc_payload_bin->md)));
     grpc_slice_buffer_stream_init(&calld->read_stream,
                                   &calld->read_slice_buffer, 0);
-    return NULL;
-  } else {
-    return md;
+    grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_payload_bin);
   }
+
+  return error;
 }
 
 static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
@@ -193,49 +232,12 @@
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
   if (err == GRPC_ERROR_NONE) {
-    grpc_metadata_batch_filter(exec_ctx, calld->recv_initial_metadata,
-                               server_filter, elem);
-    /* Have we seen the required http2 transport headers?
-       (:method, :scheme, content-type, with :path and :authority covered
-       at the channel level right now) */
-    if (calld->seen_method && calld->seen_scheme && calld->seen_te_trailers &&
-        calld->seen_path && calld->seen_authority) {
-      /* do nothing */
-    } else {
-      err = GRPC_ERROR_CREATE("Bad incoming HTTP headers");
-      if (!calld->seen_path) {
-        err = grpc_error_add_child(err,
-                                   GRPC_ERROR_CREATE("Missing :path header"));
-      }
-      if (!calld->seen_authority) {
-        err = grpc_error_add_child(
-            err, GRPC_ERROR_CREATE("Missing :authority header"));
-      }
-      if (!calld->seen_method) {
-        err = grpc_error_add_child(err,
-                                   GRPC_ERROR_CREATE("Missing :method header"));
-      }
-      if (!calld->seen_scheme) {
-        err = grpc_error_add_child(err,
-                                   GRPC_ERROR_CREATE("Missing :scheme header"));
-      }
-      if (!calld->seen_te_trailers) {
-        err = grpc_error_add_child(
-            err, GRPC_ERROR_CREATE("Missing te: trailers header"));
-      }
-      /* Error this call out */
-      if (grpc_http_trace) {
-        const char *error_str = grpc_error_string(err);
-        gpr_log(GPR_ERROR, "Invalid http2 headers: %s", error_str);
-        grpc_error_free_string(error_str);
-      }
-      grpc_call_element_send_cancel(exec_ctx, elem);
-    }
+    err = server_filter_incoming_metadata(exec_ctx, elem,
+                                          calld->recv_initial_metadata);
   } else {
     GRPC_ERROR_REF(err);
   }
-  calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, err);
-  GRPC_ERROR_UNREF(err);
+  grpc_closure_run(exec_ctx, calld->on_done_recv, err);
 }
 
 static void hs_on_complete(grpc_exec_ctx *exec_ctx, void *user_data,
@@ -273,13 +275,23 @@
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
 
-  if (op->send_initial_metadata != NULL && !calld->sent_status) {
-    calld->sent_status = 1;
-    grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->status,
-                                 GRPC_MDELEM_STATUS_200);
-    grpc_metadata_batch_add_tail(
-        op->send_initial_metadata, &calld->content_type,
-        GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
+  if (op->send_initial_metadata != NULL) {
+    grpc_error *error = GRPC_ERROR_NONE;
+    static const char *error_name = "Failed sending initial metadata";
+    add_error(error_name, &error, grpc_metadata_batch_add_head(
+                                      exec_ctx, op->send_initial_metadata,
+                                      &calld->status, GRPC_MDELEM_STATUS_200));
+    add_error(error_name, &error,
+              grpc_metadata_batch_add_tail(
+                  exec_ctx, op->send_initial_metadata, &calld->content_type,
+                  GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC));
+    add_error(error_name, &error,
+              server_filter_outgoing_metadata(exec_ctx, elem,
+                                              op->send_initial_metadata));
+    if (error != GRPC_ERROR_NONE) {
+      grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error);
+      return;
+    }
   }
 
   if (op->recv_initial_metadata) {
@@ -306,8 +318,12 @@
   }
 
   if (op->send_trailing_metadata) {
-    grpc_metadata_batch_filter(exec_ctx, op->send_trailing_metadata,
-                               server_filter_outgoing_metadata, elem);
+    grpc_error *error = server_filter_outgoing_metadata(
+        exec_ctx, elem, op->send_trailing_metadata);
+    if (error != GRPC_ERROR_NONE) {
+      grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error);
+      return;
+    }
   }
 }
 
diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c
index 862090b..5e22860 100644
--- a/src/core/lib/channel/message_size_filter.c
+++ b/src/core/lib/channel/message_size_filter.c
@@ -58,7 +58,7 @@
   gpr_free(value);
 }
 
-static const grpc_mdstr_hash_table_vtable message_size_limits_vtable = {
+static const grpc_slice_hash_table_vtable message_size_limits_vtable = {
     message_size_limits_free, message_size_limits_copy};
 
 static void* message_size_limits_create_from_json(const grpc_json* json) {
@@ -68,12 +68,16 @@
     if (field->key == NULL) continue;
     if (strcmp(field->key, "maxRequestMessageBytes") == 0) {
       if (max_request_message_bytes >= 0) return NULL;  // Duplicate.
-      if (field->type != GRPC_JSON_STRING) return NULL;
+      if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) {
+        return NULL;
+      }
       max_request_message_bytes = gpr_parse_nonnegative_int(field->value);
       if (max_request_message_bytes == -1) return NULL;
     } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) {
       if (max_response_message_bytes >= 0) return NULL;  // Duplicate.
-      if (field->type != GRPC_JSON_STRING) return NULL;
+      if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) {
+        return NULL;
+      }
       max_response_message_bytes = gpr_parse_nonnegative_int(field->value);
       if (max_response_message_bytes == -1) return NULL;
     }
@@ -101,7 +105,7 @@
   int max_send_size;
   int max_recv_size;
   // Maps path names to message_size_limits structs.
-  grpc_mdstr_hash_table* method_limit_table;
+  grpc_slice_hash_table* method_limit_table;
 } channel_data;
 
 // Callback invoked when we receive a message.  Here we check the max
@@ -142,10 +146,12 @@
     char* message_string;
     gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)",
                  op->send_message->length, calld->max_send_size);
-    grpc_slice message = grpc_slice_from_copied_string(message_string);
+    grpc_transport_stream_op_finish_with_failure(
+        exec_ctx, op, grpc_error_set_int(GRPC_ERROR_CREATE(message_string),
+                                         GRPC_ERROR_INT_GRPC_STATUS,
+                                         GRPC_STATUS_INVALID_ARGUMENT));
     gpr_free(message_string);
-    grpc_call_element_send_close_with_message(
-        exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, &message);
+    return;
   }
   // Inject callback for receiving a message.
   if (op->recv_message_ready != NULL) {
@@ -243,7 +249,7 @@
 static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
                                  grpc_channel_element* elem) {
   channel_data* chand = elem->channel_data;
-  grpc_mdstr_hash_table_unref(exec_ctx, chand->method_limit_table);
+  grpc_slice_hash_table_unref(exec_ctx, chand->method_limit_table);
 }
 
 const grpc_channel_filter grpc_message_size_filter = {
diff --git a/src/core/lib/compression/algorithm_metadata.h b/src/core/lib/compression/algorithm_metadata.h
index 1f9cc15..58dfe62 100644
--- a/src/core/lib/compression/algorithm_metadata.h
+++ b/src/core/lib/compression/algorithm_metadata.h
@@ -38,16 +38,16 @@
 #include "src/core/lib/transport/metadata.h"
 
 /** Return compression algorithm based metadata value */
-grpc_mdstr *grpc_compression_algorithm_mdstr(
+grpc_slice grpc_compression_algorithm_slice(
     grpc_compression_algorithm algorithm);
 
 /** Return compression algorithm based metadata element (grpc-encoding: xxx) */
-grpc_mdelem *grpc_compression_encoding_mdelem(
+grpc_mdelem grpc_compression_encoding_mdelem(
     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_mdstr(
-    grpc_mdstr *str);
+grpc_compression_algorithm grpc_compression_algorithm_from_slice(
+    grpc_slice str);
 
 #endif /* GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H */
diff --git a/src/core/lib/compression/compression.c b/src/core/lib/compression/compression.c
index 54efb5e..ce4f597 100644
--- a/src/core/lib/compression/compression.c
+++ b/src/core/lib/compression/compression.c
@@ -41,30 +41,24 @@
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/transport/static_metadata.h"
 
-int grpc_compression_algorithm_parse(const char *name, size_t name_length,
+int grpc_compression_algorithm_parse(grpc_slice name,
                                      grpc_compression_algorithm *algorithm) {
   /* we use strncmp not only because it's safer (even though in this case it
    * doesn't matter, given that we are comparing against string literals, but
    * because this way we needn't have "name" nil-terminated (useful for slice
    * data, for example) */
-  GRPC_API_TRACE(
-      "grpc_compression_algorithm_parse("
-      "name=%*.*s, name_length=%lu, algorithm=%p)",
-      5, ((int)name_length, (int)name_length, name, (unsigned long)name_length,
-          algorithm));
-  if (name_length == 0) {
-    return 0;
-  }
-  if (strncmp(name, "identity", name_length) == 0) {
+  if (grpc_slice_eq(name, GRPC_MDSTR_IDENTITY)) {
     *algorithm = GRPC_COMPRESS_NONE;
-  } else if (strncmp(name, "gzip", name_length) == 0) {
+    return 1;
+  } else if (grpc_slice_eq(name, GRPC_MDSTR_GZIP)) {
     *algorithm = GRPC_COMPRESS_GZIP;
-  } else if (strncmp(name, "deflate", name_length) == 0) {
+    return 1;
+  } else if (grpc_slice_eq(name, GRPC_MDSTR_DEFLATE)) {
     *algorithm = GRPC_COMPRESS_DEFLATE;
+    return 1;
   } else {
     return 0;
   }
-  return 1;
 }
 
 int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm,
@@ -87,15 +81,15 @@
   return 0;
 }
 
-grpc_compression_algorithm grpc_compression_algorithm_from_mdstr(
-    grpc_mdstr *str) {
-  if (str == GRPC_MDSTR_IDENTITY) return GRPC_COMPRESS_NONE;
-  if (str == GRPC_MDSTR_DEFLATE) return GRPC_COMPRESS_DEFLATE;
-  if (str == GRPC_MDSTR_GZIP) return GRPC_COMPRESS_GZIP;
+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;
   return GRPC_COMPRESS_ALGORITHMS_COUNT;
 }
 
-grpc_mdstr *grpc_compression_algorithm_mdstr(
+grpc_slice grpc_compression_algorithm_slice(
     grpc_compression_algorithm algorithm) {
   switch (algorithm) {
     case GRPC_COMPRESS_NONE:
@@ -105,12 +99,12 @@
     case GRPC_COMPRESS_GZIP:
       return GRPC_MDSTR_GZIP;
     case GRPC_COMPRESS_ALGORITHMS_COUNT:
-      return NULL;
+      return grpc_empty_slice();
   }
-  return NULL;
+  return grpc_empty_slice();
 }
 
-grpc_mdelem *grpc_compression_encoding_mdelem(
+grpc_mdelem grpc_compression_encoding_mdelem(
     grpc_compression_algorithm algorithm) {
   switch (algorithm) {
     case GRPC_COMPRESS_NONE:
@@ -122,7 +116,7 @@
     default:
       break;
   }
-  return NULL;
+  return GRPC_MDNULL;
 }
 
 void grpc_compression_options_init(grpc_compression_options *opts) {
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
index 440817c..f4f6f3c 100644
--- a/src/core/lib/http/httpcli_security_connector.c
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -156,7 +156,7 @@
   if (error != GRPC_ERROR_NONE) {
     const char *msg = grpc_error_string(error);
     gpr_log(GPR_ERROR, "Secure transport setup failed: %s", msg);
-    grpc_error_free_string(msg);
+
     c->func(exec_ctx, c->arg, NULL);
   } else {
     grpc_channel_args_destroy(exec_ctx, args->args);
diff --git a/src/core/lib/iomgr/closure.c b/src/core/lib/iomgr/closure.c
index da0ec87..8e4efd3 100644
--- a/src/core/lib/iomgr/closure.c
+++ b/src/core/lib/iomgr/closure.c
@@ -34,6 +34,7 @@
 #include "src/core/lib/iomgr/closure.h"
 
 #include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
 
 #include "src/core/lib/profiling/timers.h"
 
diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
index ee386fb..6748b21 100644
--- a/src/core/lib/iomgr/closure.h
+++ b/src/core/lib/iomgr/closure.h
@@ -66,6 +66,7 @@
               grpc_error *error);
   void (*sched)(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
                 grpc_error *error);
+  const char *name;
 } grpc_closure_scheduler_vtable;
 
 /** Abstract type that can schedule closures for execution */
diff --git a/src/core/lib/iomgr/combiner.c b/src/core/lib/iomgr/combiner.c
index c26a73b..ba6c708 100644
--- a/src/core/lib/iomgr/combiner.c
+++ b/src/core/lib/iomgr/combiner.c
@@ -86,13 +86,17 @@
                                           grpc_error *error);
 
 static const grpc_closure_scheduler_vtable scheduler_uncovered = {
-    combiner_exec_uncovered, combiner_exec_uncovered};
+    combiner_exec_uncovered, combiner_exec_uncovered,
+    "combiner:immediately:uncovered"};
 static const grpc_closure_scheduler_vtable scheduler_covered = {
-    combiner_exec_covered, combiner_exec_covered};
+    combiner_exec_covered, combiner_exec_covered,
+    "combiner:immediately:covered"};
 static const grpc_closure_scheduler_vtable finally_scheduler_uncovered = {
-    combiner_finally_exec_uncovered, combiner_finally_exec_uncovered};
+    combiner_finally_exec_uncovered, combiner_finally_exec_uncovered,
+    "combiner:finally:uncovered"};
 static const grpc_closure_scheduler_vtable finally_scheduler_covered = {
-    combiner_finally_exec_covered, combiner_finally_exec_covered};
+    combiner_finally_exec_covered, combiner_finally_exec_covered,
+    "combiner:finally:covered"};
 
 static void offload(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error);
 
diff --git a/src/core/lib/iomgr/error.c b/src/core/lib/iomgr/error.c
index f6bb3a0..dbe5b13 100644
--- a/src/core/lib/iomgr/error.c
+++ b/src/core/lib/iomgr/error.c
@@ -33,13 +33,10 @@
 
 #include "src/core/lib/iomgr/error.h"
 
-#include <inttypes.h>
-#include <stdbool.h>
 #include <string.h>
 
 #include <grpc/status.h>
 #include <grpc/support/alloc.h>
-#include <grpc/support/avl.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
@@ -48,6 +45,7 @@
 #include <grpc/support/log_windows.h>
 #endif
 
+#include "src/core/lib/iomgr/error_internal.h"
 #include "src/core/lib/profiling/timers.h"
 
 static void destroy_integer(void *key) {}
@@ -128,6 +126,10 @@
 
 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:
@@ -160,16 +162,7 @@
   GPR_UNREACHABLE_CODE(return "unknown");
 }
 
-struct grpc_error {
-  gpr_refcount refs;
-  gpr_avl ints;
-  gpr_avl strs;
-  gpr_avl times;
-  gpr_avl errs;
-  uintptr_t next_err;
-};
-
-static bool is_special(grpc_error *err) {
+bool grpc_error_is_special(grpc_error *err) {
   return err == GRPC_ERROR_NONE || err == GRPC_ERROR_OOM ||
          err == GRPC_ERROR_CANCELLED;
 }
@@ -177,7 +170,7 @@
 #ifdef GRPC_ERROR_REFCOUNT_DEBUG
 grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line,
                            const char *func) {
-  if (is_special(err)) return err;
+  if (grpc_error_is_special(err)) return err;
   gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d %s]", err,
           err->refs.count, err->refs.count + 1, file, line, func);
   gpr_ref(&err->refs);
@@ -185,25 +178,26 @@
 }
 #else
 grpc_error *grpc_error_ref(grpc_error *err) {
-  if (is_special(err)) return err;
+  if (grpc_error_is_special(err)) return err;
   gpr_ref(&err->refs);
   return err;
 }
 #endif
 
 static void error_destroy(grpc_error *err) {
-  GPR_ASSERT(!is_special(err));
+  GPR_ASSERT(!grpc_error_is_special(err));
   gpr_avl_unref(err->ints);
   gpr_avl_unref(err->strs);
   gpr_avl_unref(err->errs);
   gpr_avl_unref(err->times);
+  gpr_free((void *)gpr_atm_acq_load(&err->error_string));
   gpr_free(err);
 }
 
 #ifdef GRPC_ERROR_REFCOUNT_DEBUG
 void grpc_error_unref(grpc_error *err, const char *file, int line,
                       const char *func) {
-  if (is_special(err)) return;
+  if (grpc_error_is_special(err)) return;
   gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d %s]", err,
           err->refs.count, err->refs.count - 1, file, line, func);
   if (gpr_unref(&err->refs)) {
@@ -212,7 +206,7 @@
 }
 #else
 void grpc_error_unref(grpc_error *err) {
-  if (is_special(err)) return;
+  if (grpc_error_is_special(err)) return;
   if (gpr_unref(&err->refs)) {
     error_destroy(err);
   }
@@ -247,6 +241,7 @@
   err->times = gpr_avl_add(gpr_avl_create(&avl_vtable_times),
                            (void *)(uintptr_t)GRPC_ERROR_TIME_CREATED,
                            box_time(gpr_now(GPR_CLOCK_REALTIME)));
+  gpr_atm_no_barrier_store(&err->error_string, 0);
   gpr_ref_init(&err->refs, 1);
   GPR_TIMER_END("grpc_error_create", 0);
   return err;
@@ -255,9 +250,10 @@
 static grpc_error *copy_error_and_unref(grpc_error *in) {
   GPR_TIMER_BEGIN("copy_error_and_unref", 0);
   grpc_error *out;
-  if (is_special(in)) {
+  if (grpc_error_is_special(in)) {
     if (in == GRPC_ERROR_NONE)
-      out = GRPC_ERROR_CREATE("no error");
+      out = grpc_error_set_int(GRPC_ERROR_CREATE("no error"),
+                               GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK);
     else if (in == GRPC_ERROR_OOM)
       out = GRPC_ERROR_CREATE("oom");
     else if (in == GRPC_ERROR_CANCELLED)
@@ -275,6 +271,7 @@
     out->strs = gpr_avl_ref(in->strs);
     out->errs = gpr_avl_ref(in->errs);
     out->times = gpr_avl_ref(in->times);
+    gpr_atm_no_barrier_store(&out->error_string, 0);
     out->next_err = in->next_err;
     gpr_ref_init(&out->refs, 1);
     GRPC_ERROR_UNREF(in);
@@ -292,14 +289,29 @@
   return new;
 }
 
+typedef struct {
+  grpc_error *error;
+  grpc_status_code code;
+  const char *msg;
+} special_error_status_map;
+static special_error_status_map error_status_map[] = {
+    {GRPC_ERROR_NONE, GRPC_STATUS_OK, NULL},
+    {GRPC_ERROR_CANCELLED, GRPC_STATUS_CANCELLED, "Cancelled"},
+    {GRPC_ERROR_OOM, GRPC_STATUS_RESOURCE_EXHAUSTED, "Out of memory"},
+};
+
 bool grpc_error_get_int(grpc_error *err, grpc_error_ints which, intptr_t *p) {
   GPR_TIMER_BEGIN("grpc_error_get_int", 0);
   void *pp;
-  if (is_special(err)) {
-    if (err == GRPC_ERROR_CANCELLED && which == GRPC_ERROR_INT_GRPC_STATUS) {
-      *p = GRPC_STATUS_CANCELLED;
-      GPR_TIMER_END("grpc_error_get_int", 0);
-      return true;
+  if (grpc_error_is_special(err)) {
+    if (which == GRPC_ERROR_INT_GRPC_STATUS) {
+      for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) {
+        if (error_status_map[i].error == err) {
+          if (p != NULL) *p = error_status_map[i].code;
+          GPR_TIMER_END("grpc_error_get_int", 0);
+          return true;
+        }
+      }
     }
     GPR_TIMER_END("grpc_error_get_int", 0);
     return false;
@@ -324,66 +336,17 @@
 }
 
 const char *grpc_error_get_str(grpc_error *err, grpc_error_strs which) {
-  if (is_special(err)) return NULL;
-  return gpr_avl_get(err->strs, (void *)(uintptr_t)which);
-}
-
-typedef struct {
-  grpc_error *error;
-  grpc_status_code code;
-  const char *msg;
-} special_error_status_map;
-static special_error_status_map error_status_map[] = {
-    {GRPC_ERROR_NONE, GRPC_STATUS_OK, ""},
-    {GRPC_ERROR_CANCELLED, GRPC_STATUS_CANCELLED, "RPC cancelled"},
-    {GRPC_ERROR_OOM, GRPC_STATUS_RESOURCE_EXHAUSTED, "Out of memory"},
-};
-
-static grpc_error *recursively_find_error_with_status(grpc_error *error,
-                                                      intptr_t *status) {
-  // If the error itself has a status code, return it.
-  if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, status)) {
-    return error;
-  }
-  // Otherwise, search through its children.
-  intptr_t key = 0;
-  while (true) {
-    grpc_error *child_error = gpr_avl_get(error->errs, (void *)key++);
-    if (child_error == NULL) break;
-    grpc_error *result =
-        recursively_find_error_with_status(child_error, status);
-    if (result != NULL) return result;
-  }
-  return NULL;
-}
-
-void grpc_error_get_status(grpc_error *error, grpc_status_code *code,
-                           const char **msg) {
-  // Handle special errors via the static map.
-  for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); ++i) {
-    if (error == error_status_map[i].error) {
-      *code = error_status_map[i].code;
-      *msg = error_status_map[i].msg;
-      return;
+  if (grpc_error_is_special(err)) {
+    if (which == GRPC_ERROR_STR_GRPC_MESSAGE) {
+      for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) {
+        if (error_status_map[i].error == err) {
+          return error_status_map[i].msg;
+        }
+      }
     }
+    return NULL;
   }
-  // Populate code.
-  // Start with the parent error and recurse through the tree of children
-  // until we find the first one that has a status code.
-  intptr_t status = GRPC_STATUS_UNKNOWN;  // Default in case we don't find one.
-  grpc_error *found_error = recursively_find_error_with_status(error, &status);
-  *code = (grpc_status_code)status;
-  // Now populate msg.
-  // If we found an error with a status code above, use that; otherwise,
-  // fall back to using the parent error.
-  if (found_error == NULL) found_error = error;
-  // If the error has a status message, use it.  Otherwise, fall back to
-  // the error description.
-  *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE);
-  if (*msg == NULL) {
-    *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION);
-    if (*msg == NULL) *msg = "uknown error";  // Just in case.
-  }
+  return gpr_avl_get(err->strs, (void *)(uintptr_t)which);
 }
 
 grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) {
@@ -535,7 +498,6 @@
   *first = false;
   const char *e = grpc_error_string(n->value);
   append_str(e, s, sz, cap);
-  grpc_error_free_string(e);
   add_errs(n->right, s, sz, cap, first);
 }
 
@@ -557,7 +519,7 @@
   return strcmp(ka->key, kb->key);
 }
 
-static const char *finish_kvs(kv_pairs *kvs) {
+static char *finish_kvs(kv_pairs *kvs) {
   char *s = NULL;
   size_t sz = 0;
   size_t cap = 0;
@@ -578,19 +540,18 @@
   return s;
 }
 
-void grpc_error_free_string(const char *str) {
-  if (str == no_error_string) return;
-  if (str == oom_error_string) return;
-  if (str == cancelled_error_string) return;
-  gpr_free((char *)str);
-}
-
 const char *grpc_error_string(grpc_error *err) {
   GPR_TIMER_BEGIN("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->error_string);
+  if (p != NULL) {
+    GPR_TIMER_END("grpc_error_string", 0);
+    return p;
+  }
+
   kv_pairs kvs;
   memset(&kvs, 0, sizeof(kvs));
 
@@ -603,7 +564,13 @@
 
   qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs);
 
-  const char *out = finish_kvs(&kvs);
+  char *out = finish_kvs(&kvs);
+
+  if (!gpr_atm_rel_cas(&err->error_string, 0, (gpr_atm)out)) {
+    gpr_free(out);
+    out = (char *)gpr_atm_no_barrier_load(&err->error_string);
+  }
+
   GPR_TIMER_END("grpc_error_string", 0);
   return out;
 }
@@ -638,7 +605,6 @@
   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_free_string(msg);
   GRPC_ERROR_UNREF(error);
   return false;
 }
diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h
index f3f3b80..ffacdac 100644
--- a/src/core/lib/iomgr/error.h
+++ b/src/core/lib/iomgr/error.h
@@ -124,7 +124,11 @@
   /// 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
+  GRPC_ERROR_STR_QUEUED_BUFFERS,
+  /// key associated with the error
+  GRPC_ERROR_STR_KEY,
+  /// value associated with the error
+  GRPC_ERROR_STR_VALUE,
 } grpc_error_strs;
 
 typedef enum {
@@ -141,7 +145,6 @@
 #define GRPC_ERROR_CANCELLED ((grpc_error *)4)
 
 const char *grpc_error_string(grpc_error *error);
-void grpc_error_free_string(const char *str);
 
 /// Create an error - but use GRPC_ERROR_CREATE instead
 grpc_error *grpc_error_create(const char *file, int line, const char *desc,
@@ -189,12 +192,6 @@
 /// Caller does NOT own return value.
 const char *grpc_error_get_str(grpc_error *error, grpc_error_strs which);
 
-/// A utility function to get the status code and message to be returned
-/// to the application.  If not set in the top-level message, looks
-/// through child errors until it finds the first one with these attributes.
-void grpc_error_get_status(grpc_error *error, grpc_status_code *code,
-                           const char **msg);
-
 /// Add a child error: an error that is believed to have contributed to this
 /// error occurring. Allows root causing high level errors from lower level
 /// errors that contributed to them.
diff --git a/src/core/ext/transport/chttp2/transport/status_conversion.h b/src/core/lib/iomgr/error_internal.h
similarity index 64%
copy from src/core/ext/transport/chttp2/transport/status_conversion.h
copy to src/core/lib/iomgr/error_internal.h
index 953bc9f..1c89ead 100644
--- a/src/core/ext/transport/chttp2/transport/status_conversion.h
+++ b/src/core/lib/iomgr/error_internal.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,20 +31,24 @@
  *
  */
 
-#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
-#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
+#ifndef GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H
+#define GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H
 
-#include <grpc/grpc.h>
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
+#include <inttypes.h>
+#include <stdbool.h>
 
-/* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */
-grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error(
-    grpc_status_code status);
-grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error, gpr_timespec deadline);
+#include <grpc/support/avl.h>
 
-/* Conversion of HTTP status codes (:status) to grpc status codes */
-grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status);
-int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status);
+struct grpc_error {
+  gpr_refcount refs;
+  gpr_avl ints;
+  gpr_avl strs;
+  gpr_avl times;
+  gpr_avl errs;
+  uintptr_t next_err;
+  gpr_atm error_string;
+};
 
-#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H */
+bool grpc_error_is_special(grpc_error *err);
+
+#endif /* GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H */
diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c
index 715d057..39b4d9c 100644
--- a/src/core/lib/iomgr/ev_epoll_linux.c
+++ b/src/core/lib/iomgr/ev_epoll_linux.c
@@ -321,7 +321,7 @@
 #endif /* defined(GRPC_TSAN) */
 
 static const grpc_closure_scheduler_vtable workqueue_scheduler_vtable = {
-    workqueue_enqueue, workqueue_enqueue};
+    workqueue_enqueue, workqueue_enqueue, "workqueue"};
 
 static void pi_add_ref(polling_island *pi);
 static void pi_unref(grpc_exec_ctx *exec_ctx, polling_island *pi);
@@ -1527,7 +1527,8 @@
         append_error(error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd),
                      err_desc);
       } else if (data_ptr == &pi->workqueue_wakeup_fd) {
-        append_error(error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd),
+        append_error(error,
+                     grpc_wakeup_fd_consume_wakeup(&pi->workqueue_wakeup_fd),
                      err_desc);
         maybe_do_workqueue_work(exec_ctx, pi);
       } else if (data_ptr == &polling_island_wakeup_fd) {
diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c
index 2975d61..c106ba5 100644
--- a/src/core/lib/iomgr/ev_posix.c
+++ b/src/core/lib/iomgr/ev_posix.c
@@ -52,6 +52,8 @@
  *  tests */
 grpc_poll_function_type grpc_poll_function = poll;
 
+grpc_wakeup_fd grpc_global_wakeup_fd;
+
 static const grpc_event_engine_vtable *g_event_engine;
 static const char *g_poll_strategy_name = NULL;
 
diff --git a/src/core/lib/iomgr/exec_ctx.c b/src/core/lib/iomgr/exec_ctx.c
index 6aa788f..83bb436 100644
--- a/src/core/lib/iomgr/exec_ctx.c
+++ b/src/core/lib/iomgr/exec_ctx.c
@@ -42,11 +42,16 @@
 #include "src/core/lib/profiling/timers.h"
 
 bool grpc_exec_ctx_ready_to_finish(grpc_exec_ctx *exec_ctx) {
-  if (!exec_ctx->cached_ready_to_finish) {
-    exec_ctx->cached_ready_to_finish = exec_ctx->check_ready_to_finish(
-        exec_ctx, exec_ctx->check_ready_to_finish_arg);
+  if ((exec_ctx->flags & GRPC_EXEC_CTX_FLAG_IS_FINISHED) == 0) {
+    if (exec_ctx->check_ready_to_finish(exec_ctx,
+                                        exec_ctx->check_ready_to_finish_arg)) {
+      exec_ctx->flags |= GRPC_EXEC_CTX_FLAG_IS_FINISHED;
+      return true;
+    }
+    return false;
+  } else {
+    return true;
   }
-  return exec_ctx->cached_ready_to_finish;
 }
 
 bool grpc_never_ready_to_finish(grpc_exec_ctx *exec_ctx, void *arg_ignored) {
@@ -82,7 +87,7 @@
 }
 
 void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx) {
-  exec_ctx->cached_ready_to_finish = true;
+  exec_ctx->flags |= GRPC_EXEC_CTX_FLAG_IS_FINISHED;
   grpc_exec_ctx_flush(exec_ctx);
 }
 
@@ -101,6 +106,6 @@
 void grpc_exec_ctx_global_shutdown(void) {}
 
 static const grpc_closure_scheduler_vtable exec_ctx_scheduler_vtable = {
-    exec_ctx_run, exec_ctx_sched};
+    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;
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index e566f1b..f99a0fe 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -43,6 +43,13 @@
 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
+
 /** Execution context.
  *  A bag of data that collects information along a callstack.
  *  Generally created at public API entry points, and passed down as
@@ -63,36 +70,26 @@
  *  - Instances are always passed as the first argument to a function that
  *    takes it, and always as a pointer (grpc_exec_ctx is never copied).
  */
-#ifndef GRPC_EXECUTION_CONTEXT_SANITIZER
 struct grpc_exec_ctx {
   grpc_closure_list closure_list;
   /** currently active combiner: updated only via combiner.c */
   grpc_combiner *active_combiner;
   /** last active combiner in the active combiner list */
   grpc_combiner *last_combiner;
-  bool cached_ready_to_finish;
+  uintptr_t flags;
   void *check_ready_to_finish_arg;
   bool (*check_ready_to_finish)(grpc_exec_ctx *exec_ctx, void *arg);
 };
 
 /* initializer for grpc_exec_ctx:
    prefer to use GRPC_EXEC_CTX_INIT whenever possible */
-#define GRPC_EXEC_CTX_INIT_WITH_FINISH_CHECK(finish_check, finish_check_arg) \
-  { GRPC_CLOSURE_LIST_INIT, NULL, NULL, false, finish_check_arg, finish_check }
-#else
-struct grpc_exec_ctx {
-  bool cached_ready_to_finish;
-  void *check_ready_to_finish_arg;
-  bool (*check_ready_to_finish)(grpc_exec_ctx *exec_ctx, void *arg);
-};
-#define GRPC_EXEC_CTX_INIT_WITH_FINISH_CHECK(finish_check, finish_check_arg) \
-  { false, finish_check_arg, finish_check }
-#endif
+#define GRPC_EXEC_CTX_INITIALIZER(flags, finish_check, finish_check_arg) \
+  { GRPC_CLOSURE_LIST_INIT, NULL, NULL, flags, finish_check_arg, finish_check }
 
 /* initialize an execution context at the top level of an API call into grpc
    (this is safe to use elsewhere, though possibly not as efficient) */
 #define GRPC_EXEC_CTX_INIT \
-  GRPC_EXEC_CTX_INIT_WITH_FINISH_CHECK(grpc_always_ready_to_finish, NULL)
+  GRPC_EXEC_CTX_INITIALIZER(GRPC_EXEC_CTX_FLAG_IS_FINISHED, NULL, NULL)
 
 extern grpc_closure_scheduler *grpc_schedule_on_exec_ctx;
 
diff --git a/src/core/lib/iomgr/executor.c b/src/core/lib/iomgr/executor.c
index 8527755..a5b62aa 100644
--- a/src/core/lib/iomgr/executor.c
+++ b/src/core/lib/iomgr/executor.c
@@ -158,7 +158,7 @@
   gpr_mu_destroy(&g_executor.mu);
 }
 
-static const grpc_closure_scheduler_vtable executor_vtable = {executor_push,
-                                                              executor_push};
+static const grpc_closure_scheduler_vtable executor_vtable = {
+    executor_push, executor_push, "executor"};
 static grpc_closure_scheduler executor_scheduler = {&executor_vtable};
 grpc_closure_scheduler *grpc_executor_scheduler = &executor_scheduler;
diff --git a/src/core/lib/iomgr/load_file.c b/src/core/lib/iomgr/load_file.c
index 217bc5d..f40c8b2 100644
--- a/src/core/lib/iomgr/load_file.c
+++ b/src/core/lib/iomgr/load_file.c
@@ -47,7 +47,7 @@
                            grpc_slice *output) {
   unsigned char *contents = NULL;
   size_t contents_size = 0;
-  grpc_slice result = gpr_empty_slice();
+  grpc_slice result = grpc_empty_slice();
   FILE *file;
   size_t bytes_read = 0;
   grpc_error *error = GRPC_ERROR_NONE;
diff --git a/src/core/lib/iomgr/resource_quota.c b/src/core/lib/iomgr/resource_quota.c
index 31590cd..d5995a5 100644
--- a/src/core/lib/iomgr/resource_quota.c
+++ b/src/core/lib/iomgr/resource_quota.c
@@ -378,11 +378,15 @@
   }
 }
 
+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 = gpr_malloc(sizeof(ru_slice_refcount) + size);
-  rc->base.ref = ru_slice_ref;
-  rc->base.unref = ru_slice_unref;
+  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;
@@ -804,12 +808,10 @@
 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_combiner_scheduler(resource_user->resource_quota->combiner, false));
-  grpc_closure_init(
-      &slice_allocator->on_done, cb, p,
-      grpc_combiner_scheduler(resource_user->resource_quota->combiner, false));
+  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;
 }
 
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
index 9a77c92..16b0f4e 100644
--- a/src/core/lib/iomgr/tcp_client_posix.c
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -118,7 +118,6 @@
     const char *str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str,
             str);
-    grpc_error_free_string(str);
   }
   gpr_mu_lock(&ac->mu);
   if (ac->fd != NULL) {
@@ -178,7 +177,6 @@
     const char *str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: error=%s",
             ac->addr_str, str);
-    grpc_error_free_string(str);
   }
 
   gpr_mu_lock(&ac->mu);
diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
index ece4497..a33e63e 100644
--- a/src/core/lib/iomgr/tcp_posix.c
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -181,7 +181,7 @@
     size_t i;
     const char *str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "read: error=%s", str);
-    grpc_error_free_string(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);
@@ -435,7 +435,6 @@
     if (grpc_tcp_trace) {
       const char *str = grpc_error_string(error);
       gpr_log(GPR_DEBUG, "write: %s", str);
-      grpc_error_free_string(str);
     }
 
     grpc_closure_run(exec_ctx, cb, error);
@@ -485,7 +484,6 @@
     if (grpc_tcp_trace) {
       const char *str = grpc_error_string(error);
       gpr_log(GPR_DEBUG, "write: %s", str);
-      grpc_error_free_string(str);
     }
     grpc_closure_sched(exec_ctx, cb, error);
   }
diff --git a/src/core/lib/iomgr/tcp_server_windows.c b/src/core/lib/iomgr/tcp_server_windows.c
index dafe851..bd4b9b2 100644
--- a/src/core/lib/iomgr/tcp_server_windows.c
+++ b/src/core/lib/iomgr/tcp_server_windows.c
@@ -343,7 +343,7 @@
   if (error != GRPC_ERROR_NONE) {
     const char *msg = grpc_error_string(error);
     gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", msg);
-    grpc_error_free_string(msg);
+
     gpr_mu_unlock(&sp->server->mu);
     return;
   }
diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c
index 3ddc797..7f4ea49 100644
--- a/src/core/lib/iomgr/tcp_uv.c
+++ b/src/core/lib/iomgr/tcp_uv.c
@@ -48,6 +48,7 @@
 #include "src/core/lib/iomgr/network_status_tracker.h"
 #include "src/core/lib/iomgr/resource_quota.h"
 #include "src/core/lib/iomgr/tcp_uv.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
@@ -156,7 +157,7 @@
       size_t i;
       const char *str = grpc_error_string(error);
       gpr_log(GPR_DEBUG, "read: error=%s", str);
-      grpc_error_free_string(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);
diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.c b/src/core/lib/security/credentials/google_default/google_default_credentials.c
index d6e1fe3..a098741 100644
--- a/src/core/lib/security/credentials/google_default/google_default_credentials.c
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.c
@@ -177,7 +177,7 @@
   grpc_auth_json_key key;
   grpc_auth_refresh_token token;
   grpc_call_credentials *result = NULL;
-  grpc_slice creds_data = gpr_empty_slice();
+  grpc_slice creds_data = grpc_empty_slice();
   grpc_error *error = GRPC_ERROR_NONE;
   if (creds_path == NULL) {
     error = GRPC_ERROR_CREATE("creds_path unset");
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c
index f90d7dc..7bc5dfb 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.c
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.c
@@ -42,7 +42,9 @@
 #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"
 
 typedef struct {
   void *user_data;
@@ -63,7 +65,9 @@
                                              grpc_status_code status,
                                              const char *error_details) {
   /* called from application code */
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INITIALIZER(
+      GRPC_EXEC_CTX_FLAG_IS_FINISHED | GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP,
+      NULL, NULL);
   grpc_metadata_plugin_request *r = (grpc_metadata_plugin_request *)request;
   if (status != GRPC_STATUS_OK) {
     if (error_details != NULL) {
@@ -77,13 +81,14 @@
     bool seen_illegal_header = false;
     grpc_credentials_md *md_array = NULL;
     for (i = 0; i < num_md; i++) {
-      if (!grpc_header_key_is_legal(md[i].key, strlen(md[i].key))) {
-        gpr_log(GPR_ERROR, "Plugin added invalid metadata key: %s", md[i].key);
+      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, strlen(md[i].key)) &&
-                 !grpc_header_nonbin_value_is_legal(md[i].value,
-                                                    md[i].value_length)) {
+      } 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;
@@ -95,9 +100,8 @@
     } else if (num_md > 0) {
       md_array = gpr_malloc(num_md * sizeof(grpc_credentials_md));
       for (i = 0; i < num_md; i++) {
-        md_array[i].key = grpc_slice_from_copied_string(md[i].key);
-        md_array[i].value =
-            grpc_slice_from_copied_buffer(md[i].value, md[i].value_length);
+        md_array[i].key = grpc_slice_ref_internal(md[i].key);
+        md_array[i].value = grpc_slice_ref_internal(md[i].value);
       }
       r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK,
             NULL);
diff --git a/src/core/lib/security/transport/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c
index b7f6fd2..cf056e8 100644
--- a/src/core/lib/security/transport/client_auth_filter.c
+++ b/src/core/lib/security/transport/client_auth_filter.c
@@ -45,6 +45,7 @@
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/transport/static_metadata.h"
@@ -54,8 +55,10 @@
 /* We can have a per-call credentials. */
 typedef struct {
   grpc_call_credentials *creds;
-  grpc_mdstr *host;
-  grpc_mdstr *method;
+  bool have_host;
+  bool have_method;
+  grpc_slice host;
+  grpc_slice method;
   /* 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
@@ -89,14 +92,12 @@
   auth_md_context->channel_auth_context = NULL;
 }
 
-static void bubble_up_error(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                            grpc_status_code status, const char *error_msg) {
-  call_data *calld = elem->call_data;
-  gpr_log(GPR_ERROR, "Client side authentication failure: %s", error_msg);
-  grpc_slice error_slice = grpc_slice_from_copied_string(error_msg);
-  grpc_transport_stream_op_add_close(exec_ctx, &calld->op, status,
-                                     &error_slice);
-  grpc_call_next_op(exec_ctx, elem, &calld->op);
+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("Client auth metadata plugin error");
+  }
+  *combined = grpc_error_add_child(*combined, error);
 }
 
 static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
@@ -110,30 +111,37 @@
   grpc_metadata_batch *mdb;
   size_t i;
   reset_auth_metadata_context(&calld->auth_md_context);
+  grpc_error *error = GRPC_ERROR_NONE;
   if (status != GRPC_CREDENTIALS_OK) {
-    bubble_up_error(exec_ctx, elem, GRPC_STATUS_UNAUTHENTICATED,
-                    (error_details != NULL && strlen(error_details) > 0)
-                        ? error_details
-                        : "Credentials failed to get metadata.");
-    return;
+    error = grpc_error_set_int(
+        GRPC_ERROR_CREATE(error_details != NULL && strlen(error_details) > 0
+                              ? error_details
+                              : "Credentials failed to get metadata."),
+        GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED);
+  } else {
+    GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
+    GPR_ASSERT(op->send_initial_metadata != NULL);
+    mdb = op->send_initial_metadata;
+    for (i = 0; i < num_md; i++) {
+      add_error(&error,
+                grpc_metadata_batch_add_tail(
+                    exec_ctx, mdb, &calld->md_links[i],
+                    grpc_mdelem_from_slices(
+                        exec_ctx, grpc_slice_ref_internal(md_elems[i].key),
+                        grpc_slice_ref_internal(md_elems[i].value))));
+    }
   }
-  GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
-  GPR_ASSERT(op->send_initial_metadata != NULL);
-  mdb = op->send_initial_metadata;
-  for (i = 0; i < num_md; i++) {
-    grpc_metadata_batch_add_tail(
-        mdb, &calld->md_links[i],
-        grpc_mdelem_from_slices(exec_ctx,
-                                grpc_slice_ref_internal(md_elems[i].key),
-                                grpc_slice_ref_internal(md_elems[i].value)));
+  if (error == GRPC_ERROR_NONE) {
+    grpc_call_next_op(exec_ctx, elem, op);
+  } else {
+    grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error);
   }
-  grpc_call_next_op(exec_ctx, elem, op);
 }
 
 void build_auth_metadata_context(grpc_security_connector *sc,
                                  grpc_auth_context *auth_context,
                                  call_data *calld) {
-  char *service = gpr_strdup(grpc_mdstr_as_c_string(calld->method));
+  char *service = grpc_slice_to_c_string(calld->method);
   char *last_slash = strrchr(service, '/');
   char *method_name = NULL;
   char *service_url = NULL;
@@ -149,14 +157,15 @@
     method_name = gpr_strdup(last_slash + 1);
   }
   if (method_name == NULL) method_name = gpr_strdup("");
+  char *host = grpc_slice_to_c_string(calld->host);
   gpr_asprintf(&service_url, "%s://%s%s",
-               sc->url_scheme == NULL ? "" : sc->url_scheme,
-               grpc_mdstr_as_c_string(calld->host), service);
+               sc->url_scheme == NULL ? "" : sc->url_scheme, host, service);
   calld->auth_md_context.service_url = service_url;
   calld->auth_md_context.method_name = method_name;
   calld->auth_md_context.channel_auth_context =
       GRPC_AUTH_CONTEXT_REF(auth_context, "grpc_auth_metadata_context");
   gpr_free(service);
+  gpr_free(host);
 }
 
 static void send_security_metadata(grpc_exec_ctx *exec_ctx,
@@ -180,8 +189,12 @@
     calld->creds = grpc_composite_call_credentials_create(channel_call_creds,
                                                           ctx->creds, NULL);
     if (calld->creds == NULL) {
-      bubble_up_error(exec_ctx, elem, GRPC_STATUS_UNAUTHENTICATED,
-                      "Incompatible credentials set on channel and call.");
+      grpc_transport_stream_op_finish_with_failure(
+          exec_ctx, op,
+          grpc_error_set_int(
+              GRPC_ERROR_CREATE(
+                  "Incompatible credentials set on channel and call."),
+              GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED));
       return;
     }
   } else {
@@ -207,9 +220,14 @@
     send_security_metadata(exec_ctx, elem, &calld->op);
   } else {
     char *error_msg;
+    char *host = grpc_slice_to_c_string(calld->host);
     gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
-                 grpc_mdstr_as_c_string(calld->host));
-    bubble_up_error(exec_ctx, elem, GRPC_STATUS_UNAUTHENTICATED, error_msg);
+                 host);
+    gpr_free(host);
+    grpc_call_element_signal_error(
+        exec_ctx, elem, grpc_error_set_int(GRPC_ERROR_CREATE(error_msg),
+                                           GRPC_ERROR_INT_GRPC_STATUS,
+                                           GRPC_STATUS_UNAUTHENTICATED));
     gpr_free(error_msg);
   }
 }
@@ -247,23 +265,30 @@
 
   if (op->send_initial_metadata != NULL) {
     for (l = op->send_initial_metadata->list.head; l != NULL; l = l->next) {
-      grpc_mdelem *md = l->md;
+      grpc_mdelem md = l->md;
       /* Pointer comparison is OK for md_elems created from the same context.
        */
-      if (md->key == GRPC_MDSTR_AUTHORITY) {
-        if (calld->host != NULL) GRPC_MDSTR_UNREF(exec_ctx, calld->host);
-        calld->host = GRPC_MDSTR_REF(md->value);
-      } else if (md->key == GRPC_MDSTR_PATH) {
-        if (calld->method != NULL) GRPC_MDSTR_UNREF(exec_ctx, calld->method);
-        calld->method = GRPC_MDSTR_REF(md->value);
+      if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_AUTHORITY)) {
+        if (calld->have_host) {
+          grpc_slice_unref_internal(exec_ctx, calld->host);
+        }
+        calld->host = grpc_slice_ref_internal(GRPC_MDVALUE(md));
+        calld->have_host = true;
+      } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_PATH)) {
+        if (calld->have_method) {
+          grpc_slice_unref_internal(exec_ctx, calld->method);
+        }
+        calld->method = grpc_slice_ref_internal(GRPC_MDVALUE(md));
+        calld->have_method = true;
       }
     }
-    if (calld->host != NULL) {
-      const char *call_host = grpc_mdstr_as_c_string(calld->host);
+    if (calld->have_host) {
+      char *call_host = grpc_slice_to_c_string(calld->host);
       calld->op = *op; /* Copy op (originates from the caller's stack). */
       grpc_channel_security_connector_check_call_host(
           exec_ctx, chand->security_connector, call_host, chand->auth_context,
           on_host_checked, elem);
+      gpr_free(call_host);
       GPR_TIMER_END("auth_start_transport_op", 0);
       return; /* early exit */
     }
@@ -296,11 +321,11 @@
                               void *ignored) {
   call_data *calld = elem->call_data;
   grpc_call_credentials_unref(exec_ctx, calld->creds);
-  if (calld->host != NULL) {
-    GRPC_MDSTR_UNREF(exec_ctx, calld->host);
+  if (calld->have_host) {
+    grpc_slice_unref_internal(exec_ctx, calld->host);
   }
-  if (calld->method != NULL) {
-    GRPC_MDSTR_UNREF(exec_ctx, calld->method);
+  if (calld->have_method) {
+    grpc_slice_unref_internal(exec_ctx, calld->method);
   }
   reset_auth_metadata_context(&calld->auth_md_context);
 }
diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c
index 5aa26e0..b091278 100644
--- a/src/core/lib/security/transport/security_connector.c
+++ b/src/core/lib/security/transport/security_connector.c
@@ -601,7 +601,7 @@
     ssl_server_destroy, ssl_server_check_peer};
 
 static grpc_slice compute_default_pem_root_certs_once(void) {
-  grpc_slice result = gpr_empty_slice();
+  grpc_slice result = grpc_empty_slice();
 
   /* First try to load the roots from the environment. */
   char *default_root_certs_path =
diff --git a/src/core/lib/security/transport/security_handshaker.c b/src/core/lib/security/transport/security_handshaker.c
index 5e75856..37d57d7 100644
--- a/src/core/lib/security/transport/security_handshaker.c
+++ b/src/core/lib/security/transport/security_handshaker.c
@@ -124,7 +124,7 @@
   }
   const char *msg = grpc_error_string(error);
   gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg);
-  grpc_error_free_string(msg);
+
   if (!h->shutdown) {
     // TODO(ctiller): It is currently necessary to shutdown endpoints
     // before destroying them, even if we know that there are no
diff --git a/src/core/lib/security/transport/server_auth_filter.c b/src/core/lib/security/transport/server_auth_filter.c
index 5e98ba8..36e81d6 100644
--- a/src/core/lib/security/transport/server_auth_filter.c
+++ b/src/core/lib/security/transport/server_auth_filter.c
@@ -33,12 +33,13 @@
 
 #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 <grpc/support/alloc.h>
-#include <grpc/support/log.h>
+#include "src/core/lib/slice/slice_internal.h"
 
 typedef struct call_data {
   grpc_metadata_batch *recv_initial_metadata;
@@ -67,48 +68,34 @@
   grpc_metadata_array_init(&result);
   for (l = batch->list.head; l != NULL; l = l->next) {
     grpc_metadata *usr_md = NULL;
-    grpc_mdelem *md = l->md;
-    grpc_mdstr *key = md->key;
-    grpc_mdstr *value = md->value;
+    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 =
           gpr_realloc(result.metadata, result.capacity * sizeof(grpc_metadata));
     }
     usr_md = &result.metadata[result.count++];
-    usr_md->key = grpc_mdstr_as_c_string(key);
-    usr_md->value = grpc_mdstr_as_c_string(value);
-    usr_md->value_length = GRPC_SLICE_LENGTH(value->slice);
+    usr_md->key = grpc_slice_ref_internal(key);
+    usr_md->value = grpc_slice_ref_internal(value);
   }
   return result;
 }
 
-static grpc_mdelem *remove_consumed_md(grpc_exec_ctx *exec_ctx, void *user_data,
-                                       grpc_mdelem *md) {
+static grpc_filtered_mdelem remove_consumed_md(grpc_exec_ctx *exec_ctx,
+                                               void *user_data,
+                                               grpc_mdelem md) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
   size_t i;
   for (i = 0; i < calld->num_consumed_md; i++) {
     const grpc_metadata *consumed_md = &calld->consumed_md[i];
-    /* Maybe we could do a pointer comparison but we do not have any guarantee
-       that the metadata processor used the same pointers for consumed_md in the
-       callback. */
-    if (GRPC_SLICE_LENGTH(md->key->slice) != strlen(consumed_md->key) ||
-        GRPC_SLICE_LENGTH(md->value->slice) != consumed_md->value_length) {
-      continue;
-    }
-    if (memcmp(GRPC_SLICE_START_PTR(md->key->slice), consumed_md->key,
-               GRPC_SLICE_LENGTH(md->key->slice)) == 0 &&
-        memcmp(GRPC_SLICE_START_PTR(md->value->slice), consumed_md->value,
-               GRPC_SLICE_LENGTH(md->value->slice)) == 0) {
-      return NULL; /* Delete. */
-    }
+    if (grpc_slice_eq(GRPC_MDKEY(md), consumed_md->key) &&
+        grpc_slice_eq(GRPC_MDVALUE(md), consumed_md->value))
+      return GRPC_FILTERED_REMOVE();
   }
-  return md;
-}
-
-static void destroy_op(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
-  gpr_free(arg);
+  return GRPC_FILTERED_MDELEM(md);
 }
 
 /* called from application code */
@@ -130,29 +117,33 @@
   if (status == GRPC_STATUS_OK) {
     calld->consumed_md = consumed_md;
     calld->num_consumed_md = num_consumed_md;
-    grpc_metadata_batch_filter(&exec_ctx, calld->recv_initial_metadata,
-                               remove_consumed_md, elem);
+    /* TODO(ctiller): propagate error */
+    GRPC_LOG_IF_ERROR(
+        "grpc_metadata_batch_filter",
+        grpc_metadata_batch_filter(&exec_ctx, calld->recv_initial_metadata,
+                                   remove_consumed_md, elem,
+                                   "Response metadata filtering error"));
+    for (size_t i = 0; i < calld->md.count; i++) {
+      grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].key);
+      grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].value);
+    }
     grpc_metadata_array_destroy(&calld->md);
     grpc_closure_sched(&exec_ctx, calld->on_done_recv, GRPC_ERROR_NONE);
   } else {
-    grpc_slice message;
-    grpc_transport_stream_op *close_op = gpr_malloc(sizeof(*close_op));
-    memset(close_op, 0, sizeof(*close_op));
+    for (size_t i = 0; i < calld->md.count; i++) {
+      grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].key);
+      grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].value);
+    }
     grpc_metadata_array_destroy(&calld->md);
     error_details = error_details != NULL
                         ? error_details
                         : "Authentication metadata processing failed.";
-    message = grpc_slice_from_copied_string(error_details);
     calld->transport_op->send_initial_metadata = NULL;
     if (calld->transport_op->send_message != NULL) {
       grpc_byte_stream_destroy(&exec_ctx, calld->transport_op->send_message);
       calld->transport_op->send_message = NULL;
     }
     calld->transport_op->send_trailing_metadata = NULL;
-    close_op->on_complete =
-        grpc_closure_create(destroy_op, close_op, grpc_schedule_on_exec_ctx);
-    grpc_transport_stream_op_add_close(&exec_ctx, close_op, status, &message);
-    grpc_call_next_op(&exec_ctx, elem, close_op);
     grpc_closure_sched(&exec_ctx, calld->on_done_recv,
                        grpc_error_set_int(GRPC_ERROR_CREATE(error_details),
                                           GRPC_ERROR_INT_GRPC_STATUS, status));
diff --git a/src/core/lib/security/util/b64.c b/src/core/lib/security/util/b64.c
index bbd7e33..09c8213 100644
--- a/src/core/lib/security/util/b64.c
+++ b/src/core/lib/security/util/b64.c
@@ -232,5 +232,5 @@
 
 fail:
   grpc_slice_unref_internal(exec_ctx, result);
-  return gpr_empty_slice();
+  return grpc_empty_slice();
 }
diff --git a/src/core/lib/slice/slice.c b/src/core/lib/slice/slice.c
index 7611810..1cddf06 100644
--- a/src/core/lib/slice/slice.c
+++ b/src/core/lib/slice/slice.c
@@ -41,23 +41,30 @@
 
 #include "src/core/lib/iomgr/exec_ctx.h"
 
-grpc_slice gpr_empty_slice(void) {
+char *grpc_slice_to_c_string(grpc_slice slice) {
+  char *out = 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 = 0;
+  out.refcount = NULL;
   out.data.inlined.length = 0;
   return out;
 }
 
 grpc_slice grpc_slice_ref_internal(grpc_slice slice) {
   if (slice.refcount) {
-    slice.refcount->ref(slice.refcount);
+    slice.refcount->vtable->ref(slice.refcount);
   }
   return slice;
 }
 
 void grpc_slice_unref_internal(grpc_exec_ctx *exec_ctx, grpc_slice slice) {
   if (slice.refcount) {
-    slice.refcount->unref(exec_ctx, slice.refcount);
+    slice.refcount->vtable->unref(exec_ctx, slice.refcount);
   }
 }
 
@@ -78,16 +85,24 @@
 static void noop_ref(void *unused) {}
 static void noop_unref(grpc_exec_ctx *exec_ctx, void *unused) {}
 
-static grpc_slice_refcount noop_refcount = {noop_ref, noop_unref};
+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};
 
-grpc_slice grpc_slice_from_static_string(const char *s) {
+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 = strlen(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 {
@@ -110,14 +125,18 @@
   }
 }
 
+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 = gpr_malloc(sizeof(new_slice_refcount));
   gpr_ref_init(&rc->refs, 1);
-  rc->rc.ref = new_slice_ref;
-  rc->rc.unref = new_slice_unref;
+  rc->rc.vtable = &new_slice_vtable;
+  rc->rc.sub_refcount = &rc->rc;
   rc->user_destroy = destroy;
   rc->user_data = user_data;
 
@@ -155,14 +174,18 @@
   }
 }
 
+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 =
       gpr_malloc(sizeof(new_with_len_slice_refcount));
   gpr_ref_init(&rc->refs, 1);
-  rc->rc.ref = new_with_len_ref;
-  rc->rc.unref = new_with_len_unref;
+  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;
@@ -200,6 +223,10 @@
   }
 }
 
+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(size_t length) {
   grpc_slice slice;
 
@@ -219,8 +246,8 @@
        this reference. */
     gpr_ref_init(&rc->refs, 1);
 
-    rc->base.ref = malloc_ref;
-    rc->base.unref = malloc_unref;
+    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. */
@@ -247,7 +274,7 @@
     GPR_ASSERT(source.data.refcounted.length >= end);
 
     /* Build the result */
-    subset.refcount = source.refcount;
+    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;
@@ -273,7 +300,7 @@
   } else {
     subset = grpc_slice_sub_no_ref(source, begin, end);
     /* Bump the refcount */
-    subset.refcount->ref(subset.refcount);
+    subset.refcount->vtable->ref(subset.refcount);
   }
   return subset;
 }
@@ -300,13 +327,14 @@
              tail_length);
     } else {
       /* Build the result */
-      tail.refcount = source->refcount;
+      tail.refcount = source->refcount->sub_refcount;
       /* Bump the refcount */
-      tail.refcount->ref(tail.refcount);
+      tail.refcount->vtable->ref(tail.refcount);
       /* Point into the source array */
       tail.data.refcounted.bytes = source->data.refcounted.bytes + split;
       tail.data.refcounted.length = tail_length;
     }
+    source->refcount = source->refcount->sub_refcount;
     source->data.refcounted.length = split;
   }
 
@@ -332,18 +360,20 @@
     head.refcount = NULL;
     head.data.inlined.length = (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;
+    head.refcount = source->refcount->sub_refcount;
     /* Bump the refcount */
-    head.refcount->ref(head.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;
   }
@@ -351,6 +381,19 @@
   return head;
 }
 
+int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b) {
+  return GRPC_SLICE_LENGTH(a) == GRPC_SLICE_LENGTH(b) &&
+         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 = (int)(GRPC_SLICE_LENGTH(a) - GRPC_SLICE_LENGTH(b));
   if (d != 0) return d;
@@ -367,8 +410,55 @@
 
 int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b) {
   if (a.refcount == NULL || b.refcount == NULL) {
-    return grpc_slice_cmp(a, b) == 0;
+    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 = (const char *)GRPC_SLICE_START_PTR(s);
+  int i;
+  for (i = (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 = (const char *)GRPC_SLICE_START_PTR(s);
+  const char *p = memchr(b, c, GRPC_SLICE_LENGTH(s));
+  return p == NULL ? -1 : (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, (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 (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/src/core/lib/transport/mdstr_hash_table.c b/src/core/lib/slice/slice_hash_table.c
similarity index 61%
rename from src/core/lib/transport/mdstr_hash_table.c
rename to src/core/lib/slice/slice_hash_table.c
index 2791bf6..46f807f 100644
--- a/src/core/lib/transport/mdstr_hash_table.c
+++ b/src/core/lib/slice/slice_hash_table.c
@@ -29,7 +29,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
 
-#include "src/core/lib/transport/mdstr_hash_table.h"
+#include "src/core/lib/slice/slice_hash_table.h"
 
 #include <stdbool.h>
 #include <string.h>
@@ -37,70 +37,79 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/metadata.h"
 
-struct grpc_mdstr_hash_table {
+struct grpc_slice_hash_table {
   gpr_refcount refs;
   size_t size;
-  grpc_mdstr_hash_table_entry* entries;
+  grpc_slice_hash_table_entry* entries;
 };
 
+static bool is_empty(grpc_slice_hash_table_entry* entry) {
+  return entry->vtable == NULL;
+}
+
 // Helper function for insert and get operations that performs quadratic
 // probing (https://en.wikipedia.org/wiki/Quadratic_probing).
-static size_t grpc_mdstr_hash_table_find_index(
-    const grpc_mdstr_hash_table* table, const grpc_mdstr* key,
-    bool find_empty) {
+static size_t grpc_slice_hash_table_find_index(
+    const grpc_slice_hash_table* table, const grpc_slice key, bool find_empty) {
+  size_t hash = grpc_slice_hash(key);
   for (size_t i = 0; i < table->size; ++i) {
-    const size_t idx = (key->hash + i * i) % table->size;
-    if (table->entries[idx].key == NULL) return find_empty ? idx : table->size;
-    if (table->entries[idx].key == key) return idx;
+    const size_t idx = (hash + i * i) % table->size;
+    if (is_empty(&table->entries[idx])) {
+      return find_empty ? idx : table->size;
+    }
+    if (grpc_slice_eq(table->entries[idx].key, key)) {
+      return idx;
+    }
   }
   return table->size;  // Not found.
 }
 
-static void grpc_mdstr_hash_table_add(
-    grpc_mdstr_hash_table* table, grpc_mdstr* key, void* value,
-    const grpc_mdstr_hash_table_vtable* vtable) {
+static void grpc_slice_hash_table_add(
+    grpc_slice_hash_table* table, grpc_slice key, void* value,
+    const grpc_slice_hash_table_vtable* vtable) {
   GPR_ASSERT(value != NULL);
   const size_t idx =
-      grpc_mdstr_hash_table_find_index(table, key, true /* find_empty */);
+      grpc_slice_hash_table_find_index(table, key, true /* find_empty */);
   GPR_ASSERT(idx != table->size);  // Table should never be full.
-  grpc_mdstr_hash_table_entry* entry = &table->entries[idx];
-  entry->key = GRPC_MDSTR_REF(key);
+  grpc_slice_hash_table_entry* entry = &table->entries[idx];
+  entry->key = grpc_slice_ref_internal(key);
   entry->value = vtable->copy_value(value);
   entry->vtable = vtable;
 }
 
-grpc_mdstr_hash_table* grpc_mdstr_hash_table_create(
-    size_t num_entries, grpc_mdstr_hash_table_entry* entries) {
-  grpc_mdstr_hash_table* table = gpr_malloc(sizeof(*table));
+grpc_slice_hash_table* grpc_slice_hash_table_create(
+    size_t num_entries, grpc_slice_hash_table_entry* entries) {
+  grpc_slice_hash_table* table = gpr_malloc(sizeof(*table));
   memset(table, 0, sizeof(*table));
   gpr_ref_init(&table->refs, 1);
   // Quadratic probing gets best performance when the table is no more
   // than half full.
   table->size = num_entries * 2;
-  const size_t entry_size = sizeof(grpc_mdstr_hash_table_entry) * table->size;
+  const size_t entry_size = sizeof(grpc_slice_hash_table_entry) * table->size;
   table->entries = gpr_malloc(entry_size);
   memset(table->entries, 0, entry_size);
   for (size_t i = 0; i < num_entries; ++i) {
-    grpc_mdstr_hash_table_entry* entry = &entries[i];
-    grpc_mdstr_hash_table_add(table, entry->key, entry->value, entry->vtable);
+    grpc_slice_hash_table_entry* entry = &entries[i];
+    grpc_slice_hash_table_add(table, entry->key, entry->value, entry->vtable);
   }
   return table;
 }
 
-grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table) {
+grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table) {
   if (table != NULL) gpr_ref(&table->refs);
   return table;
 }
 
-void grpc_mdstr_hash_table_unref(grpc_exec_ctx* exec_ctx,
-                                 grpc_mdstr_hash_table* table) {
+void grpc_slice_hash_table_unref(grpc_exec_ctx* exec_ctx,
+                                 grpc_slice_hash_table* table) {
   if (table != NULL && gpr_unref(&table->refs)) {
     for (size_t i = 0; i < table->size; ++i) {
-      grpc_mdstr_hash_table_entry* entry = &table->entries[i];
-      if (entry->key != NULL) {
-        GRPC_MDSTR_UNREF(exec_ctx, entry->key);
+      grpc_slice_hash_table_entry* entry = &table->entries[i];
+      if (!is_empty(entry)) {
+        grpc_slice_unref_internal(exec_ctx, entry->key);
         entry->vtable->destroy_value(exec_ctx, entry->value);
       }
     }
@@ -109,10 +118,10 @@
   }
 }
 
-void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table,
-                                const grpc_mdstr* key) {
+void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table,
+                                const grpc_slice key) {
   const size_t idx =
-      grpc_mdstr_hash_table_find_index(table, key, false /* find_empty */);
+      grpc_slice_hash_table_find_index(table, key, false /* find_empty */);
   if (idx == table->size) return NULL;  // Not found.
   return table->entries[idx].value;
 }
diff --git a/src/core/lib/transport/mdstr_hash_table.h b/src/core/lib/slice/slice_hash_table.h
similarity index 67%
rename from src/core/lib/transport/mdstr_hash_table.h
rename to src/core/lib/slice/slice_hash_table.h
index 57f497e..d0c2712 100644
--- a/src/core/lib/transport/mdstr_hash_table.h
+++ b/src/core/lib/slice/slice_hash_table.h
@@ -29,8 +29,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef GRPC_CORE_LIB_TRANSPORT_MDSTR_HASH_TABLE_H
-#define GRPC_CORE_LIB_TRANSPORT_MDSTR_HASH_TABLE_H
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H
+#define GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H
 
 #include "src/core/lib/transport/metadata.h"
 
@@ -40,38 +40,38 @@
  * (https://en.wikipedia.org/wiki/Open_addressing) with quadratic
  * probing (https://en.wikipedia.org/wiki/Quadratic_probing).
  *
- * The keys are \a grpc_mdstr objects.  The values are arbitrary pointers
+ * The keys are \a grpc_slice objects.  The values are arbitrary pointers
  * with a common vtable.
  *
  * Hash tables are intentionally immutable, to avoid the need for locking.
  */
 
-typedef struct grpc_mdstr_hash_table grpc_mdstr_hash_table;
+typedef struct grpc_slice_hash_table grpc_slice_hash_table;
 
-typedef struct grpc_mdstr_hash_table_vtable {
-  void (*destroy_value)(grpc_exec_ctx* exec_ctx, void* value);
-  void* (*copy_value)(void* value);
-} grpc_mdstr_hash_table_vtable;
+typedef struct grpc_slice_hash_table_vtable {
+  void (*destroy_value)(grpc_exec_ctx *exec_ctx, void *value);
+  void *(*copy_value)(void *value);
+} grpc_slice_hash_table_vtable;
 
-typedef struct grpc_mdstr_hash_table_entry {
-  grpc_mdstr* key;
-  void* value; /* Must not be NULL. */
-  const grpc_mdstr_hash_table_vtable* vtable;
-} grpc_mdstr_hash_table_entry;
+typedef struct grpc_slice_hash_table_entry {
+  grpc_slice key;
+  void *value; /* Must not be NULL. */
+  const grpc_slice_hash_table_vtable *vtable;
+} grpc_slice_hash_table_entry;
 
 /** Creates a new hash table of containing \a entries, which is an array
     of length \a num_entries.
     Creates its own copy of all keys and values from \a entries. */
-grpc_mdstr_hash_table* grpc_mdstr_hash_table_create(
-    size_t num_entries, grpc_mdstr_hash_table_entry* entries);
+grpc_slice_hash_table *grpc_slice_hash_table_create(
+    size_t num_entries, grpc_slice_hash_table_entry *entries);
 
-grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table);
-void grpc_mdstr_hash_table_unref(grpc_exec_ctx* exec_ctx,
-                                 grpc_mdstr_hash_table* table);
+grpc_slice_hash_table *grpc_slice_hash_table_ref(grpc_slice_hash_table *table);
+void grpc_slice_hash_table_unref(grpc_exec_ctx *exec_ctx,
+                                 grpc_slice_hash_table *table);
 
 /** Returns the value from \a table associated with \a key.
     Returns NULL if \a key is not found. */
-void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table,
-                                const grpc_mdstr* key);
+void *grpc_slice_hash_table_get(const grpc_slice_hash_table *table,
+                                const grpc_slice key);
 
-#endif /* GRPC_CORE_LIB_TRANSPORT_MDSTR_HASH_TABLE_H */
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H */
diff --git a/src/core/lib/slice/slice_intern.c b/src/core/lib/slice/slice_intern.c
new file mode 100644
index 0000000..7cbd17b
--- /dev/null
+++ b/src/core/lib/slice/slice_intern.c
@@ -0,0 +1,344 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/slice/slice_internal.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.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/support/murmur_hash.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 = 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(grpc_exec_ctx *exec_ctx, void *p) {
+  interned_slice_refcount *s = 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(((char *)p) - offsetof(interned_slice_refcount, sub));
+}
+
+static void interned_slice_sub_unref(grpc_exec_ctx *exec_ctx, void *p) {
+  interned_slice_unref(exec_ctx,
+                       ((char *)p) - offsetof(interned_slice_refcount, sub));
+}
+
+static uint32_t interned_slice_hash(grpc_slice slice) {
+  interned_slice_refcount *s = (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) {
+  size_t capacity = shard->capacity * 2;
+  size_t i;
+  interned_slice_refcount **strtab;
+  interned_slice_refcount *s, *next;
+
+  GPR_TIMER_BEGIN("grow_strtab", 0);
+
+  strtab = gpr_malloc(sizeof(interned_slice_refcount *) * capacity);
+  memset(strtab, 0, 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;
+
+  GPR_TIMER_END("grow_strtab", 0);
+}
+
+static grpc_slice materialize(interned_slice_refcount *s) {
+  grpc_slice slice;
+  slice.refcount = &s->base;
+  slice.data.refcounted.bytes = (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 == NULL ? 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) {
+  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);
+        GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
+        return materialize(s);
+      }
+    }
+  }
+
+  /* not found: create a new string */
+  /* string data goes after the internal_string header */
+  s = 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 = (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 = gpr_malloc(sizeof(*shard->strs) * shard->capacity);
+    memset(shard->strs, 0, 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 = (uint32_t)i;
+        if (j > max_static_metadata_hash_probe) {
+          max_static_metadata_hash_probe = (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/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h
index 6185333..6467b0a 100644
--- a/src/core/lib/slice/slice_internal.h
+++ b/src/core/lib/slice/slice_internal.h
@@ -46,4 +46,19 @@
 void grpc_slice_buffer_destroy_internal(grpc_exec_ctx *exec_ctx,
                                         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);
+
 #endif /* GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H */
diff --git a/src/core/lib/slice/slice_string_helpers.c b/src/core/lib/slice/slice_string_helpers.c
index 839c366..9969500 100644
--- a/src/core/lib/slice/slice_string_helpers.c
+++ b/src/core/lib/slice/slice_string_helpers.c
@@ -88,3 +88,8 @@
     grpc_slice_buffer_add_indexed(dst, grpc_slice_ref_internal(str));
   }
 }
+
+bool grpc_parse_slice_to_uint32(grpc_slice str, uint32_t *result) {
+  return gpr_parse_bytes_to_uint32((const char *)GRPC_SLICE_START_PTR(str),
+                                   GRPC_SLICE_LENGTH(str), result) != 0;
+}
diff --git a/src/core/lib/slice/slice_string_helpers.h b/src/core/lib/slice/slice_string_helpers.h
index 151c720..4a4deec 100644
--- a/src/core/lib/slice/slice_string_helpers.h
+++ b/src/core/lib/slice/slice_string_helpers.h
@@ -34,12 +34,15 @@
 #ifndef GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H
 #define GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H
 
+#include <stdbool.h>
 #include <stddef.h>
 
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
 #include <grpc/support/port_platform.h>
 
+#include "src/core/lib/support/string.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -51,6 +54,8 @@
  * should be a properly initialized instance. */
 void grpc_slice_split(grpc_slice str, const char *sep, grpc_slice_buffer *dst);
 
+bool grpc_parse_slice_to_uint32(grpc_slice str, uint32_t *result);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/core/ext/transport/chttp2/transport/status_conversion.h b/src/core/lib/slice/slice_traits.h
similarity index 64%
copy from src/core/ext/transport/chttp2/transport/status_conversion.h
copy to src/core/lib/slice/slice_traits.h
index 953bc9f..8a283dc 100644
--- a/src/core/ext/transport/chttp2/transport/status_conversion.h
+++ b/src/core/lib/slice/slice_traits.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,20 +31,14 @@
  *
  */
 
-#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
-#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H
+#define GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H
 
-#include <grpc/grpc.h>
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
+#include <grpc/slice.h>
+#include <stdbool.h>
 
-/* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */
-grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error(
-    grpc_status_code status);
-grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error, gpr_timespec deadline);
+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);
 
-/* Conversion of HTTP status codes (:status) to grpc status codes */
-grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status);
-int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status);
-
-#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H */
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H */
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index 899e8fa..5519a1e 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -56,13 +56,15 @@
 #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/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/transport.h"
 
 /** The maximum number of concurrent batches possible.
     Based upon the maximum number of individually queueable ops in the batch
-   api:
+    api:
       - initial metadata send
       - message send
       - status/close send (depending on client/server)
@@ -92,18 +94,21 @@
 } status_source;
 
 typedef struct {
-  uint8_t is_set;
-  grpc_status_code code;
-  grpc_mdstr *details;
+  bool is_set;
+  grpc_error *error;
 } received_status;
 
+#define MAX_ERRORS_PER_BATCH 3
+
 typedef struct batch_control {
   grpc_call *call;
   grpc_cq_completion cq_completion;
   grpc_closure finish_batch;
   void *notify_tag;
   gpr_refcount steps_to_complete;
-  grpc_error *error;
+
+  grpc_error *errors[MAX_ERRORS_PER_BATCH];
+  gpr_atm num_errors;
 
   uint8_t send_initial_metadata;
   uint8_t send_message;
@@ -185,6 +190,7 @@
   grpc_call *sibling_prev;
 
   grpc_slice_buffer_stream sending_stream;
+
   grpc_byte_stream *receiving_stream;
   grpc_byte_buffer **receiving_buffer;
   grpc_slice receiving_slice;
@@ -196,8 +202,7 @@
   union {
     struct {
       grpc_status_code *status;
-      char **status_details;
-      size_t *status_details_capacity;
+      grpc_slice *status_details;
     } client;
     struct {
       int *cancelled;
@@ -219,13 +224,23 @@
 static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
                                           grpc_status_code status,
                                           const char *description);
-static grpc_call_error close_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
-                                         grpc_status_code status,
-                                         const char *description);
+static void cancel_with_error(grpc_exec_ctx *exec_ctx, grpc_call *c,
+                              grpc_error *error);
 static void destroy_call(grpc_exec_ctx *exec_ctx, void *call_stack,
                          grpc_error *error);
 static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
                                   grpc_error *error);
+static void get_final_status(grpc_call *call,
+                             void (*set_value)(grpc_status_code code,
+                                               void *user_data),
+                             void *set_value_user_data, grpc_slice *details);
+static void set_status_value_directly(grpc_status_code status, void *dest);
+static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call,
+                                  status_source source, grpc_error *error);
+static void process_data_after_md(grpc_exec_ctx *exec_ctx, batch_control *bctl);
+static void post_batch_completion(grpc_exec_ctx *exec_ctx, batch_control *bctl);
+static void add_batch_error(grpc_exec_ctx *exec_ctx, batch_control *bctl,
+                            grpc_error *error);
 
 grpc_error *grpc_call_create(grpc_exec_ctx *exec_ctx,
                              const grpc_call_create_args *args,
@@ -246,14 +261,16 @@
   /* Always support no compression */
   GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE);
   call->is_client = args->server_transport_data == NULL;
-  grpc_mdstr *path = NULL;
+  grpc_slice path = grpc_empty_slice();
   if (call->is_client) {
     GPR_ASSERT(args->add_initial_metadata_count <
                MAX_SEND_EXTRA_METADATA_COUNT);
     for (i = 0; i < args->add_initial_metadata_count; i++) {
       call->send_extra_metadata[i].md = args->add_initial_metadata[i];
-      if (args->add_initial_metadata[i]->key == GRPC_MDSTR_PATH) {
-        path = GRPC_MDSTR_REF(args->add_initial_metadata[i]->value);
+      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 = (int)args->add_initial_metadata_count;
@@ -320,10 +337,7 @@
       args->server_transport_data, path, call->start_time, send_deadline,
       CALL_STACK_FROM_CALL(call));
   if (error != GRPC_ERROR_NONE) {
-    grpc_status_code status;
-    const char *error_str;
-    grpc_error_get_status(error, &status, &error_str);
-    close_with_status(exec_ctx, call, status, error_str);
+    cancel_with_error(exec_ctx, call, GRPC_ERROR_REF(error));
   }
   if (args->cq != NULL) {
     GPR_ASSERT(
@@ -342,7 +356,7 @@
         exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent);
   }
 
-  if (path != NULL) GRPC_MDSTR_UNREF(exec_ctx, path);
+  grpc_slice_unref_internal(exec_ctx, path);
 
   GPR_TIMER_END("grpc_call_create", 0);
   return error;
@@ -377,24 +391,6 @@
   GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON);
 }
 
-static void get_final_status(grpc_call *call,
-                             void (*set_value)(grpc_status_code code,
-                                               void *user_data),
-                             void *set_value_user_data) {
-  int i;
-  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
-    if (call->status[i].is_set) {
-      set_value(call->status[i].code, set_value_user_data);
-      return;
-    }
-  }
-  if (call->is_client) {
-    set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
-  } else {
-    set_value(GRPC_STATUS_OK, set_value_user_data);
-  }
-}
-
 static void set_status_value_directly(grpc_status_code status, void *dest);
 static void destroy_call(grpc_exec_ctx *exec_ctx, void *call,
                          grpc_error *error) {
@@ -410,11 +406,6 @@
     grpc_byte_stream_destroy(exec_ctx, c->receiving_stream);
   }
   gpr_mu_destroy(&c->mu);
-  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
-    if (c->status[i].details) {
-      GRPC_MDSTR_UNREF(exec_ctx, c->status[i].details);
-    }
-  }
   for (ii = 0; ii < c->send_extra_metadata_count; ii++) {
     GRPC_MDELEM_UNREF(exec_ctx, c->send_extra_metadata[ii].md);
   }
@@ -428,285 +419,20 @@
   }
   grpc_channel *channel = c->channel;
 
-  get_final_status(call, set_status_value_directly,
-                   &c->final_info.final_status);
+  get_final_status(call, set_status_value_directly, &c->final_info.final_status,
+                   NULL);
   c->final_info.stats.latency =
       gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time);
 
+  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
+    GRPC_ERROR_UNREF(c->status[i].error);
+  }
+
   grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), &c->final_info, c);
   GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "call");
   GPR_TIMER_END("destroy_call", 0);
 }
 
-static void set_status_code(grpc_call *call, status_source source,
-                            uint32_t status) {
-  if (call->status[source].is_set) return;
-
-  call->status[source].is_set = 1;
-  call->status[source].code = (grpc_status_code)status;
-}
-
-static void set_status_details(grpc_exec_ctx *exec_ctx, grpc_call *call,
-                               status_source source, grpc_mdstr *status) {
-  if (call->status[source].details != NULL) {
-    GRPC_MDSTR_UNREF(exec_ctx, status);
-  } else {
-    call->status[source].details = status;
-  }
-}
-
-static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call,
-                                  status_source source, grpc_error *error) {
-  grpc_status_code status;
-  const char *msg;
-  grpc_error_get_status(error, &status, &msg);
-  set_status_code(call, source, (uint32_t)status);
-  set_status_details(exec_ctx, call, source, grpc_mdstr_from_string(msg));
-}
-
-static void set_incoming_compression_algorithm(
-    grpc_call *call, grpc_compression_algorithm algo) {
-  GPR_ASSERT(algo < GRPC_COMPRESS_ALGORITHMS_COUNT);
-  call->incoming_compression_algorithm = algo;
-}
-
-grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm(
-    grpc_call *call) {
-  grpc_compression_algorithm algorithm;
-  gpr_mu_lock(&call->mu);
-  algorithm = call->incoming_compression_algorithm;
-  gpr_mu_unlock(&call->mu);
-  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;
-  gpr_mu_lock(&call->mu);
-  flags = call->test_only_last_message_flags;
-  gpr_mu_unlock(&call->mu);
-  return flags;
-}
-
-static void destroy_encodings_accepted_by_peer(void *p) { return; }
-
-static void set_encodings_accepted_by_peer(grpc_exec_ctx *exec_ctx,
-                                           grpc_call *call, grpc_mdelem *mdel) {
-  size_t i;
-  grpc_compression_algorithm 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 != NULL) {
-    call->encodings_accepted_by_peer =
-        (uint32_t)(((uintptr_t)accepted_user_data) - 1);
-    return;
-  }
-
-  accept_encoding_slice = mdel->value->slice;
-  grpc_slice_buffer_init(&accept_encoding_parts);
-  grpc_slice_split(accept_encoding_slice, ",", &accept_encoding_parts);
-
-  /* No need to zero call->encodings_accepted_by_peer: grpc_call_create already
-   * zeroes the whole grpc_call */
-  /* Always support no compression */
-  GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE);
-  for (i = 0; i < accept_encoding_parts.count; i++) {
-    const grpc_slice *accept_encoding_entry_slice =
-        &accept_encoding_parts.slices[i];
-    if (grpc_compression_algorithm_parse(
-            (const char *)GRPC_SLICE_START_PTR(*accept_encoding_entry_slice),
-            GRPC_SLICE_LENGTH(*accept_encoding_entry_slice), &algorithm)) {
-      GPR_BITSET(&call->encodings_accepted_by_peer, algorithm);
-    } else {
-      char *accept_encoding_entry_str =
-          grpc_dump_slice(*accept_encoding_entry_slice, GPR_DUMP_ASCII);
-      gpr_log(GPR_ERROR,
-              "Invalid entry in accept encoding metadata: '%s'. Ignoring.",
-              accept_encoding_entry_str);
-      gpr_free(accept_encoding_entry_str);
-    }
-  }
-
-  grpc_slice_buffer_destroy_internal(exec_ctx, &accept_encoding_parts);
-
-  grpc_mdelem_set_user_data(
-      mdel, destroy_encodings_accepted_by_peer,
-      (void *)(((uintptr_t)call->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;
-  gpr_mu_lock(&call->mu);
-  encodings_accepted_by_peer = call->encodings_accepted_by_peer;
-  gpr_mu_unlock(&call->mu);
-  return encodings_accepted_by_peer;
-}
-
-static void get_final_details(grpc_call *call, char **out_details,
-                              size_t *out_details_capacity) {
-  int i;
-  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
-    if (call->status[i].is_set) {
-      if (call->status[i].details) {
-        grpc_slice details = call->status[i].details->slice;
-        size_t len = GRPC_SLICE_LENGTH(details);
-        if (len + 1 > *out_details_capacity) {
-          *out_details_capacity =
-              GPR_MAX(len + 1, *out_details_capacity * 3 / 2);
-          *out_details = gpr_realloc(*out_details, *out_details_capacity);
-        }
-        memcpy(*out_details, GRPC_SLICE_START_PTR(details), len);
-        (*out_details)[len] = 0;
-      } else {
-        goto no_details;
-      }
-      return;
-    }
-  }
-
-no_details:
-  if (0 == *out_details_capacity) {
-    *out_details_capacity = 8;
-    *out_details = gpr_malloc(*out_details_capacity);
-  }
-  **out_details = 0;
-}
-
-static grpc_linked_mdelem *linked_from_md(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_exec_ctx *exec_ctx, 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 = (grpc_linked_mdelem *)&md->internal_data;
-    GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data));
-    l->md = grpc_mdelem_from_string_and_buffer(
-        exec_ctx, md->key, (const uint8_t *)md->value, md->value_length);
-    if (!grpc_header_key_is_legal(grpc_mdstr_as_c_string(l->md->key),
-                                  GRPC_MDSTR_LENGTH(l->md->key))) {
-      gpr_log(GPR_ERROR, "attempt to send invalid metadata key: %s",
-              grpc_mdstr_as_c_string(l->md->key));
-      break;
-    } else if (!grpc_is_binary_header(grpc_mdstr_as_c_string(l->md->key),
-                                      GRPC_MDSTR_LENGTH(l->md->key)) &&
-               !grpc_header_nonbin_value_is_legal(
-                   grpc_mdstr_as_c_string(l->md->value),
-                   GRPC_MDSTR_LENGTH(l->md->value))) {
-      gpr_log(GPR_ERROR, "attempt to send invalid metadata value");
-      break;
-    }
-  }
-  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 = (grpc_linked_mdelem *)&md->internal_data;
-      GRPC_MDELEM_UNREF(exec_ctx, l->md);
-    }
-    return 0;
-  }
-  if (prepend_extra_metadata) {
-    if (call->send_extra_metadata_count == 0) {
-      prepend_extra_metadata = 0;
-    } else {
-      for (i = 1; i < call->send_extra_metadata_count; i++) {
-        call->send_extra_metadata[i].prev = &call->send_extra_metadata[i - 1];
-      }
-      for (i = 0; i < call->send_extra_metadata_count - 1; i++) {
-        call->send_extra_metadata[i].next = &call->send_extra_metadata[i + 1];
-      }
-    }
-  }
-  for (i = 1; i < total_count; i++) {
-    grpc_metadata *md = get_md_elem(metadata, additional_metadata, i, count);
-    grpc_metadata *prev_md =
-        get_md_elem(metadata, additional_metadata, i - 1, count);
-    linked_from_md(md)->prev = linked_from_md(prev_md);
-  }
-  for (i = 0; i < total_count - 1; i++) {
-    grpc_metadata *md = get_md_elem(metadata, additional_metadata, i, count);
-    grpc_metadata *next_md =
-        get_md_elem(metadata, additional_metadata, i + 1, count);
-    linked_from_md(md)->next = linked_from_md(next_md);
-  }
-
-  switch (prepend_extra_metadata * 2 + (total_count != 0)) {
-    case 0:
-      /* no prepend, no metadata => nothing to do */
-      batch->list.head = batch->list.tail = NULL;
-      break;
-    case 1: {
-      /* metadata, but no prepend */
-      grpc_metadata *first_md =
-          get_md_elem(metadata, additional_metadata, 0, count);
-      grpc_metadata *last_md =
-          get_md_elem(metadata, additional_metadata, total_count - 1, count);
-      batch->list.head = linked_from_md(first_md);
-      batch->list.tail = linked_from_md(last_md);
-      batch->list.head->prev = NULL;
-      batch->list.tail->next = NULL;
-      break;
-    }
-    case 2:
-      /* prepend, but no md */
-      batch->list.head = &call->send_extra_metadata[0];
-      batch->list.tail =
-          &call->send_extra_metadata[call->send_extra_metadata_count - 1];
-      batch->list.head->prev = NULL;
-      batch->list.tail->next = NULL;
-      call->send_extra_metadata_count = 0;
-      break;
-    case 3: {
-      /* prepend AND md */
-      grpc_metadata *first_md =
-          get_md_elem(metadata, additional_metadata, 0, count);
-      grpc_metadata *last_md =
-          get_md_elem(metadata, additional_metadata, total_count - 1, count);
-      batch->list.head = &call->send_extra_metadata[0];
-      call->send_extra_metadata[call->send_extra_metadata_count - 1].next =
-          linked_from_md(first_md);
-      linked_from_md(first_md)->prev =
-          &call->send_extra_metadata[call->send_extra_metadata_count - 1];
-      batch->list.tail = linked_from_md(last_md);
-      batch->list.head->prev = NULL;
-      batch->list.tail->next = NULL;
-      call->send_extra_metadata_count = 0;
-      break;
-    }
-    default:
-      GPR_UNREACHABLE_CODE(return 0);
-  }
-
-  return 1;
-}
-
 void grpc_call_destroy(grpc_call *c) {
   int cancel;
   grpc_call *parent = c->parent;
@@ -747,117 +473,6 @@
                                       NULL);
 }
 
-grpc_call_error grpc_call_cancel_with_status(grpc_call *c,
-                                             grpc_status_code status,
-                                             const char *description,
-                                             void *reserved) {
-  grpc_call_error r;
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  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 == NULL);
-  gpr_mu_lock(&c->mu);
-  r = cancel_with_status(&exec_ctx, c, status, description);
-  gpr_mu_unlock(&c->mu);
-  grpc_exec_ctx_finish(&exec_ctx);
-  return r;
-}
-
-typedef struct termination_closure {
-  grpc_closure closure;
-  grpc_call *call;
-  grpc_error *error;
-  enum { TC_CANCEL, TC_CLOSE } type;
-  grpc_transport_stream_op op;
-} termination_closure;
-
-static void done_termination(grpc_exec_ctx *exec_ctx, void *tcp,
-                             grpc_error *error) {
-  termination_closure *tc = tcp;
-  switch (tc->type) {
-    case TC_CANCEL:
-      GRPC_CALL_INTERNAL_UNREF(exec_ctx, tc->call, "cancel");
-      break;
-    case TC_CLOSE:
-      GRPC_CALL_INTERNAL_UNREF(exec_ctx, tc->call, "close");
-      break;
-  }
-  GRPC_ERROR_UNREF(tc->error);
-  gpr_free(tc);
-}
-
-static void send_cancel(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
-  termination_closure *tc = tcp;
-  memset(&tc->op, 0, sizeof(tc->op));
-  tc->op.cancel_error = tc->error;
-  /* reuse closure to catch completion */
-  grpc_closure_init(&tc->closure, done_termination, tc,
-                    grpc_schedule_on_exec_ctx);
-  tc->op.on_complete = &tc->closure;
-  execute_op(exec_ctx, tc->call, &tc->op);
-}
-
-static void send_close(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
-  termination_closure *tc = tcp;
-  memset(&tc->op, 0, sizeof(tc->op));
-  tc->op.close_error = tc->error;
-  /* reuse closure to catch completion */
-  grpc_closure_init(&tc->closure, done_termination, tc,
-                    grpc_schedule_on_exec_ctx);
-  tc->op.on_complete = &tc->closure;
-  execute_op(exec_ctx, tc->call, &tc->op);
-}
-
-static grpc_call_error terminate_with_status(grpc_exec_ctx *exec_ctx,
-                                             termination_closure *tc) {
-  set_status_from_error(exec_ctx, tc->call, STATUS_FROM_API_OVERRIDE,
-                        tc->error);
-
-  if (tc->type == TC_CANCEL) {
-    grpc_closure_init(&tc->closure, send_cancel, tc, grpc_schedule_on_exec_ctx);
-    GRPC_CALL_INTERNAL_REF(tc->call, "cancel");
-  } else if (tc->type == TC_CLOSE) {
-    grpc_closure_init(&tc->closure, send_close, tc, grpc_schedule_on_exec_ctx);
-    GRPC_CALL_INTERNAL_REF(tc->call, "close");
-  }
-  grpc_closure_sched(exec_ctx, &tc->closure, GRPC_ERROR_NONE);
-  return GRPC_CALL_OK;
-}
-
-static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
-                                          grpc_status_code status,
-                                          const char *description) {
-  GPR_ASSERT(status != GRPC_STATUS_OK);
-  termination_closure *tc = gpr_malloc(sizeof(*tc));
-  memset(tc, 0, sizeof(termination_closure));
-  tc->type = TC_CANCEL;
-  tc->call = c;
-  tc->error = grpc_error_set_int(
-      grpc_error_set_str(GRPC_ERROR_CREATE(description),
-                         GRPC_ERROR_STR_GRPC_MESSAGE, description),
-      GRPC_ERROR_INT_GRPC_STATUS, status);
-
-  return terminate_with_status(exec_ctx, tc);
-}
-
-static grpc_call_error close_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
-                                         grpc_status_code status,
-                                         const char *description) {
-  GPR_ASSERT(status != GRPC_STATUS_OK);
-  termination_closure *tc = gpr_malloc(sizeof(*tc));
-  memset(tc, 0, sizeof(termination_closure));
-  tc->type = TC_CLOSE;
-  tc->call = c;
-  tc->error = grpc_error_set_int(
-      grpc_error_set_str(GRPC_ERROR_CREATE(description),
-                         GRPC_ERROR_STR_GRPC_MESSAGE, description),
-      GRPC_ERROR_INT_GRPC_STATUS, status);
-
-  return terminate_with_status(exec_ctx, tc);
-}
-
 static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call,
                        grpc_transport_stream_op *op) {
   grpc_call_element *elem;
@@ -889,25 +504,330 @@
   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_call_error r;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  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 == NULL);
+  gpr_mu_lock(&c->mu);
+  r = cancel_with_status(&exec_ctx, c, status, description);
+  gpr_mu_unlock(&c->mu);
+  grpc_exec_ctx_finish(&exec_ctx);
+  return r;
+}
+
+typedef struct termination_closure {
+  grpc_closure closure;
+  grpc_call *call;
+  grpc_error *error;
+  grpc_transport_stream_op op;
+} termination_closure;
+
+static void done_termination(grpc_exec_ctx *exec_ctx, void *tcp,
+                             grpc_error *error) {
+  termination_closure *tc = tcp;
+  GRPC_CALL_INTERNAL_UNREF(exec_ctx, tc->call, "termination");
+  gpr_free(tc);
+}
+
+static void send_termination(grpc_exec_ctx *exec_ctx, void *tcp,
+                             grpc_error *error) {
+  termination_closure *tc = tcp;
+  memset(&tc->op, 0, sizeof(tc->op));
+  tc->op.cancel_error = tc->error;
+  /* reuse closure to catch completion */
+  grpc_closure_init(&tc->closure, done_termination, tc,
+                    grpc_schedule_on_exec_ctx);
+  tc->op.on_complete = &tc->closure;
+  execute_op(exec_ctx, tc->call, &tc->op);
+}
+
+static grpc_call_error terminate_with_status(grpc_exec_ctx *exec_ctx,
+                                             termination_closure *tc) {
+  set_status_from_error(exec_ctx, tc->call, STATUS_FROM_API_OVERRIDE,
+                        GRPC_ERROR_REF(tc->error));
+  grpc_closure_init(&tc->closure, send_termination, tc,
+                    grpc_schedule_on_exec_ctx);
+  GRPC_CALL_INTERNAL_REF(tc->call, "termination");
+  grpc_closure_sched(exec_ctx, &tc->closure, GRPC_ERROR_NONE);
+  return GRPC_CALL_OK;
+}
+
+static grpc_call_error terminate_with_error(grpc_exec_ctx *exec_ctx,
+                                            grpc_call *c, grpc_error *error) {
+  termination_closure *tc = gpr_malloc(sizeof(*tc));
+  memset(tc, 0, sizeof(*tc));
+  tc->call = c;
+  tc->error = error;
+  return terminate_with_status(exec_ctx, tc);
+}
+
+static void cancel_with_error(grpc_exec_ctx *exec_ctx, grpc_call *c,
+                              grpc_error *error) {
+  terminate_with_error(exec_ctx, c, error);
+}
+
+static grpc_error *error_from_status(grpc_status_code status,
+                                     const char *description) {
+  return grpc_error_set_int(
+      grpc_error_set_str(GRPC_ERROR_CREATE(description),
+                         GRPC_ERROR_STR_GRPC_MESSAGE, description),
+      GRPC_ERROR_INT_GRPC_STATUS, status);
+}
+
+static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
+                                          grpc_status_code status,
+                                          const char *description) {
+  return terminate_with_error(exec_ctx, c,
+                              error_from_status(status, description));
+}
+
+/*******************************************************************************
+ * FINAL STATUS CODE MANIPULATION
+ */
+
+static void get_final_status_from(grpc_call *call, status_source from_source,
+                                  void (*set_value)(grpc_status_code code,
+                                                    void *user_data),
+                                  void *set_value_user_data,
+                                  grpc_slice *details) {
+  grpc_status_code code;
+  const char *msg = NULL;
+  grpc_error_get_status(call->status[from_source].error, call->send_deadline,
+                        &code, &msg, NULL);
+
+  set_value(code, set_value_user_data);
+  if (details != NULL) {
+    *details =
+        msg == NULL ? grpc_empty_slice() : grpc_slice_from_copied_string(msg);
+  }
+}
+
+static void get_final_status(grpc_call *call,
+                             void (*set_value)(grpc_status_code code,
+                                               void *user_data),
+                             void *set_value_user_data, grpc_slice *details) {
+  int i;
+  /* search for the best status we can present: ideally the error we use has a
+     clearly defined grpc-status, and we'll prefer that. */
+  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
+    if (call->status[i].is_set &&
+        grpc_error_has_clear_grpc_status(call->status[i].error)) {
+      get_final_status_from(call, (status_source)i, set_value,
+                            set_value_user_data, details);
+      return;
+    }
+  }
+  /* If no clearly defined status exists, search for 'anything' */
+  for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
+    if (call->status[i].is_set) {
+      get_final_status_from(call, (status_source)i, set_value,
+                            set_value_user_data, details);
+      return;
+    }
+  }
+  /* If nothing exists, set some default */
+  if (call->is_client) {
+    set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
+  } else {
+    set_value(GRPC_STATUS_OK, set_value_user_data);
+  }
+}
+
+static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call,
+                                  status_source source, grpc_error *error) {
+  if (call->status[source].is_set) {
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  call->status[source].is_set = true;
+  call->status[source].error = error;
+}
+
+/*******************************************************************************
+ * COMPRESSION
+ */
+
+static void set_incoming_compression_algorithm(
+    grpc_call *call, grpc_compression_algorithm algo) {
+  GPR_ASSERT(algo < GRPC_COMPRESS_ALGORITHMS_COUNT);
+  call->incoming_compression_algorithm = algo;
+}
+
+grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm(
+    grpc_call *call) {
+  grpc_compression_algorithm algorithm;
+  gpr_mu_lock(&call->mu);
+  algorithm = call->incoming_compression_algorithm;
+  gpr_mu_unlock(&call->mu);
+  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;
+  gpr_mu_lock(&call->mu);
+  flags = call->test_only_last_message_flags;
+  gpr_mu_unlock(&call->mu);
+  return flags;
+}
+
+static void destroy_encodings_accepted_by_peer(void *p) { return; }
+
+static void set_encodings_accepted_by_peer(grpc_exec_ctx *exec_ctx,
+                                           grpc_call *call, grpc_mdelem mdel) {
+  size_t i;
+  grpc_compression_algorithm 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 != NULL) {
+    call->encodings_accepted_by_peer =
+        (uint32_t)(((uintptr_t)accepted_user_data) - 1);
+    return;
+  }
+
+  accept_encoding_slice = GRPC_MDVALUE(mdel);
+  grpc_slice_buffer_init(&accept_encoding_parts);
+  grpc_slice_split(accept_encoding_slice, ",", &accept_encoding_parts);
+
+  /* No need to zero call->encodings_accepted_by_peer: grpc_call_create already
+   * zeroes the whole grpc_call */
+  /* Always support no compression */
+  GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE);
+  for (i = 0; i < accept_encoding_parts.count; i++) {
+    grpc_slice accept_encoding_entry_slice = accept_encoding_parts.slices[i];
+    if (grpc_compression_algorithm_parse(accept_encoding_entry_slice,
+                                         &algorithm)) {
+      GPR_BITSET(&call->encodings_accepted_by_peer, algorithm);
+    } else {
+      char *accept_encoding_entry_str =
+          grpc_slice_to_c_string(accept_encoding_entry_slice);
+      gpr_log(GPR_ERROR,
+              "Invalid entry in accept encoding metadata: '%s'. Ignoring.",
+              accept_encoding_entry_str);
+      gpr_free(accept_encoding_entry_str);
+    }
+  }
+
+  grpc_slice_buffer_destroy_internal(exec_ctx, &accept_encoding_parts);
+
+  grpc_mdelem_set_user_data(
+      mdel, destroy_encodings_accepted_by_peer,
+      (void *)(((uintptr_t)call->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;
+  gpr_mu_lock(&call->mu);
+  encodings_accepted_by_peer = call->encodings_accepted_by_peer;
+  gpr_mu_unlock(&call->mu);
+  return encodings_accepted_by_peer;
+}
+
+static grpc_linked_mdelem *linked_from_md(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_exec_ctx *exec_ctx, 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 = (grpc_linked_mdelem *)&md->internal_data;
+    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(exec_ctx, (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 = (grpc_linked_mdelem *)&md->internal_data;
+      GRPC_MDELEM_UNREF(exec_ctx, 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(
+                              exec_ctx, 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_LOG_IF_ERROR(
+        "prepare_application_metadata",
+        grpc_metadata_batch_link_tail(exec_ctx, batch, linked_from_md(md)));
+  }
+  call->send_extra_metadata_count = 0;
+
+  return 1;
+}
+
 /* 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) {}
 
-static uint32_t decode_status(grpc_mdelem *md) {
+static uint32_t decode_status(grpc_mdelem md) {
   uint32_t status;
   void *user_data;
-  if (md == GRPC_MDELEM_GRPC_STATUS_0) return 0;
-  if (md == GRPC_MDELEM_GRPC_STATUS_1) return 1;
-  if (md == GRPC_MDELEM_GRPC_STATUS_2) return 2;
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) return 0;
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) return 1;
+  if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) return 2;
   user_data = grpc_mdelem_get_user_data(md, destroy_status);
   if (user_data != NULL) {
     status = ((uint32_t)(intptr_t)user_data) - STATUS_OFFSET;
   } else {
-    if (!gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value),
-                                   GRPC_SLICE_LENGTH(md->value->slice),
-                                   &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,
@@ -916,93 +836,104 @@
   return status;
 }
 
-static grpc_compression_algorithm decode_compression(grpc_mdelem *md) {
+static grpc_compression_algorithm decode_compression(grpc_mdelem md) {
   grpc_compression_algorithm algorithm =
-      grpc_compression_algorithm_from_mdstr(md->value);
+      grpc_compression_algorithm_from_slice(GRPC_MDVALUE(md));
   if (algorithm == GRPC_COMPRESS_ALGORITHMS_COUNT) {
-    const char *md_c_str = grpc_mdstr_as_c_string(md->value);
+    char *md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md));
     gpr_log(GPR_ERROR,
             "Invalid incoming compression algorithm: '%s'. Interpreting "
             "incoming data as uncompressed.",
             md_c_str);
+    gpr_free(md_c_str);
     return GRPC_COMPRESS_NONE;
   }
   return algorithm;
 }
 
-static grpc_mdelem *recv_common_filter(grpc_exec_ctx *exec_ctx, grpc_call *call,
-                                       grpc_mdelem *elem) {
-  if (elem->key == GRPC_MDSTR_GRPC_STATUS) {
-    GPR_TIMER_BEGIN("status", 0);
-    set_status_code(call, STATUS_FROM_WIRE, decode_status(elem));
-    GPR_TIMER_END("status", 0);
-    return NULL;
-  } else if (elem->key == GRPC_MDSTR_GRPC_MESSAGE) {
-    GPR_TIMER_BEGIN("status-details", 0);
-    set_status_details(exec_ctx, call, STATUS_FROM_WIRE,
-                       GRPC_MDSTR_REF(elem->value));
-    GPR_TIMER_END("status-details", 0);
-    return NULL;
+static void recv_common_filter(grpc_exec_ctx *exec_ctx, grpc_call *call,
+                               grpc_metadata_batch *b) {
+  if (b->idx.named.grpc_status != NULL) {
+    uint32_t status_code = decode_status(b->idx.named.grpc_status->md);
+    grpc_error *error =
+        status_code == GRPC_STATUS_OK
+            ? GRPC_ERROR_NONE
+            : grpc_error_set_int(GRPC_ERROR_CREATE("Error received from peer"),
+                                 GRPC_ERROR_INT_GRPC_STATUS,
+                                 (intptr_t)status_code);
+
+    if (b->idx.named.grpc_message != NULL) {
+      char *msg =
+          grpc_slice_to_c_string(GRPC_MDVALUE(b->idx.named.grpc_message->md));
+      error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, msg);
+      gpr_free(msg);
+      grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_message);
+    } else {
+      error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, "");
+    }
+
+    set_status_from_error(exec_ctx, call, STATUS_FROM_WIRE, error);
+    grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_status);
   }
-  return elem;
 }
 
-static grpc_mdelem *publish_app_metadata(grpc_call *call, grpc_mdelem *elem,
-                                         int is_trailing) {
+static void publish_app_metadata(grpc_call *call, grpc_metadata_batch *b,
+                                 int is_trailing) {
+  if (b->list.count == 0) return;
+  GPR_TIMER_BEGIN("publish_app_metadata", 0);
   grpc_metadata_array *dest;
   grpc_metadata *mdusr;
-  GPR_TIMER_BEGIN("publish_app_metadata", 0);
   dest = call->buffered_metadata[is_trailing];
-  if (dest->count == dest->capacity) {
-    dest->capacity = GPR_MAX(dest->capacity + 8, dest->capacity * 2);
+  if (dest->count + b->list.count > dest->capacity) {
+    dest->capacity =
+        GPR_MAX(dest->capacity + b->list.count, dest->capacity * 3 / 2);
     dest->metadata =
         gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity);
   }
-  mdusr = &dest->metadata[dest->count++];
-  mdusr->key = grpc_mdstr_as_c_string(elem->key);
-  mdusr->value = grpc_mdstr_as_c_string(elem->value);
-  mdusr->value_length = GRPC_SLICE_LENGTH(elem->value->slice);
+  for (grpc_linked_mdelem *l = b->list.head; l != NULL; 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);
+  }
   GPR_TIMER_END("publish_app_metadata", 0);
-  return elem;
 }
 
-static grpc_mdelem *recv_initial_filter(grpc_exec_ctx *exec_ctx, void *args,
-                                        grpc_mdelem *elem) {
-  grpc_call *call = args;
-  elem = recv_common_filter(exec_ctx, call, elem);
-  if (elem == NULL) {
-    return NULL;
-  } else if (elem->key == GRPC_MDSTR_GRPC_ENCODING) {
+static void recv_initial_filter(grpc_exec_ctx *exec_ctx, grpc_call *call,
+                                grpc_metadata_batch *b) {
+  recv_common_filter(exec_ctx, call, b);
+
+  if (b->idx.named.grpc_encoding != NULL) {
     GPR_TIMER_BEGIN("incoming_compression_algorithm", 0);
-    set_incoming_compression_algorithm(call, decode_compression(elem));
+    set_incoming_compression_algorithm(
+        call, decode_compression(b->idx.named.grpc_encoding->md));
     GPR_TIMER_END("incoming_compression_algorithm", 0);
-    return NULL;
-  } else if (elem->key == GRPC_MDSTR_GRPC_ACCEPT_ENCODING) {
-    GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0);
-    set_encodings_accepted_by_peer(exec_ctx, call, elem);
-    GPR_TIMER_END("encodings_accepted_by_peer", 0);
-    return NULL;
-  } else {
-    return publish_app_metadata(call, elem, 0);
+    grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_encoding);
   }
+
+  if (b->idx.named.grpc_accept_encoding != NULL) {
+    GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0);
+    set_encodings_accepted_by_peer(exec_ctx, call,
+                                   b->idx.named.grpc_accept_encoding->md);
+    grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_accept_encoding);
+    GPR_TIMER_END("encodings_accepted_by_peer", 0);
+  }
+
+  publish_app_metadata(call, b, false);
 }
 
-static grpc_mdelem *recv_trailing_filter(grpc_exec_ctx *exec_ctx, void *args,
-                                         grpc_mdelem *elem) {
+static void recv_trailing_filter(grpc_exec_ctx *exec_ctx, void *args,
+                                 grpc_metadata_batch *b) {
   grpc_call *call = args;
-  elem = recv_common_filter(exec_ctx, call, elem);
-  if (elem == NULL) {
-    return NULL;
-  } else {
-    return publish_app_metadata(call, elem, 1);
-  }
+  recv_common_filter(exec_ctx, call, b);
+  publish_app_metadata(call, b, true);
 }
 
 grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) {
   return CALL_STACK_FROM_CALL(call);
 }
 
-/*
+/*******************************************************************************
  * BATCH API IMPLEMENTATION
  */
 
@@ -1053,14 +984,88 @@
   GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion");
 }
 
+static grpc_error *consolidate_batch_errors(batch_control *bctl) {
+  size_t n = (size_t)gpr_atm_no_barrier_load(&bctl->num_errors);
+  if (n == 0) {
+    return GRPC_ERROR_NONE;
+  } else if (n == 1) {
+    /* Skip creating a composite error in the case that only one error was
+       logged */
+    grpc_error *e = bctl->errors[0];
+    bctl->errors[0] = NULL;
+    return e;
+  } else {
+    grpc_error *error =
+        GRPC_ERROR_CREATE_REFERENCING("Call batch failed", bctl->errors, n);
+    for (size_t i = 0; i < n; i++) {
+      GRPC_ERROR_UNREF(bctl->errors[i]);
+      bctl->errors[i] = NULL;
+    }
+    return error;
+  }
+}
+
 static void post_batch_completion(grpc_exec_ctx *exec_ctx,
                                   batch_control *bctl) {
+  grpc_call *child_call;
+  grpc_call *next_child_call;
   grpc_call *call = bctl->call;
-  grpc_error *error = bctl->error;
+  grpc_error *error = consolidate_batch_errors(bctl);
+
+  gpr_mu_lock(&call->mu);
+
+  if (error != GRPC_ERROR_NONE) {
+    set_status_from_error(exec_ctx, call, STATUS_FROM_CORE,
+                          GRPC_ERROR_REF(error));
+  }
+
+  if (bctl->send_initial_metadata) {
+    grpc_metadata_batch_destroy(
+        exec_ctx,
+        &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
+  }
+  if (bctl->send_message) {
+    call->sending_message = false;
+  }
+  if (bctl->send_final_op) {
+    grpc_metadata_batch_destroy(
+        exec_ctx,
+        &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]);
+  }
   if (bctl->recv_final_op) {
+    grpc_metadata_batch *md =
+        &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
+    recv_trailing_filter(exec_ctx, call, md);
+
+    call->received_final_op = true;
+    /* propagate cancellation to any interested children */
+    child_call = call->first_child;
+    if (child_call != NULL) {
+      do {
+        next_child_call = child_call->sibling_next;
+        if (child_call->cancellation_is_inherited) {
+          GRPC_CALL_INTERNAL_REF(child_call, "propagate_cancel");
+          grpc_call_cancel(child_call, NULL);
+          GRPC_CALL_INTERNAL_UNREF(exec_ctx, child_call, "propagate_cancel");
+        }
+        child_call = next_child_call;
+      } while (child_call != call->first_child);
+    }
+
+    if (call->is_client) {
+      get_final_status(call, set_status_value_directly,
+                       call->final_op.client.status,
+                       call->final_op.client.status_details);
+    } else {
+      get_final_status(call, set_cancelled_value,
+                       call->final_op.server.cancelled, NULL);
+    }
+
     GRPC_ERROR_UNREF(error);
     error = GRPC_ERROR_NONE;
   }
+  gpr_mu_unlock(&call->mu);
+
   if (bctl->is_notify_tag_closure) {
     /* unrefs bctl->error */
     grpc_closure_run(exec_ctx, bctl->notify_tag, error);
@@ -1077,6 +1082,12 @@
   }
 }
 
+static void finish_batch_step(grpc_exec_ctx *exec_ctx, batch_control *bctl) {
+  if (gpr_unref(&bctl->steps_to_complete)) {
+    post_batch_completion(exec_ctx, bctl);
+  }
+}
+
 static void continue_receiving_slices(grpc_exec_ctx *exec_ctx,
                                       batch_control *bctl) {
   grpc_call *call = bctl->call;
@@ -1087,9 +1098,7 @@
       call->receiving_message = 0;
       grpc_byte_stream_destroy(exec_ctx, call->receiving_stream);
       call->receiving_stream = NULL;
-      if (gpr_unref(&bctl->steps_to_complete)) {
-        post_batch_completion(exec_ctx, bctl);
-      }
+      finish_batch_step(exec_ctx, bctl);
       return;
     }
     if (grpc_byte_stream_next(exec_ctx, call->receiving_stream,
@@ -1120,9 +1129,7 @@
     call->receiving_stream = NULL;
     grpc_byte_buffer_destroy(*call->receiving_buffer);
     *call->receiving_buffer = NULL;
-    if (gpr_unref(&bctl->steps_to_complete)) {
-      post_batch_completion(exec_ctx, bctl);
-    }
+    finish_batch_step(exec_ctx, bctl);
   }
 }
 
@@ -1132,9 +1139,7 @@
   if (call->receiving_stream == NULL) {
     *call->receiving_buffer = NULL;
     call->receiving_message = 0;
-    if (gpr_unref(&bctl->steps_to_complete)) {
-      post_batch_completion(exec_ctx, bctl);
-    }
+    finish_batch_step(exec_ctx, bctl);
   } else {
     call->test_only_last_message_flags = call->receiving_stream->flags;
     if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) &&
@@ -1154,14 +1159,11 @@
                                    grpc_error *error) {
   batch_control *bctl = bctlp;
   grpc_call *call = bctl->call;
-  if (error != GRPC_ERROR_NONE) {
-    grpc_status_code status;
-    const char *msg;
-    grpc_error_get_status(error, &status, &msg);
-    close_with_status(exec_ctx, call, status, msg);
-  }
   gpr_mu_lock(&bctl->call->mu);
-  if (bctl->call->has_initial_md_been_received || error != GRPC_ERROR_NONE ||
+  if (error != GRPC_ERROR_NONE) {
+    cancel_with_error(exec_ctx, call, GRPC_ERROR_REF(error));
+  }
+  if (call->has_initial_md_been_received || error != GRPC_ERROR_NONE ||
       call->receiving_stream == NULL) {
     gpr_mu_unlock(&bctl->call->mu);
     process_data_after_md(exec_ctx, bctlp);
@@ -1186,7 +1188,7 @@
       gpr_asprintf(&error_msg, "Invalid compression algorithm value '%d'.",
                    algo);
       gpr_log(GPR_ERROR, "%s", error_msg);
-      close_with_status(exec_ctx, call, GRPC_STATUS_UNIMPLEMENTED, error_msg);
+      cancel_with_status(exec_ctx, call, GRPC_STATUS_UNIMPLEMENTED, error_msg);
     } else if (grpc_compression_options_is_algorithm_enabled(
                    &compression_options, algo) == 0) {
       /* check if algorithm is supported by current channel config */
@@ -1195,7 +1197,7 @@
       gpr_asprintf(&error_msg, "Compression algorithm '%s' is disabled.",
                    algo_name);
       gpr_log(GPR_ERROR, "%s", error_msg);
-      close_with_status(exec_ctx, call, GRPC_STATUS_UNIMPLEMENTED, error_msg);
+      cancel_with_status(exec_ctx, call, GRPC_STATUS_UNIMPLEMENTED, error_msg);
     } else {
       call->incoming_compression_algorithm = algo;
     }
@@ -1221,12 +1223,12 @@
   }
 }
 
-static void add_batch_error(batch_control *bctl, grpc_error *error) {
+static void add_batch_error(grpc_exec_ctx *exec_ctx, batch_control *bctl,
+                            grpc_error *error) {
   if (error == GRPC_ERROR_NONE) return;
-  if (bctl->error == GRPC_ERROR_NONE) {
-    bctl->error = GRPC_ERROR_CREATE("Call batch operation failed");
-  }
-  bctl->error = grpc_error_add_child(bctl->error, error);
+  int idx = (int)gpr_atm_no_barrier_fetch_add(&bctl->num_errors, 1);
+  if (idx == 0) cancel_with_error(exec_ctx, bctl->call, GRPC_ERROR_REF(error));
+  bctl->errors[idx] = error;
 }
 
 static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx,
@@ -1236,12 +1238,13 @@
 
   gpr_mu_lock(&call->mu);
 
-  add_batch_error(bctl, GRPC_ERROR_REF(error));
+  add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error));
   if (error == GRPC_ERROR_NONE) {
     grpc_metadata_batch *md =
         &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
-    grpc_metadata_batch_filter(exec_ctx, md, recv_initial_filter, call);
+    recv_initial_filter(exec_ctx, call, md);
 
+    /* TODO(ctiller): this could be moved into recv_initial_filter now */
     GPR_TIMER_BEGIN("validate_filtered_metadata", 0);
     validate_filtered_metadata(exec_ctx, bctl);
     GPR_TIMER_END("validate_filtered_metadata", 0);
@@ -1260,90 +1263,20 @@
         receiving_stream_ready, call->saved_receiving_stream_ready_bctlp,
         grpc_schedule_on_exec_ctx);
     call->saved_receiving_stream_ready_bctlp = NULL;
-    grpc_closure_sched(exec_ctx, saved_rsr_closure, error);
+    grpc_closure_sched(exec_ctx, saved_rsr_closure, GRPC_ERROR_REF(error));
   }
 
   gpr_mu_unlock(&call->mu);
 
-  if (gpr_unref(&bctl->steps_to_complete)) {
-    post_batch_completion(exec_ctx, bctl);
-  }
+  finish_batch_step(exec_ctx, bctl);
 }
 
 static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp,
                          grpc_error *error) {
   batch_control *bctl = bctlp;
-  grpc_call *call = bctl->call;
-  grpc_call *child_call;
-  grpc_call *next_child_call;
 
-  GRPC_ERROR_REF(error);
-
-  gpr_mu_lock(&call->mu);
-
-  // If the error has an associated status code, set the call's status.
-  intptr_t status;
-  if (error != GRPC_ERROR_NONE &&
-      grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &status)) {
-    set_status_from_error(exec_ctx, call, STATUS_FROM_CORE, error);
-  }
-
-  if (bctl->send_initial_metadata) {
-    if (error != GRPC_ERROR_NONE) {
-      set_status_from_error(exec_ctx, call, STATUS_FROM_CORE, error);
-    }
-    grpc_metadata_batch_destroy(
-        exec_ctx,
-        &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
-  }
-  if (bctl->send_message) {
-    call->sending_message = 0;
-  }
-  if (bctl->send_final_op) {
-    grpc_metadata_batch_destroy(
-        exec_ctx,
-        &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]);
-  }
-  if (bctl->recv_final_op) {
-    grpc_metadata_batch *md =
-        &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
-    grpc_metadata_batch_filter(exec_ctx, md, recv_trailing_filter, call);
-
-    call->received_final_op = true;
-    /* propagate cancellation to any interested children */
-    child_call = call->first_child;
-    if (child_call != NULL) {
-      do {
-        next_child_call = child_call->sibling_next;
-        if (child_call->cancellation_is_inherited) {
-          GRPC_CALL_INTERNAL_REF(child_call, "propagate_cancel");
-          grpc_call_cancel(child_call, NULL);
-          GRPC_CALL_INTERNAL_UNREF(exec_ctx, child_call, "propagate_cancel");
-        }
-        child_call = next_child_call;
-      } while (child_call != call->first_child);
-    }
-
-    if (call->is_client) {
-      get_final_status(call, set_status_value_directly,
-                       call->final_op.client.status);
-      get_final_details(call, call->final_op.client.status_details,
-                        call->final_op.client.status_details_capacity);
-    } else {
-      get_final_status(call, set_cancelled_value,
-                       call->final_op.server.cancelled);
-    }
-
-    GRPC_ERROR_UNREF(error);
-    error = GRPC_ERROR_NONE;
-  }
-  add_batch_error(bctl, GRPC_ERROR_REF(error));
-  gpr_mu_unlock(&call->mu);
-  if (gpr_unref(&bctl->steps_to_complete)) {
-    post_batch_completion(exec_ctx, bctl);
-  }
-
-  GRPC_ERROR_UNREF(error);
+  add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error));
+  finish_batch_step(exec_ctx, bctl);
 }
 
 static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
@@ -1377,7 +1310,6 @@
 
   if (nops == 0) {
     GRPC_CALL_INTERNAL_REF(call, "completion");
-    bctl->error = GRPC_ERROR_NONE;
     if (!is_notify_tag_closure) {
       grpc_cq_begin_op(call->cq, notify_tag);
     }
@@ -1426,13 +1358,10 @@
           const grpc_compression_algorithm calgo =
               compression_algorithm_for_level_locked(
                   call, effective_compression_level);
-          char *calgo_name = NULL;
-          grpc_compression_algorithm_name(calgo, &calgo_name);
           // the following will be picked up by the compress filter and used as
           // the call's compression algorithm.
-          compression_md.key = GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY;
-          compression_md.value = calgo_name;
-          compression_md.value_length = strlen(calgo_name);
+          compression_md.key = GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST;
+          compression_md.value = grpc_compression_algorithm_slice(calgo);
           additional_metadata_count++;
         }
 
@@ -1461,7 +1390,7 @@
           error = GRPC_CALL_ERROR_INVALID_FLAGS;
           goto done_with_error;
         }
-        if (op->data.send_message == NULL) {
+        if (op->data.send_message.send_message == NULL) {
           error = GRPC_CALL_ERROR_INVALID_MESSAGE;
           goto done_with_error;
         }
@@ -1473,11 +1402,13 @@
         call->sending_message = 1;
         grpc_slice_buffer_stream_init(
             &call->sending_stream,
-            &op->data.send_message->data.raw.slice_buffer, op->flags);
+            &op->data.send_message.send_message->data.raw.slice_buffer,
+            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->data.raw.compression > GRPC_COMPRESS_NONE) {
+        if (op->data.send_message.send_message->data.raw.compression >
+            GRPC_COMPRESS_NONE) {
           call->sending_stream.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS;
         }
         stream_op->send_message = &call->sending_stream.base;
@@ -1525,19 +1456,25 @@
         call->send_extra_metadata_count = 1;
         call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem(
             exec_ctx, call->channel, op->data.send_status_from_server.status);
-        if (op->data.send_status_from_server.status_details != NULL) {
-          call->send_extra_metadata[1].md = grpc_mdelem_from_metadata_strings(
-              exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
-              grpc_mdstr_from_string(
-                  op->data.send_status_from_server.status_details));
-          call->send_extra_metadata_count++;
-          set_status_details(
-              exec_ctx, call, STATUS_FROM_API_OVERRIDE,
-              GRPC_MDSTR_REF(call->send_extra_metadata[1].md->value));
-        }
-        if (op->data.send_status_from_server.status != GRPC_STATUS_OK) {
-          set_status_code(call, STATUS_FROM_API_OVERRIDE,
-                          (uint32_t)op->data.send_status_from_server.status);
+        {
+          grpc_error *override_error = GRPC_ERROR_NONE;
+          if (op->data.send_status_from_server.status != GRPC_STATUS_OK) {
+            override_error = GRPC_ERROR_CREATE("Error from server send status");
+          }
+          if (op->data.send_status_from_server.status_details != NULL) {
+            call->send_extra_metadata[1].md = grpc_mdelem_from_slices(
+                exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
+                grpc_slice_ref_internal(
+                    *op->data.send_status_from_server.status_details));
+            call->send_extra_metadata_count++;
+            char *msg = grpc_slice_to_c_string(
+                GRPC_MDVALUE(call->send_extra_metadata[1].md));
+            override_error = grpc_error_set_str(
+                override_error, GRPC_ERROR_STR_GRPC_MESSAGE, msg);
+            gpr_free(msg);
+          }
+          set_status_from_error(exec_ctx, call, STATUS_FROM_API_OVERRIDE,
+                                override_error);
         }
         if (!prepare_application_metadata(
                 exec_ctx, call,
@@ -1565,7 +1502,8 @@
            that case we're not necessarily covered by a poller. */
         stream_op->covered_by_poller = call->is_client;
         call->received_initial_metadata = 1;
-        call->buffered_metadata[0] = op->data.recv_initial_metadata;
+        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);
@@ -1588,7 +1526,7 @@
         }
         call->receiving_message = 1;
         bctl->recv_message = 1;
-        call->receiving_buffer = op->data.recv_message;
+        call->receiving_buffer = op->data.recv_message.recv_message;
         stream_op->recv_message = &call->receiving_stream;
         grpc_closure_init(&call->receiving_stream_ready, receiving_stream_ready,
                           bctl, grpc_schedule_on_exec_ctx);
@@ -1615,8 +1553,6 @@
         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.status_details_capacity =
-            op->data.recv_status_on_client.status_details_capacity;
         bctl->recv_final_op = 1;
         stream_op->recv_trailing_metadata =
             &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index 233340c..8c46a83 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -61,7 +61,7 @@
 
   const void *server_transport_data;
 
-  grpc_mdelem **add_initial_metadata;
+  grpc_mdelem *add_initial_metadata;
   size_t add_initial_metadata_count;
 
   gpr_timespec send_deadline;
diff --git a/src/core/lib/surface/call_details.c b/src/core/lib/surface/call_details.c
index fe73da3..d0f88e1 100644
--- a/src/core/lib/surface/call_details.c
+++ b/src/core/lib/surface/call_details.c
@@ -36,15 +36,21 @@
 
 #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));
-  gpr_free(cd->method);
-  gpr_free(cd->host);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_slice_unref_internal(&exec_ctx, cd->method);
+  grpc_slice_unref_internal(&exec_ctx, cd->host);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/src/core/lib/surface/call_log_batch.c b/src/core/lib/surface/call_log_batch.c
index 31c074f..529a1ef 100644
--- a/src/core/lib/surface/call_log_batch.c
+++ b/src/core/lib/surface/call_log_batch.c
@@ -35,17 +35,22 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/string_util.h>
+#include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
 static void add_metadata(gpr_strvec *b, const grpc_metadata *md, size_t count) {
   size_t i;
+  if (md == NULL) {
+    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, gpr_strdup(md[i].key));
+    gpr_strvec_add(b, grpc_slice_to_c_string(md[i].key));
 
     gpr_strvec_add(b, gpr_strdup(" value="));
-    gpr_strvec_add(b, gpr_dump(md[i].value, md[i].value_length,
-                               GPR_DUMP_HEX | GPR_DUMP_ASCII));
+    gpr_strvec_add(b,
+                   grpc_dump_slice(md[i].value, GPR_DUMP_HEX | GPR_DUMP_ASCII));
   }
 }
 
@@ -63,27 +68,35 @@
                    op->data.send_initial_metadata.count);
       break;
     case GRPC_OP_SEND_MESSAGE:
-      gpr_asprintf(&tmp, "SEND_MESSAGE ptr=%p", op->data.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=%s",
-                   op->data.send_status_from_server.status,
-                   op->data.send_status_from_server.status_details);
+      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 != NULL) {
+        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);
+                   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);
+      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:
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
index b872957..429dbad 100644
--- a/src/core/lib/surface/channel.c
+++ b/src/core/lib/surface/channel.c
@@ -43,6 +43,7 @@
 
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/call.h"
@@ -57,15 +58,15 @@
 #define NUM_CACHED_STATUS_ELEMS 3
 
 typedef struct registered_call {
-  grpc_mdelem *path;
-  grpc_mdelem *authority;
+  grpc_mdelem path;
+  grpc_mdelem authority;
   struct registered_call *next;
 } registered_call;
 
 struct grpc_channel {
   int is_client;
   grpc_compression_options compression_options;
-  grpc_mdelem *default_authority;
+  grpc_mdelem default_authority;
 
   gpr_mu registered_call_mu;
   registered_call *registered_calls;
@@ -102,9 +103,8 @@
       exec_ctx, builder, sizeof(grpc_channel), 1, destroy_channel, NULL,
       (void **)&channel);
   if (error != GRPC_ERROR_NONE) {
-    const char *msg = grpc_error_string(error);
-    gpr_log(GPR_ERROR, "channel stack builder failed: %s", msg);
-    grpc_error_free_string(msg);
+    gpr_log(GPR_ERROR, "channel stack builder failed: %s",
+            grpc_error_string(error));
     GRPC_ERROR_UNREF(error);
     goto done;
   }
@@ -116,19 +116,19 @@
   channel->registered_calls = NULL;
 
   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_ARG_DEFAULT_AUTHORITY)) {
       if (args->args[i].type != GRPC_ARG_STRING) {
         gpr_log(GPR_ERROR, "%s ignored: it must be a string",
                 GRPC_ARG_DEFAULT_AUTHORITY);
       } else {
-        if (channel->default_authority) {
+        if (!GRPC_MDISNULL(channel->default_authority)) {
           /* setting this takes precedence over anything else */
           GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority);
         }
-        channel->default_authority = grpc_mdelem_from_strings(
-            exec_ctx, ":authority", args->args[i].value.string);
+        channel->default_authority = grpc_mdelem_from_slices(
+            exec_ctx, GRPC_MDSTR_AUTHORITY,
+            grpc_slice_from_copied_string(args->args[i].value.string));
       }
     } else if (0 ==
                strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) {
@@ -136,14 +136,15 @@
         gpr_log(GPR_ERROR, "%s ignored: it must be a string",
                 GRPC_SSL_TARGET_NAME_OVERRIDE_ARG);
       } else {
-        if (channel->default_authority) {
+        if (!GRPC_MDISNULL(channel->default_authority)) {
           /* other ways of setting this (notably ssl) take precedence */
           gpr_log(GPR_ERROR,
                   "%s ignored: default host already set some other way",
                   GRPC_SSL_TARGET_NAME_OVERRIDE_ARG);
         } else {
-          channel->default_authority = grpc_mdelem_from_strings(
-              exec_ctx, ":authority", args->args[i].value.string);
+          channel->default_authority = grpc_mdelem_from_slices(
+              exec_ctx, GRPC_MDSTR_AUTHORITY,
+              grpc_slice_from_copied_string(args->args[i].value.string));
         }
       }
     } else if (0 == strcmp(args->args[i].key,
@@ -191,18 +192,18 @@
 static grpc_call *grpc_channel_create_call_internal(
     grpc_exec_ctx *exec_ctx, 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, gpr_timespec deadline) {
-  grpc_mdelem *send_metadata[2];
+    grpc_pollset_set *pollset_set_alternative, grpc_mdelem path_mdelem,
+    grpc_mdelem authority_mdelem, gpr_timespec deadline) {
+  grpc_mdelem send_metadata[2];
   size_t num_metadata = 0;
 
   GPR_ASSERT(channel->is_client);
   GPR_ASSERT(!(cq != NULL && pollset_set_alternative != NULL));
 
   send_metadata[num_metadata++] = path_mdelem;
-  if (authority_mdelem != NULL) {
+  if (!GRPC_MDISNULL(authority_mdelem)) {
     send_metadata[num_metadata++] = authority_mdelem;
-  } else if (channel->default_authority != NULL) {
+  } else if (!GRPC_MDISNULL(channel->default_authority)) {
     send_metadata[num_metadata++] = GRPC_MDELEM_REF(channel->default_authority);
   }
 
@@ -227,27 +228,17 @@
                                     grpc_call *parent_call,
                                     uint32_t propagation_mask,
                                     grpc_completion_queue *cq,
-                                    const char *method, const char *host,
+                                    grpc_slice method, const grpc_slice *host,
                                     gpr_timespec deadline, void *reserved) {
-  GRPC_API_TRACE(
-      "grpc_channel_create_call("
-      "channel=%p, parent_call=%p, propagation_mask=%x, cq=%p, method=%s, "
-      "host=%s, "
-      "deadline=gpr_timespec { tv_sec: %" PRId64
-      ", tv_nsec: %d, clock_type: %d }, "
-      "reserved=%p)",
-      10,
-      (channel, parent_call, (unsigned)propagation_mask, cq, method, host,
-       deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, reserved));
   GPR_ASSERT(!reserved);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_call *call = grpc_channel_create_call_internal(
       &exec_ctx, channel, parent_call, propagation_mask, cq, NULL,
-      grpc_mdelem_from_metadata_strings(&exec_ctx, GRPC_MDSTR_PATH,
-                                        grpc_mdstr_from_string(method)),
-      host ? grpc_mdelem_from_metadata_strings(&exec_ctx, GRPC_MDSTR_AUTHORITY,
-                                               grpc_mdstr_from_string(host))
-           : NULL,
+      grpc_mdelem_from_slices(&exec_ctx, GRPC_MDSTR_PATH,
+                              grpc_slice_ref_internal(method)),
+      host != NULL ? grpc_mdelem_from_slices(&exec_ctx, GRPC_MDSTR_AUTHORITY,
+                                             grpc_slice_ref_internal(*host))
+                   : GRPC_MDNULL,
       deadline);
   grpc_exec_ctx_finish(&exec_ctx);
   return call;
@@ -255,17 +246,16 @@
 
 grpc_call *grpc_channel_create_pollset_set_call(
     grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call,
-    uint32_t propagation_mask, grpc_pollset_set *pollset_set,
-    const char *method, const char *host, gpr_timespec deadline,
-    void *reserved) {
+    uint32_t propagation_mask, grpc_pollset_set *pollset_set, grpc_slice method,
+    const grpc_slice *host, gpr_timespec deadline, void *reserved) {
   GPR_ASSERT(!reserved);
   return grpc_channel_create_call_internal(
       exec_ctx, channel, parent_call, propagation_mask, NULL, pollset_set,
-      grpc_mdelem_from_metadata_strings(exec_ctx, GRPC_MDSTR_PATH,
-                                        grpc_mdstr_from_string(method)),
-      host ? grpc_mdelem_from_metadata_strings(exec_ctx, GRPC_MDSTR_AUTHORITY,
-                                               grpc_mdstr_from_string(host))
-           : NULL,
+      grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_PATH,
+                              grpc_slice_ref_internal(method)),
+      host != NULL ? grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_AUTHORITY,
+                                             grpc_slice_ref_internal(*host))
+                   : GRPC_MDNULL,
       deadline);
 }
 
@@ -277,12 +267,15 @@
       4, (channel, method, host, reserved));
   GPR_ASSERT(!reserved);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  rc->path = grpc_mdelem_from_metadata_strings(&exec_ctx, GRPC_MDSTR_PATH,
-                                               grpc_mdstr_from_string(method));
+
+  rc->path = grpc_mdelem_from_slices(
+      &exec_ctx, GRPC_MDSTR_PATH,
+      grpc_slice_intern(grpc_slice_from_static_string(method)));
   rc->authority =
-      host ? grpc_mdelem_from_metadata_strings(&exec_ctx, GRPC_MDSTR_AUTHORITY,
-                                               grpc_mdstr_from_string(host))
-           : NULL;
+      host ? grpc_mdelem_from_slices(
+                 &exec_ctx, 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;
@@ -310,8 +303,7 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_call *call = grpc_channel_create_call_internal(
       &exec_ctx, channel, parent_call, propagation_mask, completion_queue, NULL,
-      GRPC_MDELEM_REF(rc->path),
-      rc->authority ? GRPC_MDELEM_REF(rc->authority) : NULL, deadline);
+      GRPC_MDELEM_REF(rc->path), GRPC_MDELEM_REF(rc->authority), deadline);
   grpc_exec_ctx_finish(&exec_ctx);
   return call;
 }
@@ -340,14 +332,10 @@
     registered_call *rc = channel->registered_calls;
     channel->registered_calls = rc->next;
     GRPC_MDELEM_UNREF(exec_ctx, rc->path);
-    if (rc->authority) {
-      GRPC_MDELEM_UNREF(exec_ctx, rc->authority);
-    }
+    GRPC_MDELEM_UNREF(exec_ctx, rc->authority);
     gpr_free(rc);
   }
-  if (channel->default_authority != NULL) {
-    GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority);
-  }
+  GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority);
   gpr_mu_destroy(&channel->registered_call_mu);
   gpr_free(channel->target);
   gpr_free(channel);
@@ -376,8 +364,8 @@
   return channel->compression_options;
 }
 
-grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_exec_ctx *exec_ctx,
-                                                 grpc_channel *channel, int i) {
+grpc_mdelem grpc_channel_get_reffed_status_elem(grpc_exec_ctx *exec_ctx,
+                                                grpc_channel *channel, int i) {
   char tmp[GPR_LTOA_MIN_BUFSIZE];
   switch (i) {
     case 0:
@@ -388,6 +376,6 @@
       return GRPC_MDELEM_GRPC_STATUS_2;
   }
   gpr_ltoa(i, tmp);
-  return grpc_mdelem_from_metadata_strings(exec_ctx, GRPC_MDSTR_GRPC_STATUS,
-                                           grpc_mdstr_from_string(tmp));
+  return grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_STATUS,
+                                 grpc_slice_from_copied_string(tmp));
 }
diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
index 2ebadb7..3a441d7 100644
--- a/src/core/lib/surface/channel.h
+++ b/src/core/lib/surface/channel.h
@@ -52,9 +52,8 @@
     value of \a propagation_mask (see propagation_bits.h for possible values) */
 grpc_call *grpc_channel_create_pollset_set_call(
     grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call,
-    uint32_t propagation_mask, grpc_pollset_set *pollset_set,
-    const char *method, const char *host, gpr_timespec deadline,
-    void *reserved);
+    uint32_t propagation_mask, grpc_pollset_set *pollset_set, grpc_slice method,
+    const grpc_slice *host, gpr_timespec deadline, void *reserved);
 
 /** Get a (borrowed) pointer to this channels underlying channel stack */
 grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
@@ -63,9 +62,9 @@
     status_code.
 
     The returned elem is owned by the caller. */
-grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_exec_ctx *exec_ctx,
-                                                 grpc_channel *channel,
-                                                 int status_code);
+grpc_mdelem grpc_channel_get_reffed_status_elem(grpc_exec_ctx *exec_ctx,
+                                                grpc_channel *channel,
+                                                int status_code);
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
 void grpc_channel_internal_ref(grpc_channel *channel, const char *reason);
diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c
index 4613c90..1830842 100644
--- a/src/core/lib/surface/completion_queue.c
+++ b/src/core/lib/surface/completion_queue.c
@@ -253,7 +253,6 @@
     if (grpc_trace_operation_failures && error != GRPC_ERROR_NONE) {
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
-    grpc_error_free_string(errmsg);
   }
 
   storage->tag = tag;
@@ -294,7 +293,7 @@
     if (kick_error != GRPC_ERROR_NONE) {
       const char *msg = grpc_error_string(kick_error);
       gpr_log(GPR_ERROR, "Kick failed: %s", msg);
-      grpc_error_free_string(msg);
+
       GRPC_ERROR_UNREF(kick_error);
     }
   } else {
@@ -403,8 +402,8 @@
       .stolen_completion = NULL,
       .tag = NULL,
       .first_loop = true};
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT_WITH_FINISH_CHECK(
-      cq_is_next_finished, &is_finished_arg);
+  grpc_exec_ctx exec_ctx =
+      GRPC_EXEC_CTX_INITIALIZER(0, cq_is_next_finished, &is_finished_arg);
   for (;;) {
     if (is_finished_arg.stolen_completion != NULL) {
       gpr_mu_unlock(cc->mu);
@@ -461,7 +460,7 @@
         gpr_mu_unlock(cc->mu);
         const char *msg = grpc_error_string(err);
         gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg);
-        grpc_error_free_string(msg);
+
         GRPC_ERROR_UNREF(err);
         memset(&ret, 0, sizeof(ret));
         ret.type = GRPC_QUEUE_TIMEOUT;
@@ -572,8 +571,8 @@
       .stolen_completion = NULL,
       .tag = tag,
       .first_loop = true};
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT_WITH_FINISH_CHECK(
-      cq_is_pluck_finished, &is_finished_arg);
+  grpc_exec_ctx exec_ctx =
+      GRPC_EXEC_CTX_INITIALIZER(0, cq_is_pluck_finished, &is_finished_arg);
   for (;;) {
     if (is_finished_arg.stolen_completion != NULL) {
       gpr_mu_unlock(cc->mu);
@@ -647,7 +646,7 @@
         gpr_mu_unlock(cc->mu);
         const char *msg = grpc_error_string(err);
         gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg);
-        grpc_error_free_string(msg);
+
         GRPC_ERROR_UNREF(err);
         memset(&ret, 0, sizeof(ret));
         ret.type = GRPC_QUEUE_TIMEOUT;
diff --git a/src/core/lib/surface/init.c b/src/core/lib/surface/init.c
index f61bf15..cfa1882 100644
--- a/src/core/lib/surface/init.c
+++ b/src/core/lib/surface/init.c
@@ -55,6 +55,7 @@
 #include "src/core/lib/iomgr/iomgr.h"
 #include "src/core/lib/iomgr/resource_quota.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"
@@ -178,6 +179,7 @@
   gpr_mu_lock(&g_init_mu);
   if (++g_initializations == 1) {
     gpr_time_init();
+    grpc_slice_intern_init();
     grpc_mdctx_global_init();
     grpc_channel_init_init();
     grpc_register_tracer("api", &grpc_api_trace);
@@ -242,6 +244,7 @@
     }
     grpc_mdctx_global_shutdown(&exec_ctx);
     grpc_handshaker_factory_registry_shutdown(&exec_ctx);
+    grpc_slice_intern_shutdown();
   }
   gpr_mu_unlock(&g_init_mu);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/src/core/lib/surface/lame_client.c b/src/core/lib/surface/lame_client.c
index ae1eac0..48de0e1 100644
--- a/src/core/lib/surface/lame_client.c
+++ b/src/core/lib/surface/lame_client.c
@@ -44,10 +44,12 @@
 #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/transport/static_metadata.h"
 
 typedef struct {
   grpc_linked_mdelem status;
   grpc_linked_mdelem details;
+  gpr_atm filled_metadata;
 } call_data;
 
 typedef struct {
@@ -58,17 +60,23 @@
 static void fill_metadata(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
                           grpc_metadata_batch *mdb) {
   call_data *calld = elem->call_data;
+  if (!gpr_atm_no_barrier_cas(&calld->filled_metadata, 0, 1)) {
+    return;
+  }
   channel_data *chand = elem->channel_data;
   char tmp[GPR_LTOA_MIN_BUFSIZE];
   gpr_ltoa(chand->error_code, tmp);
-  calld->status.md = grpc_mdelem_from_strings(exec_ctx, "grpc-status", tmp);
-  calld->details.md =
-      grpc_mdelem_from_strings(exec_ctx, "grpc-message", chand->error_message);
+  calld->status.md = grpc_mdelem_from_slices(
+      exec_ctx, GRPC_MDSTR_GRPC_STATUS, grpc_slice_from_copied_string(tmp));
+  calld->details.md = grpc_mdelem_from_slices(
+      exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
+      grpc_slice_from_copied_string(chand->error_message));
   calld->status.prev = calld->details.next = NULL;
   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 = gpr_inf_future(GPR_CLOCK_REALTIME);
 }
 
@@ -115,6 +123,8 @@
 static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
                                   grpc_call_element *elem,
                                   grpc_call_element_args *args) {
+  call_data *calld = elem->call_data;
+  gpr_atm_no_barrier_store(&calld->filled_metadata, 0);
   return GRPC_ERROR_NONE;
 }
 
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index addb7c4..6ab1c0d 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -98,8 +98,9 @@
 typedef struct channel_registered_method {
   registered_method *server_registered_method;
   uint32_t flags;
-  grpc_mdstr *method;
-  grpc_mdstr *host;
+  bool has_host;
+  grpc_slice method;
+  grpc_slice host;
 } channel_registered_method;
 
 struct channel_data {
@@ -144,8 +145,10 @@
   /** the current state of a call - see call_state */
   call_state state;
 
-  grpc_mdstr *path;
-  grpc_mdstr *host;
+  bool path_set;
+  bool host_set;
+  grpc_slice path;
+  grpc_slice host;
   gpr_timespec deadline;
 
   grpc_completion_queue *cq_new;
@@ -277,18 +280,20 @@
 }
 
 static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel,
-                          int send_goaway, grpc_error *send_disconnect) {
+                          bool send_goaway, grpc_error *send_disconnect) {
   struct shutdown_cleanup_args *sc = 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->send_goaway = send_goaway;
+  op->goaway_error =
+      send_goaway
+          ? grpc_error_set_int(GRPC_ERROR_CREATE("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->goaway_message = &sc->slice;
-  op->goaway_status = GRPC_STATUS_OK;
   op->disconnect_with_error = send_disconnect;
 
   elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
@@ -448,7 +453,6 @@
   if (grpc_server_channel_trace && error != GRPC_ERROR_NONE) {
     const char *msg = grpc_error_string(error);
     gpr_log(GPR_INFO, "Disconnected client: %s", msg);
-    grpc_error_free_string(msg);
   }
   GRPC_ERROR_UNREF(error);
 
@@ -461,17 +465,6 @@
                        op);
 }
 
-static void cpstr(char **dest, size_t *capacity, grpc_mdstr *value) {
-  grpc_slice slice = value->slice;
-  size_t len = GRPC_SLICE_LENGTH(slice);
-
-  if (len + 1 > *capacity) {
-    *capacity = GPR_MAX(len + 1, *capacity * 2);
-    *dest = gpr_realloc(*dest, *capacity);
-  }
-  memcpy(*dest, grpc_mdstr_as_c_string(value), len + 1);
-}
-
 static void done_request_event(grpc_exec_ctx *exec_ctx, void *req,
                                grpc_cq_completion *c) {
   requested_call *rc = req;
@@ -500,12 +493,10 @@
   GPR_SWAP(grpc_metadata_array, *rc->initial_metadata, calld->initial_metadata);
   switch (rc->type) {
     case BATCH_CALL:
-      GPR_ASSERT(calld->host != NULL);
-      GPR_ASSERT(calld->path != NULL);
-      cpstr(&rc->data.batch.details->host,
-            &rc->data.batch.details->host_capacity, calld->host);
-      cpstr(&rc->data.batch.details->method,
-            &rc->data.batch.details->method_capacity, calld->path);
+      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 = calld->deadline;
       rc->data.batch.details->flags =
           (calld->recv_idempotent_request
@@ -609,7 +600,7 @@
       grpc_op op;
       memset(&op, 0, sizeof(op));
       op.op = GRPC_OP_RECV_MESSAGE;
-      op.data.recv_message = &calld->payload;
+      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(exec_ctx, calld->call, &op, 1,
@@ -627,35 +618,39 @@
   uint32_t hash;
   channel_registered_method *rm;
 
-  if (chand->registered_methods && calld->path && calld->host) {
+  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(calld->host->hash, calld->path->hash);
+    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->host != calld->host) continue;
-      if (rm->method != calld->path) continue;
+      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) &&
-          !calld->recv_idempotent_request)
+          !calld->recv_idempotent_request) {
         continue;
+      }
       finish_start_new_rpc(exec_ctx, server, elem,
                            &rm->server_registered_method->request_matcher,
                            rm->server_registered_method->payload_handling);
       return;
     }
     /* check for a wildcard method definition (no host set) */
-    hash = GRPC_MDSTR_KV_HASH(0, calld->path->hash);
+    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->host != NULL) continue;
-      if (rm->method != calld->path) continue;
+      if (rm->has_host) continue;
+      if (!grpc_slice_eq(rm->method, calld->path)) continue;
       if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
-          !calld->recv_idempotent_request)
+          !calld->recv_idempotent_request) {
         continue;
+      }
       finish_start_new_rpc(exec_ctx, server, elem,
                            &rm->server_registered_method->request_matcher,
                            rm->server_registered_method->payload_handling);
@@ -744,43 +739,40 @@
   }
 }
 
-static grpc_mdelem *server_filter(grpc_exec_ctx *exec_ctx, void *user_data,
-                                  grpc_mdelem *md) {
-  grpc_call_element *elem = user_data;
-  call_data *calld = elem->call_data;
-  if (md->key == GRPC_MDSTR_PATH) {
-    if (calld->path == NULL) {
-      calld->path = GRPC_MDSTR_REF(md->value);
-    }
-    return NULL;
-  } else if (md->key == GRPC_MDSTR_AUTHORITY) {
-    if (calld->host == NULL) {
-      calld->host = GRPC_MDSTR_REF(md->value);
-    }
-    return NULL;
-  }
-  return md;
-}
-
 static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
                                             grpc_error *error) {
   grpc_call_element *elem = ptr;
   call_data *calld = elem->call_data;
   gpr_timespec op_deadline;
 
-  GRPC_ERROR_REF(error);
-  grpc_metadata_batch_filter(exec_ctx, calld->recv_initial_metadata,
-                             server_filter, elem);
+  if (error == GRPC_ERROR_NONE) {
+    GPR_ASSERT(calld->recv_initial_metadata->idx.named.path != NULL);
+    GPR_ASSERT(calld->recv_initial_metadata->idx.named.authority != NULL);
+    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(exec_ctx, calld->recv_initial_metadata,
+                               calld->recv_initial_metadata->idx.named.path);
+    grpc_metadata_batch_remove(
+        exec_ctx, calld->recv_initial_metadata,
+        calld->recv_initial_metadata->idx.named.authority);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
   op_deadline = calld->recv_initial_metadata->deadline;
   if (0 != gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) {
     calld->deadline = op_deadline;
   }
-  if (calld->host && calld->path) {
+  if (calld->host_set && calld->path_set) {
     /* do nothing */
   } else {
-    GRPC_ERROR_UNREF(error);
+    grpc_error *src_error = error;
     error =
         GRPC_ERROR_CREATE_REFERENCING("Missing :authority or :path", &error, 1);
+    GRPC_ERROR_UNREF(src_error);
   }
 
   grpc_closure_run(exec_ctx, calld->on_done_recv_initial_metadata, error);
@@ -857,7 +849,8 @@
   grpc_op op;
   memset(&op, 0, sizeof(op));
   op.op = GRPC_OP_RECV_INITIAL_METADATA;
-  op.data.recv_initial_metadata = &calld->initial_metadata;
+  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(exec_ctx, call, &op, 1,
@@ -910,11 +903,11 @@
 
   GPR_ASSERT(calld->state != PENDING);
 
-  if (calld->host) {
-    GRPC_MDSTR_UNREF(exec_ctx, calld->host);
+  if (calld->host_set) {
+    grpc_slice_unref_internal(exec_ctx, calld->host);
   }
-  if (calld->path) {
-    GRPC_MDSTR_UNREF(exec_ctx, calld->path);
+  if (calld->path_set) {
+    grpc_slice_unref_internal(exec_ctx, calld->path);
   }
   grpc_metadata_array_destroy(&calld->initial_metadata);
 
@@ -946,11 +939,9 @@
   channel_data *chand = elem->channel_data;
   if (chand->registered_methods) {
     for (i = 0; i < chand->registered_method_slots; i++) {
-      if (chand->registered_methods[i].method) {
-        GRPC_MDSTR_UNREF(exec_ctx, chand->registered_methods[i].method);
-      }
-      if (chand->registered_methods[i].host) {
-        GRPC_MDSTR_UNREF(exec_ctx, chand->registered_methods[i].host);
+      grpc_slice_unref_internal(exec_ctx, chand->registered_methods[i].method);
+      if (chand->registered_methods[i].has_host) {
+        grpc_slice_unref_internal(exec_ctx, chand->registered_methods[i].host);
       }
     }
     gpr_free(chand->registered_methods);
@@ -1148,8 +1139,6 @@
   channel_registered_method *crm;
   grpc_channel *channel;
   channel_data *chand;
-  grpc_mdstr *host;
-  grpc_mdstr *method;
   uint32_t hash;
   size_t slots;
   uint32_t probes;
@@ -1188,9 +1177,18 @@
     chand->registered_methods = gpr_malloc(alloc);
     memset(chand->registered_methods, 0, alloc);
     for (rm = s->registered_methods; rm; rm = rm->next) {
-      host = rm->host ? grpc_mdstr_from_string(rm->host) : NULL;
-      method = grpc_mdstr_from_string(rm->method);
-      hash = GRPC_MDSTR_KV_HASH(host ? host->hash : 0, method->hash);
+      grpc_slice host;
+      bool has_host;
+      grpc_slice method;
+      if (rm->host != NULL) {
+        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 != NULL;
            probes++)
@@ -1199,6 +1197,7 @@
       crm = &chand->registered_methods[(hash + probes) % slots];
       crm->server_registered_method = rm;
       crm->flags = rm->flags;
+      crm->has_host = has_host;
       crm->host = host;
       crm->method = method;
     }
diff --git a/src/core/lib/surface/validate_metadata.c b/src/core/lib/surface/validate_metadata.c
index f49dd85..7ec9137 100644
--- a/src/core/lib/surface/validate_metadata.c
+++ b/src/core/lib/surface/validate_metadata.c
@@ -34,40 +34,71 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
 #include <grpc/support/port_platform.h>
 
-static int conforms_to(const char *s, size_t len, const uint8_t *legal_bits) {
-  const char *p = s;
-  const char *e = s + len;
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/slice/slice_string_helpers.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 = (uint8_t)*p;
+    int idx = *p;
     int byte = idx / 8;
     int bit = idx % 8;
-    if ((legal_bits[byte] & (1 << bit)) == 0) return 0;
+    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(err_desc), GRPC_ERROR_INT_OFFSET,
+                             p - GRPC_SLICE_START_PTR(slice)),
+          GRPC_ERROR_STR_RAW_BYTES, dump);
+      gpr_free(dump);
+      return error;
+    }
   }
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-int grpc_header_key_is_legal(const char *key, size_t length) {
+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 (length == 0 || key[0] == ':') {
-    return 0;
+  if (GRPC_SLICE_LENGTH(slice) == 0) {
+    return GRPC_ERROR_CREATE("Metadata keys cannot be zero length");
   }
-  return conforms_to(key, length, legal_header_bits);
+  if (GRPC_SLICE_START_PTR(slice)[0] == ':') {
+    return GRPC_ERROR_CREATE("Metadata keys cannot start with :");
+  }
+  return conforms_to(slice, legal_header_bits, "Illegal header key");
 }
 
-int grpc_header_nonbin_value_is_legal(const char *value, size_t length) {
+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(value, length, legal_header_bits);
+  return conforms_to(slice, legal_header_bits, "Illegal header value");
 }
 
-int grpc_is_binary_header(const char *key, size_t length) {
-  if (length < 5) return 0;
-  return 0 == memcmp(key + length - 4, "-bin", 4);
+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/src/core/ext/transport/chttp2/transport/status_conversion.h b/src/core/lib/surface/validate_metadata.h
similarity index 64%
copy from src/core/ext/transport/chttp2/transport/status_conversion.h
copy to src/core/lib/surface/validate_metadata.h
index 953bc9f..2b800d2 100644
--- a/src/core/ext/transport/chttp2/transport/status_conversion.h
+++ b/src/core/lib/surface/validate_metadata.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2017, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,20 +31,13 @@
  *
  */
 
-#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
-#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
+#ifndef GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H
+#define GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H
 
-#include <grpc/grpc.h>
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
+#include <grpc/slice.h>
+#include "src/core/lib/iomgr/error.h"
 
-/* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */
-grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error(
-    grpc_status_code status);
-grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error, gpr_timespec deadline);
+grpc_error *grpc_validate_header_key_is_legal(grpc_slice slice);
+grpc_error *grpc_validate_header_nonbin_value_is_legal(grpc_slice slice);
 
-/* Conversion of HTTP status codes (:status) to grpc status codes */
-grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status);
-int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status);
-
-#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H */
+#endif /* GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H */
diff --git a/src/core/lib/transport/connectivity_state.c b/src/core/lib/transport/connectivity_state.c
index c656d93..8fc5bf3 100644
--- a/src/core/lib/transport/connectivity_state.c
+++ b/src/core/lib/transport/connectivity_state.c
@@ -163,7 +163,6 @@
     gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker,
             tracker->name, grpc_connectivity_state_name(tracker->current_state),
             grpc_connectivity_state_name(state), reason, error, error_string);
-    grpc_error_free_string(error_string);
   }
   switch (state) {
     case GRPC_CHANNEL_INIT:
diff --git a/src/core/lib/transport/error_utils.c b/src/core/lib/transport/error_utils.c
new file mode 100644
index 0000000..da77828
--- /dev/null
+++ b/src/core/lib/transport/error_utils.c
@@ -0,0 +1,124 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/transport/error_utils.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) {
+  // If the error itself has a status code, return it.
+  if (grpc_error_get_int(error, which, NULL)) {
+    return error;
+  }
+  if (grpc_error_is_special(error)) return NULL;
+  // Otherwise, search through its children.
+  intptr_t key = 0;
+  while (true) {
+    grpc_error *child_error = gpr_avl_get(error->errs, (void *)key++);
+    if (child_error == NULL) break;
+    grpc_error *result = recursively_find_error_with_field(child_error, which);
+    if (result != NULL) return result;
+  }
+  return NULL;
+}
+
+void grpc_error_get_status(grpc_error *error, gpr_timespec deadline,
+                           grpc_status_code *code, const char **msg,
+                           grpc_http2_error_code *http_error) {
+  // 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 == NULL) {
+    /// 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 == NULL) 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 = (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((grpc_http2_error_code)integer,
+                                             deadline);
+  }
+  if (code != NULL) *code = status;
+
+  if (http_error != NULL) {
+    if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) {
+      *http_error = (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((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 (msg != NULL) {
+    *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE);
+    if (*msg == NULL && error != GRPC_ERROR_NONE) {
+      *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION);
+      if (*msg == NULL) *msg = "unknown error";  // Just in case.
+    }
+  }
+
+  if (found_error == NULL) found_error = error;
+}
+
+bool grpc_error_has_clear_grpc_status(grpc_error *error) {
+  if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) {
+    return true;
+  }
+  intptr_t key = 0;
+  while (true) {
+    grpc_error *child_error = gpr_avl_get(error->errs, (void *)key++);
+    if (child_error == NULL) break;
+    if (grpc_error_has_clear_grpc_status(child_error)) {
+      return true;
+    }
+  }
+  return false;
+}
diff --git a/src/core/lib/transport/error_utils.h b/src/core/lib/transport/error_utils.h
new file mode 100644
index 0000000..1053388
--- /dev/null
+++ b/src/core/lib/transport/error_utils.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H
+#define GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H
+
+#include "src/core/lib/iomgr/error.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. If any of the
+/// attributes (code, msg, http_status) are unneeded, they can be passed as
+/// NULL.
+void grpc_error_get_status(grpc_error *error, gpr_timespec deadline,
+                           grpc_status_code *code, const char **msg,
+                           grpc_http2_error_code *http_status);
+
+/// 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/src/core/ext/transport/chttp2/transport/http2_errors.h b/src/core/lib/transport/http2_errors.h
similarity index 69%
rename from src/core/ext/transport/chttp2/transport/http2_errors.h
rename to src/core/lib/transport/http2_errors.h
index deab2b7..330bc98 100644
--- a/src/core/ext/transport/chttp2/transport/http2_errors.h
+++ b/src/core/lib/transport/http2_errors.h
@@ -31,26 +31,26 @@
  *
  */
 
-#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_ERRORS_H
-#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_ERRORS_H
+#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_CHTTP2_NO_ERROR = 0x0,
-  GRPC_CHTTP2_PROTOCOL_ERROR = 0x1,
-  GRPC_CHTTP2_INTERNAL_ERROR = 0x2,
-  GRPC_CHTTP2_FLOW_CONTROL_ERROR = 0x3,
-  GRPC_CHTTP2_SETTINGS_TIMEOUT = 0x4,
-  GRPC_CHTTP2_STREAM_CLOSED = 0x5,
-  GRPC_CHTTP2_FRAME_SIZE_ERROR = 0x6,
-  GRPC_CHTTP2_REFUSED_STREAM = 0x7,
-  GRPC_CHTTP2_CANCEL = 0x8,
-  GRPC_CHTTP2_COMPRESSION_ERROR = 0x9,
-  GRPC_CHTTP2_CONNECT_ERROR = 0xa,
-  GRPC_CHTTP2_ENHANCE_YOUR_CALM = 0xb,
-  GRPC_CHTTP2_INADEQUATE_SECURITY = 0xc,
+  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_CHTTP2__ERROR_DO_NOT_USE = -1
-} grpc_chttp2_error_code;
+  GRPC_HTTP2__ERROR_DO_NOT_USE = -1
+} grpc_http2_error_code;
 
-#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_ERRORS_H */
+#endif /* GRPC_CORE_LIB_TRANSPORT_HTTP2_ERRORS_H */
diff --git a/src/core/lib/transport/metadata.c b/src/core/lib/transport/metadata.c
index 54c39df..489c20c 100644
--- a/src/core/lib/transport/metadata.c
+++ b/src/core/lib/transport/metadata.c
@@ -48,12 +48,11 @@
 #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/support/murmur_hash.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/static_metadata.h"
 
-grpc_slice (*grpc_chttp2_base64_encode_and_huffman_compress)(grpc_slice input);
-
 /* 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().
@@ -63,9 +62,6 @@
  * used to determine which kind of element a pointer refers to.
  */
 
-#define INITIAL_STRTAB_CAPACITY 4
-#define INITIAL_MDTAB_CAPACITY 4
-
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
 #define DEBUG_ARGS , const char *file, int line
 #define FWD_DEBUG_ARGS , file, line
@@ -76,37 +72,20 @@
 #define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
 #endif
 
-#define TABLE_IDX(hash, log2_shards, capacity) \
-  (((hash) >> (log2_shards)) % (capacity))
-#define SHARD_IDX(hash, log2_shards) ((hash) & ((1 << (log2_shards)) - 1))
+#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);
 
-#define SIZE_IN_DECODER_TABLE_NOT_SET -1
-/* Shadow structure for grpc_mdstr for non-static values */
-typedef struct internal_string {
-  /* must be byte compatible with grpc_mdstr */
-  grpc_slice slice;
-  uint32_t hash;
-
-  /* private only data */
-  gpr_atm refcnt;
-
-  uint8_t has_base64_and_huffman_encoded;
-  grpc_slice_refcount refcount;
-
-  grpc_slice base64_and_huffman;
-
-  gpr_atm size_in_decoder_table;
-
-  struct internal_string *bucket_next;
-} internal_string;
-
-/* Shadow structure for grpc_mdelem for non-static elements */
-typedef struct internal_metadata {
-  /* must be byte compatible with grpc_mdelem */
-  internal_string *key;
-  internal_string *value;
+/* 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;
@@ -115,19 +94,22 @@
   gpr_atm destroy_user_data;
   gpr_atm user_data;
 
-  struct internal_metadata *bucket_next;
-} internal_metadata;
+  struct interned_metadata *bucket_next;
+} interned_metadata;
 
-typedef struct strtab_shard {
-  gpr_mu mu;
-  internal_string **strs;
-  size_t count;
-  size_t capacity;
-} strtab_shard;
+/* 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;
-  internal_metadata **elems;
+  interned_metadata **elems;
   size_t count;
   size_t capacity;
   /** Estimate of the number of unreferenced mdelems in the hash table.
@@ -136,102 +118,26 @@
   gpr_atm free_estimate;
 } mdtab_shard;
 
-#define LOG2_STRTAB_SHARD_COUNT 5
-#define LOG2_MDTAB_SHARD_COUNT 4
-#define STRTAB_SHARD_COUNT ((size_t)(1 << LOG2_STRTAB_SHARD_COUNT))
-#define MDTAB_SHARD_COUNT ((size_t)(1 << LOG2_MDTAB_SHARD_COUNT))
-
-/* hash seed: decided at initialization time */
-static uint32_t g_hash_seed;
-static int g_forced_hash_seed = 0;
-
-/* linearly probed hash tables for static element lookup */
-static grpc_mdstr *g_static_strtab[GRPC_STATIC_MDSTR_COUNT * 2];
-static grpc_mdelem *g_static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2];
-static size_t g_static_strtab_maxprobe;
-static size_t g_static_mdtab_maxprobe;
-
-static strtab_shard g_strtab_shard[STRTAB_SHARD_COUNT];
-static mdtab_shard g_mdtab_shard[MDTAB_SHARD_COUNT];
+static mdtab_shard g_shards[SHARD_COUNT];
 
 static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard);
 
-void grpc_test_only_set_metadata_hash_seed(uint32_t seed) {
-  g_hash_seed = seed;
-  g_forced_hash_seed = 1;
-}
-
 void grpc_mdctx_global_init(void) {
-  size_t i, j;
-  if (!g_forced_hash_seed) {
-    g_hash_seed = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec;
-  }
-  g_static_strtab_maxprobe = 0;
-  g_static_mdtab_maxprobe = 0;
-  /* build static tables */
-  memset(g_static_mdtab, 0, sizeof(g_static_mdtab));
-  memset(g_static_strtab, 0, sizeof(g_static_strtab));
-  for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
-    grpc_mdstr *elem = &grpc_static_mdstr_table[i];
-    const char *str = grpc_static_metadata_strings[i];
-    uint32_t hash = gpr_murmur_hash3(str, strlen(str), g_hash_seed);
-    *(grpc_slice *)&elem->slice = grpc_slice_from_static_string(str);
-    *(uint32_t *)&elem->hash = hash;
-    for (j = 0;; j++) {
-      size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_strtab);
-      if (g_static_strtab[idx] == NULL) {
-        g_static_strtab[idx] = &grpc_static_mdstr_table[i];
-        break;
-      }
-    }
-    if (j > g_static_strtab_maxprobe) {
-      g_static_strtab_maxprobe = j;
-    }
-  }
-  for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
-    grpc_mdelem *elem = &grpc_static_mdelem_table[i];
-    grpc_mdstr *key =
-        &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 0]];
-    grpc_mdstr *value =
-        &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 1]];
-    uint32_t hash = GRPC_MDSTR_KV_HASH(key->hash, value->hash);
-    *(grpc_mdstr **)&elem->key = key;
-    *(grpc_mdstr **)&elem->value = value;
-    for (j = 0;; j++) {
-      size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_mdtab);
-      if (g_static_mdtab[idx] == NULL) {
-        g_static_mdtab[idx] = elem;
-        break;
-      }
-    }
-    if (j > g_static_mdtab_maxprobe) {
-      g_static_mdtab_maxprobe = j;
-    }
-  }
   /* initialize shards */
-  for (i = 0; i < STRTAB_SHARD_COUNT; i++) {
-    strtab_shard *shard = &g_strtab_shard[i];
-    gpr_mu_init(&shard->mu);
-    shard->count = 0;
-    shard->capacity = INITIAL_STRTAB_CAPACITY;
-    shard->strs = gpr_malloc(sizeof(*shard->strs) * shard->capacity);
-    memset(shard->strs, 0, sizeof(*shard->strs) * shard->capacity);
-  }
-  for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
-    mdtab_shard *shard = &g_mdtab_shard[i];
+  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_MDTAB_CAPACITY;
+    shard->capacity = INITIAL_SHARD_CAPACITY;
     shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity);
     memset(shard->elems, 0, sizeof(*shard->elems) * shard->capacity);
   }
 }
 
 void grpc_mdctx_global_shutdown(grpc_exec_ctx *exec_ctx) {
-  size_t i;
-  for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
-    mdtab_shard *shard = &g_mdtab_shard[i];
+  for (size_t i = 0; i < SHARD_COUNT; i++) {
+    mdtab_shard *shard = &g_shards[i];
     gpr_mu_destroy(&shard->mu);
     gc_mdtab(exec_ctx, shard);
     /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
@@ -244,212 +150,35 @@
     }
     gpr_free(shard->elems);
   }
-  for (i = 0; i < STRTAB_SHARD_COUNT; i++) {
-    strtab_shard *shard = &g_strtab_shard[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 (internal_string *s = shard->strs[j]; s; s = s->bucket_next) {
-          gpr_log(GPR_DEBUG, "LEAKED: %s",
-                  grpc_mdstr_as_c_string((grpc_mdstr *)s));
-        }
-      }
-      if (grpc_iomgr_abort_on_leaks()) {
-        abort();
-      }
-    }
-    gpr_free(shard->strs);
-  }
 }
 
-static int is_mdstr_static(grpc_mdstr *s) {
-  return s >= &grpc_static_mdstr_table[0] &&
-         s < &grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
-}
-
-static int is_mdelem_static(grpc_mdelem *e) {
-  return e >= &grpc_static_mdelem_table[0] &&
-         e < &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+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,
-                          internal_metadata *md DEBUG_ARGS) {
+                          interned_metadata *md DEBUG_ARGS) {
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
+  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:%zu->%zu: '%s' = '%s'", (void *)md,
           gpr_atm_no_barrier_load(&md->refcnt),
-          gpr_atm_no_barrier_load(&md->refcnt) + 1,
-          grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
-          grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
+          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 grow_strtab(strtab_shard *shard) {
-  size_t capacity = shard->capacity * 2;
-  size_t i;
-  internal_string **strtab;
-  internal_string *s, *next;
-
-  GPR_TIMER_BEGIN("grow_strtab", 0);
-
-  strtab = gpr_malloc(sizeof(internal_string *) * capacity);
-  memset(strtab, 0, sizeof(internal_string *) * capacity);
-
-  for (i = 0; i < shard->capacity; i++) {
-    for (s = shard->strs[i]; s; s = next) {
-      size_t idx = TABLE_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT, capacity);
-      next = s->bucket_next;
-      s->bucket_next = strtab[idx];
-      strtab[idx] = s;
-    }
-  }
-
-  gpr_free(shard->strs);
-  shard->strs = strtab;
-  shard->capacity = capacity;
-
-  GPR_TIMER_END("grow_strtab", 0);
-}
-
-static void internal_destroy_string(grpc_exec_ctx *exec_ctx,
-                                    strtab_shard *shard, internal_string *is) {
-  internal_string **prev_next;
-  internal_string *cur;
-  GPR_TIMER_BEGIN("internal_destroy_string", 0);
-  if (is->has_base64_and_huffman_encoded) {
-    grpc_slice_unref_internal(exec_ctx, is->base64_and_huffman);
-  }
-  for (prev_next = &shard->strs[TABLE_IDX(is->hash, LOG2_STRTAB_SHARD_COUNT,
-                                          shard->capacity)],
-      cur = *prev_next;
-       cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next)
-    ;
-  *prev_next = cur->bucket_next;
-  shard->count--;
-  gpr_free(is);
-  GPR_TIMER_END("internal_destroy_string", 0);
-}
-
-static void slice_ref(void *p) {
-  internal_string *is =
-      (internal_string *)((char *)p - offsetof(internal_string, refcount));
-  GRPC_MDSTR_REF((grpc_mdstr *)(is));
-}
-
-static void slice_unref(grpc_exec_ctx *exec_ctx, void *p) {
-  internal_string *is =
-      (internal_string *)((char *)p - offsetof(internal_string, refcount));
-  GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)(is));
-}
-
-grpc_mdstr *grpc_mdstr_from_string(const char *str) {
-  return grpc_mdstr_from_buffer((const uint8_t *)str, strlen(str));
-}
-
-grpc_mdstr *grpc_mdstr_from_slice(grpc_exec_ctx *exec_ctx, grpc_slice slice) {
-  grpc_mdstr *result = grpc_mdstr_from_buffer(GRPC_SLICE_START_PTR(slice),
-                                              GRPC_SLICE_LENGTH(slice));
-  grpc_slice_unref_internal(exec_ctx, slice);
-  return result;
-}
-
-grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *buf, size_t length) {
-  uint32_t hash = gpr_murmur_hash3(buf, length, g_hash_seed);
-  internal_string *s;
-  strtab_shard *shard =
-      &g_strtab_shard[SHARD_IDX(hash, LOG2_STRTAB_SHARD_COUNT)];
-  size_t i;
-  size_t idx;
-
-  GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0);
-
-  /* search for a static string */
-  for (i = 0; i <= g_static_strtab_maxprobe; i++) {
-    grpc_mdstr *ss;
-    idx = (hash + i) % GPR_ARRAY_SIZE(g_static_strtab);
-    ss = g_static_strtab[idx];
-    if (ss == NULL) break;
-    if (ss->hash == hash && GRPC_SLICE_LENGTH(ss->slice) == length &&
-        (length == 0 ||
-         0 == memcmp(buf, GRPC_SLICE_START_PTR(ss->slice), length))) {
-      GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
-      return ss;
-    }
-  }
-
-  gpr_mu_lock(&shard->mu);
-
-  /* search for an existing string */
-  idx = TABLE_IDX(hash, LOG2_STRTAB_SHARD_COUNT, shard->capacity);
-  for (s = shard->strs[idx]; s; s = s->bucket_next) {
-    if (s->hash == hash && GRPC_SLICE_LENGTH(s->slice) == length &&
-        0 == memcmp(buf, GRPC_SLICE_START_PTR(s->slice), length)) {
-      if (gpr_atm_full_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);
-        GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
-        return (grpc_mdstr *)s;
-      }
-    }
-  }
-
-  /* not found: create a new string */
-  if (length + 1 < GRPC_SLICE_INLINED_SIZE) {
-    /* string data goes directly into the slice */
-    s = gpr_malloc(sizeof(internal_string));
-    gpr_atm_rel_store(&s->refcnt, 1);
-    s->slice.refcount = NULL;
-    memcpy(s->slice.data.inlined.bytes, buf, length);
-    s->slice.data.inlined.bytes[length] = 0;
-    s->slice.data.inlined.length = (uint8_t)length;
-  } else {
-    /* string data goes after the internal_string header, and we +1 for null
-       terminator */
-    s = gpr_malloc(sizeof(internal_string) + length + 1);
-    gpr_atm_rel_store(&s->refcnt, 1);
-    s->refcount.ref = slice_ref;
-    s->refcount.unref = slice_unref;
-    s->slice.refcount = &s->refcount;
-    s->slice.data.refcounted.bytes = (uint8_t *)(s + 1);
-    s->slice.data.refcounted.length = length;
-    memcpy(s->slice.data.refcounted.bytes, buf, length);
-    /* add a null terminator for cheap c string conversion when desired */
-    s->slice.data.refcounted.bytes[length] = 0;
-  }
-  s->has_base64_and_huffman_encoded = 0;
-  s->hash = hash;
-  s->size_in_decoder_table = SIZE_IN_DECODER_TABLE_NOT_SET;
-  s->bucket_next = shard->strs[idx];
-  shard->strs[idx] = s;
-
-  shard->count++;
-
-  if (shard->count > shard->capacity * 2) {
-    grow_strtab(shard);
-  }
-
-  gpr_mu_unlock(&shard->mu);
-  GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
-
-  return (grpc_mdstr *)s;
-}
-
 static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) {
   size_t i;
-  internal_metadata **prev_next;
-  internal_metadata *md, *next;
+  interned_metadata **prev_next;
+  interned_metadata *md, *next;
   gpr_atm num_freed = 0;
 
   GPR_TIMER_BEGIN("gc_mdtab", 0);
@@ -459,8 +188,8 @@
       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_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)md->key);
-        GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)md->value);
+        grpc_slice_unref_internal(exec_ctx, md->key);
+        grpc_slice_unref_internal(exec_ctx, md->value);
         if (md->user_data) {
           ((destroy_user_data_func)gpr_atm_no_barrier_load(
               &md->destroy_user_data))(user_data);
@@ -481,21 +210,22 @@
 static void grow_mdtab(mdtab_shard *shard) {
   size_t capacity = shard->capacity * 2;
   size_t i;
-  internal_metadata **mdtab;
-  internal_metadata *md, *next;
+  interned_metadata **mdtab;
+  interned_metadata *md, *next;
   uint32_t hash;
 
   GPR_TIMER_BEGIN("grow_mdtab", 0);
 
-  mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity);
-  memset(mdtab, 0, sizeof(internal_metadata *) * capacity);
+  mdtab = gpr_malloc(sizeof(interned_metadata *) * capacity);
+  memset(mdtab, 0, 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(md->key->hash, md->value->hash);
+      hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key),
+                                grpc_slice_hash(md->value));
       next = md->bucket_next;
-      idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, capacity);
+      idx = TABLE_IDX(hash, capacity);
       md->bucket_next = mdtab[idx];
       mdtab[idx] = md;
     }
@@ -517,62 +247,77 @@
   }
 }
 
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_exec_ctx *exec_ctx,
-                                               grpc_mdstr *mkey,
-                                               grpc_mdstr *mvalue) {
-  internal_string *key = (internal_string *)mkey;
-  internal_string *value = (internal_string *)mvalue;
-  uint32_t hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
-  internal_metadata *md;
-  mdtab_shard *shard = &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
-  size_t i;
+grpc_mdelem grpc_mdelem_create(
+    grpc_exec_ctx *exec_ctx, grpc_slice key, 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 != NULL) {
+      return GRPC_MAKE_MDELEM(compatible_external_backing_store,
+                              GRPC_MDELEM_STORAGE_EXTERNAL);
+    }
+
+    allocated_metadata *allocated = 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);
+#ifdef GRPC_METADATA_REFCOUNT_DEBUG
+    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:%zu: '%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_BEGIN("grpc_mdelem_from_metadata_strings", 0);
 
-  if (is_mdstr_static(mkey) && is_mdstr_static(mvalue)) {
-    for (i = 0; i <= g_static_mdtab_maxprobe; i++) {
-      grpc_mdelem *smd;
-      idx = (hash + i) % GPR_ARRAY_SIZE(g_static_mdtab);
-      smd = g_static_mdtab[idx];
-      if (smd == NULL) break;
-      if (smd->key == mkey && smd->value == mvalue) {
-        GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
-        return smd;
-      }
-    }
-  }
-
   gpr_mu_lock(&shard->mu);
 
-  idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, shard->capacity);
+  idx = TABLE_IDX(hash, shard->capacity);
   /* search for an existing pair */
   for (md = shard->elems[idx]; md; md = md->bucket_next) {
-    if (md->key == key && md->value == value) {
+    if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) {
       REF_MD_LOCKED(shard, md);
-      GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)key);
-      GRPC_MDSTR_UNREF(exec_ctx, (grpc_mdstr *)value);
       gpr_mu_unlock(&shard->mu);
       GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
-      return (grpc_mdelem *)md;
+      return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
     }
   }
 
   /* not found: create a new pair */
-  md = gpr_malloc(sizeof(internal_metadata));
+  md = gpr_malloc(sizeof(interned_metadata));
   gpr_atm_rel_store(&md->refcnt, 1);
-  md->key = key;
-  md->value = value;
+  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);
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
+  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:%zu: '%s' = '%s'", (void *)md,
-          gpr_atm_no_barrier_load(&md->refcnt),
-          grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
-          grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
+          gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str);
+  gpr_free(key_str);
+  gpr_free(value_str);
 #endif
   shard->count++;
 
@@ -584,29 +329,26 @@
 
   GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
 
-  return (grpc_mdelem *)md;
+  return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED);
 }
 
-grpc_mdelem *grpc_mdelem_from_strings(grpc_exec_ctx *exec_ctx, const char *key,
-                                      const char *value) {
-  return grpc_mdelem_from_metadata_strings(
-      exec_ctx, grpc_mdstr_from_string(key), grpc_mdstr_from_string(value));
+grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
+                                    grpc_slice value) {
+  grpc_mdelem out = grpc_mdelem_create(exec_ctx, key, value, NULL);
+  grpc_slice_unref_internal(exec_ctx, key);
+  grpc_slice_unref_internal(exec_ctx, value);
+  return out;
 }
 
-grpc_mdelem *grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
-                                     grpc_slice value) {
-  return grpc_mdelem_from_metadata_strings(
-      exec_ctx, grpc_mdstr_from_slice(exec_ctx, key),
-      grpc_mdstr_from_slice(exec_ctx, value));
-}
-
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_exec_ctx *exec_ctx,
-                                                const char *key,
-                                                const uint8_t *value,
-                                                size_t value_length) {
-  return grpc_mdelem_from_metadata_strings(
-      exec_ctx, grpc_mdstr_from_string(key),
-      grpc_mdstr_from_buffer(value, value_length));
+grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_exec_ctx *exec_ctx,
+                                           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(exec_ctx, key_slice, value_slice,
+                            changed ? NULL : (grpc_mdelem_data *)metadata);
 }
 
 static size_t get_base64_encoded_size(size_t raw_length) {
@@ -614,160 +356,176 @@
   return raw_length / 3 * 4 + tail_xtra[raw_length % 3];
 }
 
-size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem *elem) {
-  size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(elem->key->slice);
-  size_t value_len = GRPC_SLICE_LENGTH(elem->value->slice);
-  if (is_mdstr_static(elem->value)) {
-    if (grpc_is_binary_header(
-            (const char *)GRPC_SLICE_START_PTR(elem->key->slice),
-            GRPC_SLICE_LENGTH(elem->key->slice))) {
-      return overhead_and_key + get_base64_encoded_size(value_len);
-    } else {
-      return overhead_and_key + value_len;
-    }
+size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem) {
+  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 + get_base64_encoded_size(value_len);
   } else {
-    internal_string *is = (internal_string *)elem->value;
-    gpr_atm current_size = gpr_atm_acq_load(&is->size_in_decoder_table);
-    if (current_size == SIZE_IN_DECODER_TABLE_NOT_SET) {
-      if (grpc_is_binary_header(
-              (const char *)GRPC_SLICE_START_PTR(elem->key->slice),
-              GRPC_SLICE_LENGTH(elem->key->slice))) {
-        current_size = (gpr_atm)get_base64_encoded_size(value_len);
-      } else {
-        current_size = (gpr_atm)value_len;
-      }
-      gpr_atm_rel_store(&is->size_in_decoder_table, current_size);
-    }
-    return overhead_and_key + (size_t)current_size;
+    return overhead_and_key + value_len;
   }
 }
 
-grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
-  internal_metadata *md = (internal_metadata *)gmd;
-  if (is_mdelem_static(gmd)) return gmd;
+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 = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-          "ELM   REF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
-          gpr_atm_no_barrier_load(&md->refcnt),
-          gpr_atm_no_barrier_load(&md->refcnt) + 1,
-          grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
-          grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
+      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:%zu->%zu: '%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);
+      /* 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 = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
+#ifdef GRPC_METADATA_REFCOUNT_DEBUG
+      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:%zu->%zu: '%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_exec_ctx *exec_ctx, grpc_mdelem *gmd DEBUG_ARGS) {
-  internal_metadata *md = (internal_metadata *)gmd;
-  if (!md) return;
-  if (is_mdelem_static(gmd)) return;
+void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, 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 = (interned_metadata *)GRPC_MDELEM_DATA(gmd);
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
-          "ELM UNREF:%p:%zu->%zu: '%s' = '%s'", (void *)md,
-          gpr_atm_no_barrier_load(&md->refcnt),
-          gpr_atm_no_barrier_load(&md->refcnt) - 1,
-          grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
-          grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
+      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:%zu->%zu: '%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(md->key->hash, md->value->hash);
-  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_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
-    gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
-  }
-}
-
-const char *grpc_mdstr_as_c_string(const grpc_mdstr *s) {
-  return (const char *)GRPC_SLICE_START_PTR(s->slice);
-}
-
-size_t grpc_mdstr_length(const grpc_mdstr *s) { return GRPC_MDSTR_LENGTH(s); }
-
-grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
-  internal_string *s = (internal_string *)gs;
-  if (is_mdstr_static(gs)) return gs;
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR   REF:%p:%zu->%zu: '%s'",
-          (void *)s, gpr_atm_no_barrier_load(&s->refcnt),
-          gpr_atm_no_barrier_load(&s->refcnt) + 1, grpc_mdstr_as_c_string(gs));
-#endif
-  GPR_ASSERT(gpr_atm_full_fetch_add(&s->refcnt, 1) > 0);
-  return gs;
-}
-
-void grpc_mdstr_unref(grpc_exec_ctx *exec_ctx, grpc_mdstr *gs DEBUG_ARGS) {
-  internal_string *s = (internal_string *)gs;
-  if (is_mdstr_static(gs)) return;
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%zu->%zu: '%s'",
-          (void *)s, gpr_atm_no_barrier_load(&s->refcnt),
-          gpr_atm_no_barrier_load(&s->refcnt) - 1, grpc_mdstr_as_c_string(gs));
-#endif
-  if (1 == gpr_atm_full_fetch_add(&s->refcnt, -1)) {
-    strtab_shard *shard =
-        &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
-    gpr_mu_lock(&shard->mu);
-    GPR_ASSERT(0 == gpr_atm_no_barrier_load(&s->refcnt));
-    internal_destroy_string(exec_ctx, shard, s);
-    gpr_mu_unlock(&shard->mu);
-  }
-}
-
-void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
-  internal_metadata *im = (internal_metadata *)md;
-  void *result;
-  if (is_mdelem_static(md)) {
-    return (void *)grpc_static_mdelem_user_data[md - grpc_static_mdelem_table];
-  }
-  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 NULL;
-  }
-  return result;
-}
-
-void *grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
-                                void *user_data) {
-  internal_metadata *im = (internal_metadata *)md;
-  GPR_ASSERT(!is_mdelem_static(md));
-  GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
-  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 != NULL) {
-      destroy_func(user_data);
+      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;
     }
-    return (void *)gpr_atm_no_barrier_load(&im->user_data);
+    case GRPC_MDELEM_STORAGE_ALLOCATED: {
+      allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd);
+#ifdef GRPC_METADATA_REFCOUNT_DEBUG
+      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:%zu->%zu: '%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(exec_ctx, md->key);
+        grpc_slice_unref_internal(exec_ctx, md->value);
+        gpr_free(md);
+      }
+      break;
+    }
   }
-  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;
 }
 
-grpc_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
-  internal_string *s = (internal_string *)gs;
-  grpc_slice slice;
-  strtab_shard *shard =
-      &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
-  gpr_mu_lock(&shard->mu);
-  if (!s->has_base64_and_huffman_encoded) {
-    s->base64_and_huffman =
-        grpc_chttp2_base64_encode_and_huffman_compress(s->slice);
-    s->has_base64_and_huffman_encoded = 1;
+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 NULL;
+    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 = (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 NULL;
+      }
+      return result;
+    }
   }
-  slice = s->base64_and_huffman;
-  gpr_mu_unlock(&shard->mu);
-  return slice;
+  GPR_UNREACHABLE_CODE(return NULL);
+}
+
+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 NULL;
+    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 = (interned_metadata *)GRPC_MDELEM_DATA(md);
+      GPR_ASSERT(!is_mdelem_static(md));
+      GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
+      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 != NULL) {
+          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 NULL);
+}
+
+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/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h
index 991eee9..f4ba86c 100644
--- a/src/core/lib/transport/metadata.h
+++ b/src/core/lib/transport/metadata.h
@@ -34,6 +34,7 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_H
 #define GRPC_CORE_LIB_TRANSPORT_METADATA_H
 
+#include <grpc/grpc.h>
 #include <grpc/slice.h>
 #include <grpc/support/useful.h>
 
@@ -74,110 +75,110 @@
    declared here - in which case those functions are effectively no-ops. */
 
 /* Forward declarations */
-typedef struct grpc_mdstr grpc_mdstr;
 typedef struct grpc_mdelem grpc_mdelem;
 
-/* if changing this, make identical changes in internal_string in metadata.c */
-struct grpc_mdstr {
-  const grpc_slice slice;
-  const uint32_t hash;
+/* 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;
 
-/* if changing this, make identical changes in internal_metadata in
-   metadata.c */
+/* 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 {
-  grpc_mdstr *const key;
-  grpc_mdstr *const value;
-  /* there is a private part to this in metadata.c */
+  /* a grpc_mdelem_data* generally, with the two lower bits signalling memory
+     ownership as per grpc_mdelem_data_storage */
+  uintptr_t payload;
 };
 
-void grpc_test_only_set_metadata_hash_seed(uint32_t seed);
+#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))
+#define GRPC_MAKE_MDELEM(data, storage) \
+  ((grpc_mdelem){((uintptr_t)(data)) | ((uintptr_t)storage)})
+#define GRPC_MDELEM_IS_INTERNED(md)          \
+  ((grpc_mdelem_data_storage)((md).payload & \
+                              (uintptr_t)GRPC_MDELEM_STORAGE_INTERNED_BIT))
 
-/* Constructors for grpc_mdstr instances; take a variety of data types that
-   clients may have handy */
-grpc_mdstr *grpc_mdstr_from_string(const char *str);
-/* Unrefs the slice. */
-grpc_mdstr *grpc_mdstr_from_slice(grpc_exec_ctx *exec_ctx, grpc_slice slice);
-grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *str, size_t length);
-
-/* Returns a borrowed slice from the mdstr with its contents base64 encoded
-   and huffman compressed */
-grpc_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *str);
-
-/* Constructors for grpc_mdelem instances; take a variety of data types that
-   clients may have handy */
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_exec_ctx *exec_ctx,
-                                               grpc_mdstr *key,
-                                               grpc_mdstr *value);
-grpc_mdelem *grpc_mdelem_from_strings(grpc_exec_ctx *exec_ctx, const char *key,
-                                      const char *value);
 /* Unrefs the slices. */
-grpc_mdelem *grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
-                                     grpc_slice value);
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_exec_ctx *exec_ctx,
-                                                const char *key,
-                                                const uint8_t *value,
-                                                size_t value_length);
+grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key,
+                                    grpc_slice value);
 
-size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem *elem);
+/* 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_exec_ctx *exec_ctx,
+                                           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(
+    grpc_exec_ctx *exec_ctx, grpc_slice key, grpc_slice value,
+    grpc_mdelem_data *compatible_external_backing_store);
+
+bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b);
+
+size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem);
 
 /* 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 *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 *grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void *),
                                 void *user_data);
 
 /* Reference counting */
 //#define GRPC_METADATA_REFCOUNT_DEBUG
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
-#define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s), __FILE__, __LINE__)
-#define GRPC_MDSTR_UNREF(exec_ctx, s) \
-  grpc_mdstr_unref((exec_ctx), (s), __FILE__, __LINE__)
 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__)
 #define GRPC_MDELEM_UNREF(exec_ctx, s) \
   grpc_mdelem_unref((exec_ctx), (s), __FILE__, __LINE__)
-grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s, const char *file, int line);
-void grpc_mdstr_unref(grpc_exec_ctx *exec_ctx, grpc_mdstr *s, const char *file,
-                      int line);
-grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md, const char *file, int line);
-void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem *md,
+grpc_mdelem grpc_mdelem_ref(grpc_mdelem md, const char *file, int line);
+void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem md,
                        const char *file, int line);
 #else
-#define GRPC_MDSTR_REF(s) grpc_mdstr_ref((s))
-#define GRPC_MDSTR_UNREF(exec_ctx, s) grpc_mdstr_unref((exec_ctx), (s))
 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
 #define GRPC_MDELEM_UNREF(exec_ctx, s) grpc_mdelem_unref((exec_ctx), (s))
-grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *s);
-void grpc_mdstr_unref(grpc_exec_ctx *exec_ctx, grpc_mdstr *s);
-grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *md);
-void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem *md);
+grpc_mdelem grpc_mdelem_ref(grpc_mdelem md);
+void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem md);
 #endif
 
-/* Recover a char* from a grpc_mdstr. The returned string is null terminated.
-   Does not promise that the returned string has no embedded nulls however. */
-const char *grpc_mdstr_as_c_string(const grpc_mdstr *s);
+#define GRPC_MDKEY(md) (GRPC_MDELEM_DATA(md)->key)
+#define GRPC_MDVALUE(md) (GRPC_MDELEM_DATA(md)->value)
 
-#define GRPC_MDSTR_LENGTH(s) (GRPC_SLICE_LENGTH(s->slice))
+#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_MDSTR_LENGTH((e)->key) + GRPC_MDSTR_LENGTH((e)->value) + 32)
-
-int grpc_mdstr_is_legal_header(grpc_mdstr *s);
-int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s);
-int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s);
+#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(grpc_exec_ctx *exec_ctx);
 
-/* Implementation provided by chttp2_transport */
-extern grpc_slice (*grpc_chttp2_base64_encode_and_huffman_compress)(
-    grpc_slice input);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/core/lib/transport/metadata_batch.c b/src/core/lib/transport/metadata_batch.c
index b62ecc3..95b71d3 100644
--- a/src/core/lib/transport/metadata_batch.c
+++ b/src/core/lib/transport/metadata_batch.c
@@ -40,6 +40,8 @@
 #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
@@ -51,16 +53,34 @@
   GPR_ASSERT(list->tail->next == NULL);
   GPR_ASSERT((list->head == list->tail) == (list->head->next == NULL));
 
+  size_t verified_count = 0;
   for (l = list->head; l; l = l->next) {
-    GPR_ASSERT(l->md);
+    GPR_ASSERT(!GRPC_MDISNULL(l->md));
     GPR_ASSERT((l->prev == NULL) == (l == list->head));
     GPR_ASSERT((l->next == NULL) == (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_exec_ctx *exec_ctx,
+                                  grpc_metadata_batch *batch) {
+#ifndef NDEBUG
+  for (grpc_linked_mdelem *l = batch->list.head; l != NULL; 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(exec_ctx, key_interned);
+  }
+#endif
+}
+
 #ifndef NDEBUG
 void grpc_metadata_batch_assert_ok(grpc_metadata_batch *batch) {
   assert_valid_list(&batch->list);
@@ -68,7 +88,7 @@
 #endif /* NDEBUG */
 
 void grpc_metadata_batch_init(grpc_metadata_batch *batch) {
-  batch->list.head = batch->list.tail = NULL;
+  memset(batch, 0, sizeof(*batch));
   batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
 }
 
@@ -80,17 +100,58 @@
   }
 }
 
-void grpc_metadata_batch_add_head(grpc_metadata_batch *batch,
-                                  grpc_linked_mdelem *storage,
-                                  grpc_mdelem *elem_to_add) {
-  GPR_ASSERT(elem_to_add);
+grpc_error *grpc_attach_md_to_error(grpc_error *src, grpc_mdelem md) {
+  char *k = grpc_slice_to_c_string(GRPC_MDKEY(md));
+  char *v = grpc_slice_to_c_string(GRPC_MDVALUE(md));
+  grpc_error *out = grpc_error_set_str(
+      grpc_error_set_str(src, GRPC_ERROR_STR_KEY, k), GRPC_ERROR_STR_VALUE, v);
+  gpr_free(k);
+  gpr_free(v);
+  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] == NULL) {
+    batch->idx.array[idx] = storage;
+    return GRPC_ERROR_NONE;
+  }
+  return grpc_attach_md_to_error(
+      GRPC_ERROR_CREATE("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;
+  }
+  GPR_ASSERT(batch->idx.array[idx] != NULL);
+  batch->idx.array[idx] = NULL;
+}
+
+grpc_error *grpc_metadata_batch_add_head(grpc_exec_ctx *exec_ctx,
+                                         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;
-  grpc_metadata_batch_link_head(batch, storage);
+  return grpc_metadata_batch_link_head(exec_ctx, batch, storage);
 }
 
 static void link_head(grpc_mdelem_list *list, grpc_linked_mdelem *storage) {
   assert_valid_list(list);
-  GPR_ASSERT(storage->md);
+  GPR_ASSERT(!GRPC_MDISNULL(storage->md));
   storage->prev = NULL;
   storage->next = list->head;
   if (list->head != NULL) {
@@ -99,25 +160,36 @@
     list->tail = storage;
   }
   list->head = storage;
+  list->count++;
   assert_valid_list(list);
 }
 
-void grpc_metadata_batch_link_head(grpc_metadata_batch *batch,
-                                   grpc_linked_mdelem *storage) {
+grpc_error *grpc_metadata_batch_link_head(grpc_exec_ctx *exec_ctx,
+                                          grpc_metadata_batch *batch,
+                                          grpc_linked_mdelem *storage) {
+  assert_valid_callouts(exec_ctx, batch);
+  grpc_error *err = maybe_link_callout(batch, storage);
+  if (err != GRPC_ERROR_NONE) {
+    assert_valid_callouts(exec_ctx, batch);
+    return err;
+  }
   link_head(&batch->list, storage);
+  assert_valid_callouts(exec_ctx, batch);
+  return GRPC_ERROR_NONE;
 }
 
-void grpc_metadata_batch_add_tail(grpc_metadata_batch *batch,
-                                  grpc_linked_mdelem *storage,
-                                  grpc_mdelem *elem_to_add) {
-  GPR_ASSERT(elem_to_add);
+grpc_error *grpc_metadata_batch_add_tail(grpc_exec_ctx *exec_ctx,
+                                         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;
-  grpc_metadata_batch_link_tail(batch, storage);
+  return grpc_metadata_batch_link_tail(exec_ctx, batch, storage);
 }
 
 static void link_tail(grpc_mdelem_list *list, grpc_linked_mdelem *storage) {
   assert_valid_list(list);
-  GPR_ASSERT(storage->md);
+  GPR_ASSERT(!GRPC_MDISNULL(storage->md));
   storage->prev = list->tail;
   storage->next = NULL;
   storage->reserved = NULL;
@@ -127,70 +199,82 @@
     list->head = storage;
   }
   list->tail = storage;
+  list->count++;
   assert_valid_list(list);
 }
 
-void grpc_metadata_batch_link_tail(grpc_metadata_batch *batch,
-                                   grpc_linked_mdelem *storage) {
-  link_tail(&batch->list, storage);
-}
-
-void grpc_metadata_batch_move(grpc_metadata_batch *dst,
-                              grpc_metadata_batch *src) {
-  *dst = *src;
-  memset(src, 0, sizeof(grpc_metadata_batch));
-}
-
-void grpc_metadata_batch_filter(grpc_exec_ctx *exec_ctx,
-                                grpc_metadata_batch *batch,
-                                grpc_mdelem *(*filter)(grpc_exec_ctx *exec_ctx,
-                                                       void *user_data,
-                                                       grpc_mdelem *elem),
-                                void *user_data) {
-  grpc_linked_mdelem *l;
-  grpc_linked_mdelem *next;
-
-  GPR_TIMER_BEGIN("grpc_metadata_batch_filter", 0);
-
-  assert_valid_list(&batch->list);
-  for (l = batch->list.head; l; l = next) {
-    grpc_mdelem *orig = l->md;
-    grpc_mdelem *filt = filter(exec_ctx, user_data, orig);
-    next = l->next;
-    if (filt == NULL) {
-      if (l->prev) {
-        l->prev->next = l->next;
-      }
-      if (l->next) {
-        l->next->prev = l->prev;
-      }
-      if (batch->list.head == l) {
-        batch->list.head = l->next;
-      }
-      if (batch->list.tail == l) {
-        batch->list.tail = l->prev;
-      }
-      assert_valid_list(&batch->list);
-      GRPC_MDELEM_UNREF(exec_ctx, l->md);
-    } else if (filt != orig) {
-      GRPC_MDELEM_UNREF(exec_ctx, orig);
-      l->md = filt;
-    }
+grpc_error *grpc_metadata_batch_link_tail(grpc_exec_ctx *exec_ctx,
+                                          grpc_metadata_batch *batch,
+                                          grpc_linked_mdelem *storage) {
+  assert_valid_callouts(exec_ctx, batch);
+  grpc_error *err = maybe_link_callout(batch, storage);
+  if (err != GRPC_ERROR_NONE) {
+    assert_valid_callouts(exec_ctx, batch);
+    return err;
   }
-  assert_valid_list(&batch->list);
-
-  GPR_TIMER_END("grpc_metadata_batch_filter", 0);
+  link_tail(&batch->list, storage);
+  assert_valid_callouts(exec_ctx, batch);
+  return GRPC_ERROR_NONE;
 }
 
-static grpc_mdelem *no_metadata_for_you(grpc_exec_ctx *exec_ctx,
-                                        void *user_data, grpc_mdelem *elem) {
-  return NULL;
+static void unlink_storage(grpc_mdelem_list *list,
+                           grpc_linked_mdelem *storage) {
+  assert_valid_list(list);
+  if (storage->prev != NULL) {
+    storage->prev->next = storage->next;
+  } else {
+    list->head = storage->next;
+  }
+  if (storage->next != NULL) {
+    storage->next->prev = storage->prev;
+  } else {
+    list->tail = storage->prev;
+  }
+  list->count--;
+  assert_valid_list(list);
+}
+
+void grpc_metadata_batch_remove(grpc_exec_ctx *exec_ctx,
+                                grpc_metadata_batch *batch,
+                                grpc_linked_mdelem *storage) {
+  assert_valid_callouts(exec_ctx, batch);
+  maybe_unlink_callout(batch, storage);
+  unlink_storage(&batch->list, storage);
+  GRPC_MDELEM_UNREF(exec_ctx, storage->md);
+  assert_valid_callouts(exec_ctx, batch);
+}
+
+void grpc_metadata_batch_set_value(grpc_exec_ctx *exec_ctx,
+                                   grpc_linked_mdelem *storage,
+                                   grpc_slice value) {
+  grpc_mdelem old = storage->md;
+  grpc_mdelem new = grpc_mdelem_from_slices(
+      exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(old)), value);
+  storage->md = new;
+  GRPC_MDELEM_UNREF(exec_ctx, old);
+}
+
+grpc_error *grpc_metadata_batch_substitute(grpc_exec_ctx *exec_ctx,
+                                           grpc_metadata_batch *batch,
+                                           grpc_linked_mdelem *storage,
+                                           grpc_mdelem new) {
+  grpc_error *error = GRPC_ERROR_NONE;
+  grpc_mdelem old = storage->md;
+  if (!grpc_slice_eq(GRPC_MDKEY(new), GRPC_MDKEY(old))) {
+    maybe_unlink_callout(batch, storage);
+    storage->md = new;
+    error = maybe_link_callout(batch, storage);
+  } else {
+    storage->md = new;
+  }
+  GRPC_MDELEM_UNREF(exec_ctx, old);
+  return error;
 }
 
 void grpc_metadata_batch_clear(grpc_exec_ctx *exec_ctx,
                                grpc_metadata_batch *batch) {
-  batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
-  grpc_metadata_batch_filter(exec_ctx, batch, no_metadata_for_you, NULL);
+  grpc_metadata_batch_destroy(exec_ctx, batch);
+  grpc_metadata_batch_init(batch);
 }
 
 bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) {
@@ -207,3 +291,33 @@
   }
   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(composite_error_string);
+  }
+  *composite = grpc_error_add_child(*composite, error);
+}
+
+grpc_error *grpc_metadata_batch_filter(grpc_exec_ctx *exec_ctx,
+                                       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 = func(exec_ctx, user_data, l->md);
+    add_error(&error, new.error, composite_error_string);
+    if (GRPC_MDISNULL(new.md)) {
+      grpc_metadata_batch_remove(exec_ctx, batch, l);
+    } else if (new.md.payload != l->md.payload) {
+      grpc_metadata_batch_substitute(exec_ctx, batch, l, new.md);
+    }
+    l = next;
+  }
+  return error;
+}
diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h
index c0bd517..5471539 100644
--- a/src/core/lib/transport/metadata_batch.h
+++ b/src/core/lib/transport/metadata_batch.h
@@ -41,19 +41,21 @@
 #include <grpc/support/port_platform.h>
 #include <grpc/support/time.h>
 #include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/static_metadata.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 typedef struct grpc_linked_mdelem {
-  grpc_mdelem *md;
+  grpc_mdelem md;
   struct grpc_linked_mdelem *next;
   struct grpc_linked_mdelem *prev;
   void *reserved;
 } grpc_linked_mdelem;
 
 typedef struct grpc_mdelem_list {
+  size_t count;
   grpc_linked_mdelem *head;
   grpc_linked_mdelem *tail;
 } grpc_mdelem_list;
@@ -61,6 +63,7 @@
 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 gpr_inf_future if this batch does not need to send a
       grpc-timeout */
@@ -77,25 +80,37 @@
 /* Returns the transport size of the batch. */
 size_t grpc_metadata_batch_size(grpc_metadata_batch *batch);
 
-/** Moves the metadata information from \a src to \a dst. Upon return, \a src is
- * zeroed. */
-void grpc_metadata_batch_move(grpc_metadata_batch *dst,
-                              grpc_metadata_batch *src);
+/** Remove \a storage from the batch, unreffing the mdelem contained */
+void grpc_metadata_batch_remove(grpc_exec_ctx *exec_ctx,
+                                grpc_metadata_batch *batch,
+                                grpc_linked_mdelem *storage);
+
+/** Substitute a new mdelem for an old value */
+grpc_error *grpc_metadata_batch_substitute(grpc_exec_ctx *exec_ctx,
+                                           grpc_metadata_batch *batch,
+                                           grpc_linked_mdelem *storage,
+                                           grpc_mdelem new_value);
+
+void grpc_metadata_batch_set_value(grpc_exec_ctx *exec_ctx,
+                                   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. */
-void grpc_metadata_batch_link_head(grpc_metadata_batch *batch,
-                                   grpc_linked_mdelem *storage);
+grpc_error *grpc_metadata_batch_link_head(
+    grpc_exec_ctx *exec_ctx, 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. */
-void grpc_metadata_batch_link_tail(grpc_metadata_batch *batch,
-                                   grpc_linked_mdelem *storage);
+grpc_error *grpc_metadata_batch_link_tail(
+    grpc_exec_ctx *exec_ctx, 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.
@@ -103,29 +118,38 @@
     lifetime of batch. This usually means it should be around
     for the lifetime of the call.
     Takes ownership of \a elem_to_add */
-void grpc_metadata_batch_add_head(grpc_metadata_batch *batch,
-                                  grpc_linked_mdelem *storage,
-                                  grpc_mdelem *elem_to_add);
+grpc_error *grpc_metadata_batch_add_head(
+    grpc_exec_ctx *exec_ctx, 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 */
-void grpc_metadata_batch_add_tail(grpc_metadata_batch *batch,
-                                  grpc_linked_mdelem *storage,
-                                  grpc_mdelem *elem_to_add);
+grpc_error *grpc_metadata_batch_add_tail(
+    grpc_exec_ctx *exec_ctx, grpc_metadata_batch *batch,
+    grpc_linked_mdelem *storage, grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT;
 
-/** For each element in \a batch, execute \a filter.
-    The return value from \a filter will be substituted for the
-    grpc_mdelem passed to \a filter. If \a filter returns NULL,
-    the element will be moved to the garbage list. */
-void grpc_metadata_batch_filter(grpc_exec_ctx *exec_ctx,
-                                grpc_metadata_batch *batch,
-                                grpc_mdelem *(*filter)(grpc_exec_ctx *exec_ctx,
-                                                       void *user_data,
-                                                       grpc_mdelem *elem),
-                                void *user_data);
+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) \
+  ((grpc_filtered_mdelem){(error), GRPC_MDNULL})
+#define GRPC_FILTERED_MDELEM(md) ((grpc_filtered_mdelem){GRPC_ERROR_NONE, (md)})
+#define GRPC_FILTERED_REMOVE() \
+  ((grpc_filtered_mdelem){GRPC_ERROR_NONE, GRPC_MDNULL})
+
+typedef grpc_filtered_mdelem (*grpc_metadata_batch_filter_func)(
+    grpc_exec_ctx *exec_ctx, void *user_data, grpc_mdelem elem);
+grpc_error *grpc_metadata_batch_filter(
+    grpc_exec_ctx *exec_ctx, 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);
diff --git a/src/core/lib/transport/method_config.c b/src/core/lib/transport/method_config.c
deleted file mode 100644
index 25fb54b..0000000
--- a/src/core/lib/transport/method_config.c
+++ /dev/null
@@ -1,347 +0,0 @@
-//
-// Copyright 2015, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-
-#include "src/core/lib/transport/method_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 <grpc/support/time.h>
-
-#include "src/core/lib/transport/mdstr_hash_table.h"
-#include "src/core/lib/transport/metadata.h"
-
-//
-// grpc_method_config
-//
-
-// bool vtable
-
-static void* bool_copy(void* valuep) {
-  bool value = *(bool*)valuep;
-  bool* new_value = gpr_malloc(sizeof(bool));
-  *new_value = value;
-  return new_value;
-}
-
-static int bool_cmp(void* v1, void* v2) {
-  bool b1 = *(bool*)v1;
-  bool b2 = *(bool*)v2;
-  if (!b1 && b2) return -1;
-  if (b1 && !b2) return 1;
-  return 0;
-}
-
-static void free_mem(grpc_exec_ctx* exec_ctx, void* p) { gpr_free(p); }
-
-static grpc_mdstr_hash_table_vtable bool_vtable = {free_mem, bool_copy,
-                                                   bool_cmp};
-
-// timespec vtable
-
-static void* timespec_copy(void* valuep) {
-  gpr_timespec value = *(gpr_timespec*)valuep;
-  gpr_timespec* new_value = gpr_malloc(sizeof(gpr_timespec));
-  *new_value = value;
-  return new_value;
-}
-
-static int timespec_cmp(void* v1, void* v2) {
-  return gpr_time_cmp(*(gpr_timespec*)v1, *(gpr_timespec*)v2);
-}
-
-static grpc_mdstr_hash_table_vtable timespec_vtable = {free_mem, timespec_copy,
-                                                       timespec_cmp};
-
-// int32 vtable
-
-static void* int32_copy(void* valuep) {
-  int32_t value = *(int32_t*)valuep;
-  int32_t* new_value = gpr_malloc(sizeof(int32_t));
-  *new_value = value;
-  return new_value;
-}
-
-static int int32_cmp(void* v1, void* v2) {
-  int32_t i1 = *(int32_t*)v1;
-  int32_t i2 = *(int32_t*)v2;
-  if (i1 < i2) return -1;
-  if (i1 > i2) return 1;
-  return 0;
-}
-
-static grpc_mdstr_hash_table_vtable int32_vtable = {free_mem, int32_copy,
-                                                    int32_cmp};
-
-// Hash table keys.
-#define GRPC_METHOD_CONFIG_WAIT_FOR_READY "grpc.wait_for_ready"  // bool
-#define GRPC_METHOD_CONFIG_TIMEOUT "grpc.timeout"                // gpr_timespec
-#define GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES \
-  "grpc.max_request_message_bytes"  // int32
-#define GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES \
-  "grpc.max_response_message_bytes"  // int32
-
-struct grpc_method_config {
-  grpc_mdstr_hash_table* table;
-  grpc_mdstr* wait_for_ready_key;
-  grpc_mdstr* timeout_key;
-  grpc_mdstr* max_request_message_bytes_key;
-  grpc_mdstr* max_response_message_bytes_key;
-};
-
-grpc_method_config* grpc_method_config_create(
-    bool* wait_for_ready, gpr_timespec* timeout,
-    int32_t* max_request_message_bytes, int32_t* max_response_message_bytes) {
-  grpc_method_config* method_config = gpr_malloc(sizeof(grpc_method_config));
-  memset(method_config, 0, sizeof(grpc_method_config));
-  method_config->wait_for_ready_key =
-      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_WAIT_FOR_READY);
-  method_config->timeout_key =
-      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_TIMEOUT);
-  method_config->max_request_message_bytes_key =
-      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES);
-  method_config->max_response_message_bytes_key =
-      grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES);
-  grpc_mdstr_hash_table_entry entries[4];
-  size_t num_entries = 0;
-  if (wait_for_ready != NULL) {
-    entries[num_entries].key = method_config->wait_for_ready_key;
-    entries[num_entries].value = wait_for_ready;
-    entries[num_entries].vtable = &bool_vtable;
-    ++num_entries;
-  }
-  if (timeout != NULL) {
-    entries[num_entries].key = method_config->timeout_key;
-    entries[num_entries].value = timeout;
-    entries[num_entries].vtable = &timespec_vtable;
-    ++num_entries;
-  }
-  if (max_request_message_bytes != NULL) {
-    entries[num_entries].key = method_config->max_request_message_bytes_key;
-    entries[num_entries].value = max_request_message_bytes;
-    entries[num_entries].vtable = &int32_vtable;
-    ++num_entries;
-  }
-  if (max_response_message_bytes != NULL) {
-    entries[num_entries].key = method_config->max_response_message_bytes_key;
-    entries[num_entries].value = max_response_message_bytes;
-    entries[num_entries].vtable = &int32_vtable;
-    ++num_entries;
-  }
-  method_config->table = grpc_mdstr_hash_table_create(num_entries, entries);
-  return method_config;
-}
-
-grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config) {
-  grpc_mdstr_hash_table_ref(method_config->table);
-  return method_config;
-}
-
-void grpc_method_config_unref(grpc_exec_ctx* exec_ctx,
-                              grpc_method_config* method_config) {
-  if (grpc_mdstr_hash_table_unref(exec_ctx, method_config->table)) {
-    GRPC_MDSTR_UNREF(exec_ctx, method_config->wait_for_ready_key);
-    GRPC_MDSTR_UNREF(exec_ctx, method_config->timeout_key);
-    GRPC_MDSTR_UNREF(exec_ctx, method_config->max_request_message_bytes_key);
-    GRPC_MDSTR_UNREF(exec_ctx, method_config->max_response_message_bytes_key);
-    gpr_free(method_config);
-  }
-}
-
-int grpc_method_config_cmp(const grpc_method_config* method_config1,
-                           const grpc_method_config* method_config2) {
-  return grpc_mdstr_hash_table_cmp(method_config1->table,
-                                   method_config2->table);
-}
-
-const bool* grpc_method_config_get_wait_for_ready(
-    const grpc_method_config* method_config) {
-  return grpc_mdstr_hash_table_get(method_config->table,
-                                   method_config->wait_for_ready_key);
-}
-
-const gpr_timespec* grpc_method_config_get_timeout(
-    const grpc_method_config* method_config) {
-  return grpc_mdstr_hash_table_get(method_config->table,
-                                   method_config->timeout_key);
-}
-
-const int32_t* grpc_method_config_get_max_request_message_bytes(
-    const grpc_method_config* method_config) {
-  return grpc_mdstr_hash_table_get(
-      method_config->table, method_config->max_request_message_bytes_key);
-}
-
-const int32_t* grpc_method_config_get_max_response_message_bytes(
-    const grpc_method_config* method_config) {
-  return grpc_mdstr_hash_table_get(
-      method_config->table, method_config->max_response_message_bytes_key);
-}
-
-//
-// grpc_method_config_table
-//
-
-static void method_config_unref(grpc_exec_ctx* exec_ctx, void* valuep) {
-  grpc_method_config_unref(exec_ctx, valuep);
-}
-
-static void* method_config_ref(void* valuep) {
-  return grpc_method_config_ref(valuep);
-}
-
-static int method_config_cmp(void* valuep1, void* valuep2) {
-  return grpc_method_config_cmp(valuep1, valuep2);
-}
-
-static const grpc_mdstr_hash_table_vtable method_config_table_vtable = {
-    method_config_unref, method_config_ref, method_config_cmp};
-
-grpc_method_config_table* grpc_method_config_table_create(
-    size_t num_entries, grpc_method_config_table_entry* entries) {
-  grpc_mdstr_hash_table_entry* hash_table_entries =
-      gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) * num_entries);
-  for (size_t i = 0; i < num_entries; ++i) {
-    hash_table_entries[i].key = entries[i].method_name;
-    hash_table_entries[i].value = entries[i].method_config;
-    hash_table_entries[i].vtable = &method_config_table_vtable;
-  }
-  grpc_method_config_table* method_config_table =
-      grpc_mdstr_hash_table_create(num_entries, hash_table_entries);
-  gpr_free(hash_table_entries);
-  return method_config_table;
-}
-
-grpc_method_config_table* grpc_method_config_table_ref(
-    grpc_method_config_table* table) {
-  return grpc_mdstr_hash_table_ref(table);
-}
-
-void grpc_method_config_table_unref(grpc_exec_ctx* exec_ctx,
-                                    grpc_method_config_table* table) {
-  grpc_mdstr_hash_table_unref(exec_ctx, table);
-}
-
-int grpc_method_config_table_cmp(const grpc_method_config_table* table1,
-                                 const grpc_method_config_table* table2) {
-  return grpc_mdstr_hash_table_cmp(table1, table2);
-}
-
-void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx,
-                                   const grpc_mdstr_hash_table* table,
-                                   const grpc_mdstr* path) {
-  void* value = grpc_mdstr_hash_table_get(table, 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 == NULL) {
-    const char* path_str = grpc_mdstr_as_c_string(path);
-    const char* sep = strrchr(path_str, '/') + 1;
-    const size_t len = (size_t)(sep - path_str);
-    char* buf = gpr_malloc(len + 2);  // '*' and NUL
-    memcpy(buf, path_str, len);
-    buf[len] = '*';
-    buf[len + 1] = '\0';
-    grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf);
-    gpr_free(buf);
-    value = grpc_mdstr_hash_table_get(table, wildcard_path);
-    GRPC_MDSTR_UNREF(exec_ctx, wildcard_path);
-  }
-  return value;
-}
-
-static void* copy_arg(void* p) { return grpc_method_config_table_ref(p); }
-
-static void destroy_arg(grpc_exec_ctx* exec_ctx, void* p) {
-  grpc_method_config_table_unref(exec_ctx, p);
-}
-
-static int cmp_arg(void* p1, void* p2) {
-  return grpc_method_config_table_cmp(p1, p2);
-}
-
-static grpc_arg_pointer_vtable arg_vtable = {copy_arg, destroy_arg, cmp_arg};
-
-grpc_arg grpc_method_config_table_create_channel_arg(
-    grpc_method_config_table* table) {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_ARG_SERVICE_CONFIG;
-  arg.value.pointer.p = table;
-  arg.value.pointer.vtable = &arg_vtable;
-  return arg;
-}
-
-// State used by convert_entry() below.
-typedef struct conversion_state {
-  void* (*convert_value)(const grpc_method_config* method_config);
-  const grpc_mdstr_hash_table_vtable* vtable;
-  size_t num_entries;
-  grpc_mdstr_hash_table_entry* entries;
-} conversion_state;
-
-// A function to be passed to grpc_mdstr_hash_table_iterate() to create
-// a copy of the entries.
-static void convert_entry(const grpc_mdstr_hash_table_entry* entry,
-                          void* user_data) {
-  conversion_state* state = user_data;
-  state->entries[state->num_entries].key = GRPC_MDSTR_REF(entry->key);
-  state->entries[state->num_entries].value = state->convert_value(entry->value);
-  state->entries[state->num_entries].vtable = state->vtable;
-  ++state->num_entries;
-}
-
-grpc_mdstr_hash_table* grpc_method_config_table_convert(
-    grpc_exec_ctx* exec_ctx, const grpc_method_config_table* table,
-    void* (*convert_value)(const grpc_method_config* method_config),
-    const grpc_mdstr_hash_table_vtable* vtable) {
-  // Create an array of the entries in the table with converted values.
-  conversion_state state;
-  state.convert_value = convert_value;
-  state.vtable = vtable;
-  state.num_entries = 0;
-  state.entries = gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) *
-                             grpc_mdstr_hash_table_num_entries(table));
-  grpc_mdstr_hash_table_iterate(table, convert_entry, &state);
-  // Create a new table based on the array we just constructed.
-  grpc_mdstr_hash_table* new_table =
-      grpc_mdstr_hash_table_create(state.num_entries, state.entries);
-  // Clean up the array.
-  for (size_t i = 0; i < state.num_entries; ++i) {
-    GRPC_MDSTR_UNREF(exec_ctx, state.entries[i].key);
-    vtable->destroy_value(exec_ctx, state.entries[i].value);
-  }
-  gpr_free(state.entries);
-  // Return the new table.
-  return new_table;
-}
diff --git a/src/core/lib/transport/method_config.h b/src/core/lib/transport/method_config.h
deleted file mode 100644
index d17a493..0000000
--- a/src/core/lib/transport/method_config.h
+++ /dev/null
@@ -1,139 +0,0 @@
-//
-// Copyright 2016, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-
-#ifndef GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H
-#define GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H
-
-#include <stdbool.h>
-
-#include <grpc/impl/codegen/gpr_types.h>
-#include <grpc/impl/codegen/grpc_types.h>
-
-#include "src/core/lib/transport/mdstr_hash_table.h"
-#include "src/core/lib/transport/metadata.h"
-
-/// Per-method configuration.
-typedef struct grpc_method_config grpc_method_config;
-
-/// Creates a grpc_method_config with the specified parameters.
-/// Any parameter may be NULL to indicate that the value is unset.
-///
-/// \a wait_for_ready indicates whether the client should wait until the
-/// request deadline for the channel to become ready, even if there is a
-/// temporary failure before the deadline while attempting to connect.
-///
-/// \a timeout indicates the timeout for calls.
-///
-/// \a max_request_message_bytes and \a max_response_message_bytes
-/// indicate the maximum sizes of the request (checked when sending) and
-/// response (checked when receiving) messages.
-grpc_method_config* grpc_method_config_create(
-    bool* wait_for_ready, gpr_timespec* timeout,
-    int32_t* max_request_message_bytes, int32_t* max_response_message_bytes);
-
-grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config);
-void grpc_method_config_unref(grpc_exec_ctx* exec_ctx,
-                              grpc_method_config* method_config);
-
-/// Compares two grpc_method_configs.
-/// The sort order is stable but undefined.
-int grpc_method_config_cmp(const grpc_method_config* method_config1,
-                           const grpc_method_config* method_config2);
-
-/// These methods return NULL if the requested field is unset.
-/// The caller does NOT take ownership of the result.
-const bool* grpc_method_config_get_wait_for_ready(
-    const grpc_method_config* method_config);
-const gpr_timespec* grpc_method_config_get_timeout(
-    const grpc_method_config* method_config);
-const int32_t* grpc_method_config_get_max_request_message_bytes(
-    const grpc_method_config* method_config);
-const int32_t* grpc_method_config_get_max_response_message_bytes(
-    const grpc_method_config* method_config);
-
-/// A table of method configs.
-typedef grpc_mdstr_hash_table grpc_method_config_table;
-
-typedef struct grpc_method_config_table_entry {
-  /// The name is of one of the following forms:
-  ///   service/method -- specifies exact service and method name
-  ///   service/*      -- matches all methods for the specified service
-  grpc_mdstr* method_name;
-  grpc_method_config* method_config;
-} grpc_method_config_table_entry;
-
-/// Takes new references to all keys and values in \a entries.
-grpc_method_config_table* grpc_method_config_table_create(
-    size_t num_entries, grpc_method_config_table_entry* entries);
-
-grpc_method_config_table* grpc_method_config_table_ref(
-    grpc_method_config_table* table);
-void grpc_method_config_table_unref(grpc_exec_ctx* exec_ctx,
-                                    grpc_method_config_table* table);
-
-/// Compares two grpc_method_config_tables.
-/// The sort order is stable but undefined.
-int grpc_method_config_table_cmp(const grpc_method_config_table* table1,
-                                 const grpc_method_config_table* table2);
-
-/// 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.
-///
-/// Note: This returns a void* instead of a grpc_method_config* so that
-/// it can also be used for tables constructed via
-/// grpc_method_config_table_convert().
-void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx,
-                                   const grpc_mdstr_hash_table* table,
-                                   const grpc_mdstr* path);
-
-/// Returns a channel arg containing \a table.
-grpc_arg grpc_method_config_table_create_channel_arg(
-    grpc_method_config_table* table);
-
-/// Generates a new table from \a table whose values are converted to a
-/// new form via the \a convert_value function.  The new table will use
-/// \a vtable for its values.
-///
-/// This is generally used to convert the table's value type from
-/// grpc_method_config to a simple struct containing only the parameters
-/// relevant to a particular filter, thus avoiding the need for a hash
-/// table lookup on the fast path.  In that scenario, \a convert_value
-/// will return a new instance of the struct containing the values from
-/// the grpc_method_config, and \a vtable provides the methods for
-/// operating on the struct type.
-grpc_mdstr_hash_table* grpc_method_config_table_convert(
-    grpc_exec_ctx* exec_ctx, const grpc_method_config_table* table,
-    void* (*convert_value)(const grpc_method_config* method_config),
-    const grpc_mdstr_hash_table_vtable* vtable);
-
-#endif /* GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H */
diff --git a/src/core/lib/transport/service_config.c b/src/core/lib/transport/service_config.c
index 552d3ec..12da2a8 100644
--- a/src/core/lib/transport/service_config.c
+++ b/src/core/lib/transport/service_config.c
@@ -39,8 +39,10 @@
 #include <grpc/support/string_util.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"
 #include "src/core/lib/support/string.h"
-#include "src/core/lib/transport/mdstr_hash_table.h"
 
 // The main purpose of the code here is to parse the service config in
 // JSON form, which will look like this:
@@ -148,8 +150,8 @@
 static bool parse_json_method_config(
     grpc_exec_ctx* exec_ctx, grpc_json* json,
     void* (*create_value)(const grpc_json* method_config_json),
-    const grpc_mdstr_hash_table_vtable* vtable,
-    grpc_mdstr_hash_table_entry* entries, size_t* idx) {
+    const grpc_slice_hash_table_vtable* vtable,
+    grpc_slice_hash_table_entry* entries, size_t* idx) {
   // Construct value.
   void* method_config = create_value(json);
   if (method_config == NULL) return false;
@@ -170,7 +172,7 @@
   if (paths.count == 0) goto done;  // No names specified.
   // Add entry for each path.
   for (size_t i = 0; i < paths.count; ++i) {
-    entries[*idx].key = grpc_mdstr_from_string(paths.strs[i]);
+    entries[*idx].key = grpc_slice_from_copied_string(paths.strs[i]);
     entries[*idx].value = vtable->copy_value(method_config);
     entries[*idx].vtable = vtable;
     ++*idx;
@@ -182,15 +184,15 @@
   return success;
 }
 
-grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
+grpc_slice_hash_table* grpc_service_config_create_method_config_table(
     grpc_exec_ctx* exec_ctx, const grpc_service_config* service_config,
     void* (*create_value)(const grpc_json* method_config_json),
-    const grpc_mdstr_hash_table_vtable* vtable) {
+    const grpc_slice_hash_table_vtable* vtable) {
   const grpc_json* json = service_config->json_tree;
   // Traverse parsed JSON tree.
   if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return NULL;
   size_t num_entries = 0;
-  grpc_mdstr_hash_table_entry* entries = NULL;
+  grpc_slice_hash_table_entry* entries = NULL;
   for (grpc_json* field = json->child; field != NULL; field = field->next) {
     if (field->key == NULL) return NULL;
     if (strcmp(field->key, "methodConfig") == 0) {
@@ -202,7 +204,7 @@
         num_entries += count_names_in_method_config_json(method);
       }
       // Populate method config table entries.
-      entries = gpr_malloc(num_entries * sizeof(grpc_mdstr_hash_table_entry));
+      entries = gpr_malloc(num_entries * sizeof(grpc_slice_hash_table_entry));
       size_t idx = 0;
       for (grpc_json* method = field->child; method != NULL;
            method = method->next) {
@@ -215,12 +217,12 @@
     }
   }
   // Instantiate method config table.
-  grpc_mdstr_hash_table* method_config_table = NULL;
+  grpc_slice_hash_table* method_config_table = NULL;
   if (entries != NULL) {
-    method_config_table = grpc_mdstr_hash_table_create(num_entries, entries);
+    method_config_table = grpc_slice_hash_table_create(num_entries, entries);
     // Clean up.
     for (size_t i = 0; i < num_entries; ++i) {
-      GRPC_MDSTR_UNREF(exec_ctx, entries[i].key);
+      grpc_slice_unref_internal(exec_ctx, entries[i].key);
       vtable->destroy_value(exec_ctx, entries[i].value);
     }
     gpr_free(entries);
@@ -229,23 +231,24 @@
 }
 
 void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx,
-                                   const grpc_mdstr_hash_table* table,
-                                   const grpc_mdstr* path) {
-  void* value = grpc_mdstr_hash_table_get(table, path);
+                                   const grpc_slice_hash_table* table,
+                                   grpc_slice path) {
+  void* value = grpc_slice_hash_table_get(table, 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 == NULL) {
-    const char* path_str = grpc_mdstr_as_c_string(path);
+    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 = gpr_malloc(len + 2);  // '*' and NUL
     memcpy(buf, path_str, len);
     buf[len] = '*';
     buf[len + 1] = '\0';
-    grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf);
+    grpc_slice wildcard_path = grpc_slice_from_copied_string(buf);
     gpr_free(buf);
-    value = grpc_mdstr_hash_table_get(table, wildcard_path);
-    GRPC_MDSTR_UNREF(exec_ctx, wildcard_path);
+    value = grpc_slice_hash_table_get(table, wildcard_path);
+    grpc_slice_unref_internal(exec_ctx, wildcard_path);
+    gpr_free(path_str);
   }
   return value;
 }
diff --git a/src/core/lib/transport/service_config.h b/src/core/lib/transport/service_config.h
index f089717..cd739a5 100644
--- a/src/core/lib/transport/service_config.h
+++ b/src/core/lib/transport/service_config.h
@@ -35,7 +35,7 @@
 #include <grpc/impl/codegen/grpc_types.h>
 
 #include "src/core/lib/json/json.h"
-#include "src/core/lib/transport/mdstr_hash_table.h"
+#include "src/core/lib/slice/slice_hash_table.h"
 
 typedef struct grpc_service_config grpc_service_config;
 
@@ -53,10 +53,10 @@
 /// returned by \a create_value(), based on data parsed from the JSON tree.
 /// \a vtable provides methods used to manage the values.
 /// Returns NULL on error.
-grpc_mdstr_hash_table* grpc_service_config_create_method_config_table(
+grpc_slice_hash_table* grpc_service_config_create_method_config_table(
     grpc_exec_ctx* exec_ctx, const grpc_service_config* service_config,
     void* (*create_value)(const grpc_json* method_config_json),
-    const grpc_mdstr_hash_table_vtable* vtable);
+    const grpc_slice_hash_table_vtable* vtable);
 
 /// A helper function for looking up values in the table returned by
 /// \a grpc_service_config_create_method_config_table().
@@ -65,7 +65,7 @@
 /// Returns NULL if the method has no config.
 /// Caller does NOT own a reference to the result.
 void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx,
-                                   const grpc_mdstr_hash_table* table,
-                                   const grpc_mdstr* path);
+                                   const grpc_slice_hash_table* table,
+                                   grpc_slice path);
 
 #endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */
diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c
index 8b22592..750711b 100644
--- a/src/core/lib/transport/static_metadata.c
+++ b/src/core/lib/transport/static_metadata.c
@@ -41,120 +41,770 @@
 
 #include "src/core/lib/transport/static_metadata.h"
 
-grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
+#include "src/core/lib/slice/slice_internal.h"
 
-grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+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, 99,  111, 110, 116, 101, 110, 116, 45,  116, 121,
+    112, 101, 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, 117, 115, 101, 114, 45,  97,  103, 101, 110, 116, 104, 111, 115,
+    116, 108, 98,  45,  116, 111, 107, 101, 110, 108, 98,  45,  99,  111, 115,
+    116, 45,  98,  105, 110, 103, 114, 112, 99,  45,  116, 105, 109, 101, 111,
+    117, 116, 103, 114, 112, 99,  45,  116, 114, 97,  99,  105, 110, 103, 45,
+    98,  105, 110, 103, 114, 112, 99,  45,  115, 116, 97,  116, 115, 45,  98,
+    105, 110, 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, 48,  49,  50,  105, 100,
+    101, 110, 116, 105, 116, 121, 103, 122, 105, 112, 100, 101, 102, 108, 97,
+    116, 101, 116, 114, 97,  105, 108, 101, 114, 115, 97,  112, 112, 108, 105,
+    99,  97,  116, 105, 111, 110, 47,  103, 114, 112, 99,  80,  79,  83,  84,
+    50,  48,  48,  52,  48,  52,  104, 116, 116, 112, 104, 116, 116, 112, 115,
+    103, 114, 112, 99,  71,  69,  84,  80,  85,  84,  47,  47,  105, 110, 100,
+    101, 120, 46,  104, 116, 109, 108, 50,  48,  52,  50,  48,  54,  51,  48,
+    52,  52,  48,  48,  53,  48,  48,  97,  99,  99,  101, 112, 116, 45,  99,
+    104, 97,  114, 115, 101, 116, 97,  99,  99,  101, 112, 116, 45,  101, 110,
+    99,  111, 100, 105, 110, 103, 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,  101, 110, 99,  111, 100, 105, 110, 103, 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, 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(grpc_exec_ctx *exec_ctx, 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},
+};
+
+const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = {
+    {.refcount = &grpc_static_metadata_refcounts[0],
+     .data.refcounted = {g_bytes + 0, 5}},
+    {.refcount = &grpc_static_metadata_refcounts[1],
+     .data.refcounted = {g_bytes + 5, 7}},
+    {.refcount = &grpc_static_metadata_refcounts[2],
+     .data.refcounted = {g_bytes + 12, 7}},
+    {.refcount = &grpc_static_metadata_refcounts[3],
+     .data.refcounted = {g_bytes + 19, 10}},
+    {.refcount = &grpc_static_metadata_refcounts[4],
+     .data.refcounted = {g_bytes + 29, 7}},
+    {.refcount = &grpc_static_metadata_refcounts[5],
+     .data.refcounted = {g_bytes + 36, 2}},
+    {.refcount = &grpc_static_metadata_refcounts[6],
+     .data.refcounted = {g_bytes + 38, 12}},
+    {.refcount = &grpc_static_metadata_refcounts[7],
+     .data.refcounted = {g_bytes + 50, 11}},
+    {.refcount = &grpc_static_metadata_refcounts[8],
+     .data.refcounted = {g_bytes + 61, 16}},
+    {.refcount = &grpc_static_metadata_refcounts[9],
+     .data.refcounted = {g_bytes + 77, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[10],
+     .data.refcounted = {g_bytes + 90, 20}},
+    {.refcount = &grpc_static_metadata_refcounts[11],
+     .data.refcounted = {g_bytes + 110, 12}},
+    {.refcount = &grpc_static_metadata_refcounts[12],
+     .data.refcounted = {g_bytes + 122, 30}},
+    {.refcount = &grpc_static_metadata_refcounts[13],
+     .data.refcounted = {g_bytes + 152, 10}},
+    {.refcount = &grpc_static_metadata_refcounts[14],
+     .data.refcounted = {g_bytes + 162, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[15],
+     .data.refcounted = {g_bytes + 166, 8}},
+    {.refcount = &grpc_static_metadata_refcounts[16],
+     .data.refcounted = {g_bytes + 174, 11}},
+    {.refcount = &grpc_static_metadata_refcounts[17],
+     .data.refcounted = {g_bytes + 185, 12}},
+    {.refcount = &grpc_static_metadata_refcounts[18],
+     .data.refcounted = {g_bytes + 197, 16}},
+    {.refcount = &grpc_static_metadata_refcounts[19],
+     .data.refcounted = {g_bytes + 213, 14}},
+    {.refcount = &grpc_static_metadata_refcounts[20],
+     .data.refcounted = {g_bytes + 227, 0}},
+    {.refcount = &grpc_static_metadata_refcounts[21],
+     .data.refcounted = {g_bytes + 227, 19}},
+    {.refcount = &grpc_static_metadata_refcounts[22],
+     .data.refcounted = {g_bytes + 246, 12}},
+    {.refcount = &grpc_static_metadata_refcounts[23],
+     .data.refcounted = {g_bytes + 258, 30}},
+    {.refcount = &grpc_static_metadata_refcounts[24],
+     .data.refcounted = {g_bytes + 288, 31}},
+    {.refcount = &grpc_static_metadata_refcounts[25],
+     .data.refcounted = {g_bytes + 319, 36}},
+    {.refcount = &grpc_static_metadata_refcounts[26],
+     .data.refcounted = {g_bytes + 355, 1}},
+    {.refcount = &grpc_static_metadata_refcounts[27],
+     .data.refcounted = {g_bytes + 356, 1}},
+    {.refcount = &grpc_static_metadata_refcounts[28],
+     .data.refcounted = {g_bytes + 357, 1}},
+    {.refcount = &grpc_static_metadata_refcounts[29],
+     .data.refcounted = {g_bytes + 358, 8}},
+    {.refcount = &grpc_static_metadata_refcounts[30],
+     .data.refcounted = {g_bytes + 366, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[31],
+     .data.refcounted = {g_bytes + 370, 7}},
+    {.refcount = &grpc_static_metadata_refcounts[32],
+     .data.refcounted = {g_bytes + 377, 8}},
+    {.refcount = &grpc_static_metadata_refcounts[33],
+     .data.refcounted = {g_bytes + 385, 16}},
+    {.refcount = &grpc_static_metadata_refcounts[34],
+     .data.refcounted = {g_bytes + 401, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[35],
+     .data.refcounted = {g_bytes + 405, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[36],
+     .data.refcounted = {g_bytes + 408, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[37],
+     .data.refcounted = {g_bytes + 411, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[38],
+     .data.refcounted = {g_bytes + 415, 5}},
+    {.refcount = &grpc_static_metadata_refcounts[39],
+     .data.refcounted = {g_bytes + 420, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[40],
+     .data.refcounted = {g_bytes + 424, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[41],
+     .data.refcounted = {g_bytes + 427, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[42],
+     .data.refcounted = {g_bytes + 430, 1}},
+    {.refcount = &grpc_static_metadata_refcounts[43],
+     .data.refcounted = {g_bytes + 431, 11}},
+    {.refcount = &grpc_static_metadata_refcounts[44],
+     .data.refcounted = {g_bytes + 442, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[45],
+     .data.refcounted = {g_bytes + 445, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[46],
+     .data.refcounted = {g_bytes + 448, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[47],
+     .data.refcounted = {g_bytes + 451, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[48],
+     .data.refcounted = {g_bytes + 454, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[49],
+     .data.refcounted = {g_bytes + 457, 14}},
+    {.refcount = &grpc_static_metadata_refcounts[50],
+     .data.refcounted = {g_bytes + 471, 15}},
+    {.refcount = &grpc_static_metadata_refcounts[51],
+     .data.refcounted = {g_bytes + 486, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[52],
+     .data.refcounted = {g_bytes + 499, 15}},
+    {.refcount = &grpc_static_metadata_refcounts[53],
+     .data.refcounted = {g_bytes + 514, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[54],
+     .data.refcounted = {g_bytes + 527, 6}},
+    {.refcount = &grpc_static_metadata_refcounts[55],
+     .data.refcounted = {g_bytes + 533, 27}},
+    {.refcount = &grpc_static_metadata_refcounts[56],
+     .data.refcounted = {g_bytes + 560, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[57],
+     .data.refcounted = {g_bytes + 563, 5}},
+    {.refcount = &grpc_static_metadata_refcounts[58],
+     .data.refcounted = {g_bytes + 568, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[59],
+     .data.refcounted = {g_bytes + 581, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[60],
+     .data.refcounted = {g_bytes + 594, 19}},
+    {.refcount = &grpc_static_metadata_refcounts[61],
+     .data.refcounted = {g_bytes + 613, 16}},
+    {.refcount = &grpc_static_metadata_refcounts[62],
+     .data.refcounted = {g_bytes + 629, 16}},
+    {.refcount = &grpc_static_metadata_refcounts[63],
+     .data.refcounted = {g_bytes + 645, 14}},
+    {.refcount = &grpc_static_metadata_refcounts[64],
+     .data.refcounted = {g_bytes + 659, 16}},
+    {.refcount = &grpc_static_metadata_refcounts[65],
+     .data.refcounted = {g_bytes + 675, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[66],
+     .data.refcounted = {g_bytes + 688, 6}},
+    {.refcount = &grpc_static_metadata_refcounts[67],
+     .data.refcounted = {g_bytes + 694, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[68],
+     .data.refcounted = {g_bytes + 698, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[69],
+     .data.refcounted = {g_bytes + 702, 6}},
+    {.refcount = &grpc_static_metadata_refcounts[70],
+     .data.refcounted = {g_bytes + 708, 7}},
+    {.refcount = &grpc_static_metadata_refcounts[71],
+     .data.refcounted = {g_bytes + 715, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[72],
+     .data.refcounted = {g_bytes + 719, 8}},
+    {.refcount = &grpc_static_metadata_refcounts[73],
+     .data.refcounted = {g_bytes + 727, 17}},
+    {.refcount = &grpc_static_metadata_refcounts[74],
+     .data.refcounted = {g_bytes + 744, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[75],
+     .data.refcounted = {g_bytes + 757, 8}},
+    {.refcount = &grpc_static_metadata_refcounts[76],
+     .data.refcounted = {g_bytes + 765, 19}},
+    {.refcount = &grpc_static_metadata_refcounts[77],
+     .data.refcounted = {g_bytes + 784, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[78],
+     .data.refcounted = {g_bytes + 797, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[79],
+     .data.refcounted = {g_bytes + 801, 8}},
+    {.refcount = &grpc_static_metadata_refcounts[80],
+     .data.refcounted = {g_bytes + 809, 12}},
+    {.refcount = &grpc_static_metadata_refcounts[81],
+     .data.refcounted = {g_bytes + 821, 18}},
+    {.refcount = &grpc_static_metadata_refcounts[82],
+     .data.refcounted = {g_bytes + 839, 19}},
+    {.refcount = &grpc_static_metadata_refcounts[83],
+     .data.refcounted = {g_bytes + 858, 5}},
+    {.refcount = &grpc_static_metadata_refcounts[84],
+     .data.refcounted = {g_bytes + 863, 7}},
+    {.refcount = &grpc_static_metadata_refcounts[85],
+     .data.refcounted = {g_bytes + 870, 7}},
+    {.refcount = &grpc_static_metadata_refcounts[86],
+     .data.refcounted = {g_bytes + 877, 11}},
+    {.refcount = &grpc_static_metadata_refcounts[87],
+     .data.refcounted = {g_bytes + 888, 6}},
+    {.refcount = &grpc_static_metadata_refcounts[88],
+     .data.refcounted = {g_bytes + 894, 10}},
+    {.refcount = &grpc_static_metadata_refcounts[89],
+     .data.refcounted = {g_bytes + 904, 25}},
+    {.refcount = &grpc_static_metadata_refcounts[90],
+     .data.refcounted = {g_bytes + 929, 17}},
+    {.refcount = &grpc_static_metadata_refcounts[91],
+     .data.refcounted = {g_bytes + 946, 4}},
+    {.refcount = &grpc_static_metadata_refcounts[92],
+     .data.refcounted = {g_bytes + 950, 3}},
+    {.refcount = &grpc_static_metadata_refcounts[93],
+     .data.refcounted = {g_bytes + 953, 16}},
+    {.refcount = &grpc_static_metadata_refcounts[94],
+     .data.refcounted = {g_bytes + 969, 16}},
+    {.refcount = &grpc_static_metadata_refcounts[95],
+     .data.refcounted = {g_bytes + 985, 13}},
+    {.refcount = &grpc_static_metadata_refcounts[96],
+     .data.refcounted = {g_bytes + 998, 12}},
+    {.refcount = &grpc_static_metadata_refcounts[97],
+     .data.refcounted = {g_bytes + 1010, 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, 4, 8, 6, 2, 4, 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 6, 6, 8, 8};
 
-const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] =
-    {11, 33, 10, 33, 12, 33, 12, 50, 13, 33, 14, 33, 15, 33, 16, 33, 17, 33,
-     19, 33, 20, 33, 21, 33, 22, 33, 23, 33, 24, 33, 25, 33, 26, 33, 27, 33,
-     28, 18, 28, 33, 29, 33, 30, 33, 34, 33, 35, 33, 36, 33, 37, 33, 40, 31,
-     40, 32, 40, 49, 40, 54, 40, 55, 40, 56, 40, 57, 41, 31, 41, 49, 41, 54,
-     46, 0,  46, 1,  46, 2,  51, 33, 58, 33, 59, 33, 60, 33, 61, 33, 62, 33,
-     63, 33, 64, 33, 65, 33, 66, 33, 67, 33, 68, 33, 69, 38, 69, 71, 69, 74,
-     70, 82, 70, 83, 72, 33, 73, 33, 75, 33, 76, 33, 77, 33, 78, 33, 79, 39,
-     79, 52, 79, 53, 80, 33, 81, 33, 84, 3,  84, 4,  84, 5,  84, 6,  84, 7,
-     84, 8,  84, 9,  85, 33, 86, 87, 88, 33, 89, 33, 90, 33, 91, 33, 92, 33};
+static const int8_t elems_r[] = {
+    10, 8,   -3,  0,  9,   21,  -76, 22,  0,   10,  -7,  20, 0,  19, 18, 17,
+    16, 0,   0,   0,  0,   0,   0,   0,   0,   0,   0,   0,  0,  0,  0,  0,
+    0,  0,   0,   0,  0,   0,   0,   0,   0,   0,   0,   0,  0,  0,  0,  0,
+    0,  -49, -50, 16, -52, -53, -54, -54, -55, -56, -57, 0,  38, 37, 36, 35,
+    34, 33,  32,  31, 30,  29,  28,  27,  26,  25,  24,  23, 22, 21, 20, 19,
+    18, 17,  16,  15, 14,  13,  12,  15,  14,  13,  12,  11, 10, 9,  8,  0};
+static uint32_t elems_phash(uint32_t i) {
+  i -= 42;
+  uint32_t x = i % 96;
+  uint32_t y = i / 96;
+  uint32_t h = x;
+  if (y < GPR_ARRAY_SIZE(elems_r)) {
+    uint32_t delta = (uint32_t)elems_r[y];
+    h += delta;
+  }
+  return h;
+}
 
-const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
-    "0",
-    "1",
-    "2",
-    "200",
-    "204",
-    "206",
-    "304",
-    "400",
-    "404",
-    "500",
-    "accept",
-    "accept-charset",
-    "accept-encoding",
-    "accept-language",
-    "accept-ranges",
-    "access-control-allow-origin",
-    "age",
-    "allow",
-    "application/grpc",
-    ":authority",
-    "authorization",
-    "cache-control",
-    "content-disposition",
-    "content-encoding",
-    "content-language",
-    "content-length",
-    "content-location",
-    "content-range",
-    "content-type",
-    "cookie",
-    "date",
-    "deflate",
-    "deflate,gzip",
-    "",
-    "etag",
-    "expect",
-    "expires",
-    "from",
-    "GET",
-    "grpc",
-    "grpc-accept-encoding",
-    "grpc-encoding",
-    "grpc-internal-encoding-request",
-    "grpc-message",
-    "grpc-payload-bin",
-    "grpc-stats-bin",
-    "grpc-status",
-    "grpc-timeout",
-    "grpc-tracing-bin",
-    "gzip",
-    "gzip, deflate",
-    "host",
-    "http",
-    "https",
-    "identity",
-    "identity,deflate",
-    "identity,deflate,gzip",
-    "identity,gzip",
-    "if-match",
-    "if-modified-since",
-    "if-none-match",
-    "if-range",
-    "if-unmodified-since",
-    "last-modified",
-    "lb-cost-bin",
-    "lb-token",
-    "link",
-    "location",
-    "max-forwards",
-    ":method",
-    ":path",
-    "POST",
-    "proxy-authenticate",
-    "proxy-authorization",
-    "PUT",
-    "range",
-    "referer",
-    "refresh",
-    "retry-after",
-    ":scheme",
-    "server",
-    "set-cookie",
-    "/",
-    "/index.html",
-    ":status",
-    "strict-transport-security",
-    "te",
-    "trailers",
-    "transfer-encoding",
-    "user-agent",
-    "vary",
-    "via",
-    "www-authenticate"};
+static const uint16_t elem_keys[] = {
+    1009, 1010, 1011, 240,  241,  242,  243,  244,  138,  139,  42,   43,
+    429,  430,  431,  911,  912,  913,  712,  713,  1098, 522,  714,  1294,
+    1392, 1490, 1588, 4822, 4920, 4951, 5116, 5214, 5312, 1111, 5410, 5508,
+    5606, 5704, 5802, 5900, 5998, 6096, 6194, 6292, 6390, 6488, 6586, 6684,
+    6782, 6880, 6978, 7076, 7174, 7272, 7370, 7468, 7566, 7664, 7762, 7860,
+    7958, 8056, 8154, 8252, 8350, 1074, 1075, 1076, 1077, 8448, 8546, 8644,
+    8742, 8840, 8938, 9036, 9134, 314,  0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    132,  231,  232,  0,    0,    0,    0,    0,
+    0,    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[] = {
+    74,  77,  75,  19,  20,  21,  22,  23,  15,  16,  17,  18,  11,  12,  13,
+    3,   4,   5,   0,   1,   41,  6,   2,   70,  48,  55,  56,  24,  25,  26,
+    27,  28,  29,  7,   30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
+    42,  43,  44,  45,  46,  47,  49,  50,  51,  52,  53,  54,  57,  58,  59,
+    60,  61,  62,  63,  64,  76,  78,  79,  80,  65,  66,  67,  68,  69,  71,
+    72,  73,  14,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8,   9,   10};
 
-const uint8_t grpc_static_accept_encoding_metadata[8] = {0,  29, 26, 30,
-                                                         28, 32, 27, 31};
+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 * 98 + b);
+  uint32_t h = elems_phash(k);
+  return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k
+             ? 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] = {
+    {{.refcount = &grpc_static_metadata_refcounts[7],
+      .data.refcounted = {g_bytes + 50, 11}},
+     {.refcount = &grpc_static_metadata_refcounts[26],
+      .data.refcounted = {g_bytes + 355, 1}}},
+    {{.refcount = &grpc_static_metadata_refcounts[7],
+      .data.refcounted = {g_bytes + 50, 11}},
+     {.refcount = &grpc_static_metadata_refcounts[27],
+      .data.refcounted = {g_bytes + 356, 1}}},
+    {{.refcount = &grpc_static_metadata_refcounts[7],
+      .data.refcounted = {g_bytes + 50, 11}},
+     {.refcount = &grpc_static_metadata_refcounts[28],
+      .data.refcounted = {g_bytes + 357, 1}}},
+    {{.refcount = &grpc_static_metadata_refcounts[9],
+      .data.refcounted = {g_bytes + 77, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[29],
+      .data.refcounted = {g_bytes + 358, 8}}},
+    {{.refcount = &grpc_static_metadata_refcounts[9],
+      .data.refcounted = {g_bytes + 77, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[30],
+      .data.refcounted = {g_bytes + 366, 4}}},
+    {{.refcount = &grpc_static_metadata_refcounts[9],
+      .data.refcounted = {g_bytes + 77, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[31],
+      .data.refcounted = {g_bytes + 370, 7}}},
+    {{.refcount = &grpc_static_metadata_refcounts[5],
+      .data.refcounted = {g_bytes + 36, 2}},
+     {.refcount = &grpc_static_metadata_refcounts[32],
+      .data.refcounted = {g_bytes + 377, 8}}},
+    {{.refcount = &grpc_static_metadata_refcounts[11],
+      .data.refcounted = {g_bytes + 110, 12}},
+     {.refcount = &grpc_static_metadata_refcounts[33],
+      .data.refcounted = {g_bytes + 385, 16}}},
+    {{.refcount = &grpc_static_metadata_refcounts[1],
+      .data.refcounted = {g_bytes + 5, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[34],
+      .data.refcounted = {g_bytes + 401, 4}}},
+    {{.refcount = &grpc_static_metadata_refcounts[2],
+      .data.refcounted = {g_bytes + 12, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[35],
+      .data.refcounted = {g_bytes + 405, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[2],
+      .data.refcounted = {g_bytes + 12, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[36],
+      .data.refcounted = {g_bytes + 408, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[4],
+      .data.refcounted = {g_bytes + 29, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[37],
+      .data.refcounted = {g_bytes + 411, 4}}},
+    {{.refcount = &grpc_static_metadata_refcounts[4],
+      .data.refcounted = {g_bytes + 29, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[38],
+      .data.refcounted = {g_bytes + 415, 5}}},
+    {{.refcount = &grpc_static_metadata_refcounts[4],
+      .data.refcounted = {g_bytes + 29, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[39],
+      .data.refcounted = {g_bytes + 420, 4}}},
+    {{.refcount = &grpc_static_metadata_refcounts[3],
+      .data.refcounted = {g_bytes + 19, 10}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[1],
+      .data.refcounted = {g_bytes + 5, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[40],
+      .data.refcounted = {g_bytes + 424, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[1],
+      .data.refcounted = {g_bytes + 5, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[41],
+      .data.refcounted = {g_bytes + 427, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[0],
+      .data.refcounted = {g_bytes + 0, 5}},
+     {.refcount = &grpc_static_metadata_refcounts[42],
+      .data.refcounted = {g_bytes + 430, 1}}},
+    {{.refcount = &grpc_static_metadata_refcounts[0],
+      .data.refcounted = {g_bytes + 0, 5}},
+     {.refcount = &grpc_static_metadata_refcounts[43],
+      .data.refcounted = {g_bytes + 431, 11}}},
+    {{.refcount = &grpc_static_metadata_refcounts[2],
+      .data.refcounted = {g_bytes + 12, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[44],
+      .data.refcounted = {g_bytes + 442, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[2],
+      .data.refcounted = {g_bytes + 12, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[45],
+      .data.refcounted = {g_bytes + 445, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[2],
+      .data.refcounted = {g_bytes + 12, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[46],
+      .data.refcounted = {g_bytes + 448, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[2],
+      .data.refcounted = {g_bytes + 12, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[47],
+      .data.refcounted = {g_bytes + 451, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[2],
+      .data.refcounted = {g_bytes + 12, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[48],
+      .data.refcounted = {g_bytes + 454, 3}}},
+    {{.refcount = &grpc_static_metadata_refcounts[49],
+      .data.refcounted = {g_bytes + 457, 14}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[50],
+      .data.refcounted = {g_bytes + 471, 15}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[50],
+      .data.refcounted = {g_bytes + 471, 15}},
+     {.refcount = &grpc_static_metadata_refcounts[51],
+      .data.refcounted = {g_bytes + 486, 13}}},
+    {{.refcount = &grpc_static_metadata_refcounts[52],
+      .data.refcounted = {g_bytes + 499, 15}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[53],
+      .data.refcounted = {g_bytes + 514, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[54],
+      .data.refcounted = {g_bytes + 527, 6}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[55],
+      .data.refcounted = {g_bytes + 533, 27}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[56],
+      .data.refcounted = {g_bytes + 560, 3}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[57],
+      .data.refcounted = {g_bytes + 563, 5}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[58],
+      .data.refcounted = {g_bytes + 568, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[59],
+      .data.refcounted = {g_bytes + 581, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[60],
+      .data.refcounted = {g_bytes + 594, 19}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[61],
+      .data.refcounted = {g_bytes + 613, 16}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[62],
+      .data.refcounted = {g_bytes + 629, 16}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[63],
+      .data.refcounted = {g_bytes + 645, 14}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[64],
+      .data.refcounted = {g_bytes + 659, 16}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[65],
+      .data.refcounted = {g_bytes + 675, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[11],
+      .data.refcounted = {g_bytes + 110, 12}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[66],
+      .data.refcounted = {g_bytes + 688, 6}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[67],
+      .data.refcounted = {g_bytes + 694, 4}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[68],
+      .data.refcounted = {g_bytes + 698, 4}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[69],
+      .data.refcounted = {g_bytes + 702, 6}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[70],
+      .data.refcounted = {g_bytes + 708, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[71],
+      .data.refcounted = {g_bytes + 715, 4}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[14],
+      .data.refcounted = {g_bytes + 162, 4}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[72],
+      .data.refcounted = {g_bytes + 719, 8}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[73],
+      .data.refcounted = {g_bytes + 727, 17}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[74],
+      .data.refcounted = {g_bytes + 744, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[75],
+      .data.refcounted = {g_bytes + 757, 8}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[76],
+      .data.refcounted = {g_bytes + 765, 19}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[77],
+      .data.refcounted = {g_bytes + 784, 13}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[15],
+      .data.refcounted = {g_bytes + 166, 8}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[16],
+      .data.refcounted = {g_bytes + 174, 11}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[78],
+      .data.refcounted = {g_bytes + 797, 4}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[79],
+      .data.refcounted = {g_bytes + 801, 8}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[80],
+      .data.refcounted = {g_bytes + 809, 12}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[81],
+      .data.refcounted = {g_bytes + 821, 18}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[82],
+      .data.refcounted = {g_bytes + 839, 19}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[83],
+      .data.refcounted = {g_bytes + 858, 5}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[84],
+      .data.refcounted = {g_bytes + 863, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[85],
+      .data.refcounted = {g_bytes + 870, 7}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[86],
+      .data.refcounted = {g_bytes + 877, 11}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[87],
+      .data.refcounted = {g_bytes + 888, 6}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[88],
+      .data.refcounted = {g_bytes + 894, 10}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[89],
+      .data.refcounted = {g_bytes + 904, 25}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[90],
+      .data.refcounted = {g_bytes + 929, 17}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[13],
+      .data.refcounted = {g_bytes + 152, 10}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[91],
+      .data.refcounted = {g_bytes + 946, 4}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[92],
+      .data.refcounted = {g_bytes + 950, 3}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[93],
+      .data.refcounted = {g_bytes + 953, 16}},
+     {.refcount = &grpc_static_metadata_refcounts[20],
+      .data.refcounted = {g_bytes + 227, 0}}},
+    {{.refcount = &grpc_static_metadata_refcounts[10],
+      .data.refcounted = {g_bytes + 90, 20}},
+     {.refcount = &grpc_static_metadata_refcounts[29],
+      .data.refcounted = {g_bytes + 358, 8}}},
+    {{.refcount = &grpc_static_metadata_refcounts[10],
+      .data.refcounted = {g_bytes + 90, 20}},
+     {.refcount = &grpc_static_metadata_refcounts[31],
+      .data.refcounted = {g_bytes + 370, 7}}},
+    {{.refcount = &grpc_static_metadata_refcounts[10],
+      .data.refcounted = {g_bytes + 90, 20}},
+     {.refcount = &grpc_static_metadata_refcounts[94],
+      .data.refcounted = {g_bytes + 969, 16}}},
+    {{.refcount = &grpc_static_metadata_refcounts[10],
+      .data.refcounted = {g_bytes + 90, 20}},
+     {.refcount = &grpc_static_metadata_refcounts[30],
+      .data.refcounted = {g_bytes + 366, 4}}},
+    {{.refcount = &grpc_static_metadata_refcounts[10],
+      .data.refcounted = {g_bytes + 90, 20}},
+     {.refcount = &grpc_static_metadata_refcounts[95],
+      .data.refcounted = {g_bytes + 985, 13}}},
+    {{.refcount = &grpc_static_metadata_refcounts[10],
+      .data.refcounted = {g_bytes + 90, 20}},
+     {.refcount = &grpc_static_metadata_refcounts[96],
+      .data.refcounted = {g_bytes + 998, 12}}},
+    {{.refcount = &grpc_static_metadata_refcounts[10],
+      .data.refcounted = {g_bytes + 90, 20}},
+     {.refcount = &grpc_static_metadata_refcounts[97],
+      .data.refcounted = {g_bytes + 1010, 21}}},
+};
+const uint8_t grpc_static_accept_encoding_metadata[8] = {0,  74, 75, 76,
+                                                         77, 78, 79, 80};
diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h
index 28ad6f2..7649ccd 100644
--- a/src/core/lib/transport/static_metadata.h
+++ b/src/core/lib/transport/static_metadata.h
@@ -44,375 +44,521 @@
 
 #include "src/core/lib/transport/metadata.h"
 
-#define GRPC_STATIC_MDSTR_COUNT 93
-extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
-/* "0" */
-#define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0])
-/* "1" */
-#define GRPC_MDSTR_1 (&grpc_static_mdstr_table[1])
-/* "2" */
-#define GRPC_MDSTR_2 (&grpc_static_mdstr_table[2])
-/* "200" */
-#define GRPC_MDSTR_200 (&grpc_static_mdstr_table[3])
-/* "204" */
-#define GRPC_MDSTR_204 (&grpc_static_mdstr_table[4])
-/* "206" */
-#define GRPC_MDSTR_206 (&grpc_static_mdstr_table[5])
-/* "304" */
-#define GRPC_MDSTR_304 (&grpc_static_mdstr_table[6])
-/* "400" */
-#define GRPC_MDSTR_400 (&grpc_static_mdstr_table[7])
-/* "404" */
-#define GRPC_MDSTR_404 (&grpc_static_mdstr_table[8])
-/* "500" */
-#define GRPC_MDSTR_500 (&grpc_static_mdstr_table[9])
-/* "accept" */
-#define GRPC_MDSTR_ACCEPT (&grpc_static_mdstr_table[10])
-/* "accept-charset" */
-#define GRPC_MDSTR_ACCEPT_CHARSET (&grpc_static_mdstr_table[11])
-/* "accept-encoding" */
-#define GRPC_MDSTR_ACCEPT_ENCODING (&grpc_static_mdstr_table[12])
-/* "accept-language" */
-#define GRPC_MDSTR_ACCEPT_LANGUAGE (&grpc_static_mdstr_table[13])
-/* "accept-ranges" */
-#define GRPC_MDSTR_ACCEPT_RANGES (&grpc_static_mdstr_table[14])
-/* "access-control-allow-origin" */
-#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (&grpc_static_mdstr_table[15])
-/* "age" */
-#define GRPC_MDSTR_AGE (&grpc_static_mdstr_table[16])
-/* "allow" */
-#define GRPC_MDSTR_ALLOW (&grpc_static_mdstr_table[17])
-/* "application/grpc" */
-#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (&grpc_static_mdstr_table[18])
+#define GRPC_STATIC_MDSTR_COUNT 98
+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_mdstr_table[19])
-/* "authorization" */
-#define GRPC_MDSTR_AUTHORIZATION (&grpc_static_mdstr_table[20])
-/* "cache-control" */
-#define GRPC_MDSTR_CACHE_CONTROL (&grpc_static_mdstr_table[21])
-/* "content-disposition" */
-#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[22])
-/* "content-encoding" */
-#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[23])
-/* "content-language" */
-#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[24])
-/* "content-length" */
-#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[25])
-/* "content-location" */
-#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[26])
-/* "content-range" */
-#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[27])
-/* "content-type" */
-#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[28])
-/* "cookie" */
-#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[29])
-/* "date" */
-#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[30])
-/* "deflate" */
-#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[31])
-/* "deflate,gzip" */
-#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (&grpc_static_mdstr_table[32])
-/* "" */
-#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[33])
-/* "etag" */
-#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[34])
-/* "expect" */
-#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[35])
-/* "expires" */
-#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[36])
-/* "from" */
-#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[37])
-/* "GET" */
-#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[38])
-/* "grpc" */
-#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[39])
-/* "grpc-accept-encoding" */
-#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[40])
-/* "grpc-encoding" */
-#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[41])
-/* "grpc-internal-encoding-request" */
-#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[42])
+#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_mdstr_table[43])
-/* "grpc-payload-bin" */
-#define GRPC_MDSTR_GRPC_PAYLOAD_BIN (&grpc_static_mdstr_table[44])
-/* "grpc-stats-bin" */
-#define GRPC_MDSTR_GRPC_STATS_BIN (&grpc_static_mdstr_table[45])
+#define GRPC_MDSTR_GRPC_MESSAGE (grpc_static_slice_table[6])
 /* "grpc-status" */
-#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[46])
-/* "grpc-timeout" */
-#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[47])
-/* "grpc-tracing-bin" */
-#define GRPC_MDSTR_GRPC_TRACING_BIN (&grpc_static_mdstr_table[48])
-/* "gzip" */
-#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[49])
-/* "gzip, deflate" */
-#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[50])
+#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])
+/* "content-type" */
+#define GRPC_MDSTR_CONTENT_TYPE (grpc_static_slice_table[11])
+/* "grpc-internal-encoding-request" */
+#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (grpc_static_slice_table[12])
+/* "user-agent" */
+#define GRPC_MDSTR_USER_AGENT (grpc_static_slice_table[13])
 /* "host" */
-#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[51])
-/* "http" */
-#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[52])
-/* "https" */
-#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[53])
+#define GRPC_MDSTR_HOST (grpc_static_slice_table[14])
+/* "lb-token" */
+#define GRPC_MDSTR_LB_TOKEN (grpc_static_slice_table[15])
+/* "lb-cost-bin" */
+#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[16])
+/* "grpc-timeout" */
+#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[17])
+/* "grpc-tracing-bin" */
+#define GRPC_MDSTR_GRPC_TRACING_BIN (grpc_static_slice_table[18])
+/* "grpc-stats-bin" */
+#define GRPC_MDSTR_GRPC_STATS_BIN (grpc_static_slice_table[19])
+/* "" */
+#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[20])
+/* "grpc.wait_for_ready" */
+#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[21])
+/* "grpc.timeout" */
+#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[22])
+/* "grpc.max_request_message_bytes" */
+#define GRPC_MDSTR_GRPC_DOT_MAX_REQUEST_MESSAGE_BYTES \
+  (grpc_static_slice_table[23])
+/* "grpc.max_response_message_bytes" */
+#define GRPC_MDSTR_GRPC_DOT_MAX_RESPONSE_MESSAGE_BYTES \
+  (grpc_static_slice_table[24])
+/* "/grpc.lb.v1.LoadBalancer/BalanceLoad" */
+#define GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD \
+  (grpc_static_slice_table[25])
+/* "0" */
+#define GRPC_MDSTR_0 (grpc_static_slice_table[26])
+/* "1" */
+#define GRPC_MDSTR_1 (grpc_static_slice_table[27])
+/* "2" */
+#define GRPC_MDSTR_2 (grpc_static_slice_table[28])
 /* "identity" */
-#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[54])
+#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[29])
+/* "gzip" */
+#define GRPC_MDSTR_GZIP (grpc_static_slice_table[30])
+/* "deflate" */
+#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[31])
+/* "trailers" */
+#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[32])
+/* "application/grpc" */
+#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[33])
+/* "POST" */
+#define GRPC_MDSTR_POST (grpc_static_slice_table[34])
+/* "200" */
+#define GRPC_MDSTR_200 (grpc_static_slice_table[35])
+/* "404" */
+#define GRPC_MDSTR_404 (grpc_static_slice_table[36])
+/* "http" */
+#define GRPC_MDSTR_HTTP (grpc_static_slice_table[37])
+/* "https" */
+#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[38])
+/* "grpc" */
+#define GRPC_MDSTR_GRPC (grpc_static_slice_table[39])
+/* "GET" */
+#define GRPC_MDSTR_GET (grpc_static_slice_table[40])
+/* "PUT" */
+#define GRPC_MDSTR_PUT (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])
+/* "204" */
+#define GRPC_MDSTR_204 (grpc_static_slice_table[44])
+/* "206" */
+#define GRPC_MDSTR_206 (grpc_static_slice_table[45])
+/* "304" */
+#define GRPC_MDSTR_304 (grpc_static_slice_table[46])
+/* "400" */
+#define GRPC_MDSTR_400 (grpc_static_slice_table[47])
+/* "500" */
+#define GRPC_MDSTR_500 (grpc_static_slice_table[48])
+/* "accept-charset" */
+#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[49])
+/* "accept-encoding" */
+#define GRPC_MDSTR_ACCEPT_ENCODING (grpc_static_slice_table[50])
+/* "gzip, deflate" */
+#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[51])
+/* "accept-language" */
+#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[52])
+/* "accept-ranges" */
+#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[53])
+/* "accept" */
+#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[54])
+/* "access-control-allow-origin" */
+#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[55])
+/* "age" */
+#define GRPC_MDSTR_AGE (grpc_static_slice_table[56])
+/* "allow" */
+#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[57])
+/* "authorization" */
+#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[58])
+/* "cache-control" */
+#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[59])
+/* "content-disposition" */
+#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[60])
+/* "content-encoding" */
+#define GRPC_MDSTR_CONTENT_ENCODING (grpc_static_slice_table[61])
+/* "content-language" */
+#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[62])
+/* "content-length" */
+#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[63])
+/* "content-location" */
+#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[64])
+/* "content-range" */
+#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[65])
+/* "cookie" */
+#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[66])
+/* "date" */
+#define GRPC_MDSTR_DATE (grpc_static_slice_table[67])
+/* "etag" */
+#define GRPC_MDSTR_ETAG (grpc_static_slice_table[68])
+/* "expect" */
+#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[69])
+/* "expires" */
+#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[70])
+/* "from" */
+#define GRPC_MDSTR_FROM (grpc_static_slice_table[71])
+/* "if-match" */
+#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[72])
+/* "if-modified-since" */
+#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[73])
+/* "if-none-match" */
+#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[74])
+/* "if-range" */
+#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[75])
+/* "if-unmodified-since" */
+#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[76])
+/* "last-modified" */
+#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[77])
+/* "link" */
+#define GRPC_MDSTR_LINK (grpc_static_slice_table[78])
+/* "location" */
+#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[79])
+/* "max-forwards" */
+#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[80])
+/* "proxy-authenticate" */
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[81])
+/* "proxy-authorization" */
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[82])
+/* "range" */
+#define GRPC_MDSTR_RANGE (grpc_static_slice_table[83])
+/* "referer" */
+#define GRPC_MDSTR_REFERER (grpc_static_slice_table[84])
+/* "refresh" */
+#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[85])
+/* "retry-after" */
+#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[86])
+/* "server" */
+#define GRPC_MDSTR_SERVER (grpc_static_slice_table[87])
+/* "set-cookie" */
+#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[88])
+/* "strict-transport-security" */
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[89])
+/* "transfer-encoding" */
+#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[90])
+/* "vary" */
+#define GRPC_MDSTR_VARY (grpc_static_slice_table[91])
+/* "via" */
+#define GRPC_MDSTR_VIA (grpc_static_slice_table[92])
+/* "www-authenticate" */
+#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[93])
 /* "identity,deflate" */
-#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[55])
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[94])
+/* "identity,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[95])
+/* "deflate,gzip" */
+#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[96])
 /* "identity,deflate,gzip" */
 #define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
-  (&grpc_static_mdstr_table[56])
-/* "identity,gzip" */
-#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[57])
-/* "if-match" */
-#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[58])
-/* "if-modified-since" */
-#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[59])
-/* "if-none-match" */
-#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[60])
-/* "if-range" */
-#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[61])
-/* "if-unmodified-since" */
-#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[62])
-/* "last-modified" */
-#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[63])
-/* "lb-cost-bin" */
-#define GRPC_MDSTR_LB_COST_BIN (&grpc_static_mdstr_table[64])
-/* "lb-token" */
-#define GRPC_MDSTR_LB_TOKEN (&grpc_static_mdstr_table[65])
-/* "link" */
-#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[66])
-/* "location" */
-#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[67])
-/* "max-forwards" */
-#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[68])
-/* ":method" */
-#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[69])
-/* ":path" */
-#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[70])
-/* "POST" */
-#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[71])
-/* "proxy-authenticate" */
-#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[72])
-/* "proxy-authorization" */
-#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[73])
-/* "PUT" */
-#define GRPC_MDSTR_PUT (&grpc_static_mdstr_table[74])
-/* "range" */
-#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[75])
-/* "referer" */
-#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[76])
-/* "refresh" */
-#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[77])
-/* "retry-after" */
-#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[78])
-/* ":scheme" */
-#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[79])
-/* "server" */
-#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[80])
-/* "set-cookie" */
-#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[81])
-/* "/" */
-#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[82])
-/* "/index.html" */
-#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[83])
-/* ":status" */
-#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[84])
-/* "strict-transport-security" */
-#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[85])
-/* "te" */
-#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[86])
-/* "trailers" */
-#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[87])
-/* "transfer-encoding" */
-#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[88])
-/* "user-agent" */
-#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[89])
-/* "vary" */
-#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[90])
-/* "via" */
-#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[91])
-/* "www-authenticate" */
-#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[92])
+  (grpc_static_slice_table[97])
+
+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 81
-extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+extern grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
 extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
-/* "accept-charset": "" */
-#define GRPC_MDELEM_ACCEPT_CHARSET_EMPTY (&grpc_static_mdelem_table[0])
-/* "accept": "" */
-#define GRPC_MDELEM_ACCEPT_EMPTY (&grpc_static_mdelem_table[1])
-/* "accept-encoding": "" */
-#define GRPC_MDELEM_ACCEPT_ENCODING_EMPTY (&grpc_static_mdelem_table[2])
-/* "accept-encoding": "gzip, deflate" */
-#define GRPC_MDELEM_ACCEPT_ENCODING_GZIP_COMMA_DEFLATE \
-  (&grpc_static_mdelem_table[3])
-/* "accept-language": "" */
-#define GRPC_MDELEM_ACCEPT_LANGUAGE_EMPTY (&grpc_static_mdelem_table[4])
-/* "accept-ranges": "" */
-#define GRPC_MDELEM_ACCEPT_RANGES_EMPTY (&grpc_static_mdelem_table[5])
-/* "access-control-allow-origin": "" */
-#define GRPC_MDELEM_ACCESS_CONTROL_ALLOW_ORIGIN_EMPTY \
-  (&grpc_static_mdelem_table[6])
-/* "age": "" */
-#define GRPC_MDELEM_AGE_EMPTY (&grpc_static_mdelem_table[7])
-/* "allow": "" */
-#define GRPC_MDELEM_ALLOW_EMPTY (&grpc_static_mdelem_table[8])
-/* ":authority": "" */
-#define GRPC_MDELEM_AUTHORITY_EMPTY (&grpc_static_mdelem_table[9])
-/* "authorization": "" */
-#define GRPC_MDELEM_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[10])
-/* "cache-control": "" */
-#define GRPC_MDELEM_CACHE_CONTROL_EMPTY (&grpc_static_mdelem_table[11])
-/* "content-disposition": "" */
-#define GRPC_MDELEM_CONTENT_DISPOSITION_EMPTY (&grpc_static_mdelem_table[12])
-/* "content-encoding": "" */
-#define GRPC_MDELEM_CONTENT_ENCODING_EMPTY (&grpc_static_mdelem_table[13])
-/* "content-language": "" */
-#define GRPC_MDELEM_CONTENT_LANGUAGE_EMPTY (&grpc_static_mdelem_table[14])
-/* "content-length": "" */
-#define GRPC_MDELEM_CONTENT_LENGTH_EMPTY (&grpc_static_mdelem_table[15])
-/* "content-location": "" */
-#define GRPC_MDELEM_CONTENT_LOCATION_EMPTY (&grpc_static_mdelem_table[16])
-/* "content-range": "" */
-#define GRPC_MDELEM_CONTENT_RANGE_EMPTY (&grpc_static_mdelem_table[17])
+/* "grpc-status": "0" */
+#define GRPC_MDELEM_GRPC_STATUS_0 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[0], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-status": "1" */
+#define GRPC_MDELEM_GRPC_STATUS_1 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[1], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-status": "2" */
+#define GRPC_MDELEM_GRPC_STATUS_2 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[2], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-encoding": "identity" */
+#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[3], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-encoding": "gzip" */
+#define GRPC_MDELEM_GRPC_ENCODING_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[4], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-encoding": "deflate" */
+#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[5], GRPC_MDELEM_STORAGE_STATIC))
+/* "te": "trailers" */
+#define GRPC_MDELEM_TE_TRAILERS \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[6], GRPC_MDELEM_STORAGE_STATIC))
 /* "content-type": "application/grpc" */
 #define GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC \
-  (&grpc_static_mdelem_table[18])
-/* "content-type": "" */
-#define GRPC_MDELEM_CONTENT_TYPE_EMPTY (&grpc_static_mdelem_table[19])
-/* "cookie": "" */
-#define GRPC_MDELEM_COOKIE_EMPTY (&grpc_static_mdelem_table[20])
-/* "date": "" */
-#define GRPC_MDELEM_DATE_EMPTY (&grpc_static_mdelem_table[21])
-/* "etag": "" */
-#define GRPC_MDELEM_ETAG_EMPTY (&grpc_static_mdelem_table[22])
-/* "expect": "" */
-#define GRPC_MDELEM_EXPECT_EMPTY (&grpc_static_mdelem_table[23])
-/* "expires": "" */
-#define GRPC_MDELEM_EXPIRES_EMPTY (&grpc_static_mdelem_table[24])
-/* "from": "" */
-#define GRPC_MDELEM_FROM_EMPTY (&grpc_static_mdelem_table[25])
-/* "grpc-accept-encoding": "deflate" */
-#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE (&grpc_static_mdelem_table[26])
-/* "grpc-accept-encoding": "deflate,gzip" */
-#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE_COMMA_GZIP \
-  (&grpc_static_mdelem_table[27])
-/* "grpc-accept-encoding": "gzip" */
-#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_GZIP (&grpc_static_mdelem_table[28])
-/* "grpc-accept-encoding": "identity" */
-#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY \
-  (&grpc_static_mdelem_table[29])
-/* "grpc-accept-encoding": "identity,deflate" */
-#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE \
-  (&grpc_static_mdelem_table[30])
-/* "grpc-accept-encoding": "identity,deflate,gzip" */
-#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
-  (&grpc_static_mdelem_table[31])
-/* "grpc-accept-encoding": "identity,gzip" */
-#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_GZIP \
-  (&grpc_static_mdelem_table[32])
-/* "grpc-encoding": "deflate" */
-#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE (&grpc_static_mdelem_table[33])
-/* "grpc-encoding": "gzip" */
-#define GRPC_MDELEM_GRPC_ENCODING_GZIP (&grpc_static_mdelem_table[34])
-/* "grpc-encoding": "identity" */
-#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY (&grpc_static_mdelem_table[35])
-/* "grpc-status": "0" */
-#define GRPC_MDELEM_GRPC_STATUS_0 (&grpc_static_mdelem_table[36])
-/* "grpc-status": "1" */
-#define GRPC_MDELEM_GRPC_STATUS_1 (&grpc_static_mdelem_table[37])
-/* "grpc-status": "2" */
-#define GRPC_MDELEM_GRPC_STATUS_2 (&grpc_static_mdelem_table[38])
-/* "host": "" */
-#define GRPC_MDELEM_HOST_EMPTY (&grpc_static_mdelem_table[39])
-/* "if-match": "" */
-#define GRPC_MDELEM_IF_MATCH_EMPTY (&grpc_static_mdelem_table[40])
-/* "if-modified-since": "" */
-#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[41])
-/* "if-none-match": "" */
-#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY (&grpc_static_mdelem_table[42])
-/* "if-range": "" */
-#define GRPC_MDELEM_IF_RANGE_EMPTY (&grpc_static_mdelem_table[43])
-/* "if-unmodified-since": "" */
-#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[44])
-/* "last-modified": "" */
-#define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[45])
-/* "lb-cost-bin": "" */
-#define GRPC_MDELEM_LB_COST_BIN_EMPTY (&grpc_static_mdelem_table[46])
-/* "lb-token": "" */
-#define GRPC_MDELEM_LB_TOKEN_EMPTY (&grpc_static_mdelem_table[47])
-/* "link": "" */
-#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[48])
-/* "location": "" */
-#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[49])
-/* "max-forwards": "" */
-#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[50])
-/* ":method": "GET" */
-#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[51])
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[7], GRPC_MDELEM_STORAGE_STATIC))
 /* ":method": "POST" */
-#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[52])
-/* ":method": "PUT" */
-#define GRPC_MDELEM_METHOD_PUT (&grpc_static_mdelem_table[53])
-/* ":path": "/" */
-#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[54])
-/* ":path": "/index.html" */
-#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[55])
-/* "proxy-authenticate": "" */
-#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[56])
-/* "proxy-authorization": "" */
-#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[57])
-/* "range": "" */
-#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[58])
-/* "referer": "" */
-#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[59])
-/* "refresh": "" */
-#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[60])
-/* "retry-after": "" */
-#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[61])
-/* ":scheme": "grpc" */
-#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[62])
-/* ":scheme": "http" */
-#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[63])
-/* ":scheme": "https" */
-#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[64])
-/* "server": "" */
-#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[65])
-/* "set-cookie": "" */
-#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[66])
+#define GRPC_MDELEM_METHOD_POST \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[8], GRPC_MDELEM_STORAGE_STATIC))
 /* ":status": "200" */
-#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[67])
-/* ":status": "204" */
-#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[68])
-/* ":status": "206" */
-#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[69])
-/* ":status": "304" */
-#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[70])
-/* ":status": "400" */
-#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[71])
+#define GRPC_MDELEM_STATUS_200 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[9], GRPC_MDELEM_STORAGE_STATIC))
 /* ":status": "404" */
-#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[72])
+#define GRPC_MDELEM_STATUS_404 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[10], GRPC_MDELEM_STORAGE_STATIC))
+/* ":scheme": "http" */
+#define GRPC_MDELEM_SCHEME_HTTP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[11], GRPC_MDELEM_STORAGE_STATIC))
+/* ":scheme": "https" */
+#define GRPC_MDELEM_SCHEME_HTTPS \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[12], GRPC_MDELEM_STORAGE_STATIC))
+/* ":scheme": "grpc" */
+#define GRPC_MDELEM_SCHEME_GRPC \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[13], GRPC_MDELEM_STORAGE_STATIC))
+/* ":authority": "" */
+#define GRPC_MDELEM_AUTHORITY_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[14], GRPC_MDELEM_STORAGE_STATIC))
+/* ":method": "GET" */
+#define GRPC_MDELEM_METHOD_GET \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[15], GRPC_MDELEM_STORAGE_STATIC))
+/* ":method": "PUT" */
+#define GRPC_MDELEM_METHOD_PUT \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[16], GRPC_MDELEM_STORAGE_STATIC))
+/* ":path": "/" */
+#define GRPC_MDELEM_PATH_SLASH \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[17], GRPC_MDELEM_STORAGE_STATIC))
+/* ":path": "/index.html" */
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[18], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "204" */
+#define GRPC_MDELEM_STATUS_204 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[19], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "206" */
+#define GRPC_MDELEM_STATUS_206 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[20], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "304" */
+#define GRPC_MDELEM_STATUS_304 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[21], GRPC_MDELEM_STORAGE_STATIC))
+/* ":status": "400" */
+#define GRPC_MDELEM_STATUS_400 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[22], GRPC_MDELEM_STORAGE_STATIC))
 /* ":status": "500" */
-#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[73])
+#define GRPC_MDELEM_STATUS_500 \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[23], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-charset": "" */
+#define GRPC_MDELEM_ACCEPT_CHARSET_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[24], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-encoding": "" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[25], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-encoding": "gzip, deflate" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_GZIP_COMMA_DEFLATE \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[26], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-language": "" */
+#define GRPC_MDELEM_ACCEPT_LANGUAGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[27], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept-ranges": "" */
+#define GRPC_MDELEM_ACCEPT_RANGES_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[28], GRPC_MDELEM_STORAGE_STATIC))
+/* "accept": "" */
+#define GRPC_MDELEM_ACCEPT_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[29], GRPC_MDELEM_STORAGE_STATIC))
+/* "access-control-allow-origin": "" */
+#define GRPC_MDELEM_ACCESS_CONTROL_ALLOW_ORIGIN_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[30], GRPC_MDELEM_STORAGE_STATIC))
+/* "age": "" */
+#define GRPC_MDELEM_AGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[31], GRPC_MDELEM_STORAGE_STATIC))
+/* "allow": "" */
+#define GRPC_MDELEM_ALLOW_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[32], GRPC_MDELEM_STORAGE_STATIC))
+/* "authorization": "" */
+#define GRPC_MDELEM_AUTHORIZATION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[33], GRPC_MDELEM_STORAGE_STATIC))
+/* "cache-control": "" */
+#define GRPC_MDELEM_CACHE_CONTROL_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[34], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-disposition": "" */
+#define GRPC_MDELEM_CONTENT_DISPOSITION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[35], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-encoding": "" */
+#define GRPC_MDELEM_CONTENT_ENCODING_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[36], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-language": "" */
+#define GRPC_MDELEM_CONTENT_LANGUAGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[37], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-length": "" */
+#define GRPC_MDELEM_CONTENT_LENGTH_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[38], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-location": "" */
+#define GRPC_MDELEM_CONTENT_LOCATION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[39], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-range": "" */
+#define GRPC_MDELEM_CONTENT_RANGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[40], GRPC_MDELEM_STORAGE_STATIC))
+/* "content-type": "" */
+#define GRPC_MDELEM_CONTENT_TYPE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[41], GRPC_MDELEM_STORAGE_STATIC))
+/* "cookie": "" */
+#define GRPC_MDELEM_COOKIE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[42], GRPC_MDELEM_STORAGE_STATIC))
+/* "date": "" */
+#define GRPC_MDELEM_DATE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[43], GRPC_MDELEM_STORAGE_STATIC))
+/* "etag": "" */
+#define GRPC_MDELEM_ETAG_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[44], GRPC_MDELEM_STORAGE_STATIC))
+/* "expect": "" */
+#define GRPC_MDELEM_EXPECT_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[45], GRPC_MDELEM_STORAGE_STATIC))
+/* "expires": "" */
+#define GRPC_MDELEM_EXPIRES_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[46], GRPC_MDELEM_STORAGE_STATIC))
+/* "from": "" */
+#define GRPC_MDELEM_FROM_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[47], GRPC_MDELEM_STORAGE_STATIC))
+/* "host": "" */
+#define GRPC_MDELEM_HOST_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[48], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-match": "" */
+#define GRPC_MDELEM_IF_MATCH_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[49], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-modified-since": "" */
+#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[50], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-none-match": "" */
+#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[51], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-range": "" */
+#define GRPC_MDELEM_IF_RANGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[52], GRPC_MDELEM_STORAGE_STATIC))
+/* "if-unmodified-since": "" */
+#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[53], GRPC_MDELEM_STORAGE_STATIC))
+/* "last-modified": "" */
+#define GRPC_MDELEM_LAST_MODIFIED_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[54], GRPC_MDELEM_STORAGE_STATIC))
+/* "lb-token": "" */
+#define GRPC_MDELEM_LB_TOKEN_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[55], GRPC_MDELEM_STORAGE_STATIC))
+/* "lb-cost-bin": "" */
+#define GRPC_MDELEM_LB_COST_BIN_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[56], GRPC_MDELEM_STORAGE_STATIC))
+/* "link": "" */
+#define GRPC_MDELEM_LINK_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[57], GRPC_MDELEM_STORAGE_STATIC))
+/* "location": "" */
+#define GRPC_MDELEM_LOCATION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[58], GRPC_MDELEM_STORAGE_STATIC))
+/* "max-forwards": "" */
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[59], GRPC_MDELEM_STORAGE_STATIC))
+/* "proxy-authenticate": "" */
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[60], GRPC_MDELEM_STORAGE_STATIC))
+/* "proxy-authorization": "" */
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[61], GRPC_MDELEM_STORAGE_STATIC))
+/* "range": "" */
+#define GRPC_MDELEM_RANGE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[62], GRPC_MDELEM_STORAGE_STATIC))
+/* "referer": "" */
+#define GRPC_MDELEM_REFERER_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[63], GRPC_MDELEM_STORAGE_STATIC))
+/* "refresh": "" */
+#define GRPC_MDELEM_REFRESH_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[64], GRPC_MDELEM_STORAGE_STATIC))
+/* "retry-after": "" */
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[65], GRPC_MDELEM_STORAGE_STATIC))
+/* "server": "" */
+#define GRPC_MDELEM_SERVER_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[66], GRPC_MDELEM_STORAGE_STATIC))
+/* "set-cookie": "" */
+#define GRPC_MDELEM_SET_COOKIE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[67], GRPC_MDELEM_STORAGE_STATIC))
 /* "strict-transport-security": "" */
 #define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
-  (&grpc_static_mdelem_table[74])
-/* "te": "trailers" */
-#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[75])
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[68], GRPC_MDELEM_STORAGE_STATIC))
 /* "transfer-encoding": "" */
-#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[76])
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[69], GRPC_MDELEM_STORAGE_STATIC))
 /* "user-agent": "" */
-#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[77])
+#define GRPC_MDELEM_USER_AGENT_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[70], GRPC_MDELEM_STORAGE_STATIC))
 /* "vary": "" */
-#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[78])
+#define GRPC_MDELEM_VARY_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[71], GRPC_MDELEM_STORAGE_STATIC))
 /* "via": "" */
-#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[79])
+#define GRPC_MDELEM_VIA_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[72], GRPC_MDELEM_STORAGE_STATIC))
 /* "www-authenticate": "" */
-#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[80])
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[73], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "identity" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[74], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "deflate" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[75], 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[76], GRPC_MDELEM_STORAGE_STATIC))
+/* "grpc-accept-encoding": "gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_GZIP \
+  (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[77], 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[78], 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[79], 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[80], GRPC_MDELEM_STORAGE_STATIC))
 
-extern const uint8_t
-    grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
-extern const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];
+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_CONTENT_TYPE,
+  GRPC_BATCH_GRPC_INTERNAL_ENCODING_REQUEST,
+  GRPC_BATCH_USER_AGENT,
+  GRPC_BATCH_HOST,
+  GRPC_BATCH_LB_TOKEN,
+  GRPC_BATCH_LB_COST_BIN,
+  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 *content_type;
+    struct grpc_linked_mdelem *grpc_internal_encoding_request;
+    struct grpc_linked_mdelem *user_agent;
+    struct grpc_linked_mdelem *host;
+    struct grpc_linked_mdelem *lb_token;
+    struct grpc_linked_mdelem *lb_cost_bin;
+  } 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_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]])
+#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))
 #endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */
diff --git a/src/core/ext/transport/chttp2/transport/status_conversion.c b/src/core/lib/transport/status_conversion.c
similarity index 78%
rename from src/core/ext/transport/chttp2/transport/status_conversion.c
rename to src/core/lib/transport/status_conversion.c
index 5dce2f2..af0ac89 100644
--- a/src/core/ext/transport/chttp2/transport/status_conversion.c
+++ b/src/core/lib/transport/status_conversion.c
@@ -31,51 +31,51 @@
  *
  */
 
-#include "src/core/ext/transport/chttp2/transport/status_conversion.h"
+#include "src/core/lib/transport/status_conversion.h"
 
-int grpc_chttp2_grpc_status_to_http2_error(grpc_status_code status) {
+int grpc_status_to_http2_error(grpc_status_code status) {
   switch (status) {
     case GRPC_STATUS_OK:
-      return GRPC_CHTTP2_NO_ERROR;
+      return GRPC_HTTP2_NO_ERROR;
     case GRPC_STATUS_CANCELLED:
-      return GRPC_CHTTP2_CANCEL;
+      return GRPC_HTTP2_CANCEL;
     case GRPC_STATUS_DEADLINE_EXCEEDED:
-      return GRPC_CHTTP2_CANCEL;
+      return GRPC_HTTP2_CANCEL;
     case GRPC_STATUS_RESOURCE_EXHAUSTED:
-      return GRPC_CHTTP2_ENHANCE_YOUR_CALM;
+      return GRPC_HTTP2_ENHANCE_YOUR_CALM;
     case GRPC_STATUS_PERMISSION_DENIED:
-      return GRPC_CHTTP2_INADEQUATE_SECURITY;
+      return GRPC_HTTP2_INADEQUATE_SECURITY;
     case GRPC_STATUS_UNAVAILABLE:
-      return GRPC_CHTTP2_REFUSED_STREAM;
+      return GRPC_HTTP2_REFUSED_STREAM;
     default:
-      return GRPC_CHTTP2_INTERNAL_ERROR;
+      return GRPC_HTTP2_INTERNAL_ERROR;
   }
 }
 
-grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error, gpr_timespec deadline) {
+grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error,
+                                                 gpr_timespec deadline) {
   switch (error) {
-    case GRPC_CHTTP2_NO_ERROR:
+    case GRPC_HTTP2_NO_ERROR:
       /* should never be received */
       return GRPC_STATUS_INTERNAL;
-    case GRPC_CHTTP2_CANCEL:
+    case GRPC_HTTP2_CANCEL:
       /* http2 cancel translates to STATUS_CANCELLED iff deadline hasn't been
        * exceeded */
       return gpr_time_cmp(gpr_now(deadline.clock_type), deadline) >= 0
                  ? GRPC_STATUS_DEADLINE_EXCEEDED
                  : GRPC_STATUS_CANCELLED;
-    case GRPC_CHTTP2_ENHANCE_YOUR_CALM:
+    case GRPC_HTTP2_ENHANCE_YOUR_CALM:
       return GRPC_STATUS_RESOURCE_EXHAUSTED;
-    case GRPC_CHTTP2_INADEQUATE_SECURITY:
+    case GRPC_HTTP2_INADEQUATE_SECURITY:
       return GRPC_STATUS_PERMISSION_DENIED;
-    case GRPC_CHTTP2_REFUSED_STREAM:
+    case GRPC_HTTP2_REFUSED_STREAM:
       return GRPC_STATUS_UNAVAILABLE;
     default:
       return GRPC_STATUS_INTERNAL;
   }
 }
 
-grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status) {
+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:
@@ -110,6 +110,4 @@
   }
 }
 
-int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status) {
-  return 200;
-}
+int grpc_status_to_http2_status(grpc_status_code status) { return 200; }
diff --git a/src/core/ext/transport/chttp2/transport/status_conversion.h b/src/core/lib/transport/status_conversion.h
similarity index 73%
rename from src/core/ext/transport/chttp2/transport/status_conversion.h
rename to src/core/lib/transport/status_conversion.h
index 953bc9f..e6a23a6 100644
--- a/src/core/ext/transport/chttp2/transport/status_conversion.h
+++ b/src/core/lib/transport/status_conversion.h
@@ -31,20 +31,19 @@
  *
  */
 
-#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
-#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
+#ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H
+#define GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H
 
 #include <grpc/grpc.h>
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
+#include "src/core/lib/transport/http2_errors.h"
 
 /* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */
-grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error(
-    grpc_status_code status);
-grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error, gpr_timespec deadline);
+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,
+                                                 gpr_timespec deadline);
 
 /* Conversion of HTTP status codes (:status) to grpc status codes */
-grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status);
-int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status);
+grpc_status_code grpc_http2_status_to_grpc_status(int status);
+int grpc_status_to_http2_status(grpc_status_code status);
 
-#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H */
+#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H */
diff --git a/src/core/lib/transport/timeout_encoding.c b/src/core/lib/transport/timeout_encoding.c
index b58ebbd..0d4d7e5 100644
--- a/src/core/lib/transport/timeout_encoding.c
+++ b/src/core/lib/transport/timeout_encoding.c
@@ -131,20 +131,21 @@
   }
 }
 
-static int is_all_whitespace(const char *p) {
-  while (*p == ' ') p++;
-  return *p == 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(const char *buffer, gpr_timespec *timeout) {
+int grpc_http2_decode_timeout(grpc_slice text, gpr_timespec *timeout) {
   int32_t x = 0;
-  const uint8_t *p = (const uint8_t *)buffer;
+  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 == ' '; p++)
+  for (; p != end && *p == ' '; p++)
     ;
   /* decode numeric part */
-  for (; *p >= '0' && *p <= '9'; p++) {
+  for (; p != end && *p >= '0' && *p <= '9'; p++) {
     int32_t digit = (int32_t)(*p - (uint8_t)'0');
     have_digit = 1;
     /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */
@@ -158,8 +159,9 @@
   }
   if (!have_digit) return 0;
   /* skip whitespace */
-  for (; *p == ' '; p++)
+  for (; p != end && *p == ' '; p++)
     ;
+  if (p == end) return 0;
   /* decode unit specifier */
   switch (*p) {
     case 'n':
@@ -184,5 +186,5 @@
       return 0;
   }
   p++;
-  return is_all_whitespace((const char *)p);
+  return is_all_whitespace((const char *)p, (const char *)end);
 }
diff --git a/src/core/lib/transport/timeout_encoding.h b/src/core/lib/transport/timeout_encoding.h
index 92f02f6..4c8025d 100644
--- a/src/core/lib/transport/timeout_encoding.h
+++ b/src/core/lib/transport/timeout_encoding.h
@@ -34,7 +34,9 @@
 #ifndef GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
 #define GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H
 
+#include <grpc/slice.h>
 #include <grpc/support/time.h>
+
 #include "src/core/lib/support/string.h"
 
 #define GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1)
@@ -42,6 +44,6 @@
 /* Encode/decode timeouts to the GRPC over HTTP/2 format;
    encoding may round up arbitrarily */
 void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer);
-int grpc_http2_decode_timeout(const char *buffer, gpr_timespec *timeout);
+int grpc_http2_decode_timeout(grpc_slice text, gpr_timespec *timeout);
 
 #endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */
diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c
index 055edbb..004e748 100644
--- a/src/core/lib/transport/transport.c
+++ b/src/core/lib/transport/transport.c
@@ -40,6 +40,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/sync.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/support/string.h"
@@ -69,6 +70,16 @@
                        grpc_stream_refcount *refcount) {
 #endif
   if (gpr_unref(&refcount->refs)) {
+    if (exec_ctx->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_closure_sched(exec_ctx, &refcount->destroy, GRPC_ERROR_NONE);
   }
 }
@@ -173,93 +184,7 @@
   grpc_closure_sched(exec_ctx, op->recv_initial_metadata_ready,
                      GRPC_ERROR_REF(error));
   grpc_closure_sched(exec_ctx, op->on_complete, error);
-}
-
-typedef struct {
-  grpc_error *error;
-  grpc_closure *then_call;
-  grpc_closure closure;
-} close_message_data;
-
-static void free_message(grpc_exec_ctx *exec_ctx, void *p, grpc_error *error) {
-  close_message_data *cmd = p;
-  GRPC_ERROR_UNREF(cmd->error);
-  if (cmd->then_call != NULL) {
-    cmd->then_call->cb(exec_ctx, cmd->then_call->cb_arg, error);
-  }
-  gpr_free(cmd);
-}
-
-static void add_error(grpc_transport_stream_op *op, grpc_error **which,
-                      grpc_error *error) {
-  close_message_data *cmd;
-  cmd = gpr_malloc(sizeof(*cmd));
-  cmd->error = error;
-  cmd->then_call = op->on_complete;
-  grpc_closure_init(&cmd->closure, free_message, cmd,
-                    grpc_schedule_on_exec_ctx);
-  op->on_complete = &cmd->closure;
-  *which = error;
-}
-
-void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
-                                               grpc_status_code status) {
-  GPR_ASSERT(status != GRPC_STATUS_OK);
-  if (op->cancel_error == GRPC_ERROR_NONE) {
-    op->cancel_error = grpc_error_set_int(GRPC_ERROR_CANCELLED,
-                                          GRPC_ERROR_INT_GRPC_STATUS, status);
-    op->close_error = GRPC_ERROR_NONE;
-  }
-}
-
-void grpc_transport_stream_op_add_cancellation_with_message(
-    grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op,
-    grpc_status_code status, grpc_slice *optional_message) {
-  GPR_ASSERT(status != GRPC_STATUS_OK);
-  if (op->cancel_error != GRPC_ERROR_NONE) {
-    if (optional_message) {
-      grpc_slice_unref_internal(exec_ctx, *optional_message);
-    }
-    return;
-  }
-  grpc_error *error;
-  if (optional_message != NULL) {
-    char *msg = grpc_dump_slice(*optional_message, GPR_DUMP_ASCII);
-    error = grpc_error_set_str(GRPC_ERROR_CREATE(msg),
-                               GRPC_ERROR_STR_GRPC_MESSAGE, msg);
-    gpr_free(msg);
-    grpc_slice_unref_internal(exec_ctx, *optional_message);
-  } else {
-    error = GRPC_ERROR_CREATE("Call cancelled");
-  }
-  error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, status);
-  add_error(op, &op->cancel_error, error);
-}
-
-void grpc_transport_stream_op_add_close(grpc_exec_ctx *exec_ctx,
-                                        grpc_transport_stream_op *op,
-                                        grpc_status_code status,
-                                        grpc_slice *optional_message) {
-  GPR_ASSERT(status != GRPC_STATUS_OK);
-  if (op->cancel_error != GRPC_ERROR_NONE ||
-      op->close_error != GRPC_ERROR_NONE) {
-    if (optional_message) {
-      grpc_slice_unref_internal(exec_ctx, *optional_message);
-    }
-    return;
-  }
-  grpc_error *error;
-  if (optional_message != NULL) {
-    char *msg = grpc_dump_slice(*optional_message, GPR_DUMP_ASCII);
-    error = grpc_error_set_str(GRPC_ERROR_CREATE(msg),
-                               GRPC_ERROR_STR_GRPC_MESSAGE, msg);
-    gpr_free(msg);
-    grpc_slice_unref_internal(exec_ctx, *optional_message);
-  } else {
-    error = GRPC_ERROR_CREATE("Call force closed");
-  }
-  error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, status);
-  add_error(op, &op->close_error, error);
+  GRPC_ERROR_UNREF(op->cancel_error);
 }
 
 typedef struct {
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index d128183..9a0abe1 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -150,13 +150,18 @@
   /** Collect any stats into provided buffer, zero internal stat counters */
   grpc_transport_stream_stats *collect_stats;
 
-  /** If != GRPC_ERROR_NONE, cancel this stream */
+  /** If != GRPC_ERROR_NONE, 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. */
   grpc_error *cancel_error;
 
-  /** If != GRPC_ERROR_NONE, send grpc-status, grpc-message, and close this
-      stream for both reading and writing */
-  grpc_error *close_error;
-
   /* Indexes correspond to grpc_context_index enum values */
   grpc_call_context_element *context;
 
@@ -176,13 +181,8 @@
   grpc_connectivity_state *connectivity_state;
   /** should the transport be disconnected */
   grpc_error *disconnect_with_error;
-  /** should we send a goaway?
-      after a goaway is sent, once there are no more active calls on
-      the transport, the transport should disconnect */
-  bool send_goaway;
   /** what should the goaway contain? */
-  grpc_status_code goaway_status;
-  grpc_slice *goaway_message;
+  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
@@ -245,18 +245,6 @@
                                                   grpc_transport_stream_op *op,
                                                   grpc_error *error);
 
-void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
-                                               grpc_status_code status);
-
-void grpc_transport_stream_op_add_cancellation_with_message(
-    grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op,
-    grpc_status_code status, grpc_slice *optional_message);
-
-void grpc_transport_stream_op_add_close(grpc_exec_ctx *exec_ctx,
-                                        grpc_transport_stream_op *op,
-                                        grpc_status_code status,
-                                        grpc_slice *optional_message);
-
 char *grpc_transport_stream_op_string(grpc_transport_stream_op *op);
 char *grpc_transport_op_string(grpc_transport_op *op);
 
diff --git a/src/core/lib/transport/transport_op_string.c b/src/core/lib/transport/transport_op_string.c
index 58d6ad5..28360e3 100644
--- a/src/core/lib/transport/transport_op_string.c
+++ b/src/core/lib/transport/transport_op_string.c
@@ -47,14 +47,14 @@
 /* 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) {
+static void put_metadata(gpr_strvec *b, grpc_mdelem md) {
   gpr_strvec_add(b, gpr_strdup("key="));
   gpr_strvec_add(
-      b, grpc_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII));
+      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(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII));
+      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) {
@@ -121,15 +121,7 @@
     gpr_strvec_add(&b, gpr_strdup(" "));
     const char *msg = grpc_error_string(op->cancel_error);
     gpr_asprintf(&tmp, "CANCEL:%s", msg);
-    grpc_error_free_string(msg);
-    gpr_strvec_add(&b, tmp);
-  }
 
-  if (op->close_error != GRPC_ERROR_NONE) {
-    gpr_strvec_add(&b, gpr_strdup(" "));
-    const char *msg = grpc_error_string(op->close_error);
-    gpr_asprintf(&tmp, "CLOSE:%s", msg);
-    grpc_error_free_string(msg);
     gpr_strvec_add(&b, tmp);
   }
 
@@ -168,18 +160,14 @@
     const char *err = grpc_error_string(op->disconnect_with_error);
     gpr_asprintf(&tmp, "DISCONNECT:%s", err);
     gpr_strvec_add(&b, tmp);
-    grpc_error_free_string(err);
   }
 
-  if (op->send_goaway) {
+  if (op->goaway_error) {
     if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
     first = false;
-    char *msg = op->goaway_message == NULL
-                    ? "null"
-                    : grpc_dump_slice(*op->goaway_message,
-                                      GPR_DUMP_ASCII | GPR_DUMP_HEX);
-    gpr_asprintf(&tmp, "SEND_GOAWAY:status=%d:msg=%s", op->goaway_status, msg);
-    if (op->goaway_message != NULL) gpr_free(msg);
+    const char *msg = grpc_error_string(op->goaway_error);
+    gpr_asprintf(&tmp, "SEND_GOAWAY:%s", msg);
+
     gpr_strvec_add(&b, tmp);
   }
 
diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc
index 357d831..c985183 100644
--- a/src/cpp/client/channel_cc.cc
+++ b/src/cpp/client/channel_cc.cc
@@ -107,10 +107,20 @@
     } else if (!host_.empty()) {
       host_str = host_.c_str();
     }
-    c_call = grpc_channel_create_call(c_channel_, context->propagate_from_call_,
-                                      context->propagation_options_.c_bitmask(),
-                                      cq->cq(), method.name(), host_str,
-                                      context->raw_deadline(), nullptr);
+    grpc_slice method_slice = SliceFromCopiedString(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());
   context->set_call(c_call, shared_from_this());
diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc
index 269c523..25f6bab 100644
--- a/src/cpp/client/secure_credentials.cc
+++ b/src/cpp/client/secure_credentials.cc
@@ -206,15 +206,18 @@
   std::vector<grpc_metadata> md;
   for (auto it = metadata.begin(); it != metadata.end(); ++it) {
     grpc_metadata md_entry;
-    md_entry.key = it->first.c_str();
-    md_entry.value = it->second.data();
-    md_entry.value_length = it->second.size();
+    md_entry.key = SliceFromCopiedString(it->first);
+    md_entry.value = SliceFromCopiedString(it->second);
     md_entry.flags = 0;
     md.push_back(md_entry);
   }
   cb(user_data, md.empty() ? nullptr : &md[0], md.size(),
      static_cast<grpc_status_code>(status.error_code()),
      status.error_message().c_str());
+  for (auto it = md.begin(); it != md.end(); ++it) {
+    grpc_slice_unref(it->key);
+    grpc_slice_unref(it->value);
+  }
 }
 
 MetadataCredentialsPluginWrapper::MetadataCredentialsPluginWrapper(
diff --git a/src/cpp/client/secure_credentials.h b/src/cpp/client/secure_credentials.h
index 281db17..713654a 100644
--- a/src/cpp/client/secure_credentials.h
+++ b/src/cpp/client/secure_credentials.h
@@ -70,7 +70,7 @@
   grpc_call_credentials* const c_creds_;
 };
 
-class MetadataCredentialsPluginWrapper final {
+class MetadataCredentialsPluginWrapper final : private GrpcLibraryCodegen {
  public:
   static void Destroy(void* wrapper);
   static void GetMetadata(void* wrapper, grpc_auth_metadata_context context,
diff --git a/src/cpp/common/channel_filter.cc b/src/cpp/common/channel_filter.cc
index c0dc9dd..253614c 100644
--- a/src/cpp/common/channel_filter.cc
+++ b/src/cpp/common/channel_filter.cc
@@ -36,6 +36,8 @@
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/cpp/common/channel_filter.h"
 
+#include <grpc++/impl/codegen/slice.h>
+
 namespace grpc {
 
 // MetadataBatch
@@ -45,8 +47,10 @@
                                                const string &value) {
   grpc_linked_mdelem *storage = new grpc_linked_mdelem;
   memset(storage, 0, sizeof(grpc_linked_mdelem));
-  storage->md = grpc_mdelem_from_strings(exec_ctx, key.c_str(), value.c_str());
-  grpc_metadata_batch_link_head(batch_, storage);
+  storage->md = grpc_mdelem_from_slices(exec_ctx, SliceFromCopiedString(key),
+                                        SliceFromCopiedString(value));
+  GRPC_LOG_IF_ERROR("MetadataBatch::AddMetadata",
+                    grpc_metadata_batch_link_head(exec_ctx, batch_, storage));
   return storage;
 }
 
diff --git a/src/cpp/common/channel_filter.h b/src/cpp/common/channel_filter.h
index 5de8f5e..5f9fd87 100644
--- a/src/cpp/common/channel_filter.h
+++ b/src/cpp/common/channel_filter.h
@@ -76,8 +76,8 @@
   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 grpc_mdelem &operator*() const { return elem_->md; }
+    const grpc_mdelem operator->() const { return elem_->md; }
 
     const_iterator &operator++() {
       elem_ = elem_->next;
@@ -133,7 +133,7 @@
   grpc_error *disconnect_with_error() const {
     return op_->disconnect_with_error;
   }
-  bool send_goaway() const { return op_->send_goaway; }
+  bool send_goaway() const { return op_->goaway_error != GRPC_ERROR_NONE; }
 
   // TODO(roth): Add methods for additional fields as needed.
 
diff --git a/src/cpp/common/core_codegen.cc b/src/cpp/common/core_codegen.cc
index a07ad54..36e4c89 100644
--- a/src/cpp/common/core_codegen.cc
+++ b/src/cpp/common/core_codegen.cc
@@ -123,6 +123,17 @@
   return ::grpc_slice_split_tail(s, split);
 }
 
+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);
@@ -152,8 +163,10 @@
   return ::gpr_time_0(type);
 }
 
-void CoreCodegen::assert_fail(const char* failed_assertion) {
-  gpr_log(GPR_ERROR, "assertion failed: %s", failed_assertion);
+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();
 }
 
diff --git a/src/cpp/server/dynamic_thread_pool.cc b/src/cpp/server/dynamic_thread_pool.cc
index 1fdc2ed..afb5bea 100644
--- a/src/cpp/server/dynamic_thread_pool.cc
+++ b/src/cpp/server/dynamic_thread_pool.cc
@@ -31,12 +31,15 @@
  *
  */
 
+#include "src/cpp/server/dynamic_thread_pool.h"
+
 #include <mutex>
 #include <thread>
 
-#include "src/cpp/server/dynamic_thread_pool.h"
+#include <grpc/support/log.h>
 
 namespace grpc {
+
 DynamicThreadPool::DynamicThread::DynamicThread(DynamicThreadPool* pool)
     : pool_(pool),
       thd_(new std::thread(&DynamicThreadPool::DynamicThread::ThreadFunc,
diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
index 33bdc2a..10f662c 100644
--- a/src/cpp/server/secure_server_credentials.cc
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -35,11 +35,12 @@
 #include <map>
 #include <memory>
 
+#include <grpc++/impl/codegen/slice.h>
+#include <grpc++/security/auth_metadata_processor.h>
+
 #include "src/cpp/common/secure_auth_context.h"
 #include "src/cpp/server/secure_server_credentials.h"
 
-#include <grpc++/security/auth_metadata_processor.h>
-
 namespace grpc {
 
 void AuthMetadataProcessorAyncWrapper::Destroy(void* wrapper) {
@@ -71,8 +72,8 @@
     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(
-        md[i].key, grpc::string_ref(md[i].value, md[i].value_length)));
+    metadata.insert(std::make_pair(StringRefFromSlice(&md[i].key),
+                                   StringRefFromSlice(&md[i].value)));
   }
   SecureAuthContext context(ctx, false);
   AuthMetadataProcessor::OutputMetadata consumed_metadata;
@@ -85,9 +86,8 @@
   for (auto it = consumed_metadata.begin(); it != consumed_metadata.end();
        ++it) {
     grpc_metadata md_entry;
-    md_entry.key = it->first.c_str();
-    md_entry.value = it->second.data();
-    md_entry.value_length = it->second.size();
+    md_entry.key = SliceReferencingString(it->first);
+    md_entry.value = SliceReferencingString(it->second);
     md_entry.flags = 0;
     consumed_md.push_back(md_entry);
   }
@@ -95,9 +95,8 @@
   for (auto it = response_metadata.begin(); it != response_metadata.end();
        ++it) {
     grpc_metadata md_entry;
-    md_entry.key = it->first.c_str();
-    md_entry.value = it->second.data();
-    md_entry.value_length = it->second.size();
+    md_entry.key = SliceReferencingString(it->first);
+    md_entry.value = SliceReferencingString(it->second);
     md_entry.flags = 0;
     response_md.push_back(md_entry);
   }
diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc
index 817d85a..f6a7f1d 100644
--- a/src/cpp/server/server_cc.cc
+++ b/src/cpp/server/server_cc.cc
@@ -186,7 +186,7 @@
    public:
     explicit CallData(Server* server, SyncRequest* mrd)
         : cq_(mrd->cq_),
-          call_(mrd->call_, server, &cq_, server->max_receive_message_size_),
+          call_(mrd->call_, server, &cq_),
           ctx_(mrd->deadline_, mrd->request_metadata_.metadata,
                mrd->request_metadata_.count),
           has_request_payload_(mrd->has_request_payload_),
@@ -208,8 +208,8 @@
     void Run(std::shared_ptr<GlobalCallbacks> global_callbacks) {
       ctx_.BeginCompletionOp(&call_);
       global_callbacks->PreSynchronousRequest(&ctx_);
-      method_->handler()->RunHandler(MethodHandler::HandlerParameter(
-          &call_, &ctx_, request_payload_, call_.max_receive_message_size()));
+      method_->handler()->RunHandler(
+          MethodHandler::HandlerParameter(&call_, &ctx_, request_payload_));
       global_callbacks->PostSynchronousRequest(&ctx_);
       request_payload_ = nullptr;
       void* ignored_tag;
@@ -576,7 +576,6 @@
       delete_on_finalize_(delete_on_finalize),
       call_(nullptr) {
   call_cq_->RegisterAvalanching();  // This op will trigger more ops
-  memset(&initial_metadata_array_, 0, sizeof(initial_metadata_array_));
 }
 
 ServerInterface::BaseAsyncRequest::~BaseAsyncRequest() {
@@ -586,19 +585,11 @@
 bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag,
                                                        bool* status) {
   if (*status) {
-    for (size_t i = 0; i < initial_metadata_array_.count; i++) {
-      context_->client_metadata_.insert(
-          std::pair<grpc::string_ref, grpc::string_ref>(
-              initial_metadata_array_.metadata[i].key,
-              grpc::string_ref(
-                  initial_metadata_array_.metadata[i].value,
-                  initial_metadata_array_.metadata[i].value_length)));
-    }
+    context_->client_metadata_.FillMap();
   }
-  grpc_metadata_array_destroy(&initial_metadata_array_);
   context_->set_call(call_);
   context_->cq_ = call_cq_;
-  Call call(call_, server_, call_cq_, server_->max_receive_message_size());
+  Call call(call_, server_, call_cq_);
   if (*status && call_) {
     context_->BeginCompletionOp(&call);
   }
@@ -621,8 +612,8 @@
     ServerCompletionQueue* notification_cq) {
   grpc_server_request_registered_call(
       server_->server(), registered_method, &call_, &context_->deadline_,
-      &initial_metadata_array_, payload, call_cq_->cq(), notification_cq->cq(),
-      this);
+      context_->client_metadata_.arr(), payload, call_cq_->cq(),
+      notification_cq->cq(), this);
 }
 
 ServerInterface::GenericAsyncRequest::GenericAsyncRequest(
@@ -635,7 +626,7 @@
   GPR_ASSERT(notification_cq);
   GPR_ASSERT(call_cq);
   grpc_server_request_call(server->server(), &call_, &call_details_,
-                           &initial_metadata_array_, call_cq->cq(),
+                           context->client_metadata_.arr(), call_cq->cq(),
                            notification_cq->cq(), this);
 }
 
@@ -644,11 +635,12 @@
   // TODO(yangg) remove the copy here.
   if (*status) {
     static_cast<GenericServerContext*>(context_)->method_ =
-        call_details_.method;
-    static_cast<GenericServerContext*>(context_)->host_ = call_details_.host;
+        StringFromCopiedSlice(call_details_.method);
+    static_cast<GenericServerContext*>(context_)->host_ =
+        StringFromCopiedSlice(call_details_.host);
   }
-  gpr_free(call_details_.method);
-  gpr_free(call_details_.host);
+  grpc_slice_unref(call_details_.method);
+  grpc_slice_unref(call_details_.host);
   return BaseAsyncRequest::FinalizeResult(tag, status);
 }
 
diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc
index a66ec4a..a7aaa25 100644
--- a/src/cpp/server/server_context.cc
+++ b/src/cpp/server/server_context.cc
@@ -144,9 +144,10 @@
       sent_initial_metadata_(false),
       compression_level_set_(false) {
   for (size_t i = 0; i < metadata_count; i++) {
-    client_metadata_.insert(std::pair<grpc::string_ref, grpc::string_ref>(
-        metadata[i].key,
-        grpc::string_ref(metadata[i].value, metadata[i].value_length)));
+    client_metadata_.map()->insert(
+        std::pair<grpc::string_ref, grpc::string_ref>(
+            StringRefFromSlice(&metadata[i].key),
+            StringRefFromSlice(&metadata[i].value)));
   }
 }
 
diff --git a/src/cpp/test/server_context_test_spouse.cc b/src/cpp/test/server_context_test_spouse.cc
index b93152e..b812d16 100644
--- a/src/cpp/test/server_context_test_spouse.cc
+++ b/src/cpp/test/server_context_test_spouse.cc
@@ -40,11 +40,12 @@
                                                 const grpc::string& value) {
   client_metadata_storage_.insert(
       std::pair<grpc::string, grpc::string>(key, value));
-  ctx_->client_metadata_.clear();
+  ctx_->client_metadata_.map()->clear();
   for (auto iter = client_metadata_storage_.begin();
        iter != client_metadata_storage_.end(); ++iter) {
-    ctx_->client_metadata_.insert(std::pair<grpc::string_ref, grpc::string_ref>(
-        iter->first.c_str(), iter->second.c_str()));
+    ctx_->client_metadata_.map()->insert(
+        std::pair<grpc::string_ref, grpc::string_ref>(iter->first.c_str(),
+                                                      iter->second.c_str()));
   }
 }
 
diff --git a/src/cpp/util/slice_cc.cc b/src/cpp/util/slice_cc.cc
index c05f1cf..6efb68e 100644
--- a/src/cpp/util/slice_cc.cc
+++ b/src/cpp/util/slice_cc.cc
@@ -35,7 +35,7 @@
 
 namespace grpc {
 
-Slice::Slice() : slice_(gpr_empty_slice()) {}
+Slice::Slice() : slice_(grpc_empty_slice()) {}
 
 Slice::~Slice() { grpc_slice_unref(slice_); }
 
diff --git a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
index 26449ee..0e4a77b 100644
--- a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
@@ -71,7 +71,9 @@
         // Gets data of recv_status_on_client completion.
         public ClientSideStatus GetReceivedStatusOnClient()
         {
-            string details = Marshal.PtrToStringAnsi(Native.grpcsharp_batch_context_recv_status_on_client_details(this));
+            UIntPtr detailsLength;
+            IntPtr detailsPtr = Native.grpcsharp_batch_context_recv_status_on_client_details(this, out detailsLength);
+            string details = Marshal.PtrToStringAnsi(detailsPtr, (int) detailsLength.ToUInt32());
             var status = new Status(Native.grpcsharp_batch_context_recv_status_on_client_status(this), details);
 
             IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_recv_status_on_client_trailing_metadata(this);
diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
index 05dda5b..d5b87a6 100644
--- a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
@@ -79,9 +79,13 @@
             for (ulong i = 0; i < count; i++)
             {
                 var index = new UIntPtr(i);
-                string key = Marshal.PtrToStringAnsi(Native.grpcsharp_metadata_array_get_key(metadataArray, index));
-                var bytes = new byte[Native.grpcsharp_metadata_array_get_value_length(metadataArray, index).ToUInt64()];
-                Marshal.Copy(Native.grpcsharp_metadata_array_get_value(metadataArray, index), bytes, 0, bytes.Length);
+                UIntPtr keyLen;
+                IntPtr keyPtr = Native.grpcsharp_metadata_array_get_key(metadataArray, index, out keyLen);
+                string key = Marshal.PtrToStringAnsi(keyPtr, (int)keyLen.ToUInt32());
+                UIntPtr valueLen;
+                IntPtr valuePtr = Native.grpcsharp_metadata_array_get_value(metadataArray, index, out valueLen);
+                var bytes = new byte[valueLen.ToUInt64()];
+                Marshal.Copy(valuePtr, bytes, 0, bytes.Length);
                 metadata.Add(Metadata.Entry.CreateUnsafe(key, bytes));
             }
             return metadata;
diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.cs
index ce38e37..2f37707 100644
--- a/src/csharp/Grpc.Core/Internal/NativeMethods.cs
+++ b/src/csharp/Grpc.Core/Internal/NativeMethods.cs
@@ -128,7 +128,6 @@
         public readonly Delegates.grpcsharp_metadata_array_count_delegate grpcsharp_metadata_array_count;
         public readonly Delegates.grpcsharp_metadata_array_get_key_delegate grpcsharp_metadata_array_get_key;
         public readonly Delegates.grpcsharp_metadata_array_get_value_delegate grpcsharp_metadata_array_get_value;
-        public readonly Delegates.grpcsharp_metadata_array_get_value_length_delegate grpcsharp_metadata_array_get_value_length;
         public readonly Delegates.grpcsharp_metadata_array_destroy_full_delegate grpcsharp_metadata_array_destroy_full;
 
         public readonly Delegates.grpcsharp_redirect_log_delegate grpcsharp_redirect_log;
@@ -237,7 +236,6 @@
             this.grpcsharp_metadata_array_count = GetMethodDelegate<Delegates.grpcsharp_metadata_array_count_delegate>(library);
             this.grpcsharp_metadata_array_get_key = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_key_delegate>(library);
             this.grpcsharp_metadata_array_get_value = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_value_delegate>(library);
-            this.grpcsharp_metadata_array_get_value_length = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_value_length_delegate>(library);
             this.grpcsharp_metadata_array_destroy_full = GetMethodDelegate<Delegates.grpcsharp_metadata_array_destroy_full_delegate>(library);
 
             this.grpcsharp_redirect_log = GetMethodDelegate<Delegates.grpcsharp_redirect_log_delegate>(library);
@@ -306,15 +304,15 @@
             public delegate IntPtr grpcsharp_batch_context_recv_message_length_delegate(BatchContextSafeHandle ctx);
             public delegate void grpcsharp_batch_context_recv_message_to_buffer_delegate(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
             public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx);
-            public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx);  // returns const char*
+            public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx, out UIntPtr detailsLength);
             public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx);
             public delegate int grpcsharp_batch_context_recv_close_on_server_cancelled_delegate(BatchContextSafeHandle ctx);
             public delegate void grpcsharp_batch_context_destroy_delegate(IntPtr ctx);
 
             public delegate RequestCallContextSafeHandle grpcsharp_request_call_context_create_delegate();
             public delegate CallSafeHandle grpcsharp_request_call_context_call_delegate(RequestCallContextSafeHandle ctx);
-            public delegate IntPtr grpcsharp_request_call_context_method_delegate(RequestCallContextSafeHandle ctx);  // returns const char*
-            public delegate IntPtr grpcsharp_request_call_context_host_delegate(RequestCallContextSafeHandle ctx);  // returns const char*
+            public delegate IntPtr grpcsharp_request_call_context_method_delegate(RequestCallContextSafeHandle ctx, out UIntPtr methodLength);
+            public delegate IntPtr grpcsharp_request_call_context_host_delegate(RequestCallContextSafeHandle ctx, out UIntPtr hostLength);
             public delegate Timespec grpcsharp_request_call_context_deadline_delegate(RequestCallContextSafeHandle ctx);
             public delegate IntPtr grpcsharp_request_call_context_request_metadata_delegate(RequestCallContextSafeHandle ctx);
             public delegate void grpcsharp_request_call_context_destroy_delegate(IntPtr ctx);
@@ -384,9 +382,8 @@
             public delegate MetadataArraySafeHandle grpcsharp_metadata_array_create_delegate(UIntPtr capacity);
             public delegate void grpcsharp_metadata_array_add_delegate(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength);
             public delegate UIntPtr grpcsharp_metadata_array_count_delegate(IntPtr metadataArray);
-            public delegate IntPtr grpcsharp_metadata_array_get_key_delegate(IntPtr metadataArray, UIntPtr index);
-            public delegate IntPtr grpcsharp_metadata_array_get_value_delegate(IntPtr metadataArray, UIntPtr index);
-            public delegate UIntPtr grpcsharp_metadata_array_get_value_length_delegate(IntPtr metadataArray, UIntPtr index);
+            public delegate IntPtr grpcsharp_metadata_array_get_key_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr keyLength);
+            public delegate IntPtr grpcsharp_metadata_array_get_value_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr valueLength);
             public delegate void grpcsharp_metadata_array_destroy_full_delegate(IntPtr array);
 
             public delegate void grpcsharp_redirect_log_delegate(GprLogDelegate callback);
diff --git a/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs
index ea7819d..c1560bc 100644
--- a/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs
@@ -66,8 +66,14 @@
         {
             var call = Native.grpcsharp_request_call_context_call(this);
 
-            var method = Marshal.PtrToStringAnsi(Native.grpcsharp_request_call_context_method(this));
-            var host = Marshal.PtrToStringAnsi(Native.grpcsharp_request_call_context_host(this));
+            UIntPtr methodLen;
+            IntPtr methodPtr = Native.grpcsharp_request_call_context_method(this, out methodLen);
+            var method = Marshal.PtrToStringAnsi(methodPtr, (int) methodLen.ToUInt32());
+
+            UIntPtr hostLen;
+            IntPtr hostPtr = Native.grpcsharp_request_call_context_host(this, out hostLen);
+            var host = Marshal.PtrToStringAnsi(hostPtr, (int) hostLen.ToUInt32());
+
             var deadline = Native.grpcsharp_request_call_context_deadline(this);
 
             IntPtr metadataArrayPtr = Native.grpcsharp_request_call_context_request_metadata(this);
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 946f587..e308b02 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -73,15 +73,13 @@
   grpc_byte_buffer *send_message;
   struct {
     grpc_metadata_array trailing_metadata;
-    char *status_details;
   } send_status_from_server;
   grpc_metadata_array recv_initial_metadata;
   grpc_byte_buffer *recv_message;
   struct {
     grpc_metadata_array trailing_metadata;
     grpc_status_code status;
-    char *status_details;
-    size_t status_details_capacity;
+    grpc_slice status_details;
   } recv_status_on_client;
   int recv_close_on_server_cancelled;
 } grpcsharp_batch_context;
@@ -122,8 +120,8 @@
   size_t i;
   if (array->metadata) {
     for (i = 0; i < array->count; i++) {
-      gpr_free((void *)array->metadata[i].key);
-      gpr_free((void *)array->metadata[i].value);
+      grpc_slice_unref(array->metadata[i].key);
+      grpc_slice_unref(array->metadata[i].value);
     }
   }
   gpr_free(array->metadata);
@@ -167,10 +165,8 @@
                              const char *value, size_t value_length) {
   size_t i = array->count;
   GPR_ASSERT(array->count < array->capacity);
-  array->metadata[i].key = gpr_strdup(key);
-  array->metadata[i].value = (char *)gpr_malloc(value_length);
-  memcpy((void *)array->metadata[i].value, value, value_length);
-  array->metadata[i].value_length = value_length;
+  array->metadata[i].key = grpc_slice_from_copied_string(key);
+  array->metadata[i].value = grpc_slice_from_copied_buffer(value, value_length);
   array->count++;
 }
 
@@ -180,21 +176,17 @@
 }
 
 GPR_EXPORT const char *GPR_CALLTYPE
-grpcsharp_metadata_array_get_key(grpc_metadata_array *array, size_t index) {
+grpcsharp_metadata_array_get_key(grpc_metadata_array *array, size_t index, size_t *key_length) {
   GPR_ASSERT(index < array->count);
-  return array->metadata[index].key;
+  *key_length = GRPC_SLICE_LENGTH(array->metadata[index].key);
+  return (char *)GRPC_SLICE_START_PTR(array->metadata[index].key);
 }
 
 GPR_EXPORT const char *GPR_CALLTYPE
-grpcsharp_metadata_array_get_value(grpc_metadata_array *array, size_t index) {
+grpcsharp_metadata_array_get_value(grpc_metadata_array *array, size_t index, size_t *value_length) {
   GPR_ASSERT(index < array->count);
-  return array->metadata[index].value;
-}
-
-GPR_EXPORT intptr_t GPR_CALLTYPE grpcsharp_metadata_array_get_value_length(
-    grpc_metadata_array *array, size_t index) {
-  GPR_ASSERT(index < array->count);
-  return (intptr_t)array->metadata[index].value_length;
+  *value_length = GRPC_SLICE_LENGTH(array->metadata[index].value);
+  return (char *)GRPC_SLICE_START_PTR(array->metadata[index].value);
 }
 
 /* Move contents of metadata array */
@@ -227,7 +219,6 @@
 
   grpcsharp_metadata_array_destroy_metadata_including_entries(
       &(ctx->send_status_from_server.trailing_metadata));
-  gpr_free(ctx->send_status_from_server.status_details);
 
   grpcsharp_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata));
 
@@ -235,7 +226,7 @@
 
   grpcsharp_metadata_array_destroy_metadata_only(
       &(ctx->recv_status_on_client.trailing_metadata));
-  gpr_free((void *)ctx->recv_status_on_client.status_details);
+  grpc_slice_unref(ctx->recv_status_on_client.status_details);
 
   gpr_free(ctx);
 }
@@ -307,8 +298,9 @@
 
 GPR_EXPORT const char *GPR_CALLTYPE
 grpcsharp_batch_context_recv_status_on_client_details(
-    const grpcsharp_batch_context *ctx) {
-  return ctx->recv_status_on_client.status_details;
+    const grpcsharp_batch_context *ctx, size_t *details_length) {
+  *details_length = GRPC_SLICE_LENGTH(ctx->recv_status_on_client.status_details);
+  return (char *)GRPC_SLICE_START_PTR(ctx->recv_status_on_client.status_details);
 }
 
 GPR_EXPORT const grpc_metadata_array *GPR_CALLTYPE
@@ -324,13 +316,15 @@
 
 GPR_EXPORT const char *GPR_CALLTYPE
 grpcsharp_request_call_context_method(
-    const grpcsharp_request_call_context *ctx) {
-  return ctx->call_details.method;
+    const grpcsharp_request_call_context *ctx, size_t *method_length) {
+  *method_length = GRPC_SLICE_LENGTH(ctx->call_details.method);
+  return (char *)GRPC_SLICE_START_PTR(ctx->call_details.method);
 }
 
 GPR_EXPORT const char *GPR_CALLTYPE grpcsharp_request_call_context_host(
-    const grpcsharp_request_call_context *ctx) {
-  return ctx->call_details.host;
+    const grpcsharp_request_call_context *ctx, size_t *host_length) {
+  *host_length = GRPC_SLICE_LENGTH(ctx->call_details.host);
+  return (char *)GRPC_SLICE_START_PTR(ctx->call_details.host);
 }
 
 GPR_EXPORT gpr_timespec GPR_CALLTYPE
@@ -404,8 +398,15 @@
                               grpc_completion_queue *cq,
                               const char *method, const char *host,
                               gpr_timespec deadline) {
+  grpc_slice method_slice = grpc_slice_from_copied_string(method);
+  grpc_slice *host_slice_ptr = NULL;
+  grpc_slice host_slice;
+  if (host != NULL) {
+    host_slice = grpc_slice_from_copied_string(host);
+    host_slice_ptr = &host_slice;
+  }
   return grpc_channel_create_call(channel, parent_call, propagation_mask, cq,
-                                  method, host, deadline, NULL);
+                                  method_slice, host_slice_ptr, deadline, NULL);
 }
 
 GPR_EXPORT grpc_connectivity_state GPR_CALLTYPE
@@ -537,7 +538,7 @@
 
   ops[1].op = GRPC_OP_SEND_MESSAGE;
   ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
-  ops[1].data.send_message = ctx->send_message;
+  ops[1].data.send_message.send_message = ctx->send_message;
   ops[1].flags = write_flags;
   ops[1].reserved = NULL;
 
@@ -546,12 +547,13 @@
   ops[2].reserved = NULL;
 
   ops[3].op = GRPC_OP_RECV_INITIAL_METADATA;
-  ops[3].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
+  ops[3].data.recv_initial_metadata.recv_initial_metadata =
+      &(ctx->recv_initial_metadata);
   ops[3].flags = 0;
   ops[3].reserved = NULL;
 
   ops[4].op = GRPC_OP_RECV_MESSAGE;
-  ops[4].data.recv_message = &(ctx->recv_message);
+  ops[4].data.recv_message.recv_message = &(ctx->recv_message);
   ops[4].flags = 0;
   ops[4].reserved = NULL;
 
@@ -560,11 +562,8 @@
       &(ctx->recv_status_on_client.trailing_metadata);
   ops[5].data.recv_status_on_client.status =
       &(ctx->recv_status_on_client.status);
-  /* not using preallocation for status_details */
   ops[5].data.recv_status_on_client.status_details =
       &(ctx->recv_status_on_client.status_details);
-  ops[5].data.recv_status_on_client.status_details_capacity =
-      &(ctx->recv_status_on_client.status_details_capacity);
   ops[5].flags = 0;
   ops[5].reserved = NULL;
 
@@ -590,12 +589,13 @@
   ops[0].reserved = NULL;
 
   ops[1].op = GRPC_OP_RECV_INITIAL_METADATA;
-  ops[1].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
+  ops[1].data.recv_initial_metadata.recv_initial_metadata =
+      &(ctx->recv_initial_metadata);
   ops[1].flags = 0;
   ops[1].reserved = NULL;
 
   ops[2].op = GRPC_OP_RECV_MESSAGE;
-  ops[2].data.recv_message = &(ctx->recv_message);
+  ops[2].data.recv_message.recv_message = &(ctx->recv_message);
   ops[2].flags = 0;
   ops[2].reserved = NULL;
 
@@ -604,11 +604,8 @@
       &(ctx->recv_status_on_client.trailing_metadata);
   ops[3].data.recv_status_on_client.status =
       &(ctx->recv_status_on_client.status);
-  /* not using preallocation for status_details */
   ops[3].data.recv_status_on_client.status_details =
       &(ctx->recv_status_on_client.status_details);
-  ops[3].data.recv_status_on_client.status_details_capacity =
-      &(ctx->recv_status_on_client.status_details_capacity);
   ops[3].flags = 0;
   ops[3].reserved = NULL;
 
@@ -634,7 +631,7 @@
 
   ops[1].op = GRPC_OP_SEND_MESSAGE;
   ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
-  ops[1].data.send_message = ctx->send_message;
+  ops[1].data.send_message.send_message = ctx->send_message;
   ops[1].flags = write_flags;
   ops[1].reserved = NULL;
 
@@ -647,11 +644,8 @@
       &(ctx->recv_status_on_client.trailing_metadata);
   ops[3].data.recv_status_on_client.status =
       &(ctx->recv_status_on_client.status);
-  /* not using preallocation for status_details */
   ops[3].data.recv_status_on_client.status_details =
       &(ctx->recv_status_on_client.status_details);
-  ops[3].data.recv_status_on_client.status_details_capacity =
-      &(ctx->recv_status_on_client.status_details_capacity);
   ops[3].flags = 0;
   ops[3].reserved = NULL;
 
@@ -681,11 +675,8 @@
       &(ctx->recv_status_on_client.trailing_metadata);
   ops[1].data.recv_status_on_client.status =
       &(ctx->recv_status_on_client.status);
-  /* not using preallocation for status_details */
   ops[1].data.recv_status_on_client.status_details =
       &(ctx->recv_status_on_client.status_details);
-  ops[1].data.recv_status_on_client.status_details_capacity =
-      &(ctx->recv_status_on_client.status_details_capacity);
   ops[1].flags = 0;
   ops[1].reserved = NULL;
 
@@ -698,7 +689,8 @@
   /* TODO: don't use magic number */
   grpc_op ops[1];
   ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
-  ops[0].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
+  ops[0].data.recv_initial_metadata.recv_initial_metadata =
+      &(ctx->recv_initial_metadata);
   ops[0].flags = 0;
   ops[0].reserved = NULL;
 
@@ -717,7 +709,7 @@
   size_t nops = send_empty_initial_metadata ? 2 : 1;
   ops[0].op = GRPC_OP_SEND_MESSAGE;
   ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
-  ops[0].data.send_message = ctx->send_message;
+  ops[0].data.send_message.send_message = ctx->send_message;
   ops[0].flags = write_flags;
   ops[0].reserved = NULL;
   ops[1].op = GRPC_OP_SEND_INITIAL_METADATA;
@@ -749,10 +741,10 @@
   grpc_op ops[3];
   memset(ops, 0, sizeof(ops));
   size_t nops = 1;
+  grpc_slice status_details_slice = grpc_slice_from_copied_string(status_details);
   ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   ops[0].data.send_status_from_server.status = status_code;
-  ops[0].data.send_status_from_server.status_details =
-      gpr_strdup(status_details);
+  ops[0].data.send_status_from_server.status_details = &status_details_slice;
   grpcsharp_metadata_array_move(
       &(ctx->send_status_from_server.trailing_metadata), trailing_metadata);
   ops[0].data.send_status_from_server.trailing_metadata_count =
@@ -765,7 +757,7 @@
     ops[nops].op = GRPC_OP_SEND_MESSAGE;
     ctx->send_message = string_to_byte_buffer(optional_send_buffer,
                                               optional_send_buffer_len);
-    ops[nops].data.send_message = ctx->send_message;
+    ops[nops].data.send_message.send_message = ctx->send_message;
     ops[nops].flags = write_flags;
     ops[nops].reserved = NULL;
     nops ++;
@@ -784,7 +776,7 @@
   /* TODO: don't use magic number */
   grpc_op ops[1];
   ops[0].op = GRPC_OP_RECV_MESSAGE;
-  ops[0].data.recv_message = &(ctx->recv_message);
+  ops[0].data.recv_message.recv_message = &(ctx->recv_message);
   ops[0].flags = 0;
   ops[0].reserved = NULL;
   return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
diff --git a/src/node/ext/byte_buffer.cc b/src/node/ext/byte_buffer.cc
index fc339fc..7d6fb19 100644
--- a/src/node/ext/byte_buffer.cc
+++ b/src/node/ext/byte_buffer.cc
@@ -40,6 +40,7 @@
 #include "grpc/slice.h"
 
 #include "byte_buffer.h"
+#include "slice.h"
 
 namespace grpc {
 namespace node {
@@ -54,10 +55,7 @@
 
 grpc_byte_buffer *BufferToByteBuffer(Local<Value> buffer) {
   Nan::HandleScope scope;
-  int length = ::node::Buffer::Length(buffer);
-  char *data = ::node::Buffer::Data(buffer);
-  grpc_slice slice = grpc_slice_malloc(length);
-  memcpy(GRPC_SLICE_START_PTR(slice), data, length);
+  grpc_slice slice = CreateSliceFromBuffer(buffer);
   grpc_byte_buffer *byte_buffer(grpc_raw_byte_buffer_create(&slice, 1));
   grpc_slice_unref(slice);
   return byte_buffer;
diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc
index 191e763..244546d 100644
--- a/src/node/ext/call.cc
+++ b/src/node/ext/call.cc
@@ -48,6 +48,7 @@
 #include "completion_queue.h"
 #include "completion_queue_async_worker.h"
 #include "call_credentials.h"
+#include "slice.h"
 #include "timeval.h"
 
 using std::unique_ptr;
@@ -96,8 +97,7 @@
   return scope.Escape(err);
 }
 
-bool CreateMetadataArray(Local<Object> metadata, grpc_metadata_array *array,
-                         shared_ptr<Resources> resources) {
+bool CreateMetadataArray(Local<Object> metadata, grpc_metadata_array *array) {
   HandleScope scope;
   grpc_metadata_array_init(array);
   Local<Array> keys = Nan::GetOwnPropertyNames(metadata).ToLocalChecked();
@@ -113,32 +113,25 @@
   array->metadata = reinterpret_cast<grpc_metadata*>(
       gpr_malloc(array->capacity * sizeof(grpc_metadata)));
   for (unsigned int i = 0; i < keys->Length(); i++) {
-    Local<String> current_key(keys->Get(i)->ToString());
-    Utf8String *utf8_key = new Utf8String(current_key);
-    resources->strings.push_back(unique_ptr<Utf8String>(utf8_key));
+    Local<String> current_key(Nan::To<String>(keys->Get(i)).ToLocalChecked());
     Local<Array> values = Local<Array>::Cast(
         Nan::Get(metadata, current_key).ToLocalChecked());
+    grpc_slice key_slice = grpc_slice_intern(CreateSliceFromString(current_key));
     for (unsigned int j = 0; j < values->Length(); j++) {
       Local<Value> value = Nan::Get(values, j).ToLocalChecked();
       grpc_metadata *current = &array->metadata[array->count];
-      current->key = **utf8_key;
+      current->key = key_slice;
       // Only allow binary headers for "-bin" keys
-      if (grpc_is_binary_header(current->key, strlen(current->key))) {
+      if (grpc_is_binary_header(key_slice)) {
         if (::node::Buffer::HasInstance(value)) {
-          current->value = ::node::Buffer::Data(value);
-          current->value_length = ::node::Buffer::Length(value);
-          PersistentValue *handle = new PersistentValue(value);
-          resources->handles.push_back(unique_ptr<PersistentValue>(handle));
+          current->value = CreateSliceFromBuffer(value);
         } else {
           return false;
         }
       } else {
         if (value->IsString()) {
           Local<String> string_value = Nan::To<String>(value).ToLocalChecked();
-          Utf8String *utf8_value = new Utf8String(string_value);
-          resources->strings.push_back(unique_ptr<Utf8String>(utf8_value));
-          current->value = **utf8_value;
-          current->value_length = string_value->Length();
+          current->value = CreateSliceFromString(string_value);
         } else {
           return false;
         }
@@ -153,40 +146,25 @@
   EscapableHandleScope scope;
   grpc_metadata *metadata_elements = metadata_array->metadata;
   size_t length = metadata_array->count;
-  std::map<const char*, size_t> size_map;
-  std::map<const char*, size_t> index_map;
-
-  for (unsigned int i = 0; i < length; i++) {
-    const char *key = metadata_elements[i].key;
-    if (size_map.count(key)) {
-      size_map[key] += 1;
-    } else {
-      size_map[key] = 1;
-    }
-    index_map[key] = 0;
-  }
   Local<Object> metadata_object = Nan::New<Object>();
   for (unsigned int i = 0; i < length; i++) {
     grpc_metadata* elem = &metadata_elements[i];
-    Local<String> key_string = Nan::New(elem->key).ToLocalChecked();
+    // TODO(murgatroid99): Use zero-copy string construction instead
+    Local<String> key_string = CopyStringFromSlice(elem->key);
     Local<Array> array;
     MaybeLocal<Value> maybe_array = Nan::Get(metadata_object, key_string);
     if (maybe_array.IsEmpty() || !maybe_array.ToLocalChecked()->IsArray()) {
-      array = Nan::New<Array>(size_map[elem->key]);
+      array = Nan::New<Array>(0);
       Nan::Set(metadata_object, key_string, array);
     } else {
       array = Local<Array>::Cast(maybe_array.ToLocalChecked());
     }
-    if (grpc_is_binary_header(elem->key, strlen(elem->key))) {
-      Nan::Set(array, index_map[elem->key],
-               MakeFastBuffer(
-                   Nan::CopyBuffer(elem->value,
-                                   elem->value_length).ToLocalChecked()));
+    if (grpc_is_binary_header(elem->key)) {
+      Nan::Set(array, array->Length(), CreateBufferFromSlice(elem->value));
     } else {
-      Nan::Set(array, index_map[elem->key],
-               Nan::New(elem->value).ToLocalChecked());
+      // TODO(murgatroid99): Use zero-copy string construction instead
+      Nan::Set(array, array->Length(), CopyStringFromSlice(elem->value));
     }
-    index_map[elem->key] += 1;
   }
   return scope.Escape(metadata_object);
 }
@@ -205,8 +183,7 @@
     EscapableHandleScope scope;
     return scope.Escape(Nan::True());
   }
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
+  bool ParseOp(Local<Value> value, grpc_op *out) {
     if (!value->IsObject()) {
       return false;
     }
@@ -216,7 +193,7 @@
       return false;
     }
     if (!CreateMetadataArray(maybe_metadata.ToLocalChecked(),
-                             &array, resources)) {
+                             &array)) {
       return false;
     }
     out->data.send_initial_metadata.count = array.count;
@@ -246,8 +223,7 @@
     EscapableHandleScope scope;
     return scope.Escape(Nan::True());
   }
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
+  bool ParseOp(Local<Value> value, grpc_op *out) {
     if (!::node::Buffer::HasInstance(value)) {
       return false;
     }
@@ -262,9 +238,7 @@
       }
     }
     send_message = BufferToByteBuffer(value);
-    out->data.send_message = send_message;
-    PersistentValue *handle = new PersistentValue(value);
-    resources->handles.push_back(unique_ptr<PersistentValue>(handle));
+    out->data.send_message.send_message = send_message;
     return true;
   }
   bool IsFinalOp() {
@@ -284,8 +258,7 @@
     EscapableHandleScope scope;
     return scope.Escape(Nan::True());
   }
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
+  bool ParseOp(Local<Value> value, grpc_op *out) {
     return true;
   }
   bool IsFinalOp() {
@@ -299,12 +272,14 @@
 
 class SendServerStatusOp : public Op {
  public:
+  ~SendServerStatusOp() {
+    grpc_slice_unref(details);
+  }
   Local<Value> GetNodeValue() const {
     EscapableHandleScope scope;
     return scope.Escape(Nan::True());
   }
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
+  bool ParseOp(Local<Value> value, grpc_op *out) {
     if (!value->IsObject()) {
       return false;
     }
@@ -339,16 +314,15 @@
     Local<String> details = Nan::To<String>(
         maybe_details.ToLocalChecked()).ToLocalChecked();
     grpc_metadata_array array;
-    if (!CreateMetadataArray(metadata, &array, resources)) {
+    if (!CreateMetadataArray(metadata, &array)) {
       return false;
     }
     out->data.send_status_from_server.trailing_metadata_count = array.count;
     out->data.send_status_from_server.trailing_metadata = array.metadata;
     out->data.send_status_from_server.status =
         static_cast<grpc_status_code>(code);
-    Utf8String *str = new Utf8String(details);
-    resources->strings.push_back(unique_ptr<Utf8String>(str));
-    out->data.send_status_from_server.status_details = **str;
+    this->details = CreateSliceFromString(details);
+    out->data.send_status_from_server.status_details = &this->details;
     return true;
   }
   bool IsFinalOp() {
@@ -358,6 +332,9 @@
   std::string GetTypeString() const {
     return "send_status";
   }
+
+ private:
+  grpc_slice details;
 };
 
 class GetMetadataOp : public Op {
@@ -375,9 +352,8 @@
     return scope.Escape(ParseMetadata(&recv_metadata));
   }
 
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
-    out->data.recv_initial_metadata = &recv_metadata;
+  bool ParseOp(Local<Value> value, grpc_op *out) {
+    out->data.recv_initial_metadata.recv_initial_metadata = &recv_metadata;
     return true;
   }
   bool IsFinalOp() {
@@ -408,9 +384,8 @@
     return scope.Escape(ByteBufferToBuffer(recv_message));
   }
 
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
-    out->data.recv_message = &recv_message;
+  bool ParseOp(Local<Value> value, grpc_op *out) {
+    out->data.recv_message.recv_message = &recv_message;
     return true;
   }
   bool IsFinalOp() {
@@ -430,21 +405,16 @@
  public:
   ClientStatusOp() {
     grpc_metadata_array_init(&metadata_array);
-    status_details = NULL;
-    details_capacity = 0;
   }
 
   ~ClientStatusOp() {
     grpc_metadata_array_destroy(&metadata_array);
-    gpr_free(status_details);
   }
 
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
+  bool ParseOp(Local<Value> value, grpc_op *out) {
     out->data.recv_status_on_client.trailing_metadata = &metadata_array;
     out->data.recv_status_on_client.status = &status;
     out->data.recv_status_on_client.status_details = &status_details;
-    out->data.recv_status_on_client.status_details_capacity = &details_capacity;
     return true;
   }
 
@@ -453,10 +423,8 @@
     Local<Object> status_obj = Nan::New<Object>();
     Nan::Set(status_obj, Nan::New("code").ToLocalChecked(),
                     Nan::New<Number>(status));
-    if (status_details != NULL) {
-      Nan::Set(status_obj, Nan::New("details").ToLocalChecked(),
-               Nan::New(status_details).ToLocalChecked());
-    }
+    Nan::Set(status_obj, Nan::New("details").ToLocalChecked(),
+               CopyStringFromSlice(status_details));
     Nan::Set(status_obj, Nan::New("metadata").ToLocalChecked(),
              ParseMetadata(&metadata_array));
     return scope.Escape(status_obj);
@@ -471,8 +439,7 @@
  private:
   grpc_metadata_array metadata_array;
   grpc_status_code status;
-  char *status_details;
-  size_t details_capacity;
+  grpc_slice status_details;
 };
 
 class ServerCloseResponseOp : public Op {
@@ -482,8 +449,7 @@
     return scope.Escape(Nan::New<Boolean>(cancelled));
   }
 
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
+  bool ParseOp(Local<Value> value, grpc_op *out) {
     out->data.recv_close_on_server.cancelled = &cancelled;
     return true;
   }
@@ -500,9 +466,8 @@
   int cancelled;
 };
 
-tag::tag(Callback *callback, OpVec *ops,
-         shared_ptr<Resources> resources, Call *call) :
-    callback(callback), ops(ops), resources(resources), call(call){
+tag::tag(Callback *callback, OpVec *ops, Call *call) :
+    callback(callback), ops(ops), call(call){
 }
 
 tag::~tag() {
@@ -650,20 +615,24 @@
       if (channel->GetWrappedChannel() == NULL) {
         return Nan::ThrowError("Call cannot be created from a closed channel");
       }
-      Utf8String method(info[1]);
       double deadline = Nan::To<double>(info[2]).FromJust();
       grpc_channel *wrapped_channel = channel->GetWrappedChannel();
       grpc_call *wrapped_call;
       if (info[3]->IsString()) {
-        Utf8String host_override(info[3]);
+        grpc_slice *host = new grpc_slice;
+        *host = CreateSliceFromString(
+            Nan::To<String>(info[3]).ToLocalChecked());
         wrapped_call = grpc_channel_create_call(
             wrapped_channel, parent_call, propagate_flags,
-            GetCompletionQueue(), *method,
-            *host_override, MillisecondsToTimespec(deadline), NULL);
+            GetCompletionQueue(), CreateSliceFromString(
+                Nan::To<String>(info[1]).ToLocalChecked()),
+            host, MillisecondsToTimespec(deadline), NULL);
+        delete host;
       } else if (info[3]->IsUndefined() || info[3]->IsNull()) {
         wrapped_call = grpc_channel_create_call(
             wrapped_channel, parent_call, propagate_flags,
-            GetCompletionQueue(), *method,
+            GetCompletionQueue(), CreateSliceFromString(
+                Nan::To<String>(info[1]).ToLocalChecked()),
             NULL, MillisecondsToTimespec(deadline), NULL);
       } else {
         return Nan::ThrowTypeError("Call's fourth argument must be a string");
@@ -700,7 +669,6 @@
   }
   Local<Function> callback_func = info[1].As<Function>();
   Call *call = ObjectWrap::Unwrap<Call>(info.This());
-  shared_ptr<Resources> resources(new Resources);
   Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked();
   Local<Array> keys = Nan::GetOwnPropertyNames(obj).ToLocalChecked();
   size_t nops = keys->Length();
@@ -745,7 +713,7 @@
       default:
         return Nan::ThrowError("Argument object had an unrecognized key");
     }
-    if (!op->ParseOp(obj->Get(type), &ops[i], resources)) {
+    if (!op->ParseOp(obj->Get(type), &ops[i])) {
       return Nan::ThrowTypeError("Incorrectly typed arguments to startBatch");
     }
     op_vector->push_back(std::move(op));
@@ -753,7 +721,7 @@
   Callback *callback = new Callback(callback_func);
   grpc_call_error error = grpc_call_start_batch(
       call->wrapped_call, &ops[0], nops, new struct tag(
-          callback, op_vector.release(), resources, call), NULL);
+          callback, op_vector.release(), call), NULL);
   if (error != GRPC_CALL_OK) {
     return Nan::ThrowError(nanErrorWithCode("startBatch failed", error));
   }
diff --git a/src/node/ext/call.h b/src/node/ext/call.h
index 31c6566..cffff00 100644
--- a/src/node/ext/call.h
+++ b/src/node/ext/call.h
@@ -51,20 +51,12 @@
 using std::unique_ptr;
 using std::shared_ptr;
 
-typedef Nan::Persistent<v8::Value, Nan::CopyablePersistentTraits<v8::Value>> PersistentValue;
-
 v8::Local<v8::Value> nanErrorWithCode(const char *msg, grpc_call_error code);
 
 v8::Local<v8::Value> ParseMetadata(const grpc_metadata_array *metadata_array);
 
-struct Resources {
-  std::vector<unique_ptr<Nan::Utf8String> > strings;
-  std::vector<unique_ptr<PersistentValue> > handles;
-};
-
 bool CreateMetadataArray(v8::Local<v8::Object> metadata,
-                         grpc_metadata_array *array,
-                         shared_ptr<Resources> resources);
+                         grpc_metadata_array *array);
 
 /* Wrapper class for grpc_call structs. */
 class Call : public Nan::ObjectWrap {
@@ -106,8 +98,7 @@
 class Op {
  public:
   virtual v8::Local<v8::Value> GetNodeValue() const = 0;
-  virtual bool ParseOp(v8::Local<v8::Value> value, grpc_op *out,
-                       shared_ptr<Resources> resources) = 0;
+  virtual bool ParseOp(v8::Local<v8::Value> value, grpc_op *out) = 0;
   virtual ~Op();
   v8::Local<v8::Value> GetOpType() const;
   virtual bool IsFinalOp() = 0;
@@ -118,12 +109,10 @@
 
 typedef std::vector<unique_ptr<Op>> OpVec;
 struct tag {
-  tag(Nan::Callback *callback, OpVec *ops,
-      shared_ptr<Resources> resources, Call *call);
+  tag(Nan::Callback *callback, OpVec *ops, Call *call);
   ~tag();
   Nan::Callback *callback;
   OpVec *ops;
-  shared_ptr<Resources> resources;
   Call *call;
 };
 
diff --git a/src/node/ext/call_credentials.cc b/src/node/ext/call_credentials.cc
index 41f6c29..afcc363 100644
--- a/src/node/ext/call_credentials.cc
+++ b/src/node/ext/call_credentials.cc
@@ -206,7 +206,6 @@
     return Nan::ThrowTypeError(
         "The callback's fourth argument must be an object");
   }
-  shared_ptr<Resources> resources(new Resources);
   grpc_status_code code = static_cast<grpc_status_code>(
       Nan::To<uint32_t>(info[0]).FromJust());
   Utf8String details_utf8_str(info[1]);
@@ -214,7 +213,7 @@
   grpc_metadata_array array;
   Local<Object> callback_data = Nan::To<Object>(info[3]).ToLocalChecked();
   if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(),
-                           &array, resources)){
+                           &array)){
     return Nan::ThrowError("Failed to parse metadata");
   }
   grpc_credentials_plugin_metadata_cb cb =
diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc
index 5bc58b9..c795ff7 100644
--- a/src/node/ext/channel.cc
+++ b/src/node/ext/channel.cc
@@ -280,8 +280,7 @@
       channel->wrapped_channel, last_state, MillisecondsToTimespec(deadline),
       GetCompletionQueue(),
       new struct tag(callback,
-                     ops.release(),
-                     shared_ptr<Resources>(nullptr), NULL));
+                     ops.release(), NULL));
   CompletionQueueNext();
 }
 
diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc
index 614f2e9..95e273f 100644
--- a/src/node/ext/node_grpc.cc
+++ b/src/node/ext/node_grpc.cc
@@ -56,9 +56,12 @@
 #include "server.h"
 #include "completion_queue_async_worker.h"
 #include "server_credentials.h"
+#include "slice.h"
 #include "timeval.h"
 #include "completion_queue.h"
 
+using grpc::node::CreateSliceFromString;
+
 using v8::FunctionTemplate;
 using v8::Local;
 using v8::Value;
@@ -283,10 +286,8 @@
         "headerKeyIsLegal's argument must be a string");
   }
   Local<String> key = Nan::To<String>(info[0]).ToLocalChecked();
-  Nan::Utf8String key_utf8_str(key);
-  char *key_str = *key_utf8_str;
   info.GetReturnValue().Set(static_cast<bool>(
-      grpc_header_key_is_legal(key_str, static_cast<size_t>(key->Length()))));
+      grpc_header_key_is_legal(CreateSliceFromString(key))));
 }
 
 NAN_METHOD(MetadataNonbinValueIsLegal) {
@@ -295,11 +296,8 @@
         "metadataNonbinValueIsLegal's argument must be a string");
   }
   Local<String> value = Nan::To<String>(info[0]).ToLocalChecked();
-  Nan::Utf8String value_utf8_str(value);
-  char *value_str = *value_utf8_str;
   info.GetReturnValue().Set(static_cast<bool>(
-      grpc_header_nonbin_value_is_legal(
-          value_str, static_cast<size_t>(value->Length()))));
+      grpc_header_nonbin_value_is_legal(CreateSliceFromString(value))));
 }
 
 NAN_METHOD(MetadataKeyIsBinary) {
@@ -308,10 +306,8 @@
         "metadataKeyIsLegal's argument must be a string");
   }
   Local<String> key = Nan::To<String>(info[0]).ToLocalChecked();
-  Nan::Utf8String key_utf8_str(key);
-  char *key_str = *key_utf8_str;
   info.GetReturnValue().Set(static_cast<bool>(
-      grpc_is_binary_header(key_str, static_cast<size_t>(key->Length()))));
+      grpc_is_binary_header(CreateSliceFromString(key))));
 }
 
 static grpc_ssl_roots_override_result get_ssl_roots_override(
diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc
index 70d5b96..4761b28 100644
--- a/src/node/ext/server.cc
+++ b/src/node/ext/server.cc
@@ -46,6 +46,7 @@
 #include "grpc/grpc_security.h"
 #include "grpc/support/log.h"
 #include "server_credentials.h"
+#include "slice.h"
 #include "timeval.h"
 
 namespace grpc {
@@ -99,10 +100,11 @@
     }
     Local<Object> obj = Nan::New<Object>();
     Nan::Set(obj, Nan::New("call").ToLocalChecked(), Call::WrapStruct(call));
+    // TODO(murgatroid99): Use zero-copy string construction instead
     Nan::Set(obj, Nan::New("method").ToLocalChecked(),
-             Nan::New(details.method).ToLocalChecked());
+             CopyStringFromSlice(details.method));
     Nan::Set(obj, Nan::New("host").ToLocalChecked(),
-             Nan::New(details.host).ToLocalChecked());
+             CopyStringFromSlice(details.host));
     Nan::Set(obj, Nan::New("deadline").ToLocalChecked(),
              Nan::New<Date>(TimespecToMilliseconds(details.deadline))
                  .ToLocalChecked());
@@ -111,8 +113,7 @@
     return scope.Escape(obj);
   }
 
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
+  bool ParseOp(Local<Value> value, grpc_op *out) {
     return true;
   }
   bool IsFinalOp() {
@@ -139,8 +140,7 @@
     return Nan::New<External>(reinterpret_cast<void *>(server));
   }
 
-  bool ParseOp(Local<Value> value, grpc_op *out,
-               shared_ptr<Resources> resources) {
+  bool ParseOp(Local<Value> value, grpc_op *out) {
     return true;
   }
   bool IsFinalOp() {
@@ -207,8 +207,7 @@
 
     grpc_server_shutdown_and_notify(
         this->wrapped_server, GetCompletionQueue(),
-        new struct tag(new Callback(**shutdown_callback), ops.release(),
-                       shared_ptr<Resources>(nullptr), NULL));
+        new struct tag(new Callback(**shutdown_callback), ops.release(), NULL));
     grpc_server_cancel_all_calls(this->wrapped_server);
     CompletionQueueNext();
     this->wrapped_server = NULL;
@@ -261,7 +260,7 @@
       GetCompletionQueue(),
       GetCompletionQueue(),
       new struct tag(new Callback(info[0].As<Function>()), ops.release(),
-                     shared_ptr<Resources>(nullptr), NULL));
+                     NULL));
   if (error != GRPC_CALL_OK) {
     return Nan::ThrowError(nanErrorWithCode("requestCall failed", error));
   }
@@ -314,7 +313,7 @@
   grpc_server_shutdown_and_notify(
       server->wrapped_server, GetCompletionQueue(),
       new struct tag(new Nan::Callback(info[0].As<Function>()), ops.release(),
-                     shared_ptr<Resources>(nullptr), NULL));
+                     NULL));
   CompletionQueueNext();
 }
 
diff --git a/src/node/ext/slice.cc b/src/node/ext/slice.cc
new file mode 100644
index 0000000..98a80b3
--- /dev/null
+++ b/src/node/ext/slice.cc
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <node.h>
+#include <nan.h>
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
+
+#include "slice.h"
+#include "byte_buffer.h"
+
+namespace grpc {
+namespace node {
+
+using Nan::Persistent;
+
+using v8::Local;
+using v8::String;
+using v8::Value;
+
+namespace {
+void SliceFreeCallback(char *data, void *hint) {
+  grpc_slice *slice = reinterpret_cast<grpc_slice*>(hint);
+  grpc_slice_unref(*slice);
+  delete slice;
+}
+
+void string_destroy_func(void *user_data) {
+  delete reinterpret_cast<Nan::Utf8String*>(user_data);
+}
+
+void buffer_destroy_func(void *user_data) {
+  delete reinterpret_cast<PersistentValue*>(user_data);
+}
+} // namespace
+
+grpc_slice CreateSliceFromString(const Local<String> source) {
+  Nan::HandleScope scope;
+  Nan::Utf8String *utf8_value = new Nan::Utf8String(source);
+  return grpc_slice_new_with_user_data(**utf8_value, source->Length(),
+                                       string_destroy_func, utf8_value);
+}
+
+grpc_slice CreateSliceFromBuffer(const Local<Value> source) {
+  // Prerequisite: ::node::Buffer::HasInstance(source)
+  Nan::HandleScope scope;
+  return grpc_slice_new_with_user_data(::node::Buffer::Data(source),
+                                       ::node::Buffer::Length(source),
+                                       buffer_destroy_func,
+                                       new PersistentValue(source));
+}
+Local<String> CopyStringFromSlice(const grpc_slice slice) {
+  Nan::EscapableHandleScope scope;
+  if (GRPC_SLICE_LENGTH(slice) == 0) {
+    return scope.Escape(Nan::EmptyString());
+  }
+  return scope.Escape(Nan::New<String>(
+      const_cast<char *>(reinterpret_cast<const char *>(GRPC_SLICE_START_PTR(slice))),
+      GRPC_SLICE_LENGTH(slice)).ToLocalChecked());
+}
+
+Local<Value> CreateBufferFromSlice(const grpc_slice slice) {
+  Nan::EscapableHandleScope scope;
+  grpc_slice *slice_ptr = new grpc_slice;
+  *slice_ptr = grpc_slice_ref(slice);
+  return scope.Escape(MakeFastBuffer(Nan::NewBuffer(
+      const_cast<char *>(reinterpret_cast<const char *>(GRPC_SLICE_START_PTR(*slice_ptr))),
+      GRPC_SLICE_LENGTH(*slice_ptr), SliceFreeCallback, slice_ptr).ToLocalChecked()));
+}
+
+}  // namespace node
+}  // namespace grpc
diff --git a/src/core/ext/transport/chttp2/transport/status_conversion.h b/src/node/ext/slice.h
similarity index 64%
copy from src/core/ext/transport/chttp2/transport/status_conversion.h
copy to src/node/ext/slice.h
index 953bc9f..7dcb1bd 100644
--- a/src/core/ext/transport/chttp2/transport/status_conversion.h
+++ b/src/node/ext/slice.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,20 +31,22 @@
  *
  */
 
-#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
-#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H
+#include <node.h>
+#include <nan.h>
+#include <grpc/slice.h>
 
-#include <grpc/grpc.h>
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
+namespace grpc {
+namespace node {
 
-/* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */
-grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error(
-    grpc_status_code status);
-grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error, gpr_timespec deadline);
+typedef Nan::Persistent<v8::Value, Nan::CopyablePersistentTraits<v8::Value>> PersistentValue;
 
-/* Conversion of HTTP status codes (:status) to grpc status codes */
-grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status);
-int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status);
+grpc_slice CreateSliceFromString(const v8::Local<v8::String> source);
 
-#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H */
+grpc_slice CreateSliceFromBuffer(const v8::Local<v8::Value> source);
+
+v8::Local<v8::String> CopyStringFromSlice(const grpc_slice slice);
+
+v8::Local<v8::Value> CreateBufferFromSlice(const grpc_slice slice);
+
+}  // namespace node
+}  // namespace grpc
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m
index 2397c92..c533c5a 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.m
@@ -200,7 +200,7 @@
   return grpc_channel_create_call(_unmanagedChannel,
                                   NULL, GRPC_PROPAGATE_DEFAULTS,
                                   queue.unmanagedQueue,
-                                  path.UTF8String,
+                                  grpc_slice_from_copied_string(path.UTF8String),
                                   NULL, // Passing NULL for host
                                   gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
 }
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
index 38fcae0..9f1901a 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
@@ -105,14 +105,14 @@
   }
   if (self = [super init]) {
     _op.op = GRPC_OP_SEND_MESSAGE;
-    _op.data.send_message = message.grpc_byteBuffer;
+    _op.data.send_message.send_message = message.grpc_byteBuffer;
     _handler = handler;
   }
   return self;
 }
 
 - (void)dealloc {
-  grpc_byte_buffer_destroy(_op.data.send_message);
+  grpc_byte_buffer_destroy(_op.data.send_message.send_message);
 }
 
 @end
@@ -145,7 +145,7 @@
   if (self = [super init]) {
     _op.op = GRPC_OP_RECV_INITIAL_METADATA;
     grpc_metadata_array_init(&_headers);
-    _op.data.recv_initial_metadata = &_headers;
+    _op.data.recv_initial_metadata.recv_initial_metadata = &_headers;
     if (handler) {
       // Prevent reference cycle with _handler
       __weak typeof(self) weakSelf = self;
@@ -177,7 +177,7 @@
 - (instancetype)initWithHandler:(void (^)(grpc_byte_buffer *))handler {
   if (self = [super init]) {
     _op.op = GRPC_OP_RECV_MESSAGE;
-    _op.data.recv_message = &_receivedMessage;
+    _op.data.recv_message.recv_message = &_receivedMessage;
     if (handler) {
       // Prevent reference cycle with _handler
       __weak typeof(self) weakSelf = self;
@@ -194,7 +194,7 @@
 
 @implementation GRPCOpRecvStatus{
   grpc_status_code _statusCode;
-  char *_details;
+  grpc_slice _details;
   size_t _detailsCapacity;
   grpc_metadata_array _trailers;
 }
@@ -208,7 +208,6 @@
     _op.op = GRPC_OP_RECV_STATUS_ON_CLIENT;
     _op.data.recv_status_on_client.status = &_statusCode;
     _op.data.recv_status_on_client.status_details = &_details;
-    _op.data.recv_status_on_client.status_details_capacity = &_detailsCapacity;
     grpc_metadata_array_init(&_trailers);
     _op.data.recv_status_on_client.trailing_metadata = &_trailers;
     if (handler) {
@@ -216,11 +215,15 @@
       __weak typeof(self) weakSelf = self;
       _handler = ^{
         __strong typeof(self) strongSelf = weakSelf;
-        NSError *error = [NSError grpc_errorFromStatusCode:strongSelf->_statusCode
-                                                   details:strongSelf->_details];
-        NSDictionary *trailers = [NSDictionary
-                                  grpc_dictionaryFromMetadataArray:strongSelf->_trailers];
-        handler(error, trailers);
+        if (strongSelf) {
+          char *details = grpc_slice_to_c_string(strongSelf->_details);
+          NSError *error = [NSError grpc_errorFromStatusCode:strongSelf->_statusCode
+                                                     details:details];
+          NSDictionary *trailers = [NSDictionary
+                                    grpc_dictionaryFromMetadataArray:strongSelf->_trailers];
+          handler(error, trailers);
+          gpr_free(details);
+        }
       };
     }
   }
@@ -229,7 +232,7 @@
 
 - (void)dealloc {
   grpc_metadata_array_destroy(&_trailers);
-  gpr_free(_details);
+  grpc_slice_unref(_details);
 }
 
 @end
diff --git a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
index 7477da7..feb2bb5 100644
--- a/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
+++ b/src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
@@ -47,12 +47,12 @@
 @implementation NSData (GRPCMetadata)
 + (instancetype)grpc_dataFromMetadataValue:(grpc_metadata *)metadata {
   // TODO(jcanizales): Should we use a non-copy constructor?
-  return [self dataWithBytes:metadata->value length:metadata->value_length];
+  return [self dataWithBytes:GRPC_SLICE_START_PTR(metadata->value)
+                      length:GRPC_SLICE_LENGTH(metadata->value)];
 }
 
 - (void)grpc_initMetadata:(grpc_metadata *)metadata {
-  metadata->value = self.bytes;
-  metadata->value_length = self.length;
+  metadata->value = grpc_slice_from_copied_buffer(self.bytes, self.length);
 }
 @end
 
@@ -67,15 +67,14 @@
 
 @implementation NSString (GRPCMetadata)
 + (instancetype)grpc_stringFromMetadataValue:(grpc_metadata *)metadata {
-  return [[self alloc] initWithBytes:metadata->value
-                              length:metadata->value_length
+  return [[self alloc] initWithBytes:GRPC_SLICE_START_PTR(metadata->value)
+                              length:GRPC_SLICE_LENGTH(metadata->value)
                             encoding:NSASCIIStringEncoding];
 }
 
 // Precondition: This object contains only ASCII characters.
 - (void)grpc_initMetadata:(grpc_metadata *)metadata {
-  metadata->value = self.UTF8String;
-  metadata->value_length = self.length;
+  metadata->value = grpc_slice_from_copied_string(self.UTF8String);
 }
 @end
 
@@ -89,7 +88,10 @@
 + (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count {
   NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithCapacity:count];
   for (grpc_metadata *entry = entries; entry < entries + count; entry++) {
-    NSString *name = [NSString stringWithCString:entry->key encoding:NSASCIIStringEncoding];
+    char *key = grpc_slice_to_c_string(entry->key);
+    NSString *name = [NSString stringWithCString:key
+                                        encoding:NSASCIIStringEncoding];
+    gpr_free(key);
     if (!name || metadata[name]) {
       // Log if name is nil?
       continue;
@@ -112,7 +114,7 @@
   grpc_metadata *current = metadata;
   for (NSString* key in self) {
     id value = self[key];
-    current->key = key.UTF8String;
+    current->key = grpc_slice_from_copied_string(key.UTF8String);
     if ([value respondsToSelector:@selector(grpc_initMetadata:)]) {
       [value grpc_initMetadata:current];
     } else {
diff --git a/src/objective-c/tests/CronetUnitTests/CronetUnitTests.m b/src/objective-c/tests/CronetUnitTests/CronetUnitTests.m
index dbd2807..43b91a0 100644
--- a/src/objective-c/tests/CronetUnitTests/CronetUnitTests.m
+++ b/src/objective-c/tests/CronetUnitTests/CronetUnitTests.m
@@ -142,7 +142,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -151,12 +151,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
index 64b1137..be07190 100644
--- a/src/php/ext/grpc/call.c
+++ b/src/php/ext/grpc/call.c
@@ -100,11 +100,11 @@
   grpc_metadata *elem;
   for (i = 0; i < count; i++) {
     elem = &elements[i];
-    key_len = strlen(elem->key);
+    key_len = GRPC_SLICE_LENGTH(elem->key);
     str_key = ecalloc(key_len + 1, sizeof(char));
-    memcpy(str_key, elem->key, key_len);
-    str_val = ecalloc(elem->value_length + 1, sizeof(char));
-    memcpy(str_val, elem->value, elem->value_length);
+    memcpy(str_key, GRPC_SLICE_START_PTR(elem->key), key_len);
+    str_val = ecalloc(GRPC_SLICE_LENGTH(elem->value) + 1, sizeof(char));
+    memcpy(str_val, GRPC_SLICE_START_PTR(elem->value), GRPC_SLICE_LENGTH(elem->value));
     if (php_grpc_zend_hash_find(array_hash, str_key, key_len, (void **)&data)
         == SUCCESS) {
       if (Z_TYPE_P(data) != IS_ARRAY) {
@@ -115,13 +115,13 @@
         efree(str_val);
         return NULL;
       }
-      php_grpc_add_next_index_stringl(data, str_val, elem->value_length,
+      php_grpc_add_next_index_stringl(data, str_val, GRPC_SLICE_LENGTH(elem->value),
                                       false);
     } else {
       PHP_GRPC_MAKE_STD_ZVAL(inner_array);
       array_init(inner_array);
       php_grpc_add_next_index_stringl(inner_array, str_val,
-                                      elem->value_length, false);
+                                      GRPC_SLICE_LENGTH(elem->value), false);
       add_assoc_zval(array, str_key, inner_array);
     }
   }
@@ -164,7 +164,7 @@
     if (key_type1 != HASH_KEY_IS_STRING) {
       return false;
     }
-    if (!grpc_header_key_is_legal(key1, strlen(key1))) {
+    if (!grpc_header_key_is_legal(grpc_slice_from_static_string(key1))) {
       return false;
     }
     inner_array_hash = Z_ARRVAL_P(inner_array);
@@ -172,9 +172,8 @@
       if (Z_TYPE_P(value) != IS_STRING) {
         return false;
       }
-      metadata->metadata[metadata->count].key = key1;
-      metadata->metadata[metadata->count].value = Z_STRVAL_P(value);
-      metadata->metadata[metadata->count].value_length = Z_STRLEN_P(value);
+      metadata->metadata[metadata->count].key = grpc_slice_from_copied_string(key1);
+      metadata->metadata[metadata->count].value = grpc_slice_from_copied_buffer(Z_STRVAL_P(value), Z_STRLEN_P(value));
       metadata->count += 1;
     PHP_GRPC_HASH_FOREACH_END()
   PHP_GRPC_HASH_FOREACH_END()
@@ -229,10 +228,15 @@
   }
   add_property_zval(getThis(), "channel", channel_obj);
   wrapped_grpc_timeval *deadline = Z_WRAPPED_GRPC_TIMEVAL_P(deadline_obj);
+  grpc_slice method_slice = grpc_slice_from_copied_string(method);
+  grpc_slice host_slice = host_override != NULL ?
+      grpc_slice_from_copied_string(host_override) : grpc_empty_slice();
   call->wrapped =
     grpc_channel_create_call(channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS,
-                             completion_queue, method, host_override,
+                             completion_queue, method_slice, host_override != NULL ? &host_slice : NULL,
                              deadline->wrapped, NULL);
+  grpc_slice_unref(method_slice);
+  grpc_slice_unref(host_slice);
   call->owned = true;
 }
 
@@ -267,8 +271,8 @@
   grpc_metadata_array recv_metadata;
   grpc_metadata_array recv_trailing_metadata;
   grpc_status_code status;
-  char *status_details = NULL;
-  size_t status_details_capacity = 0;
+  grpc_slice recv_status_details = grpc_empty_slice();
+  grpc_slice send_status_details = grpc_empty_slice();
   grpc_byte_buffer *message;
   int cancelled;
   grpc_call_error error;
@@ -335,7 +339,7 @@
                              1 TSRMLS_CC);
         goto cleanup;
       }
-      ops[op_num].data.send_message =
+      ops[op_num].data.send_message.send_message =
           string_to_byte_buffer(Z_STRVAL_P(message_value),
                                 Z_STRLEN_P(message_value));
       break;
@@ -380,8 +384,8 @@
                                1 TSRMLS_CC);
           goto cleanup;
         }
-        ops[op_num].data.send_status_from_server.status_details =
-            Z_STRVAL_P(inner_value);
+        send_status_details = grpc_slice_from_copied_string(Z_STRVAL_P(inner_value));
+        ops[op_num].data.send_status_from_server.status_details = &send_status_details;
       } else {
         zend_throw_exception(spl_ce_InvalidArgumentException,
                              "String status details is required",
@@ -390,19 +394,18 @@
       }
       break;
     case GRPC_OP_RECV_INITIAL_METADATA:
-      ops[op_num].data.recv_initial_metadata = &recv_metadata;
+      ops[op_num].data.recv_initial_metadata.recv_initial_metadata =
+          &recv_metadata;
       break;
     case GRPC_OP_RECV_MESSAGE:
-      ops[op_num].data.recv_message = &message;
+      ops[op_num].data.recv_message.recv_message = &message;
       break;
     case GRPC_OP_RECV_STATUS_ON_CLIENT:
       ops[op_num].data.recv_status_on_client.trailing_metadata =
           &recv_trailing_metadata;
       ops[op_num].data.recv_status_on_client.status = &status;
       ops[op_num].data.recv_status_on_client.status_details =
-          &status_details;
-      ops[op_num].data.recv_status_on_client.status_details_capacity =
-          &status_details_capacity;
+          &recv_status_details;
       break;
     case GRPC_OP_RECV_CLOSE_ON_SERVER:
       ops[op_num].data.recv_close_on_server.cancelled = &cancelled;
@@ -474,8 +477,10 @@
 #endif
       PHP_GRPC_DELREF(array);
       add_property_long(recv_status, "code", status);
-      php_grpc_add_property_string(recv_status, "details", status_details,
+      char *status_details_text = grpc_slice_to_c_string(recv_status_details);
+      php_grpc_add_property_string(recv_status, "details", status_details_text,
                                    true);
+      gpr_free(status_details_text);
       add_property_zval(result, "status", recv_status);
       PHP_GRPC_DELREF(recv_status);
       PHP_GRPC_FREE_STD_ZVAL(recv_status);
@@ -493,12 +498,11 @@
   grpc_metadata_array_destroy(&trailing_metadata);
   grpc_metadata_array_destroy(&recv_metadata);
   grpc_metadata_array_destroy(&recv_trailing_metadata);
-  if (status_details != NULL) {
-    gpr_free(status_details);
-  }
+  grpc_slice_unref(recv_status_details);
+  grpc_slice_unref(send_status_details);
   for (int i = 0; i < op_num; i++) {
     if (ops[i].op == GRPC_OP_SEND_MESSAGE) {
-      grpc_byte_buffer_destroy(ops[i].data.send_message);
+      grpc_byte_buffer_destroy(ops[i].data.send_message.send_message);
     }
     if (ops[i].op == GRPC_OP_RECV_MESSAGE) {
       grpc_byte_buffer_destroy(message);
diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c
index 2217a4f..9ac5d2a 100644
--- a/src/php/ext/grpc/server.c
+++ b/src/php/ext/grpc/server.c
@@ -49,6 +49,8 @@
 
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
+#include <grpc/slice.h>
+#include <grpc/support/alloc.h>
 
 #include "completion_queue.h"
 #include "server.h"
@@ -149,8 +151,12 @@
                          1 TSRMLS_CC);
     goto cleanup;
   }
-  php_grpc_add_property_string(result, "method", details.method, true);
-  php_grpc_add_property_string(result, "host", details.host, true);
+  char *method_text = grpc_slice_to_c_string(details.method);
+  char *host_text = grpc_slice_to_c_string(details.host);
+  php_grpc_add_property_string(result, "method", method_text, true);
+  php_grpc_add_property_string(result, "host", host_text, true);
+  gpr_free(method_text);
+  gpr_free(host_text);
 #if PHP_MAJOR_VERSION < 7
   add_property_zval(result, "call", grpc_php_wrap_call(call, true TSRMLS_CC));
   add_property_zval(result, "absolute_deadline",
diff --git a/src/php/lib/Grpc/AbstractCall.php b/src/php/lib/Grpc/AbstractCall.php
index 9f0b02b..40387ab 100644
--- a/src/php/lib/Grpc/AbstractCall.php
+++ b/src/php/lib/Grpc/AbstractCall.php
@@ -58,12 +58,11 @@
      *                              the response
      * @param array    $options     Call options (optional)
      */
-    public function __construct(
-        Channel $channel,
-        $method,
-        $deserialize,
-        array $options = []
-    ) {
+    public function __construct(Channel $channel,
+                                $method,
+                                $deserialize,
+                                array $options = [])
+    {
         if (array_key_exists('timeout', $options) &&
             is_numeric($timeout = $options['timeout'])
         ) {
@@ -127,7 +126,7 @@
      *
      * @return string The protobuf binary format
      */
-    protected function serializeMessage($data)
+    protected function _serializeMessage($data)
     {
         // Proto3 implementation
         if (method_exists($data, 'encode')) {
@@ -145,7 +144,7 @@
      *
      * @return mixed The deserialized value
      */
-    protected function deserializeResponse($value)
+    protected function _deserializeResponse($value)
     {
         if ($value === null) {
             return;
diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php
index a9e77b9..ed504f8 100644
--- a/src/php/lib/Grpc/BaseStub.php
+++ b/src/php/lib/Grpc/BaseStub.php
@@ -146,6 +146,14 @@
     }
 
     /**
+     * Close the communication channel associated with this stub.
+     */
+    public function close()
+    {
+        $this->channel->close();
+    }
+
+    /**
      * @param $new_state Connect state
      *
      * @return bool true if state is CHANNEL_READY
@@ -164,14 +172,6 @@
     }
 
     /**
-     * Close the communication channel associated with this stub.
-     */
-    public function close()
-    {
-        $this->channel->close();
-    }
-
-    /**
      * constructs the auth uri for the jwt.
      *
      * @param string $method The method string
@@ -235,7 +235,7 @@
      *
      * @return SimpleSurfaceActiveCall The active call object
      */
-    public function _simpleRequest($method,
+    protected function _simpleRequest($method,
                                    $argument,
                                    $deserialize,
                                    array $metadata = [],
@@ -270,7 +270,7 @@
      *
      * @return ClientStreamingSurfaceActiveCall The active call object
      */
-    public function _clientStreamRequest($method,
+    protected function _clientStreamRequest($method,
                                          $deserialize,
                                          array $metadata = [],
                                          array $options = [])
@@ -305,7 +305,7 @@
      *
      * @return ServerStreamingSurfaceActiveCall The active call object
      */
-    public function _serverStreamRequest($method,
+    protected function _serverStreamRequest($method,
                                          $argument,
                                          $deserialize,
                                          array $metadata = [],
@@ -339,7 +339,7 @@
      *
      * @return BidiStreamingSurfaceActiveCall The active call object
      */
-    public function _bidiRequest($method,
+    protected function _bidiRequest($method,
                                  $deserialize,
                                  array $metadata = [],
                                  array $options = [])
diff --git a/src/php/lib/Grpc/BidiStreamingCall.php b/src/php/lib/Grpc/BidiStreamingCall.php
index b03bbd2..7cb200d 100644
--- a/src/php/lib/Grpc/BidiStreamingCall.php
+++ b/src/php/lib/Grpc/BidiStreamingCall.php
@@ -69,7 +69,7 @@
             $this->metadata = $read_event->metadata;
         }
 
-        return $this->deserializeResponse($read_event->message);
+        return $this->_deserializeResponse($read_event->message);
     }
 
     /**
@@ -82,7 +82,7 @@
      */
     public function write($data, array $options = [])
     {
-        $message_array = ['message' => $this->serializeMessage($data)];
+        $message_array = ['message' => $this->_serializeMessage($data)];
         if (array_key_exists('flags', $options)) {
             $message_array['flags'] = $options['flags'];
         }
diff --git a/src/php/lib/Grpc/ClientStreamingCall.php b/src/php/lib/Grpc/ClientStreamingCall.php
index c542f08..6454cbc 100644
--- a/src/php/lib/Grpc/ClientStreamingCall.php
+++ b/src/php/lib/Grpc/ClientStreamingCall.php
@@ -63,7 +63,7 @@
      */
     public function write($data, array $options = [])
     {
-        $message_array = ['message' => $this->serializeMessage($data)];
+        $message_array = ['message' => $this->_serializeMessage($data)];
         if (array_key_exists('flags', $options)) {
             $message_array['flags'] = $options['flags'];
         }
@@ -90,6 +90,6 @@
         $status = $event->status;
         $this->trailing_metadata = $status->metadata;
 
-        return [$this->deserializeResponse($event->message), $status];
+        return [$this->_deserializeResponse($event->message), $status];
     }
 }
diff --git a/src/php/lib/Grpc/ServerStreamingCall.php b/src/php/lib/Grpc/ServerStreamingCall.php
index 406512b..8659f6b 100644
--- a/src/php/lib/Grpc/ServerStreamingCall.php
+++ b/src/php/lib/Grpc/ServerStreamingCall.php
@@ -51,7 +51,7 @@
      */
     public function start($data, array $metadata = [], array $options = [])
     {
-        $message_array = ['message' => $this->serializeMessage($data)];
+        $message_array = ['message' => $this->_serializeMessage($data)];
         if (array_key_exists('flags', $options)) {
             $message_array['flags'] = $options['flags'];
         }
@@ -73,7 +73,7 @@
             OP_RECV_MESSAGE => true,
         ])->message;
         while ($response !== null) {
-            yield $this->deserializeResponse($response);
+            yield $this->_deserializeResponse($response);
             $response = $this->call->startBatch([
                 OP_RECV_MESSAGE => true,
             ])->message;
diff --git a/src/php/lib/Grpc/UnaryCall.php b/src/php/lib/Grpc/UnaryCall.php
index 3c1cb15..b8b1ed6 100644
--- a/src/php/lib/Grpc/UnaryCall.php
+++ b/src/php/lib/Grpc/UnaryCall.php
@@ -51,7 +51,7 @@
      */
     public function start($data, array $metadata = [], array $options = [])
     {
-        $message_array = ['message' => $this->serializeMessage($data)];
+        $message_array = ['message' => $this->_serializeMessage($data)];
         if (isset($options['flags'])) {
             $message_array['flags'] = $options['flags'];
         }
@@ -79,6 +79,6 @@
         $status = $event->status;
         $this->trailing_metadata = $status->metadata;
 
-        return [$this->deserializeResponse($event->message), $status];
+        return [$this->_deserializeResponse($event->message), $status];
     }
 }
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
index 73d1ff7..246e839 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx.pxi
@@ -60,20 +60,25 @@
                   method, host, Timespec deadline not None):
     if queue.is_shutting_down:
       raise ValueError("queue must not be shutting down or shutdown")
-    cdef char *method_c_string = method
-    cdef char *host_c_string = NULL
+    cdef grpc_slice method_slice = _slice_from_bytes(method)
+    cdef grpc_slice host_slice
+    cdef grpc_slice *host_slice_ptr = NULL
     if host is not None:
-      host_c_string = host
+      host_slice = _slice_from_bytes(host)
+      host_slice_ptr = &host_slice
     cdef Call operation_call = Call()
-    operation_call.references = [self, method, host, queue]
+    operation_call.references = [self, queue]
     cdef grpc_call *parent_call = NULL
     if parent is not None:
       parent_call = parent.c_call
     with nogil:
       operation_call.c_call = grpc_channel_create_call(
           self.c_channel, parent_call, flags,
-          queue.c_completion_queue, method_c_string, host_c_string,
+          queue.c_completion_queue, method_slice, host_slice_ptr,
           deadline.c_time, NULL)
+      grpc_slice_unref(method_slice)
+      if host_slice_ptr:
+        grpc_slice_unref(host_slice)
     return operation_call
 
   def check_connectivity_state(self, bint try_to_connect):
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
index a258ba4..d8df6c2 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx.pxi
@@ -51,6 +51,7 @@
     cdef CallDetails request_call_details = None
     cdef Metadata request_metadata = None
     cdef Operations batch_operations = None
+    cdef Operation batch_operation = None
     if event.type == GRPC_QUEUE_TIMEOUT:
       return Event(
           event.type, False, None, None, None, None, False, None)
@@ -69,8 +70,15 @@
         user_tag = tag.user_tag
         operation_call = tag.operation_call
         request_call_details = tag.request_call_details
-        request_metadata = tag.request_metadata
+        if tag.request_metadata is not None:
+          request_metadata = tag.request_metadata
+          request_metadata._claim_slice_ownership()
         batch_operations = tag.batch_operations
+        if tag.batch_operations is not None:
+          for op in batch_operations.operations:
+            batch_operation = <Operation>op
+            if batch_operation._received_metadata is not None:
+              batch_operation._received_metadata._claim_slice_ownership()
         if tag.is_new_request:
           # Stuff in the tag not explicitly handled by us needs to live through
           # the life of the call
@@ -91,7 +99,7 @@
       c_deadline = gpr_inf_future(GPR_CLOCK_REALTIME)
       if deadline is not None:
         c_deadline = deadline.c_time
-      
+
       while True:
         c_timeout = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c_increment)
         if gpr_time_cmp(c_timeout, c_deadline) > 0:
@@ -100,7 +108,7 @@
           self.c_completion_queue, c_timeout, NULL)
         if event.type != GRPC_QUEUE_TIMEOUT or gpr_time_cmp(c_timeout, c_deadline) == 0:
           break;
-        
+
         # Handle any signals
         with gil:
           cpython.PyErr_CheckSignals()
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index ad76618..bbd7242 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -51,6 +51,13 @@
     pass
 
 
+cdef extern from "grpc/impl/codegen/exec_ctx_fwd.h":
+
+  struct grpc_exec_ctx:
+    # We don't care about the internals
+    pass
+
+
 cdef extern from "grpc/grpc.h":
 
   ctypedef struct grpc_slice:
@@ -60,6 +67,7 @@
 
   grpc_slice grpc_slice_ref(grpc_slice s) nogil
   void grpc_slice_unref(grpc_slice s) nogil
+  grpc_slice grpc_empty_slice() nogil
   grpc_slice grpc_slice_new(void *p, size_t len, void (*destroy)(void *)) nogil
   grpc_slice grpc_slice_new_with_len(
       void *p, size_t len, void (*destroy)(void *, size_t)) nogil
@@ -175,7 +183,7 @@
 
   ctypedef struct grpc_arg_pointer_vtable:
     void *(*copy)(void *)
-    void (*destroy)(void *)
+    void (*destroy)(grpc_exec_ctx *, void *)
     int (*cmp)(void *, void *)
 
   ctypedef struct grpc_arg_value_pointer:
@@ -217,9 +225,8 @@
     GRPC_CHANNEL_SHUTDOWN
 
   ctypedef struct grpc_metadata:
-    const char *key
-    const char *value
-    size_t value_length
+    grpc_slice key
+    grpc_slice value
     # ignore the 'internal_data.obfuscated' fields.
 
   ctypedef enum grpc_completion_type:
@@ -241,10 +248,8 @@
   void grpc_metadata_array_destroy(grpc_metadata_array *array) nogil
 
   ctypedef struct grpc_call_details:
-    char *method
-    size_t method_capacity
-    char *host
-    size_t host_capacity
+    grpc_slice method
+    grpc_slice host
     gpr_timespec deadline
 
   void grpc_call_details_init(grpc_call_details *details) nogil
@@ -268,23 +273,31 @@
     size_t trailing_metadata_count
     grpc_metadata *trailing_metadata
     grpc_status_code status
-    const char *status_details
+    grpc_slice *status_details
 
   ctypedef struct grpc_op_data_recv_status_on_client:
     grpc_metadata_array *trailing_metadata
     grpc_status_code *status
-    char **status_details
-    size_t *status_details_capacity
+    grpc_slice *status_details
 
   ctypedef struct grpc_op_data_recv_close_on_server:
     int *cancelled
 
+  ctypedef struct grpc_op_data_send_message:
+    grpc_byte_buffer *send_message
+
+  ctypedef struct grpc_op_data_receive_message:
+    grpc_byte_buffer **receive_message "recv_message"
+
+  ctypedef struct grpc_op_data_receive_initial_metadata:
+    grpc_metadata_array *receive_initial_metadata "recv_initial_metadata"
+
   union grpc_op_data:
     grpc_op_data_send_initial_metadata send_initial_metadata
-    grpc_byte_buffer *send_message
+    grpc_op_data_send_message send_message
     grpc_op_data_send_status_from_server send_status_from_server
-    grpc_metadata_array *receive_initial_metadata "recv_initial_metadata"
-    grpc_byte_buffer **receive_message "recv_message"
+    grpc_op_data_receive_initial_metadata receive_initial_metadata "recv_initial_metadata"
+    grpc_op_data_receive_message receive_message "recv_message"
     grpc_op_data_recv_status_on_client receive_status_on_client "recv_status_on_client"
     grpc_op_data_recv_close_on_server receive_close_on_server "recv_close_on_server"
 
@@ -321,9 +334,9 @@
                                              const grpc_channel_args *args,
                                              void *reserved) nogil
   grpc_call *grpc_channel_create_call(
-      grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
-      grpc_completion_queue *completion_queue, const char *method,
-      const char *host, gpr_timespec deadline, void *reserved) nogil
+    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) nogil
   grpc_connectivity_state grpc_channel_check_connectivity_state(
       grpc_channel *channel, int try_to_connect) nogil
   void grpc_channel_watch_connectivity_state(
@@ -473,8 +486,7 @@
     grpc_compression_algorithm default_compression_algorithm
 
   int grpc_compression_algorithm_parse(
-      const char *name, size_t name_length,
-      grpc_compression_algorithm *algorithm) nogil
+      grpc_slice value, grpc_compression_algorithm *algorithm) nogil
   int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm,
                                       char **name) nogil
   grpc_compression_algorithm grpc_compression_algorithm_for_level(
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
index 00ec91b..c4a1711 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd.pxi
@@ -28,6 +28,11 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
+cdef bytes _slice_bytes(grpc_slice slice)
+cdef grpc_slice _copy_slice(grpc_slice slice) nogil
+cdef grpc_slice _slice_from_bytes(bytes value) nogil
+
+
 cdef class Timespec:
 
   cdef gpr_timespec c_time
@@ -97,13 +102,13 @@
 cdef class Metadatum:
 
   cdef grpc_metadata c_metadata
-  cdef object _key, _value
+  cdef void _copy_metadatum(self, grpc_metadata *destination) nogil
 
 
 cdef class Metadata:
 
   cdef grpc_metadata_array c_metadata_array
-  cdef object metadata
+  cdef void _claim_slice_ownership(self)
 
 
 cdef class Operation:
@@ -112,8 +117,7 @@
   cdef ByteBuffer _received_message
   cdef Metadata _received_metadata
   cdef grpc_status_code _received_status_code
-  cdef char *_received_status_details
-  cdef size_t _received_status_details_capacity
+  cdef grpc_slice _status_details
   cdef int _received_cancelled
   cdef readonly bint is_valid
   cdef object references
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
index 69b837c..c3d158d 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
@@ -29,6 +29,26 @@
 
 from libc.stdint cimport intptr_t
 
+
+cdef bytes _slice_bytes(grpc_slice slice):
+  cdef void *start = grpc_slice_start_ptr(slice)
+  cdef size_t length = grpc_slice_length(slice)
+  return (<const char *>start)[:length]
+
+cdef grpc_slice _copy_slice(grpc_slice slice) nogil:
+  cdef void *start = grpc_slice_start_ptr(slice)
+  cdef size_t length = grpc_slice_length(slice)
+  return grpc_slice_from_copied_buffer(<const char *>start, length)
+
+cdef grpc_slice _slice_from_bytes(bytes value) nogil:
+  cdef const char *value_ptr
+  cdef size_t length
+  with gil:
+    value_ptr = <const char *>value
+    length = len(value)
+  return grpc_slice_from_copied_buffer(value_ptr, length)
+
+
 class ConnectivityState:
   idle = GRPC_CHANNEL_IDLE
   connecting = GRPC_CHANNEL_CONNECTING
@@ -208,17 +228,11 @@
 
   @property
   def method(self):
-    if self.c_details.method != NULL:
-      return <bytes>self.c_details.method
-    else:
-      return None
+    return _slice_bytes(self.c_details.method)
 
   @property
   def host(self):
-    if self.c_details.host != NULL:
-      return <bytes>self.c_details.host
-    else:
-      return None
+    return _slice_bytes(self.c_details.host)
 
   @property
   def deadline(self):
@@ -329,7 +343,7 @@
   return ptr
 
 
-cdef void destroy_ptr(void* ptr):
+cdef void destroy_ptr(grpc_exec_ctx* ctx, void* ptr):
   pass
 
 
@@ -402,19 +416,20 @@
 cdef class Metadatum:
 
   def __cinit__(self, bytes key, bytes value):
-    self._key = key
-    self._value = value
-    self.c_metadata.key = self._key
-    self.c_metadata.value = self._value
-    self.c_metadata.value_length = len(self._value)
+    self.c_metadata.key = _slice_from_bytes(key)
+    self.c_metadata.value = _slice_from_bytes(value)
+
+  cdef void _copy_metadatum(self, grpc_metadata *destination) nogil:
+    destination[0].key = _copy_slice(self.c_metadata.key)
+    destination[0].value = _copy_slice(self.c_metadata.value)
 
   @property
   def key(self):
-    return <bytes>self.c_metadata.key
+    return _slice_bytes(self.c_metadata.key)
 
   @property
   def value(self):
-    return <bytes>self.c_metadata.value[:self.c_metadata.value_length]
+    return _slice_bytes(self.c_metadata.value)
 
   def __len__(self):
     return 2
@@ -430,6 +445,9 @@
   def __iter__(self):
     return iter((self.key, self.value))
 
+  def __dealloc__(self):
+    grpc_slice_unref(self.c_metadata.key)
+    grpc_slice_unref(self.c_metadata.value)
 
 cdef class _MetadataIterator:
 
@@ -454,51 +472,65 @@
 
 cdef class Metadata:
 
-  def __cinit__(self, metadata):
-    grpc_init()
-    self.metadata = list(metadata)
+  def __cinit__(self, metadata_iterable):
+    with nogil:
+      grpc_init()
+      grpc_metadata_array_init(&self.c_metadata_array)
+    metadata = list(metadata_iterable)
     for metadatum in metadata:
       if not isinstance(metadatum, Metadatum):
         raise TypeError("expected list of Metadatum")
-    with nogil:
-      grpc_metadata_array_init(&self.c_metadata_array)
-    self.c_metadata_array.count = len(self.metadata)
-    self.c_metadata_array.capacity = len(self.metadata)
+    self.c_metadata_array.count = len(metadata)
+    self.c_metadata_array.capacity = len(metadata)
     with nogil:
       self.c_metadata_array.metadata = <grpc_metadata *>gpr_malloc(
           self.c_metadata_array.count*sizeof(grpc_metadata)
       )
     for i in range(self.c_metadata_array.count):
-      self.c_metadata_array.metadata[i] = (
-          (<Metadatum>self.metadata[i]).c_metadata)
+      (<Metadatum>metadata[i])._copy_metadatum(&self.c_metadata_array.metadata[i])
 
   def __dealloc__(self):
-    # this frees the allocated memory for the grpc_metadata_array (although
-    # it'd be nice if that were documented somewhere...)
-    # TODO(atash): document this in the C core
-    grpc_metadata_array_destroy(&self.c_metadata_array)
-    grpc_shutdown()
+    with nogil:
+      # this frees the allocated memory for the grpc_metadata_array (although
+      # it'd be nice if that were documented somewhere...)
+      # TODO(atash): document this in the C core
+      grpc_metadata_array_destroy(&self.c_metadata_array)
+      grpc_shutdown()
 
   def __len__(self):
     return self.c_metadata_array.count
 
   def __getitem__(self, size_t i):
-    return Metadatum(
-        key=<bytes>self.c_metadata_array.metadata[i].key,
-        value=<bytes>self.c_metadata_array.metadata[i].value[
-            :self.c_metadata_array.metadata[i].value_length])
+    if i >= self.c_metadata_array.count:
+      raise IndexError
+    key = _slice_bytes(self.c_metadata_array.metadata[i].key)
+    value = _slice_bytes(self.c_metadata_array.metadata[i].value)
+    return Metadatum(key=key, value=value)
 
   def __iter__(self):
     return _MetadataIterator(self)
 
+  cdef void _claim_slice_ownership(self):
+    cdef grpc_metadata_array new_c_metadata_array
+    grpc_metadata_array_init(&new_c_metadata_array)
+    new_c_metadata_array.metadata = <grpc_metadata *>gpr_malloc(
+        self.c_metadata_array.count*sizeof(grpc_metadata))
+    new_c_metadata_array.count = self.c_metadata_array.count
+    for i in range(self.c_metadata_array.count):
+      new_c_metadata_array.metadata[i].key = _copy_slice(
+          self.c_metadata_array.metadata[i].key)
+      new_c_metadata_array.metadata[i].value = _copy_slice(
+          self.c_metadata_array.metadata[i].value)
+    grpc_metadata_array_destroy(&self.c_metadata_array)
+    self.c_metadata_array = new_c_metadata_array
+
 
 cdef class Operation:
 
   def __cinit__(self):
     grpc_init()
     self.references = []
-    self._received_status_details = NULL
-    self._received_status_details_capacity = 0
+    self._status_details = grpc_empty_slice()
     self.is_valid = False
 
   @property
@@ -555,19 +587,13 @@
   def received_status_details(self):
     if self.c_op.type != GRPC_OP_RECV_STATUS_ON_CLIENT:
       raise TypeError("self must be an operation receiving status details")
-    if self._received_status_details:
-      return self._received_status_details
-    else:
-      return None
+    return _slice_bytes(self._status_details)
 
   @property
   def received_status_details_or_none(self):
     if self.c_op.type != GRPC_OP_RECV_STATUS_ON_CLIENT:
       return None
-    if self._received_status_details:
-      return self._received_status_details
-    else:
-      return None
+    return _slice_bytes(self._status_details)
 
   @property
   def received_cancelled(self):
@@ -583,11 +609,7 @@
     return False if self._received_cancelled == 0 else True
 
   def __dealloc__(self):
-    # We *almost* don't need to do anything; most of the objects are handled by
-    # Python. The remaining one(s) are primitive fields filled in by GRPC core.
-    # This means that we need to clean up after receive_status_on_client.
-    if self.c_op.type == GRPC_OP_RECV_STATUS_ON_CLIENT:
-      gpr_free(self._received_status_details)
+    grpc_slice_unref(self._status_details)
     grpc_shutdown()
 
 def operation_send_initial_metadata(Metadata metadata, int flags):
@@ -606,7 +628,7 @@
   op.c_op.type = GRPC_OP_SEND_MESSAGE
   op.c_op.flags = flags
   byte_buffer = ByteBuffer(data)
-  op.c_op.data.send_message = byte_buffer.c_byte_buffer
+  op.c_op.data.send_message.send_message = byte_buffer.c_byte_buffer
   op.references.append(byte_buffer)
   op.is_valid = True
   return op
@@ -628,9 +650,10 @@
   op.c_op.data.send_status_from_server.trailing_metadata = (
       metadata.c_metadata_array.metadata)
   op.c_op.data.send_status_from_server.status = code
-  op.c_op.data.send_status_from_server.status_details = details
+  grpc_slice_unref(op._status_details)
+  op._status_details = _slice_from_bytes(details)
+  op.c_op.data.send_status_from_server.status_details = &op._status_details
   op.references.append(metadata)
-  op.references.append(details)
   op.is_valid = True
   return op
 
@@ -639,7 +662,7 @@
   op.c_op.type = GRPC_OP_RECV_INITIAL_METADATA
   op.c_op.flags = flags
   op._received_metadata = Metadata([])
-  op.c_op.data.receive_initial_metadata = (
+  op.c_op.data.receive_initial_metadata.receive_initial_metadata = (
       &op._received_metadata.c_metadata_array)
   op.is_valid = True
   return op
@@ -652,7 +675,8 @@
   # n.b. the c_op.data.receive_message field needs to be deleted by us,
   # anyway, so we just let that be handled by the ByteBuffer() we allocated
   # the line before.
-  op.c_op.data.receive_message = &op._received_message.c_byte_buffer
+  op.c_op.data.receive_message.receive_message = (
+      &op._received_message.c_byte_buffer)
   op.is_valid = True
   return op
 
@@ -666,9 +690,7 @@
   op.c_op.data.receive_status_on_client.status = (
       &op._received_status_code)
   op.c_op.data.receive_status_on_client.status_details = (
-      &op._received_status_details)
-  op.c_op.data.receive_status_on_client.status_details_capacity = (
-      &op._received_status_details_capacity)
+      &op._status_details)
   op.is_valid = True
   return op
 
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index cbbe2dc..31551e0 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.py
@@ -592,6 +592,8 @@
 
 
 def _handle_call(rpc_event, generic_handlers, thread_pool):
+    if not rpc_event.success:
+        return None
     if rpc_event.request_call_details.method is not None:
         method_handler = _find_method_handler(rpc_event, generic_handlers)
         if method_handler is None:
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index 9d470e1..68e40da 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -159,6 +159,8 @@
   'src/core/lib/slice/percent_encoding.c',
   'src/core/lib/slice/slice.c',
   'src/core/lib/slice/slice_buffer.c',
+  'src/core/lib/slice/slice_hash_table.c',
+  'src/core/lib/slice/slice_intern.c',
   'src/core/lib/slice/slice_string_helpers.c',
   'src/core/lib/surface/alarm.c',
   'src/core/lib/surface/api_trace.c',
@@ -180,12 +182,13 @@
   'src/core/lib/surface/version.c',
   'src/core/lib/transport/byte_stream.c',
   'src/core/lib/transport/connectivity_state.c',
-  'src/core/lib/transport/mdstr_hash_table.c',
+  'src/core/lib/transport/error_utils.c',
   'src/core/lib/transport/metadata.c',
   'src/core/lib/transport/metadata_batch.c',
   'src/core/lib/transport/pid_controller.c',
   'src/core/lib/transport/service_config.c',
   'src/core/lib/transport/static_metadata.c',
+  'src/core/lib/transport/status_conversion.c',
   'src/core/lib/transport/timeout_encoding.c',
   'src/core/lib/transport/transport.c',
   'src/core/lib/transport/transport_op_string.c',
@@ -206,7 +209,6 @@
   'src/core/ext/transport/chttp2/transport/huffsyms.c',
   'src/core/ext/transport/chttp2/transport/incoming_metadata.c',
   'src/core/ext/transport/chttp2/transport/parsing.c',
-  'src/core/ext/transport/chttp2/transport/status_conversion.c',
   'src/core/ext/transport/chttp2/transport/stream_lists.c',
   'src/core/ext/transport/chttp2/transport/stream_map.c',
   'src/core/ext/transport/chttp2/transport/varint.c',
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c
index 47fd6d9..65fa2f2 100644
--- a/src/ruby/ext/grpc/rb_byte_buffer.c
+++ b/src/ruby/ext/grpc/rb_byte_buffer.c
@@ -68,3 +68,10 @@
   grpc_byte_buffer_reader_destroy(&reader);
   return rb_string;
 }
+
+VALUE grpc_rb_slice_to_ruby_string(grpc_slice slice) {
+  if (GRPC_SLICE_START_PTR(slice) == NULL) {
+    rb_raise(rb_eRuntimeError, "attempt to convert uninitialized grpc_slice to ruby string");
+  }
+  return rb_str_new((char*)GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice));
+}
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.h b/src/ruby/ext/grpc/rb_byte_buffer.h
index c7ddd76..fac68fe 100644
--- a/src/ruby/ext/grpc/rb_byte_buffer.h
+++ b/src/ruby/ext/grpc/rb_byte_buffer.h
@@ -44,4 +44,7 @@
 /* Converts a grpc_byte_buffer to a ruby string */
 VALUE grpc_rb_byte_buffer_to_s(grpc_byte_buffer *buffer);
 
+/* Converts a grpc_slice to a ruby string */
+VALUE grpc_rb_slice_to_ruby_string(grpc_slice slice);
+
 #endif /* GRPC_RB_BYTE_BUFFER_H_ */
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
index 67a42af..82d340b 100644
--- a/src/ruby/ext/grpc/rb_call.c
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -121,8 +121,8 @@
   size_t i, datasize = sizeof(grpc_metadata_array);
   for (i = 0; i < ary->count; ++i) {
     const grpc_metadata *const md = &ary->metadata[i];
-    datasize += strlen(md->key);
-    datasize += md->value_length;
+    datasize += GRPC_SLICE_LENGTH(md->key);
+    datasize += GRPC_SLICE_LENGTH(md->value);
   }
   datasize += ary->capacity * sizeof(grpc_metadata);
   return datasize;
@@ -386,23 +386,23 @@
   grpc_metadata_array *md_ary = NULL;
   long array_length;
   long i;
-  char *key_str;
-  size_t key_len;
-  char *value_str;
-  size_t value_len;
+  grpc_slice key_slice;
+  grpc_slice value_slice;
+  char* tmp_str;
 
   if (TYPE(key) == T_SYMBOL) {
-    key_str = (char *)rb_id2name(SYM2ID(key));
-    key_len = strlen(key_str);
-  } else { /* StringValueCStr does all other type exclusions for us */
-    key_str = StringValueCStr(key);
-    key_len = RSTRING_LEN(key);
+    key_slice = grpc_slice_from_static_string(rb_id2name(SYM2ID(key)));
+  } else if (TYPE(key) == T_STRING) {
+    key_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(key), RSTRING_LEN(key));
+  } else {
+    rb_raise(rb_eTypeError, "grpc_rb_md_ary_fill_hash_cb: bad type for key parameter");
   }
 
-  if (!grpc_header_key_is_legal(key_str, key_len)) {
+  if (!grpc_header_key_is_legal(key_slice)) {
+    tmp_str = grpc_slice_to_c_string(key_slice);
     rb_raise(rb_eArgError,
              "'%s' is an invalid header key, must match [a-z0-9-_.]+",
-             key_str);
+             tmp_str);
     return ST_STOP;
   }
 
@@ -414,33 +414,31 @@
     array_length = RARRAY_LEN(val);
     /* If the value is an array, add capacity for each value in the array */
     for (i = 0; i < array_length; i++) {
-      value_str = RSTRING_PTR(rb_ary_entry(val, i));
-      value_len = RSTRING_LEN(rb_ary_entry(val, i));
-      if (!grpc_is_binary_header(key_str, key_len) &&
-          !grpc_header_nonbin_value_is_legal(value_str, value_len)) {
+      value_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(rb_ary_entry(val, i)), RSTRING_LEN(rb_ary_entry(val, i)));
+      if (!grpc_is_binary_header(key_slice) &&
+          !grpc_header_nonbin_value_is_legal(value_slice)) {
         // The value has invalid characters
+        tmp_str = grpc_slice_to_c_string(value_slice);
         rb_raise(rb_eArgError,
-                 "Header value '%s' has invalid characters", value_str);
+                 "Header value '%s' has invalid characters", tmp_str);
         return ST_STOP;
       }
-      md_ary->metadata[md_ary->count].key = key_str;
-      md_ary->metadata[md_ary->count].value = value_str;
-      md_ary->metadata[md_ary->count].value_length = value_len;
+      md_ary->metadata[md_ary->count].key = key_slice;
+      md_ary->metadata[md_ary->count].value = value_slice;
       md_ary->count += 1;
     }
   } else if (TYPE(val) == T_STRING) {
-    value_str = RSTRING_PTR(val);
-    value_len = RSTRING_LEN(val);
-    if (!grpc_is_binary_header(key_str, key_len) &&
-        !grpc_header_nonbin_value_is_legal(value_str, value_len)) {
+    value_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(val), RSTRING_LEN(val));
+    if (!grpc_is_binary_header(key_slice) &&
+        !grpc_header_nonbin_value_is_legal(value_slice)) {
       // The value has invalid characters
+      tmp_str = grpc_slice_to_c_string(value_slice);
       rb_raise(rb_eArgError,
-               "Header value '%s' has invalid characters", value_str);
+               "Header value '%s' has invalid characters", tmp_str);
       return ST_STOP;
     }
-    md_ary->metadata[md_ary->count].key = key_str;
-    md_ary->metadata[md_ary->count].value = value_str;
-    md_ary->metadata[md_ary->count].value_length = value_len;
+    md_ary->metadata[md_ary->count].key = key_slice;
+    md_ary->metadata[md_ary->count].value = value_slice;
     md_ary->count += 1;
   } else {
     rb_raise(rb_eArgError,
@@ -506,22 +504,19 @@
   size_t i;
 
   for (i = 0; i < md_ary->count; i++) {
-    key = rb_str_new2(md_ary->metadata[i].key);
+    key = grpc_rb_slice_to_ruby_string(md_ary->metadata[i].key);
     value = rb_hash_aref(result, key);
     if (value == Qnil) {
-      value = rb_str_new(md_ary->metadata[i].value,
-                         md_ary->metadata[i].value_length);
+      value = grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value);
       rb_hash_aset(result, key, value);
     } else if (TYPE(value) == T_ARRAY) {
       /* Add the string to the returned array */
-      rb_ary_push(value, rb_str_new(md_ary->metadata[i].value,
-                                    md_ary->metadata[i].value_length));
+      rb_ary_push(value, grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value));
     } else {
       /* Add the current value with this key and the new one to an array */
       new_ary = rb_ary_new();
       rb_ary_push(new_ary, value);
-      rb_ary_push(new_ary, rb_str_new(md_ary->metadata[i].value,
-                                      md_ary->metadata[i].value_length));
+      rb_ary_push(new_ary, grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value));
       rb_hash_aset(result, key, new_ary);
     }
   }
@@ -563,6 +558,7 @@
 */
 static void grpc_rb_op_update_status_from_server(grpc_op *op,
                                                  grpc_metadata_array *md_ary,
+                                                 grpc_slice *send_status_details,
                                                  VALUE status) {
   VALUE code = rb_struct_aref(status, sym_code);
   VALUE details = rb_struct_aref(status, sym_details);
@@ -579,8 +575,11 @@
              rb_obj_classname(code));
     return;
   }
+
+  *send_status_details = grpc_slice_from_copied_buffer(RSTRING_PTR(details), RSTRING_LEN(details));
+
   op->data.send_status_from_server.status = NUM2INT(code);
-  op->data.send_status_from_server.status_details = StringValueCStr(details);
+  op->data.send_status_from_server.status_details = send_status_details;
   grpc_rb_md_ary_convert(metadata_hash, md_ary);
   op->data.send_status_from_server.trailing_metadata_count = md_ary->count;
   op->data.send_status_from_server.trailing_metadata = md_ary->metadata;
@@ -603,9 +602,9 @@
   grpc_metadata_array recv_trailing_metadata;
   int recv_cancelled;
   grpc_status_code recv_status;
-  char *recv_status_details;
-  size_t recv_status_details_capacity;
+  grpc_slice recv_status_details;
   unsigned write_flag;
+  grpc_slice send_status_details;
 } run_batch_stack;
 
 /* grpc_run_batch_stack_init ensures the run_batch_stack is properly
@@ -631,8 +630,12 @@
   grpc_metadata_array_destroy(&st->recv_metadata);
   grpc_metadata_array_destroy(&st->recv_trailing_metadata);
 
-  if (st->recv_status_details != NULL) {
-    gpr_free(st->recv_status_details);
+  if (GRPC_SLICE_START_PTR(st->send_status_details) != NULL) {
+    grpc_slice_unref(st->send_status_details);
+  }
+
+  if (GRPC_SLICE_START_PTR(st->recv_status_details) != NULL) {
+    grpc_slice_unref(st->recv_status_details);
   }
 
   if (st->recv_message != NULL) {
@@ -641,7 +644,7 @@
 
   for (i = 0; i < st->op_num; i++) {
     if (st->ops[i].op == GRPC_OP_SEND_MESSAGE) {
-      grpc_byte_buffer_destroy(st->ops[i].data.send_message);
+      grpc_byte_buffer_destroy(st->ops[i].data.send_message.send_message);
     }
   }
 }
@@ -673,8 +676,9 @@
             st->send_metadata.metadata;
         break;
       case GRPC_OP_SEND_MESSAGE:
-        st->ops[st->op_num].data.send_message = grpc_rb_s_to_byte_buffer(
-            RSTRING_PTR(this_value), RSTRING_LEN(this_value));
+        st->ops[st->op_num].data.send_message.send_message =
+            grpc_rb_s_to_byte_buffer(RSTRING_PTR(this_value),
+                                     RSTRING_LEN(this_value));
         st->ops[st->op_num].flags = st->write_flag;
         break;
       case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
@@ -683,13 +687,14 @@
         /* N.B. later there is no need to explicitly delete the metadata keys
          * and values, they are references to data in ruby objects. */
         grpc_rb_op_update_status_from_server(
-            &st->ops[st->op_num], &st->send_trailing_metadata, this_value);
+            &st->ops[st->op_num], &st->send_trailing_metadata, &st->send_status_details, this_value);
         break;
       case GRPC_OP_RECV_INITIAL_METADATA:
-        st->ops[st->op_num].data.recv_initial_metadata = &st->recv_metadata;
+        st->ops[st->op_num].data.recv_initial_metadata.recv_initial_metadata =
+            &st->recv_metadata;
         break;
       case GRPC_OP_RECV_MESSAGE:
-        st->ops[st->op_num].data.recv_message = &st->recv_message;
+        st->ops[st->op_num].data.recv_message.recv_message = &st->recv_message;
         break;
       case GRPC_OP_RECV_STATUS_ON_CLIENT:
         st->ops[st->op_num].data.recv_status_on_client.trailing_metadata =
@@ -698,8 +703,6 @@
             &st->recv_status;
         st->ops[st->op_num].data.recv_status_on_client.status_details =
             &st->recv_status_details;
-        st->ops[st->op_num].data.recv_status_on_client.status_details_capacity =
-            &st->recv_status_details_capacity;
         break;
       case GRPC_OP_RECV_CLOSE_ON_SERVER:
         st->ops[st->op_num].data.recv_close_on_server.cancelled =
@@ -747,9 +750,9 @@
         rb_struct_aset(
             result, sym_status,
             rb_struct_new(grpc_rb_sStatus, UINT2NUM(st->recv_status),
-                          (st->recv_status_details == NULL
+                          (GRPC_SLICE_START_PTR(st->recv_status_details) == NULL
                                ? Qnil
-                               : rb_str_new2(st->recv_status_details)),
+                               : grpc_rb_slice_to_ruby_string(st->recv_status_details)),
                           grpc_rb_md_ary_to_h(&st->recv_trailing_metadata),
                           NULL));
         break;
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index 3b2b88e..84e43d3 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -35,6 +35,7 @@
 
 #include "rb_grpc_imports.generated.h"
 #include "rb_channel.h"
+#include "rb_byte_buffer.h"
 
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
@@ -252,10 +253,14 @@
   grpc_channel *ch = NULL;
   grpc_completion_queue *cq = NULL;
   int flags = GRPC_PROPAGATE_DEFAULTS;
-  char *method_chars = StringValueCStr(method);
-  char *host_chars = NULL;
+  grpc_slice method_slice;
+  grpc_slice host_slice;
+  grpc_slice *host_slice_ptr = NULL;
+  char* tmp_str = NULL;
+
   if (host != Qnil) {
-    host_chars = StringValueCStr(host);
+    host_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(host), RSTRING_LEN(host));
+    host_slice_ptr = &host_slice;
   }
   if (mask != Qnil) {
     flags = NUM2UINT(mask);
@@ -272,15 +277,25 @@
     return Qnil;
   }
 
-  call = grpc_channel_create_call(ch, parent_call, flags, cq, method_chars,
-                                  host_chars, grpc_rb_time_timeval(
+  method_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(method), RSTRING_LEN(method));
+
+  call = grpc_channel_create_call(ch, parent_call, flags, cq, method_slice,
+                                  host_slice_ptr, grpc_rb_time_timeval(
                                       deadline,
                                       /* absolute time */ 0), NULL);
+
   if (call == NULL) {
+    tmp_str = grpc_slice_to_c_string(method_slice);
     rb_raise(rb_eRuntimeError, "cannot create call with method %s",
-             method_chars);
+             tmp_str);
     return Qnil;
   }
+
+  grpc_slice_unref(method_slice);
+  if (host_slice_ptr != NULL) {
+    grpc_slice_unref(host_slice);
+  }
+
   res = grpc_rb_wrap_call(call, cq);
 
   /* Make this channel an instance attribute of the call so that it is not GCed
diff --git a/src/ruby/ext/grpc/rb_compression_options.c b/src/ruby/ext/grpc/rb_compression_options.c
index 6200dba..6b2467e 100644
--- a/src/ruby/ext/grpc/rb_compression_options.c
+++ b/src/ruby/ext/grpc/rb_compression_options.c
@@ -34,6 +34,7 @@
 #include <ruby/ruby.h>
 
 #include "rb_compression_options.h"
+#include "rb_byte_buffer.h"
 #include "rb_grpc_imports.generated.h"
 
 #include <grpc/compression.h>
@@ -168,9 +169,9 @@
  * Raises an error if the name of the algorithm passed in is invalid. */
 void grpc_rb_compression_options_algorithm_name_to_value_internal(
     grpc_compression_algorithm *algorithm_value, VALUE algorithm_name) {
-  char *name_str = NULL;
-  long name_len = 0;
+  grpc_slice name_slice;
   VALUE algorithm_name_as_string = Qnil;
+  char *tmp_str = NULL;
 
   Check_Type(algorithm_name, T_SYMBOL);
 
@@ -178,16 +179,18 @@
    * correct C string out of it. */
   algorithm_name_as_string = rb_funcall(algorithm_name, rb_intern("to_s"), 0);
 
-  name_str = RSTRING_PTR(algorithm_name_as_string);
-  name_len = RSTRING_LEN(algorithm_name_as_string);
+  name_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(algorithm_name_as_string), RSTRING_LEN(algorithm_name_as_string));
 
   /* Raise an error if the name isn't recognized as a compression algorithm by
    * the algorithm parse function
    * in GRPC core. */
-  if (!grpc_compression_algorithm_parse(name_str, name_len, algorithm_value)) {
+  if(!grpc_compression_algorithm_parse(name_slice, algorithm_value)) {
+    tmp_str = grpc_slice_to_c_string(name_slice);
     rb_raise(rb_eNameError, "Invalid compression algorithm name: %s",
-             StringValueCStr(algorithm_name_as_string));
+             tmp_str);
   }
+
+  grpc_slice_unref(name_slice);
 }
 
 /* Indicates whether a given algorithm is enabled on this instance, given the
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index b2d536e..d8d76cc 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -179,17 +179,30 @@
 grpc_slice_new_with_user_data_type grpc_slice_new_with_user_data_import;
 grpc_slice_new_with_len_type grpc_slice_new_with_len_import;
 grpc_slice_malloc_type grpc_slice_malloc_import;
+grpc_slice_intern_type grpc_slice_intern_import;
 grpc_slice_from_copied_string_type grpc_slice_from_copied_string_import;
 grpc_slice_from_copied_buffer_type grpc_slice_from_copied_buffer_import;
 grpc_slice_from_static_string_type grpc_slice_from_static_string_import;
+grpc_slice_from_static_buffer_type grpc_slice_from_static_buffer_import;
 grpc_slice_sub_type grpc_slice_sub_import;
 grpc_slice_sub_no_ref_type grpc_slice_sub_no_ref_import;
 grpc_slice_split_tail_type grpc_slice_split_tail_import;
 grpc_slice_split_head_type grpc_slice_split_head_import;
-gpr_empty_slice_type gpr_empty_slice_import;
+grpc_empty_slice_type grpc_empty_slice_import;
+grpc_slice_default_hash_impl_type grpc_slice_default_hash_impl_import;
+grpc_slice_default_eq_impl_type grpc_slice_default_eq_impl_import;
+grpc_slice_eq_type grpc_slice_eq_import;
 grpc_slice_cmp_type grpc_slice_cmp_import;
 grpc_slice_str_cmp_type grpc_slice_str_cmp_import;
+grpc_slice_buf_cmp_type grpc_slice_buf_cmp_import;
+grpc_slice_buf_start_eq_type grpc_slice_buf_start_eq_import;
+grpc_slice_rchr_type grpc_slice_rchr_import;
+grpc_slice_chr_type grpc_slice_chr_import;
+grpc_slice_slice_type grpc_slice_slice_import;
+grpc_slice_hash_type grpc_slice_hash_import;
 grpc_slice_is_equivalent_type grpc_slice_is_equivalent_import;
+grpc_slice_dup_type grpc_slice_dup_import;
+grpc_slice_to_c_string_type grpc_slice_to_c_string_import;
 grpc_slice_buffer_init_type grpc_slice_buffer_init_import;
 grpc_slice_buffer_destroy_type grpc_slice_buffer_destroy_import;
 grpc_slice_buffer_add_type grpc_slice_buffer_add_import;
@@ -457,17 +470,30 @@
   grpc_slice_new_with_user_data_import = (grpc_slice_new_with_user_data_type) GetProcAddress(library, "grpc_slice_new_with_user_data");
   grpc_slice_new_with_len_import = (grpc_slice_new_with_len_type) GetProcAddress(library, "grpc_slice_new_with_len");
   grpc_slice_malloc_import = (grpc_slice_malloc_type) GetProcAddress(library, "grpc_slice_malloc");
+  grpc_slice_intern_import = (grpc_slice_intern_type) GetProcAddress(library, "grpc_slice_intern");
   grpc_slice_from_copied_string_import = (grpc_slice_from_copied_string_type) GetProcAddress(library, "grpc_slice_from_copied_string");
   grpc_slice_from_copied_buffer_import = (grpc_slice_from_copied_buffer_type) GetProcAddress(library, "grpc_slice_from_copied_buffer");
   grpc_slice_from_static_string_import = (grpc_slice_from_static_string_type) GetProcAddress(library, "grpc_slice_from_static_string");
+  grpc_slice_from_static_buffer_import = (grpc_slice_from_static_buffer_type) GetProcAddress(library, "grpc_slice_from_static_buffer");
   grpc_slice_sub_import = (grpc_slice_sub_type) GetProcAddress(library, "grpc_slice_sub");
   grpc_slice_sub_no_ref_import = (grpc_slice_sub_no_ref_type) GetProcAddress(library, "grpc_slice_sub_no_ref");
   grpc_slice_split_tail_import = (grpc_slice_split_tail_type) GetProcAddress(library, "grpc_slice_split_tail");
   grpc_slice_split_head_import = (grpc_slice_split_head_type) GetProcAddress(library, "grpc_slice_split_head");
-  gpr_empty_slice_import = (gpr_empty_slice_type) GetProcAddress(library, "gpr_empty_slice");
+  grpc_empty_slice_import = (grpc_empty_slice_type) GetProcAddress(library, "grpc_empty_slice");
+  grpc_slice_default_hash_impl_import = (grpc_slice_default_hash_impl_type) GetProcAddress(library, "grpc_slice_default_hash_impl");
+  grpc_slice_default_eq_impl_import = (grpc_slice_default_eq_impl_type) GetProcAddress(library, "grpc_slice_default_eq_impl");
+  grpc_slice_eq_import = (grpc_slice_eq_type) GetProcAddress(library, "grpc_slice_eq");
   grpc_slice_cmp_import = (grpc_slice_cmp_type) GetProcAddress(library, "grpc_slice_cmp");
   grpc_slice_str_cmp_import = (grpc_slice_str_cmp_type) GetProcAddress(library, "grpc_slice_str_cmp");
+  grpc_slice_buf_cmp_import = (grpc_slice_buf_cmp_type) GetProcAddress(library, "grpc_slice_buf_cmp");
+  grpc_slice_buf_start_eq_import = (grpc_slice_buf_start_eq_type) GetProcAddress(library, "grpc_slice_buf_start_eq");
+  grpc_slice_rchr_import = (grpc_slice_rchr_type) GetProcAddress(library, "grpc_slice_rchr");
+  grpc_slice_chr_import = (grpc_slice_chr_type) GetProcAddress(library, "grpc_slice_chr");
+  grpc_slice_slice_import = (grpc_slice_slice_type) GetProcAddress(library, "grpc_slice_slice");
+  grpc_slice_hash_import = (grpc_slice_hash_type) GetProcAddress(library, "grpc_slice_hash");
   grpc_slice_is_equivalent_import = (grpc_slice_is_equivalent_type) GetProcAddress(library, "grpc_slice_is_equivalent");
+  grpc_slice_dup_import = (grpc_slice_dup_type) GetProcAddress(library, "grpc_slice_dup");
+  grpc_slice_to_c_string_import = (grpc_slice_to_c_string_type) GetProcAddress(library, "grpc_slice_to_c_string");
   grpc_slice_buffer_init_import = (grpc_slice_buffer_init_type) GetProcAddress(library, "grpc_slice_buffer_init");
   grpc_slice_buffer_destroy_import = (grpc_slice_buffer_destroy_type) GetProcAddress(library, "grpc_slice_buffer_destroy");
   grpc_slice_buffer_add_import = (grpc_slice_buffer_add_type) GetProcAddress(library, "grpc_slice_buffer_add");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index 4f496b7..4eab963 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -176,7 +176,7 @@
 typedef void(*census_record_values_type)(census_context *context, census_value *values, size_t nvalues);
 extern census_record_values_type census_record_values_import;
 #define census_record_values census_record_values_import
-typedef int(*grpc_compression_algorithm_parse_type)(const char *name, size_t name_length, grpc_compression_algorithm *algorithm);
+typedef int(*grpc_compression_algorithm_parse_type)(grpc_slice value, grpc_compression_algorithm *algorithm);
 extern grpc_compression_algorithm_parse_type grpc_compression_algorithm_parse_import;
 #define grpc_compression_algorithm_parse grpc_compression_algorithm_parse_import
 typedef int(*grpc_compression_algorithm_name_type)(grpc_compression_algorithm algorithm, char **name);
@@ -254,7 +254,7 @@
 typedef void(*grpc_channel_watch_connectivity_state_type)(grpc_channel *channel, grpc_connectivity_state last_observed_state, gpr_timespec deadline, grpc_completion_queue *cq, void *tag);
 extern grpc_channel_watch_connectivity_state_type grpc_channel_watch_connectivity_state_import;
 #define grpc_channel_watch_connectivity_state grpc_channel_watch_connectivity_state_import
-typedef grpc_call *(*grpc_channel_create_call_type)(grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask, grpc_completion_queue *completion_queue, const char *method, const char *host, gpr_timespec deadline, void *reserved);
+typedef grpc_call *(*grpc_channel_create_call_type)(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);
 extern grpc_channel_create_call_type grpc_channel_create_call_import;
 #define grpc_channel_create_call grpc_channel_create_call_import
 typedef void(*grpc_channel_ping_type)(grpc_channel *channel, grpc_completion_queue *cq, void *tag, void *reserved);
@@ -338,13 +338,13 @@
 typedef int(*grpc_tracer_set_enabled_type)(const char *name, int enabled);
 extern grpc_tracer_set_enabled_type grpc_tracer_set_enabled_import;
 #define grpc_tracer_set_enabled grpc_tracer_set_enabled_import
-typedef int(*grpc_header_key_is_legal_type)(const char *key, size_t length);
+typedef int(*grpc_header_key_is_legal_type)(grpc_slice slice);
 extern grpc_header_key_is_legal_type grpc_header_key_is_legal_import;
 #define grpc_header_key_is_legal grpc_header_key_is_legal_import
-typedef int(*grpc_header_nonbin_value_is_legal_type)(const char *value, size_t length);
+typedef int(*grpc_header_nonbin_value_is_legal_type)(grpc_slice slice);
 extern grpc_header_nonbin_value_is_legal_type grpc_header_nonbin_value_is_legal_import;
 #define grpc_header_nonbin_value_is_legal grpc_header_nonbin_value_is_legal_import
-typedef int(*grpc_is_binary_header_type)(const char *key, size_t length);
+typedef int(*grpc_is_binary_header_type)(grpc_slice slice);
 extern grpc_is_binary_header_type grpc_is_binary_header_import;
 #define grpc_is_binary_header grpc_is_binary_header_import
 typedef const char *(*grpc_call_error_to_string_type)(grpc_call_error error);
@@ -488,6 +488,9 @@
 typedef grpc_slice(*grpc_slice_malloc_type)(size_t length);
 extern grpc_slice_malloc_type grpc_slice_malloc_import;
 #define grpc_slice_malloc grpc_slice_malloc_import
+typedef grpc_slice(*grpc_slice_intern_type)(grpc_slice slice);
+extern grpc_slice_intern_type grpc_slice_intern_import;
+#define grpc_slice_intern grpc_slice_intern_import
 typedef grpc_slice(*grpc_slice_from_copied_string_type)(const char *source);
 extern grpc_slice_from_copied_string_type grpc_slice_from_copied_string_import;
 #define grpc_slice_from_copied_string grpc_slice_from_copied_string_import
@@ -497,6 +500,9 @@
 typedef grpc_slice(*grpc_slice_from_static_string_type)(const char *source);
 extern grpc_slice_from_static_string_type grpc_slice_from_static_string_import;
 #define grpc_slice_from_static_string grpc_slice_from_static_string_import
+typedef grpc_slice(*grpc_slice_from_static_buffer_type)(const void *source, size_t len);
+extern grpc_slice_from_static_buffer_type grpc_slice_from_static_buffer_import;
+#define grpc_slice_from_static_buffer grpc_slice_from_static_buffer_import
 typedef grpc_slice(*grpc_slice_sub_type)(grpc_slice s, size_t begin, size_t end);
 extern grpc_slice_sub_type grpc_slice_sub_import;
 #define grpc_slice_sub grpc_slice_sub_import
@@ -509,18 +515,51 @@
 typedef grpc_slice(*grpc_slice_split_head_type)(grpc_slice *s, size_t split);
 extern grpc_slice_split_head_type grpc_slice_split_head_import;
 #define grpc_slice_split_head grpc_slice_split_head_import
-typedef grpc_slice(*gpr_empty_slice_type)(void);
-extern gpr_empty_slice_type gpr_empty_slice_import;
-#define gpr_empty_slice gpr_empty_slice_import
+typedef grpc_slice(*grpc_empty_slice_type)(void);
+extern grpc_empty_slice_type grpc_empty_slice_import;
+#define grpc_empty_slice grpc_empty_slice_import
+typedef uint32_t(*grpc_slice_default_hash_impl_type)(grpc_slice s);
+extern grpc_slice_default_hash_impl_type grpc_slice_default_hash_impl_import;
+#define grpc_slice_default_hash_impl grpc_slice_default_hash_impl_import
+typedef int(*grpc_slice_default_eq_impl_type)(grpc_slice a, grpc_slice b);
+extern grpc_slice_default_eq_impl_type grpc_slice_default_eq_impl_import;
+#define grpc_slice_default_eq_impl grpc_slice_default_eq_impl_import
+typedef int(*grpc_slice_eq_type)(grpc_slice a, grpc_slice b);
+extern grpc_slice_eq_type grpc_slice_eq_import;
+#define grpc_slice_eq grpc_slice_eq_import
 typedef int(*grpc_slice_cmp_type)(grpc_slice a, grpc_slice b);
 extern grpc_slice_cmp_type grpc_slice_cmp_import;
 #define grpc_slice_cmp grpc_slice_cmp_import
 typedef int(*grpc_slice_str_cmp_type)(grpc_slice a, const char *b);
 extern grpc_slice_str_cmp_type grpc_slice_str_cmp_import;
 #define grpc_slice_str_cmp grpc_slice_str_cmp_import
+typedef int(*grpc_slice_buf_cmp_type)(grpc_slice a, const void *b, size_t blen);
+extern grpc_slice_buf_cmp_type grpc_slice_buf_cmp_import;
+#define grpc_slice_buf_cmp grpc_slice_buf_cmp_import
+typedef int(*grpc_slice_buf_start_eq_type)(grpc_slice a, const void *b, size_t blen);
+extern grpc_slice_buf_start_eq_type grpc_slice_buf_start_eq_import;
+#define grpc_slice_buf_start_eq grpc_slice_buf_start_eq_import
+typedef int(*grpc_slice_rchr_type)(grpc_slice s, char c);
+extern grpc_slice_rchr_type grpc_slice_rchr_import;
+#define grpc_slice_rchr grpc_slice_rchr_import
+typedef int(*grpc_slice_chr_type)(grpc_slice s, char c);
+extern grpc_slice_chr_type grpc_slice_chr_import;
+#define grpc_slice_chr grpc_slice_chr_import
+typedef int(*grpc_slice_slice_type)(grpc_slice haystack, grpc_slice needle);
+extern grpc_slice_slice_type grpc_slice_slice_import;
+#define grpc_slice_slice grpc_slice_slice_import
+typedef uint32_t(*grpc_slice_hash_type)(grpc_slice s);
+extern grpc_slice_hash_type grpc_slice_hash_import;
+#define grpc_slice_hash grpc_slice_hash_import
 typedef int(*grpc_slice_is_equivalent_type)(grpc_slice a, grpc_slice b);
 extern grpc_slice_is_equivalent_type grpc_slice_is_equivalent_import;
 #define grpc_slice_is_equivalent grpc_slice_is_equivalent_import
+typedef grpc_slice(*grpc_slice_dup_type)(grpc_slice a);
+extern grpc_slice_dup_type grpc_slice_dup_import;
+#define grpc_slice_dup grpc_slice_dup_import
+typedef char *(*grpc_slice_to_c_string_type)(grpc_slice s);
+extern grpc_slice_to_c_string_type grpc_slice_to_c_string_import;
+#define grpc_slice_to_c_string grpc_slice_to_c_string_import
 typedef void(*grpc_slice_buffer_init_type)(grpc_slice_buffer *sb);
 extern grpc_slice_buffer_init_type grpc_slice_buffer_init_import;
 #define grpc_slice_buffer_init grpc_slice_buffer_init_import
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index c7b112c..7b2f5774 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -44,6 +44,7 @@
 #include "rb_channel_args.h"
 #include "rb_completion_queue.h"
 #include "rb_server_credentials.h"
+#include "rb_byte_buffer.h"
 #include "rb_grpc.h"
 
 /* grpc_rb_cServer is the ruby class that proxies grpc_server. */
@@ -166,8 +167,6 @@
   MEMZERO(st, request_call_stack, 1);
   grpc_metadata_array_init(&st->md_ary);
   grpc_call_details_init(&st->details);
-  st->details.method = NULL;
-  st->details.host = NULL;
 }
 
 /* grpc_request_call_stack_cleanup ensures the request_call_stack is properly
@@ -191,6 +190,7 @@
   void *tag = (void*)&st;
   grpc_completion_queue *call_queue = grpc_completion_queue_create(NULL);
   gpr_timespec deadline;
+
   TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
   if (s->wrapped == NULL) {
     rb_raise(rb_eRuntimeError, "destroyed!");
@@ -218,11 +218,13 @@
     return Qnil;
   }
 
+
+
   /* build the NewServerRpc struct result */
   deadline = gpr_convert_clock_type(st.details.deadline, GPR_CLOCK_REALTIME);
   result = rb_struct_new(
-      grpc_rb_sNewServerRpc, rb_str_new2(st.details.method),
-      rb_str_new2(st.details.host),
+      grpc_rb_sNewServerRpc, grpc_rb_slice_to_ruby_string(st.details.method),
+      grpc_rb_slice_to_ruby_string(st.details.host),
       rb_funcall(rb_cTime, id_at, 2, INT2NUM(deadline.tv_sec),
                  INT2NUM(deadline.tv_nsec / 1000)),
       grpc_rb_md_ary_to_h(&st.md_ary), grpc_rb_wrap_call(call, call_queue),
diff --git a/test/core/bad_client/tests/large_metadata.c b/test/core/bad_client/tests/large_metadata.c
index 9c804e7..f672776 100644
--- a/test/core/bad_client/tests/large_metadata.c
+++ b/test/core/bad_client/tests/large_metadata.c
@@ -126,8 +126,8 @@
   CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
-  GPR_ASSERT(0 == strcmp(call_details.host, "localhost"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo/bar"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.host, "localhost"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo/bar"));
 
   grpc_metadata_array_destroy(&request_metadata_recv);
   grpc_call_details_destroy(&call_details);
@@ -153,16 +153,14 @@
   CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
-  GPR_ASSERT(0 == strcmp(call_details.host, "localhost"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo/bar"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.host, "localhost"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo/bar"));
 
   const size_t metadata_value_size = 16 * 1024;
   grpc_metadata meta;
-  meta.key = "key";
-  meta.value = gpr_malloc(metadata_value_size + 1);
-  memset((char *)meta.value, 'a', metadata_value_size);
-  ((char *)meta.value)[metadata_value_size] = 0;
-  meta.value_length = metadata_value_size;
+  meta.key = grpc_slice_from_static_string("key");
+  meta.value = grpc_slice_malloc(metadata_value_size);
+  memset(GRPC_SLICE_START_PTR(meta.value), 'a', metadata_value_size);
 
   grpc_op op;
   memset(&op, 0, sizeof(op));
@@ -176,7 +174,7 @@
   CQ_EXPECT_COMPLETION(cqv, tag(102), 0);  // Operation fails.
   cq_verify(cqv);
 
-  gpr_free((char *)meta.value);
+  grpc_slice_unref(meta.value);
   grpc_metadata_array_destroy(&request_metadata_recv);
   grpc_call_details_destroy(&call_details);
   grpc_call_destroy(s);
@@ -190,29 +188,26 @@
   grpc_slice_buffer_trim_end(incoming, 13, &last_frame_buffer);
   GPR_ASSERT(last_frame_buffer.count == 1);
   grpc_slice last_frame = last_frame_buffer.slices[0];
-  // Construct expected frame.
-  grpc_slice expected = grpc_slice_malloc(13);
-  uint8_t *p = GRPC_SLICE_START_PTR(expected);
-  // Length.
-  *p++ = 0;
-  *p++ = 0;
-  *p++ = 4;
-  // Frame type (RST_STREAM).
-  *p++ = 3;
-  // Flags.
-  *p++ = 0;
+  const uint8_t *p = GRPC_SLICE_START_PTR(last_frame);
+  // Length = 4
+  GPR_ASSERT(*p++ == 0);
+  GPR_ASSERT(*p++ == 0);
+  GPR_ASSERT(*p++ == 4);
+  // Frame type (RST_STREAM)
+  GPR_ASSERT(*p++ == 3);
+  // Flags
+  GPR_ASSERT(*p++ == 0);
   // Stream ID.
-  *p++ = 0;
-  *p++ = 0;
-  *p++ = 0;
-  *p++ = 1;
-  // Payload (error code).
-  *p++ = 0;
-  *p++ = 0;
-  *p++ = 0;
-  *p++ = 11;
-  // Compare actual and expected.
-  GPR_ASSERT(grpc_slice_cmp(last_frame, expected) == 0);
+  GPR_ASSERT(*p++ == 0);
+  GPR_ASSERT(*p++ == 0);
+  GPR_ASSERT(*p++ == 0);
+  GPR_ASSERT(*p++ == 1);
+  // Payload (error code)
+  GPR_ASSERT(*p++ == 0);
+  GPR_ASSERT(*p++ == 0);
+  GPR_ASSERT(*p++ == 0);
+  GPR_ASSERT(*p == 0 || *p == 11);
+
   grpc_slice_buffer_destroy(&last_frame_buffer);
 }
 
diff --git a/test/core/bad_client/tests/simple_request.c b/test/core/bad_client/tests/simple_request.c
index c08aa40..db31ba6 100644
--- a/test/core/bad_client/tests/simple_request.c
+++ b/test/core/bad_client/tests/simple_request.c
@@ -117,8 +117,8 @@
   CQ_EXPECT_COMPLETION(cqv, tag(101), 1);
   cq_verify(cqv);
 
-  GPR_ASSERT(0 == strcmp(call_details.host, "localhost"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo/bar"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.host, "localhost"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo/bar"));
 
   grpc_metadata_array_destroy(&request_metadata_recv);
   grpc_call_details_destroy(&call_details);
diff --git a/test/core/bad_ssl/bad_ssl_test.c b/test/core/bad_ssl/bad_ssl_test.c
index f8a9fe6..7a99f57 100644
--- a/test/core/bad_ssl/bad_ssl_test.c
+++ b/test/core/bad_ssl/bad_ssl_test.c
@@ -57,8 +57,7 @@
 
   grpc_metadata_array initial_metadata_recv;
   grpc_metadata_array trailing_metadata_recv;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   grpc_status_code status;
   grpc_call_error error;
   gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5);
@@ -80,9 +79,10 @@
   grpc_metadata_array_init(&trailing_metadata_recv);
 
   channel = grpc_secure_channel_create(ssl_creds, target, &args, NULL);
+  grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr:1234");
   c = grpc_channel_create_call(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
-                               "/foo", "foo.test.google.fr:1234", deadline,
-                               NULL);
+                               grpc_slice_from_static_string("/foo"), &host,
+                               deadline, NULL);
 
   memset(ops, 0, sizeof(ops));
   op = ops;
@@ -95,12 +95,11 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -117,7 +116,7 @@
   GPR_ASSERT(status != GRPC_STATUS_OK);
 
   grpc_call_destroy(c);
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
 
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index 080c56f..dd9c544 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -39,6 +39,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
+#include "src/core/lib/slice/slice_internal.h"
 #include "test/core/util/test_config.h"
 
 static grpc_error *channel_init_func(grpc_exec_ctx *exec_ctx,
@@ -120,7 +121,7 @@
   int *channel_data;
   int *call_data;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_mdstr *path = grpc_mdstr_from_string("/service/method");
+  grpc_slice path = grpc_slice_from_static_string("/service/method");
 
   arg.type = GRPC_ARG_INTEGER;
   arg.key = "test_key";
@@ -157,7 +158,7 @@
 
   GRPC_CHANNEL_STACK_UNREF(&exec_ctx, channel_stack, "done");
 
-  GRPC_MDSTR_UNREF(&exec_ctx, path);
+  grpc_slice_unref_internal(&exec_ctx, path);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
diff --git a/test/core/client_channel/lb_policies_test.c b/test/core/client_channel/lb_policies_test.c
index f46982a..82e928e 100644
--- a/test/core/client_channel/lb_policies_test.c
+++ b/test/core/client_channel/lb_policies_test.c
@@ -157,8 +157,7 @@
 typedef struct request_data {
   grpc_metadata_array initial_metadata_recv;
   grpc_metadata_array trailing_metadata_recv;
-  char *details;
-  size_t details_capacity;
+  grpc_slice details;
   grpc_status_code status;
   grpc_call_details *call_details;
 } request_data;
@@ -272,8 +271,6 @@
 
   for (iter_num = 0; iter_num < spec->num_iters; iter_num++) {
     cq_verifier *cqv = cq_verifier_create(f->cq);
-    rdata->details = NULL;
-    rdata->details_capacity = 0;
     was_cancelled = 2;
 
     for (i = 0; i < f->num_servers; i++) {
@@ -294,8 +291,9 @@
     }
     memset(s_valid, 0, f->num_servers * sizeof(int));
 
+    grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr");
     c = grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq,
-                                 "/foo", "foo.test.google.fr",
+                                 grpc_slice_from_static_string("/foo"), &host,
                                  gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
     GPR_ASSERT(c);
     completed_client = 0;
@@ -312,7 +310,8 @@
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_RECV_INITIAL_METADATA;
-    op->data.recv_initial_metadata = &rdata->initial_metadata_recv;
+    op->data.recv_initial_metadata.recv_initial_metadata =
+        &rdata->initial_metadata_recv;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -321,8 +320,6 @@
         &rdata->trailing_metadata_recv;
     op->data.recv_status_on_client.status = &rdata->status;
     op->data.recv_status_on_client.status_details = &rdata->details;
-    op->data.recv_status_on_client.status_details_capacity =
-        &rdata->details_capacity;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -365,7 +362,8 @@
       op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
       op->data.send_status_from_server.trailing_metadata_count = 0;
       op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-      op->data.send_status_from_server.status_details = "xyz";
+      grpc_slice status_details = grpc_slice_from_static_string("xyz");
+      op->data.send_status_from_server.status_details = &status_details;
       op->flags = 0;
       op->reserved = NULL;
       op++;
@@ -384,12 +382,12 @@
       }
       cq_verify(cqv);
 
-      gpr_log(GPR_DEBUG, "status=%d; %s", rdata->status, rdata->details);
       GPR_ASSERT(rdata->status == GRPC_STATUS_UNIMPLEMENTED);
-      GPR_ASSERT(0 == strcmp(rdata->details, "xyz"));
-      GPR_ASSERT(0 == strcmp(rdata->call_details[s_idx].method, "/foo"));
+      GPR_ASSERT(0 == grpc_slice_str_cmp(rdata->details, "xyz"));
       GPR_ASSERT(0 ==
-                 strcmp(rdata->call_details[s_idx].host, "foo.test.google.fr"));
+                 grpc_slice_str_cmp(rdata->call_details[s_idx].method, "/foo"));
+      GPR_ASSERT(0 == grpc_slice_str_cmp(rdata->call_details[s_idx].host,
+                                         "foo.test.google.fr"));
       GPR_ASSERT(was_cancelled == 1);
 
       grpc_call_destroy(f->server_calls[s_idx]);
@@ -422,7 +420,7 @@
     for (i = 0; i < f->num_servers; i++) {
       grpc_call_details_destroy(&rdata->call_details[i]);
     }
-    gpr_free(rdata->details);
+    grpc_slice_unref(rdata->details);
   }
 
   gpr_free(s_valid);
@@ -454,10 +452,12 @@
   op->flags = 0;
   op->reserved = NULL;
 
+  grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr");
   for (i = 0; i < concurrent_calls; i++) {
-    calls[i] = grpc_channel_create_call(
-        client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq, "/foo",
-        "foo.test.google.fr", gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+    calls[i] =
+        grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq,
+                                 grpc_slice_from_static_string("/foo"), &host,
+                                 gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
     GPR_ASSERT(calls[i]);
     GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[i], ops,
                                                      (size_t)(op - ops), tag(1),
diff --git a/test/core/client_channel/set_initial_connect_string_test.c b/test/core/client_channel/set_initial_connect_string_test.c
index b78ba98..fc0aca0 100644
--- a/test/core/client_channel/set_initial_connect_string_test.c
+++ b/test/core/client_channel/set_initial_connect_string_test.c
@@ -141,9 +141,11 @@
   } else {
     state.channel = grpc_insecure_channel_create(state.target, NULL, NULL);
   }
+  grpc_slice host = grpc_slice_from_static_string("localhost");
   state.call = grpc_channel_create_call(
-      state.channel, NULL, GRPC_PROPAGATE_DEFAULTS, state.cq, "/Service/Method",
-      "localhost", gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+      state.channel, NULL, GRPC_PROPAGATE_DEFAULTS, state.cq,
+      grpc_slice_from_static_string("/Service/Method"), &host,
+      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
   memset(&state.op, 0, sizeof(state.op));
   state.op.op = GRPC_OP_SEND_INITIAL_METADATA;
   state.op.data.send_initial_metadata.count = 0;
@@ -207,8 +209,7 @@
   size_t magic_length = strlen(magic_connect_string);
   GPR_ASSERT(buffer->length >= magic_length);
   for (i = 0, j = 0; i < state.incoming_buffer.count && j < magic_length; i++) {
-    char *dump =
-        grpc_dump_slice(state.incoming_buffer.slices[i], GPR_DUMP_ASCII);
+    char *dump = grpc_slice_to_c_string(state.incoming_buffer.slices[i]);
     cmp_length = GPR_MIN(strlen(dump), magic_length - j);
     GPR_ASSERT(strncmp(dump, magic_connect_string + j, cmp_length) == 0);
     j += cmp_length;
diff --git a/test/core/compression/algorithm_test.c b/test/core/compression/algorithm_test.c
index ff17667..37397ce 100644
--- a/test/core/compression/algorithm_test.c
+++ b/test/core/compression/algorithm_test.c
@@ -40,6 +40,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/static_metadata.h"
 #include "test/core/util/test_config.h"
 
@@ -51,32 +52,33 @@
   for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
     char *name;
     grpc_compression_algorithm parsed;
-    grpc_mdstr *mdstr;
-    grpc_mdelem *mdelem;
+    grpc_slice mdstr;
+    grpc_mdelem mdelem;
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     GPR_ASSERT(
         grpc_compression_algorithm_name((grpc_compression_algorithm)i, &name));
-    GPR_ASSERT(grpc_compression_algorithm_parse(name, strlen(name), &parsed));
+    GPR_ASSERT(grpc_compression_algorithm_parse(
+        grpc_slice_from_static_string(name), &parsed));
     GPR_ASSERT((int)parsed == i);
-    mdstr = grpc_mdstr_from_string(name);
-    GPR_ASSERT(mdstr == grpc_compression_algorithm_mdstr(parsed));
-    GPR_ASSERT(parsed == grpc_compression_algorithm_from_mdstr(mdstr));
+    mdstr = grpc_slice_from_copied_string(name);
+    GPR_ASSERT(grpc_slice_eq(mdstr, grpc_compression_algorithm_slice(parsed)));
+    GPR_ASSERT(parsed == grpc_compression_algorithm_from_slice(mdstr));
     mdelem = grpc_compression_encoding_mdelem(parsed);
-    GPR_ASSERT(mdelem->value == mdstr);
-    GPR_ASSERT(mdelem->key == GRPC_MDSTR_GRPC_ENCODING);
-    GRPC_MDSTR_UNREF(&exec_ctx, mdstr);
+    GPR_ASSERT(grpc_slice_eq(GRPC_MDVALUE(mdelem), mdstr));
+    GPR_ASSERT(grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_GRPC_ENCODING));
+    grpc_slice_unref_internal(&exec_ctx, mdstr);
     GRPC_MDELEM_UNREF(&exec_ctx, mdelem);
     grpc_exec_ctx_finish(&exec_ctx);
   }
 
   /* test failure */
-  GPR_ASSERT(NULL ==
-             grpc_compression_encoding_mdelem(GRPC_COMPRESS_ALGORITHMS_COUNT));
+  GPR_ASSERT(GRPC_MDISNULL(
+      grpc_compression_encoding_mdelem(GRPC_COMPRESS_ALGORITHMS_COUNT)));
 }
 
 static void test_algorithm_failure(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_mdstr *mdstr;
+  grpc_slice mdstr;
 
   gpr_log(GPR_DEBUG, "test_algorithm_failure");
 
@@ -84,14 +86,16 @@
                                              NULL) == 0);
   GPR_ASSERT(grpc_compression_algorithm_name(GRPC_COMPRESS_ALGORITHMS_COUNT + 1,
                                              NULL) == 0);
-  mdstr = grpc_mdstr_from_string("this-is-an-invalid-algorithm");
-  GPR_ASSERT(grpc_compression_algorithm_from_mdstr(mdstr) ==
+  mdstr = grpc_slice_from_static_string("this-is-an-invalid-algorithm");
+  GPR_ASSERT(grpc_compression_algorithm_from_slice(mdstr) ==
              GRPC_COMPRESS_ALGORITHMS_COUNT);
-  GPR_ASSERT(grpc_compression_algorithm_mdstr(GRPC_COMPRESS_ALGORITHMS_COUNT) ==
-             NULL);
-  GPR_ASSERT(grpc_compression_algorithm_mdstr(GRPC_COMPRESS_ALGORITHMS_COUNT +
-                                              1) == NULL);
-  GRPC_MDSTR_UNREF(&exec_ctx, mdstr);
+  GPR_ASSERT(grpc_slice_eq(
+      grpc_compression_algorithm_slice(GRPC_COMPRESS_ALGORITHMS_COUNT),
+      grpc_empty_slice()));
+  GPR_ASSERT(grpc_slice_eq(
+      grpc_compression_algorithm_slice(GRPC_COMPRESS_ALGORITHMS_COUNT + 1),
+      grpc_empty_slice()));
+  grpc_slice_unref_internal(&exec_ctx, mdstr);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
diff --git a/test/core/compression/compression_test.c b/test/core/compression/compression_test.c
index 4c43746..7b2e56d 100644
--- a/test/core/compression/compression_test.c
+++ b/test/core/compression/compression_test.c
@@ -54,7 +54,7 @@
     const char *valid_name = valid_names[i];
     grpc_compression_algorithm algorithm;
     const int success = grpc_compression_algorithm_parse(
-        valid_name, strlen(valid_name), &algorithm);
+        grpc_slice_from_static_string(valid_name), &algorithm);
     GPR_ASSERT(success != 0);
     GPR_ASSERT(algorithm == valid_algorithms[i]);
   }
@@ -64,7 +64,7 @@
     grpc_compression_algorithm algorithm;
     int success;
     success = grpc_compression_algorithm_parse(
-        invalid_name, strlen(invalid_name), &algorithm);
+        grpc_slice_from_static_string(invalid_name), &algorithm);
     GPR_ASSERT(success == 0);
     /* the value of "algorithm" is undefined upon failure */
   }
diff --git a/test/core/compression/message_compress_test.c b/test/core/compression/message_compress_test.c
index 2432ca7..246a2b3 100644
--- a/test/core/compression/message_compress_test.c
+++ b/test/core/compression/message_compress_test.c
@@ -114,7 +114,7 @@
   }
 
   final = grpc_slice_merge(output.slices, output.count);
-  GPR_ASSERT(0 == grpc_slice_cmp(value, final));
+  GPR_ASSERT(grpc_slice_eq(value, final));
 
   grpc_slice_buffer_destroy(&input);
   grpc_slice_buffer_destroy(&compressed);
diff --git a/test/core/end2end/bad_server_response_test.c b/test/core/end2end/bad_server_response_test.c
index a052016..42bf4fa 100644
--- a/test/core/end2end/bad_server_response_test.c
+++ b/test/core/end2end/bad_server_response_test.c
@@ -172,16 +172,17 @@
   grpc_status_code status;
   grpc_call_error error;
   cq_verifier *cqv;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
   state.cq = grpc_completion_queue_create(NULL);
   cqv = cq_verifier_create(state.cq);
   gpr_join_host_port(&state.target, "127.0.0.1", target_port);
   state.channel = grpc_insecure_channel_create(state.target, NULL, NULL);
+  grpc_slice host = grpc_slice_from_static_string("localhost");
   state.call = grpc_channel_create_call(
-      state.channel, NULL, GRPC_PROPAGATE_DEFAULTS, state.cq, "/Service/Method",
-      "localhost", gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+      state.channel, NULL, GRPC_PROPAGATE_DEFAULTS, state.cq,
+      grpc_slice_from_static_string("/Service/Method"), &host,
+      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
 
   grpc_metadata_array_init(&initial_metadata_recv);
   grpc_metadata_array_init(&trailing_metadata_recv);
@@ -198,7 +199,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -206,7 +207,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -218,13 +218,13 @@
   CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
-  gpr_log(GPR_DEBUG, "Rpc status: %d, details: %s", status, details);
   GPR_ASSERT(status == expected_status);
-  GPR_ASSERT(NULL != strstr(details, expected_detail));
+  GPR_ASSERT(-1 != grpc_slice_slice(details, grpc_slice_from_static_string(
+                                                 expected_detail)));
 
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
   cq_verifier_destroy(cqv);
 }
 
diff --git a/test/core/end2end/connection_refused_test.c b/test/core/end2end/connection_refused_test.c
index 4430804..81a6b87 100644
--- a/test/core/end2end/connection_refused_test.c
+++ b/test/core/end2end/connection_refused_test.c
@@ -40,6 +40,7 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/service_config.h"
 
@@ -59,8 +60,7 @@
   grpc_op *op;
   grpc_metadata_array trailing_metadata_recv;
   grpc_status_code status;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
   gpr_log(GPR_INFO, "TEST: wait_for_ready=%d use_service_config=%d",
           wait_for_ready, use_service_config);
@@ -97,9 +97,10 @@
   gpr_join_host_port(&addr, "127.0.0.1", port);
   gpr_log(GPR_INFO, "server: %s", addr);
   chan = grpc_insecure_channel_create(addr, args, NULL);
-  call = grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
-                                  "/service/method", "nonexistant", deadline,
-                                  NULL);
+  grpc_slice host = grpc_slice_from_static_string("nonexistant");
+  call = grpc_channel_create_call(
+      chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+      grpc_slice_from_static_string("/service/method"), &host, deadline, NULL);
 
   gpr_free(addr);
 
@@ -116,7 +117,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -142,7 +142,7 @@
   grpc_channel_destroy(chan);
   cq_verifier_destroy(cqv);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
 
   {
diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c
index 5737397..bc2d588 100644
--- a/test/core/end2end/cq_verifier.c
+++ b/test/core/end2end/cq_verifier.c
@@ -92,8 +92,8 @@
                         const char *value) {
   size_t i;
   for (i = 0; i < count; i++) {
-    if (0 == strcmp(key, md[i].key) && strlen(value) == md[i].value_length &&
-        0 == memcmp(md[i].value, value, md[i].value_length)) {
+    if (0 == grpc_slice_str_cmp(md[i].key, key) &&
+        0 == grpc_slice_str_cmp(md[i].value, value)) {
       return 1;
     }
   }
@@ -105,6 +105,22 @@
   return has_metadata(array->metadata, array->count, key, value);
 }
 
+static int has_metadata_slices(const grpc_metadata *md, size_t count,
+                               grpc_slice key, grpc_slice value) {
+  size_t i;
+  for (i = 0; i < count; i++) {
+    if (grpc_slice_eq(md[i].key, key) && grpc_slice_eq(md[i].value, value)) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+int contains_metadata_slices(grpc_metadata_array *array, grpc_slice key,
+                             grpc_slice value) {
+  return has_metadata_slices(array->metadata, array->count, key, value);
+}
+
 static grpc_slice merge_slices(grpc_slice *slices, size_t nslices) {
   size_t i;
   size_t len = 0;
diff --git a/test/core/end2end/cq_verifier.h b/test/core/end2end/cq_verifier.h
index b754de9..035aa27 100644
--- a/test/core/end2end/cq_verifier.h
+++ b/test/core/end2end/cq_verifier.h
@@ -71,5 +71,7 @@
 int byte_buffer_eq_string(grpc_byte_buffer *byte_buffer, const char *string);
 int contains_metadata(grpc_metadata_array *array, const char *key,
                       const char *value);
+int contains_metadata_slices(grpc_metadata_array *array, grpc_slice key,
+                             grpc_slice value);
 
 #endif /* GRPC_TEST_CORE_END2END_CQ_VERIFIER_H */
diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
index 11e8604..35b1b8c 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -88,8 +88,7 @@
   grpc_metadata_array request_metadata_recv;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   grpc_call_details call_details;
   char *peer;
@@ -135,7 +134,7 @@
     grpc_slice_split(uri_slice, ",", &uri_parts);
     hosts_with_port = gpr_malloc(sizeof(char *) * uri_parts.count);
     for (i = 0; i < uri_parts.count; i++) {
-      char *uri_part_str = grpc_dump_slice(uri_parts.slices[i], GPR_DUMP_ASCII);
+      char *uri_part_str = grpc_slice_to_c_string(uri_parts.slices[i]);
       gpr_asprintf(&hosts_with_port[i], "%s:%d", uri_part_str, port);
       gpr_free(uri_part_str);
     }
@@ -168,8 +167,10 @@
   }
 
   /* Send a trivial request. */
+  grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr");
   c = grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
-                               "/foo", "foo.test.google.fr", deadline, NULL);
+                               grpc_slice_from_static_string("/foo"), &host,
+                               deadline, NULL);
   GPR_ASSERT(c);
 
   memset(ops, 0, sizeof(ops));
@@ -184,7 +185,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -192,7 +193,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -216,7 +216,8 @@
     op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
     op->data.send_status_from_server.trailing_metadata_count = 0;
     op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-    op->data.send_status_from_server.status_details = "xyz";
+    grpc_slice status_details = grpc_slice_from_static_string("xyz");
+    op->data.send_status_from_server.status_details = &status_details;
     op->flags = 0;
     op++;
     op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
@@ -235,9 +236,10 @@
     gpr_free(peer);
 
     GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-    GPR_ASSERT(0 == strcmp(details, "xyz"));
-    GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
-    GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr"));
+    GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+    GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
+    GPR_ASSERT(0 ==
+               grpc_slice_str_cmp(call_details.host, "foo.test.google.fr"));
     GPR_ASSERT(was_cancelled == 1);
 
     grpc_call_destroy(s);
@@ -271,7 +273,7 @@
   grpc_metadata_array_destroy(&request_metadata_recv);
 
   grpc_call_details_destroy(&call_details);
-  gpr_free(details);
+  grpc_slice_unref(details);
   if (picked_port) {
     grpc_recycle_unused_port(port);
   }
diff --git a/test/core/end2end/end2end_test_utils.c b/test/core/end2end/end2end_test_utils.c
index 46fb4ec..8783d84 100644
--- a/test/core/end2end/end2end_test_utils.c
+++ b/test/core/end2end/end2end_test_utils.c
@@ -39,13 +39,27 @@
 
 const char *get_host_override_string(const char *str,
                                      grpc_end2end_test_config config) {
-  return (config.feature_mask & FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER ? str
-                                                                       : NULL);
+  if (config.feature_mask & FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER) {
+    return str;
+  } else {
+    return NULL;
+  }
 }
 
-void validate_host_override_string(const char *pattern, const char *str,
+const grpc_slice *get_host_override_slice(const char *str,
+                                          grpc_end2end_test_config config) {
+  const char *r = get_host_override_string(str, config);
+  if (r != NULL) {
+    static grpc_slice ret;
+    ret = grpc_slice_from_static_string(r);
+    return &ret;
+  }
+  return NULL;
+}
+
+void validate_host_override_string(const char *pattern, grpc_slice str,
                                    grpc_end2end_test_config config) {
   if (config.feature_mask & FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER) {
-    GPR_ASSERT(0 == strcmp(str, pattern));
+    GPR_ASSERT(0 == grpc_slice_str_cmp(str, pattern));
   }
 }
diff --git a/test/core/end2end/end2end_tests.h b/test/core/end2end/end2end_tests.h
index f25e90b..cb0afd9 100644
--- a/test/core/end2end/end2end_tests.h
+++ b/test/core/end2end/end2end_tests.h
@@ -72,8 +72,12 @@
 
 const char *get_host_override_string(const char *str,
                                      grpc_end2end_test_config config);
+/* Returns a pointer to a statically allocated slice: future invocations
+   overwrite past invocations, not threadsafe, etc... */
+const grpc_slice *get_host_override_slice(const char *str,
+                                          grpc_end2end_test_config config);
 
-void validate_host_override_string(const char *pattern, const char *str,
+void validate_host_override_string(const char *pattern, grpc_slice str,
                                    grpc_end2end_test_config config);
 
 #endif /* GRPC_TEST_CORE_END2END_END2END_TESTS_H */
diff --git a/test/core/end2end/fake_resolver.c b/test/core/end2end/fake_resolver.c
index 966642f..8e711c6 100644
--- a/test/core/end2end/fake_resolver.c
+++ b/test/core/end2end/fake_resolver.c
@@ -165,7 +165,7 @@
   bool errors_found = false;
   for (size_t i = 0; i < addresses->num_addresses; i++) {
     grpc_uri ith_uri = *args->uri;
-    char* part_str = grpc_dump_slice(path_parts.slices[i], GPR_DUMP_ASCII);
+    char* part_str = grpc_slice_to_c_string(path_parts.slices[i]);
     ith_uri.path = part_str;
     if (!parse_ipv4(&ith_uri, &addresses->addresses[i].address)) {
       errors_found = true;
diff --git a/test/core/end2end/fixtures/h2_oauth2.c b/test/core/end2end/fixtures/h2_oauth2.c
index 83f759c..3351652 100644
--- a/test/core/end2end/fixtures/h2_oauth2.c
+++ b/test/core/end2end/fixtures/h2_oauth2.c
@@ -59,8 +59,8 @@
                                           const char *value) {
   size_t i;
   for (i = 0; i < md_count; i++) {
-    if (strcmp(key, md[i].key) == 0 && strlen(value) == md[i].value_length &&
-        memcmp(md[i].value, value, md[i].value_length) == 0) {
+    if (grpc_slice_str_cmp(md[i].key, key) == 0 &&
+        grpc_slice_str_cmp(md[i].value, value) == 0) {
       return &md[i];
     }
   }
@@ -74,7 +74,7 @@
                                    grpc_process_auth_metadata_done_cb cb,
                                    void *user_data) {
   const grpc_metadata *oauth2 =
-      find_metadata(md, md_count, "Authorization", oauth2_md);
+      find_metadata(md, md_count, "authorization", oauth2_md);
   test_processor_state *s;
 
   GPR_ASSERT(state != NULL);
@@ -93,7 +93,7 @@
                                    grpc_process_auth_metadata_done_cb cb,
                                    void *user_data) {
   const grpc_metadata *oauth2 =
-      find_metadata(md, md_count, "Authorization", oauth2_md);
+      find_metadata(md, md_count, "authorization", oauth2_md);
   test_processor_state *s;
   GPR_ASSERT(state != NULL);
   s = (test_processor_state *)state;
@@ -154,7 +154,7 @@
   grpc_channel_credentials *ssl_creds =
       grpc_ssl_credentials_create(test_root_cert, NULL, NULL);
   grpc_call_credentials *oauth2_creds =
-      grpc_md_only_test_credentials_create("Authorization", oauth2_md, 1);
+      grpc_md_only_test_credentials_create("authorization", oauth2_md, 1);
   grpc_channel_credentials *ssl_oauth2_creds =
       grpc_composite_channel_credentials_create(ssl_creds, oauth2_creds, NULL);
   grpc_arg ssl_name_override = {GRPC_ARG_STRING,
diff --git a/test/core/end2end/fixtures/h2_ssl_cert.c b/test/core/end2end/fixtures/h2_ssl_cert.c
index ae49cc8..844e933 100644
--- a/test/core/end2end/fixtures/h2_ssl_cert.c
+++ b/test/core/end2end/fixtures/h2_ssl_cert.c
@@ -321,9 +321,10 @@
   grpc_op *op;
   grpc_call_error error;
 
+  grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr:1234");
   c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
-                               "/foo", "foo.test.google.fr:1234", deadline,
-                               NULL);
+                               grpc_slice_from_static_string("/foo"), &host,
+                               deadline, NULL);
   GPR_ASSERT(c);
 
   memset(ops, 0, sizeof(ops));
diff --git a/test/core/end2end/fixtures/http_proxy.c b/test/core/end2end/fixtures/http_proxy.c
index b62082c..dac9baf 100644
--- a/test/core/end2end/fixtures/http_proxy.c
+++ b/test/core/end2end/fixtures/http_proxy.c
@@ -132,7 +132,7 @@
                                     const char* prefix, grpc_error* error) {
   const char* msg = grpc_error_string(error);
   gpr_log(GPR_INFO, "%s: %s", prefix, msg);
-  grpc_error_free_string(msg);
+
   grpc_endpoint_shutdown(exec_ctx, conn->client_endpoint);
   if (conn->server_endpoint != NULL)
     grpc_endpoint_shutdown(exec_ctx, conn->server_endpoint);
diff --git a/test/core/end2end/fixtures/proxy.c b/test/core/end2end/fixtures/proxy.c
index beed80d..cee053e 100644
--- a/test/core/end2end/fixtures/proxy.c
+++ b/test/core/end2end/fixtures/proxy.c
@@ -80,8 +80,7 @@
 
   grpc_metadata_array p2s_trailing_metadata;
   grpc_status_code p2s_status;
-  char *p2s_status_details;
-  size_t p2s_status_details_capacity;
+  grpc_slice p2s_status_details;
 
   int c2p_server_cancelled;
 } proxy_call;
@@ -112,6 +111,7 @@
   grpc_server_register_completion_queue(proxy->server, proxy->cq, NULL);
   grpc_server_start(proxy->server);
 
+  grpc_call_details_init(&proxy->new_call_details);
   gpr_thd_options_set_joinable(&opt);
   GPR_ASSERT(gpr_thd_new(&proxy->thd, thread_main, proxy, &opt));
 
@@ -153,7 +153,7 @@
     grpc_metadata_array_destroy(&pc->c2p_initial_metadata);
     grpc_metadata_array_destroy(&pc->p2s_initial_metadata);
     grpc_metadata_array_destroy(&pc->p2s_trailing_metadata);
-    gpr_free(pc->p2s_status_details);
+    grpc_slice_unref(pc->p2s_status_details);
     gpr_free(pc);
   }
 }
@@ -203,7 +203,7 @@
     op.op = GRPC_OP_RECV_MESSAGE;
     op.flags = 0;
     op.reserved = NULL;
-    op.data.recv_message = &pc->c2p_msg;
+    op.data.recv_message.recv_message = &pc->c2p_msg;
     refpc(pc, "on_c2p_recv_msg");
     err = grpc_call_start_batch(pc->c2p, &op, 1,
                                 new_closure(on_c2p_recv_msg, pc), NULL);
@@ -228,7 +228,7 @@
       op.op = GRPC_OP_SEND_MESSAGE;
       op.flags = 0;
       op.reserved = NULL;
-      op.data.send_message = pc->c2p_msg;
+      op.data.send_message.send_message = pc->c2p_msg;
       refpc(pc, "on_p2s_sent_message");
       err = grpc_call_start_batch(pc->p2s, &op, 1,
                                   new_closure(on_p2s_sent_message, pc), NULL);
@@ -259,7 +259,7 @@
     op.op = GRPC_OP_RECV_MESSAGE;
     op.flags = 0;
     op.reserved = NULL;
-    op.data.recv_message = &pc->p2s_msg;
+    op.data.recv_message.recv_message = &pc->p2s_msg;
     refpc(pc, "on_p2s_recv_msg");
     err = grpc_call_start_batch(pc->p2s, &op, 1,
                                 new_closure(on_p2s_recv_msg, pc), NULL);
@@ -278,7 +278,7 @@
     op.op = GRPC_OP_SEND_MESSAGE;
     op.flags = 0;
     op.reserved = NULL;
-    op.data.send_message = pc->p2s_msg;
+    op.data.send_message.send_message = pc->p2s_msg;
     refpc(pc, "on_c2p_sent_message");
     err = grpc_call_start_batch(pc->c2p, &op, 1,
                                 new_closure(on_c2p_sent_message, pc), NULL);
@@ -309,7 +309,7 @@
     op.data.send_status_from_server.trailing_metadata =
         pc->p2s_trailing_metadata.metadata;
     op.data.send_status_from_server.status = pc->p2s_status;
-    op.data.send_status_from_server.status_details = pc->p2s_status_details;
+    op.data.send_status_from_server.status_details = &pc->p2s_status_details;
     refpc(pc, "on_c2p_sent_status");
     err = grpc_call_start_batch(pc->c2p, &op, 1,
                                 new_closure(on_c2p_sent_status, pc), NULL);
@@ -339,7 +339,7 @@
     pc->c2p = proxy->new_call;
     pc->p2s = grpc_channel_create_call(
         proxy->client, pc->c2p, GRPC_PROPAGATE_DEFAULTS, proxy->cq,
-        proxy->new_call_details.method, proxy->new_call_details.host,
+        proxy->new_call_details.method, &proxy->new_call_details.host,
         proxy->new_call_details.deadline, NULL);
     gpr_ref_init(&pc->refs, 1);
 
@@ -347,7 +347,8 @@
 
     op.op = GRPC_OP_RECV_INITIAL_METADATA;
     op.flags = 0;
-    op.data.recv_initial_metadata = &pc->p2s_initial_metadata;
+    op.data.recv_initial_metadata.recv_initial_metadata =
+        &pc->p2s_initial_metadata;
     refpc(pc, "on_p2s_recv_initial_metadata");
     err = grpc_call_start_batch(
         pc->p2s, &op, 1, new_closure(on_p2s_recv_initial_metadata, pc), NULL);
@@ -364,7 +365,7 @@
 
     op.op = GRPC_OP_RECV_MESSAGE;
     op.flags = 0;
-    op.data.recv_message = &pc->c2p_msg;
+    op.data.recv_message.recv_message = &pc->c2p_msg;
     refpc(pc, "on_c2p_recv_msg");
     err = grpc_call_start_batch(pc->c2p, &op, 1,
                                 new_closure(on_c2p_recv_msg, pc), NULL);
@@ -372,7 +373,7 @@
 
     op.op = GRPC_OP_RECV_MESSAGE;
     op.flags = 0;
-    op.data.recv_message = &pc->p2s_msg;
+    op.data.recv_message.recv_message = &pc->p2s_msg;
     refpc(pc, "on_p2s_recv_msg");
     err = grpc_call_start_batch(pc->p2s, &op, 1,
                                 new_closure(on_p2s_recv_msg, pc), NULL);
@@ -384,8 +385,6 @@
         &pc->p2s_trailing_metadata;
     op.data.recv_status_on_client.status = &pc->p2s_status;
     op.data.recv_status_on_client.status_details = &pc->p2s_status_details;
-    op.data.recv_status_on_client.status_details_capacity =
-        &pc->p2s_status_details_capacity;
     refpc(pc, "on_p2s_status");
     err = grpc_call_start_batch(pc->p2s, &op, 1, new_closure(on_p2s_status, pc),
                                 NULL);
@@ -401,6 +400,9 @@
 
     request_call(proxy);
 
+    grpc_call_details_destroy(&proxy->new_call_details);
+    grpc_call_details_init(&proxy->new_call_details);
+
     unrefpc(pc, "init");
   } else {
     GPR_ASSERT(proxy->new_call == NULL);
diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c
index 200a518..593c1bb 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.c
+++ b/test/core/end2end/fuzzers/api_fuzzer.c
@@ -44,6 +44,7 @@
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/tcp_client.h"
 #include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/surface/server.h"
 #include "src/core/lib/transport/metadata.h"
 #include "test/core/end2end/data/ssl_test_data.h"
@@ -90,7 +91,7 @@
 
 static void end(input_stream *inp) { inp->cur = inp->end; }
 
-static char *read_string(input_stream *inp) {
+static char *read_string(input_stream *inp, bool *special) {
   char *str = NULL;
   size_t cap = 0;
   size_t sz = 0;
@@ -102,18 +103,56 @@
     }
     c = (char)next_byte(inp);
     str[sz++] = c;
-  } while (c != 0);
+  } while (c != 0 && c != 1);
+  if (special != NULL) {
+    *special = (c == 1);
+  }
+  if (c == 1) {
+    str[sz - 1] = 0;
+  }
   return str;
 }
 
-static void read_buffer(input_stream *inp, char **buffer, size_t *length) {
+static void read_buffer(input_stream *inp, char **buffer, size_t *length,
+                        bool *special) {
   *length = next_byte(inp);
+  if (*length == 255) {
+    if (special != NULL) *special = true;
+    *length = next_byte(inp);
+  } else {
+    if (special != NULL) *special = false;
+  }
   *buffer = gpr_malloc(*length);
   for (size_t i = 0; i < *length; i++) {
     (*buffer)[i] = (char)next_byte(inp);
   }
 }
 
+static grpc_slice maybe_intern(grpc_slice s, bool intern) {
+  grpc_slice r = intern ? grpc_slice_intern(s) : grpc_slice_ref(s);
+  grpc_slice_unref(s);
+  return r;
+}
+
+static grpc_slice read_string_like_slice(input_stream *inp) {
+  bool special;
+  char *s = read_string(inp, &special);
+  grpc_slice r = maybe_intern(grpc_slice_from_copied_string(s), special);
+  gpr_free(s);
+  return r;
+}
+
+static grpc_slice read_buffer_like_slice(input_stream *inp) {
+  char *buffer;
+  size_t length;
+  bool special;
+  read_buffer(inp, &buffer, &length, &special);
+  grpc_slice r =
+      maybe_intern(grpc_slice_from_copied_buffer(buffer, length), special);
+  gpr_free(buffer);
+  return r;
+}
+
 static uint32_t read_uint22(input_stream *inp) {
   uint8_t b = next_byte(inp);
   uint32_t x = b & 0x7f;
@@ -170,12 +209,12 @@
     switch (next_byte(inp)) {
       case 1:
         args[i].type = GRPC_ARG_STRING;
-        args[i].key = read_string(inp);
-        args[i].value.string = read_string(inp);
+        args[i].key = read_string(inp, NULL);
+        args[i].value.string = read_string(inp, NULL);
         break;
       case 2:
         args[i].type = GRPC_ARG_INTEGER;
-        args[i].key = read_string(inp);
+        args[i].key = read_string(inp, NULL);
         args[i].value.integer = read_int(inp);
         break;
       case 3:
@@ -217,7 +256,7 @@
                                       size_t num_builtins) {
   uint8_t b = next_byte(inp);
   if (b == 0) return NULL;
-  if (b == 1) return ctx->release[ctx->num_release++] = read_string(inp);
+  if (b == 1) return ctx->release[ctx->num_release++] = read_string(inp, NULL);
   if (b >= num_builtins + 1) {
     end(inp);
     return NULL;
@@ -508,8 +547,7 @@
   grpc_status_code status;
   grpc_metadata_array recv_initial_metadata;
   grpc_metadata_array recv_trailing_metadata;
-  char *recv_status_details;
-  size_t recv_status_details_capacity;
+  grpc_slice recv_status_details;
   int cancelled;
   int pending_ops;
   grpc_call_details call_details;
@@ -523,6 +561,11 @@
   size_t cap_to_free;
   void **to_free;
 
+  // array of slices to unref
+  size_t num_slices_to_unref;
+  size_t cap_slices_to_unref;
+  grpc_slice *slices_to_unref;
+
   struct call_state *next;
   struct call_state *prev;
 } call_state;
@@ -558,13 +601,17 @@
   call->next->prev = call->prev;
   grpc_metadata_array_destroy(&call->recv_initial_metadata);
   grpc_metadata_array_destroy(&call->recv_trailing_metadata);
-  gpr_free(call->recv_status_details);
+  grpc_slice_unref(call->recv_status_details);
   grpc_call_details_destroy(&call->call_details);
 
   for (size_t i = 0; i < call->num_to_free; i++) {
     gpr_free(call->to_free[i]);
   }
+  for (size_t i = 0; i < call->num_slices_to_unref; i++) {
+    grpc_slice_unref(call->slices_to_unref[i]);
+  }
   gpr_free(call->to_free);
+  gpr_free(call->slices_to_unref);
 
   gpr_free(call);
 
@@ -580,6 +627,17 @@
   call->to_free[call->num_to_free++] = p;
 }
 
+static grpc_slice *add_to_slice_unref(call_state *call, grpc_slice s) {
+  if (call->num_slices_to_unref == call->cap_slices_to_unref) {
+    call->cap_slices_to_unref = GPR_MAX(8, 2 * call->cap_slices_to_unref);
+    call->slices_to_unref =
+        gpr_realloc(call->slices_to_unref,
+                    sizeof(*call->slices_to_unref) * call->cap_slices_to_unref);
+  }
+  call->slices_to_unref[call->num_slices_to_unref++] = s;
+  return &call->slices_to_unref[call->num_slices_to_unref - 1];
+}
+
 static void read_metadata(input_stream *inp, size_t *count,
                           grpc_metadata **metadata, call_state *cs) {
   *count = next_byte(inp);
@@ -587,12 +645,11 @@
     *metadata = gpr_malloc(*count * sizeof(**metadata));
     memset(*metadata, 0, *count * sizeof(**metadata));
     for (size_t i = 0; i < *count; i++) {
-      (*metadata)[i].key = read_string(inp);
-      read_buffer(inp, (char **)&(*metadata)[i].value,
-                  &(*metadata)[i].value_length);
+      (*metadata)[i].key = read_string_like_slice(inp);
+      (*metadata)[i].value = read_buffer_like_slice(inp);
       (*metadata)[i].flags = read_uint32(inp);
-      add_to_free(cs, (void *)(*metadata)[i].key);
-      add_to_free(cs, (void *)(*metadata)[i].value);
+      add_to_slice_unref(cs, (*metadata)[i].key);
+      add_to_slice_unref(cs, (*metadata)[i].value);
     }
   } else {
     *metadata = gpr_malloc(1);
@@ -656,7 +713,7 @@
 }
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
-  grpc_test_only_set_metadata_hash_seed(0);
+  grpc_test_only_set_slice_hash_seed(0);
   if (squelch) gpr_set_log_function(dont_log);
   input_stream inp = {data, data + size};
   grpc_resolve_address = my_resolve_address;
@@ -742,7 +799,7 @@
       // create an insecure channel
       case 2: {
         if (g_channel == NULL) {
-          char *target = read_string(&inp);
+          char *target = read_string(&inp, NULL);
           char *target_uri;
           gpr_asprintf(&target_uri, "dns:%s", target);
           grpc_channel_args *args = read_args(&inp);
@@ -871,8 +928,8 @@
           parent_call = g_active_call->call;
         }
         uint32_t propagation_mask = read_uint32(&inp);
-        char *method = read_string(&inp);
-        char *host = read_string(&inp);
+        grpc_slice method = read_string_like_slice(&inp);
+        grpc_slice host = read_string_like_slice(&inp);
         gpr_timespec deadline =
             gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                          gpr_time_from_micros(read_uint32(&inp), GPR_TIMESPAN));
@@ -881,12 +938,12 @@
           call_state *cs = new_call(g_active_call, CLIENT);
           cs->call =
               grpc_channel_create_call(g_channel, parent_call, propagation_mask,
-                                       cq, method, host, deadline, NULL);
+                                       cq, method, &host, deadline, NULL);
         } else {
           end(&inp);
         }
-        gpr_free(method);
-        gpr_free(host);
+        grpc_slice_unref(method);
+        grpc_slice_unref(host);
         break;
       }
       // switch the 'current' call
@@ -933,8 +990,8 @@
                 ok = false;
               } else {
                 has_ops |= 1 << GRPC_OP_SEND_MESSAGE;
-                g_active_call->send_message = op->data.send_message =
-                    read_message(&inp);
+                g_active_call->send_message =
+                    op->data.send_message.send_message = read_message(&inp);
               }
               break;
             case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
@@ -951,18 +1008,19 @@
                   g_active_call);
               op->data.send_status_from_server.status = next_byte(&inp);
               op->data.send_status_from_server.status_details =
-                  read_string(&inp);
+                  add_to_slice_unref(g_active_call,
+                                     read_buffer_like_slice(&inp));
               break;
             case GRPC_OP_RECV_INITIAL_METADATA:
               op->op = GRPC_OP_RECV_INITIAL_METADATA;
               has_ops |= 1 << GRPC_OP_RECV_INITIAL_METADATA;
-              op->data.recv_initial_metadata =
+              op->data.recv_initial_metadata.recv_initial_metadata =
                   &g_active_call->recv_initial_metadata;
               break;
             case GRPC_OP_RECV_MESSAGE:
               op->op = GRPC_OP_RECV_MESSAGE;
               has_ops |= 1 << GRPC_OP_RECV_MESSAGE;
-              op->data.recv_message = &g_active_call->recv_message;
+              op->data.recv_message.recv_message = &g_active_call->recv_message;
               break;
             case GRPC_OP_RECV_STATUS_ON_CLIENT:
               op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
@@ -971,8 +1029,6 @@
                   &g_active_call->recv_trailing_metadata;
               op->data.recv_status_on_client.status_details =
                   &g_active_call->recv_status_details;
-              op->data.recv_status_on_client.status_details_capacity =
-                  &g_active_call->recv_status_details_capacity;
               break;
             case GRPC_OP_RECV_CLOSE_ON_SERVER:
               op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
@@ -1060,14 +1116,14 @@
       }
       // enable a tracer
       case 17: {
-        char *tracer = read_string(&inp);
+        char *tracer = read_string(&inp, NULL);
         grpc_tracer_set_enabled(tracer, 1);
         gpr_free(tracer);
         break;
       }
       // disable a tracer
       case 18: {
-        char *tracer = read_string(&inp);
+        char *tracer = read_string(&inp, NULL);
         grpc_tracer_set_enabled(tracer, 0);
         gpr_free(tracer);
         break;
@@ -1109,7 +1165,7 @@
       // create a secure channel
       case 22: {
         if (g_channel == NULL) {
-          char *target = read_string(&inp);
+          char *target = read_string(&inp, NULL);
           char *target_uri;
           gpr_asprintf(&target_uri, "dns:%s", target);
           grpc_channel_args *args = read_args(&inp);
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/070aca38b5cd06626dfc98126ef1225595800427 b/test/core/end2end/fuzzers/api_fuzzer_corpus/070aca38b5cd06626dfc98126ef1225595800427
new file mode 100644
index 0000000..0be853b
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/070aca38b5cd06626dfc98126ef1225595800427
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/08398518b9b4e98d4625dfb063ab0b6d9399a239 b/test/core/end2end/fuzzers/api_fuzzer_corpus/08398518b9b4e98d4625dfb063ab0b6d9399a239
new file mode 100644
index 0000000..fc57193
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/08398518b9b4e98d4625dfb063ab0b6d9399a239
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/096f9413b1bf712e092cf8bd70754014d6242f12 b/test/core/end2end/fuzzers/api_fuzzer_corpus/096f9413b1bf712e092cf8bd70754014d6242f12
new file mode 100644
index 0000000..c04f03c
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/096f9413b1bf712e092cf8bd70754014d6242f12
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/0a85889229b729883f4c0420d1a13d9f0a2ca7c2 b/test/core/end2end/fuzzers/api_fuzzer_corpus/0a85889229b729883f4c0420d1a13d9f0a2ca7c2
new file mode 100644
index 0000000..dd537e3
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/0a85889229b729883f4c0420d1a13d9f0a2ca7c2
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/1c0dc85faed94bb89bc90f87799d7383cf9907cf b/test/core/end2end/fuzzers/api_fuzzer_corpus/1c0dc85faed94bb89bc90f87799d7383cf9907cf
new file mode 100644
index 0000000..c83ac85
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/1c0dc85faed94bb89bc90f87799d7383cf9907cf
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/2a4fc709f61977f0f6c75760af2b33007ecd5941 b/test/core/end2end/fuzzers/api_fuzzer_corpus/2a4fc709f61977f0f6c75760af2b33007ecd5941
new file mode 100644
index 0000000..8c978eb
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/2a4fc709f61977f0f6c75760af2b33007ecd5941
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/352599b2850ac5a96ec656788c897c4b36114cc4 b/test/core/end2end/fuzzers/api_fuzzer_corpus/352599b2850ac5a96ec656788c897c4b36114cc4
new file mode 100644
index 0000000..4e28b81
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/352599b2850ac5a96ec656788c897c4b36114cc4
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/35f172b0168cd0b2da4509c3463c2502274e01a6 b/test/core/end2end/fuzzers/api_fuzzer_corpus/35f172b0168cd0b2da4509c3463c2502274e01a6
new file mode 100644
index 0000000..f3798b4
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/35f172b0168cd0b2da4509c3463c2502274e01a6
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/424b6a20be32318d920d83aa2a292a0aba013a1b b/test/core/end2end/fuzzers/api_fuzzer_corpus/424b6a20be32318d920d83aa2a292a0aba013a1b
new file mode 100644
index 0000000..00026e7
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/424b6a20be32318d920d83aa2a292a0aba013a1b
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/4b303d7f245a569f507013af0b3afb2f033b6741 b/test/core/end2end/fuzzers/api_fuzzer_corpus/4b303d7f245a569f507013af0b3afb2f033b6741
new file mode 100644
index 0000000..47d9819
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/4b303d7f245a569f507013af0b3afb2f033b6741
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/559d537675dd3fc5e0c6d40e94133e9016014f6c b/test/core/end2end/fuzzers/api_fuzzer_corpus/559d537675dd3fc5e0c6d40e94133e9016014f6c
new file mode 100644
index 0000000..25327f3
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/559d537675dd3fc5e0c6d40e94133e9016014f6c
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/60b4c79213b97204d1e4f6819ad1ed5f6e191789 b/test/core/end2end/fuzzers/api_fuzzer_corpus/60b4c79213b97204d1e4f6819ad1ed5f6e191789
new file mode 100644
index 0000000..2c44e5d
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/60b4c79213b97204d1e4f6819ad1ed5f6e191789
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/671d63b9968444308d4908eb6a26bccdf32e5e29 b/test/core/end2end/fuzzers/api_fuzzer_corpus/671d63b9968444308d4908eb6a26bccdf32e5e29
new file mode 100644
index 0000000..38c69c7
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/671d63b9968444308d4908eb6a26bccdf32e5e29
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/6ad3a1170ef884a884a6f340101549df624bf5a4 b/test/core/end2end/fuzzers/api_fuzzer_corpus/6ad3a1170ef884a884a6f340101549df624bf5a4
new file mode 100644
index 0000000..27cea9e
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/6ad3a1170ef884a884a6f340101549df624bf5a4
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/71961e53698b07bfd57e9ea37067e0dd52298a3d b/test/core/end2end/fuzzers/api_fuzzer_corpus/71961e53698b07bfd57e9ea37067e0dd52298a3d
new file mode 100644
index 0000000..21a263f
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/71961e53698b07bfd57e9ea37067e0dd52298a3d
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/807a48bd33ce375438dbc158c1531891c5b54a51 b/test/core/end2end/fuzzers/api_fuzzer_corpus/807a48bd33ce375438dbc158c1531891c5b54a51
new file mode 100644
index 0000000..4fd3b4c
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/807a48bd33ce375438dbc158c1531891c5b54a51
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/8dfdb04ef558fba84dcbfa65a6aa318b10988fa9 b/test/core/end2end/fuzzers/api_fuzzer_corpus/8dfdb04ef558fba84dcbfa65a6aa318b10988fa9
new file mode 100644
index 0000000..7e71dc6
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/8dfdb04ef558fba84dcbfa65a6aa318b10988fa9
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/9916b65305d28acf75a17495b9f44d7b839b948f b/test/core/end2end/fuzzers/api_fuzzer_corpus/9916b65305d28acf75a17495b9f44d7b839b948f
new file mode 100644
index 0000000..40a6dac
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/9916b65305d28acf75a17495b9f44d7b839b948f
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/a8c0a166004ee637f22f5838960c8f4320738694 b/test/core/end2end/fuzzers/api_fuzzer_corpus/a8c0a166004ee637f22f5838960c8f4320738694
new file mode 100644
index 0000000..c654a53
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/a8c0a166004ee637f22f5838960c8f4320738694
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/abd5de7b5e9218d19a57c47d2ccd6a1bc022c7fb b/test/core/end2end/fuzzers/api_fuzzer_corpus/abd5de7b5e9218d19a57c47d2ccd6a1bc022c7fb
new file mode 100644
index 0000000..5d8ebd3
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/abd5de7b5e9218d19a57c47d2ccd6a1bc022c7fb
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/b36c3e5841c6aed7b0d439bd3b9c6d922d56c70b b/test/core/end2end/fuzzers/api_fuzzer_corpus/b36c3e5841c6aed7b0d439bd3b9c6d922d56c70b
new file mode 100644
index 0000000..4c5fc29
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/b36c3e5841c6aed7b0d439bd3b9c6d922d56c70b
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/b8bc377e53ca54ae4e30f437b69e270040c35f65 b/test/core/end2end/fuzzers/api_fuzzer_corpus/b8bc377e53ca54ae4e30f437b69e270040c35f65
new file mode 100644
index 0000000..44934bb
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/b8bc377e53ca54ae4e30f437b69e270040c35f65
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/crash-60a3f7ed4abddaa6a7c837ace86d048fa15e288b b/test/core/end2end/fuzzers/api_fuzzer_corpus/crash-60a3f7ed4abddaa6a7c837ace86d048fa15e288b
new file mode 100644
index 0000000..3e441c9
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/crash-60a3f7ed4abddaa6a7c837ace86d048fa15e288b
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/db25df0fa146d02874114b8605000e0135ff12dd b/test/core/end2end/fuzzers/api_fuzzer_corpus/db25df0fa146d02874114b8605000e0135ff12dd
new file mode 100644
index 0000000..841f40f
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/db25df0fa146d02874114b8605000e0135ff12dd
Binary files differ
diff --git a/test/core/end2end/fuzzers/api_fuzzer_corpus/f503f8d526eaec9bab6cae97d09207deefa85d8d b/test/core/end2end/fuzzers/api_fuzzer_corpus/f503f8d526eaec9bab6cae97d09207deefa85d8d
new file mode 100644
index 0000000..6bdb2b0
--- /dev/null
+++ b/test/core/end2end/fuzzers/api_fuzzer_corpus/f503f8d526eaec9bab6cae97d09207deefa85d8d
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer.c b/test/core/end2end/fuzzers/client_fuzzer.c
index 26b5208..e7e7dbe 100644
--- a/test/core/end2end/fuzzers/client_fuzzer.c
+++ b/test/core/end2end/fuzzers/client_fuzzer.c
@@ -37,6 +37,7 @@
 #include <grpc/support/alloc.h>
 
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/surface/channel.h"
 #include "test/core/util/memory_counters.h"
 #include "test/core/util/mock_endpoint.h"
@@ -51,7 +52,7 @@
 static void dont_log(gpr_log_func_args *args) {}
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
-  grpc_test_only_set_metadata_hash_seed(0);
+  grpc_test_only_set_slice_hash_seed(0);
   struct grpc_memory_counters counters;
   if (squelch) gpr_set_log_function(dont_log);
   if (leak_check) grpc_memory_counters_init();
@@ -71,9 +72,10 @@
 
   grpc_channel *channel = grpc_channel_create(
       &exec_ctx, "test-target", NULL, GRPC_CLIENT_DIRECT_CHANNEL, transport);
-  grpc_call *call =
-      grpc_channel_create_call(channel, NULL, 0, cq, "/foo", "localhost",
-                               gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  grpc_slice host = grpc_slice_from_static_string("localhost");
+  grpc_call *call = grpc_channel_create_call(
+      channel, NULL, 0, cq, grpc_slice_from_static_string("/foo"), &host,
+      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
 
   grpc_metadata_array initial_metadata_recv;
   grpc_metadata_array_init(&initial_metadata_recv);
@@ -81,8 +83,7 @@
   grpc_metadata_array trailing_metadata_recv;
   grpc_metadata_array_init(&trailing_metadata_recv);
   grpc_status_code status;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details = grpc_empty_slice();
 
   grpc_op ops[6];
   memset(ops, 0, sizeof(ops));
@@ -97,12 +98,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -110,7 +111,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -155,7 +155,7 @@
   grpc_completion_queue_destroy(cq);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_channel_destroy(channel);
   if (response_payload_recv != NULL) {
     grpc_byte_buffer_destroy(response_payload_recv);
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/175f16901997fdee41ac9ec88f7e018d46d774e7 b/test/core/end2end/fuzzers/client_fuzzer_corpus/175f16901997fdee41ac9ec88f7e018d46d774e7
new file mode 100644
index 0000000..1cf8151
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/175f16901997fdee41ac9ec88f7e018d46d774e7
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/26f250ae38865f030176a8801ce992536351a326 b/test/core/end2end/fuzzers/client_fuzzer_corpus/26f250ae38865f030176a8801ce992536351a326
new file mode 100644
index 0000000..6c970a4
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/26f250ae38865f030176a8801ce992536351a326
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/3233f2ab3b6c9431289ef7dc4f40676d0128bcad b/test/core/end2end/fuzzers/client_fuzzer_corpus/3233f2ab3b6c9431289ef7dc4f40676d0128bcad
new file mode 100644
index 0000000..64eef00
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/3233f2ab3b6c9431289ef7dc4f40676d0128bcad
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/3656614c7b0dc11d4dd7e1826f77fa96a88c420f b/test/core/end2end/fuzzers/client_fuzzer_corpus/3656614c7b0dc11d4dd7e1826f77fa96a88c420f
new file mode 100644
index 0000000..fb9667c
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/3656614c7b0dc11d4dd7e1826f77fa96a88c420f
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/3dec1981e8677ec6ad2517feba29869d53f8d884 b/test/core/end2end/fuzzers/client_fuzzer_corpus/3dec1981e8677ec6ad2517feba29869d53f8d884
new file mode 100644
index 0000000..92ff8bc
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/3dec1981e8677ec6ad2517feba29869d53f8d884
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/4bed84590a55b06d7eb30d8faa4b00a881ba9b2c b/test/core/end2end/fuzzers/client_fuzzer_corpus/4bed84590a55b06d7eb30d8faa4b00a881ba9b2c
new file mode 100644
index 0000000..3e3365e
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/4bed84590a55b06d7eb30d8faa4b00a881ba9b2c
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/591d46b83d2e6babc0ce3c753c4606a10c46d7ce b/test/core/end2end/fuzzers/client_fuzzer_corpus/591d46b83d2e6babc0ce3c753c4606a10c46d7ce
new file mode 100644
index 0000000..addaccb
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/591d46b83d2e6babc0ce3c753c4606a10c46d7ce
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/5a52d683c9342dd301b0e699db36175aad715e1d b/test/core/end2end/fuzzers/client_fuzzer_corpus/5a52d683c9342dd301b0e699db36175aad715e1d
new file mode 100644
index 0000000..e9e09a1
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/5a52d683c9342dd301b0e699db36175aad715e1d
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/5b7ff7c113c2bcf125271d22c937f758a0cd2ee4 b/test/core/end2end/fuzzers/client_fuzzer_corpus/5b7ff7c113c2bcf125271d22c937f758a0cd2ee4
new file mode 100644
index 0000000..2ccc02d
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/5b7ff7c113c2bcf125271d22c937f758a0cd2ee4
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/616e55354ba3517ce6762d6cd56600f97915e646 b/test/core/end2end/fuzzers/client_fuzzer_corpus/616e55354ba3517ce6762d6cd56600f97915e646
new file mode 100644
index 0000000..ccaeedb
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/616e55354ba3517ce6762d6cd56600f97915e646
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/7a9a12b15c798b88f1a599779f52d14edf9f4ef5 b/test/core/end2end/fuzzers/client_fuzzer_corpus/7a9a12b15c798b88f1a599779f52d14edf9f4ef5
new file mode 100644
index 0000000..05e8c39
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/7a9a12b15c798b88f1a599779f52d14edf9f4ef5
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/8f05b8da748cc04e64b688c4b435fe64699bd481 b/test/core/end2end/fuzzers/client_fuzzer_corpus/8f05b8da748cc04e64b688c4b435fe64699bd481
new file mode 100644
index 0000000..53a4403
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/8f05b8da748cc04e64b688c4b435fe64699bd481
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/8fd758e2603f1e3772cfcd295ff0f951988d7648 b/test/core/end2end/fuzzers/client_fuzzer_corpus/8fd758e2603f1e3772cfcd295ff0f951988d7648
new file mode 100644
index 0000000..33a7bd8
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/8fd758e2603f1e3772cfcd295ff0f951988d7648
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/9018925b5e791ae4b3ae9c84b8a4d47d86671c2d b/test/core/end2end/fuzzers/client_fuzzer_corpus/9018925b5e791ae4b3ae9c84b8a4d47d86671c2d
new file mode 100644
index 0000000..fccac2a
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/9018925b5e791ae4b3ae9c84b8a4d47d86671c2d
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/9912de197918ac0e305f0c0107f863823e8ae818 b/test/core/end2end/fuzzers/client_fuzzer_corpus/9912de197918ac0e305f0c0107f863823e8ae818
new file mode 100644
index 0000000..5304b1e
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/9912de197918ac0e305f0c0107f863823e8ae818
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/ab90bebd4c017827a6d5de61511445df81169eb6 b/test/core/end2end/fuzzers/client_fuzzer_corpus/ab90bebd4c017827a6d5de61511445df81169eb6
new file mode 100644
index 0000000..3cdcdf9
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/ab90bebd4c017827a6d5de61511445df81169eb6
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/b26a3a83d0b319ce13a9ae164e90fa6da1b05a57 b/test/core/end2end/fuzzers/client_fuzzer_corpus/b26a3a83d0b319ce13a9ae164e90fa6da1b05a57
new file mode 100644
index 0000000..908355a
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/b26a3a83d0b319ce13a9ae164e90fa6da1b05a57
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/ba25be123c630bd3e4aeb9bda6304dd1a7e51365 b/test/core/end2end/fuzzers/client_fuzzer_corpus/ba25be123c630bd3e4aeb9bda6304dd1a7e51365
new file mode 100644
index 0000000..1f1b88a
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/ba25be123c630bd3e4aeb9bda6304dd1a7e51365
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/c77ee0b6cb21ab7b7cf222c7f6563b3b1b1b6eed b/test/core/end2end/fuzzers/client_fuzzer_corpus/c77ee0b6cb21ab7b7cf222c7f6563b3b1b1b6eed
new file mode 100644
index 0000000..3f14a28
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/c77ee0b6cb21ab7b7cf222c7f6563b3b1b1b6eed
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/c7b17d6ae5f3b155532a5a9bf0239c098c35eec1 b/test/core/end2end/fuzzers/client_fuzzer_corpus/c7b17d6ae5f3b155532a5a9bf0239c098c35eec1
new file mode 100644
index 0000000..e0d5825
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/c7b17d6ae5f3b155532a5a9bf0239c098c35eec1
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/ccc36ee2c6a0daad65a4d929599fda5cd38b8fd1 b/test/core/end2end/fuzzers/client_fuzzer_corpus/ccc36ee2c6a0daad65a4d929599fda5cd38b8fd1
new file mode 100644
index 0000000..e612b05
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/ccc36ee2c6a0daad65a4d929599fda5cd38b8fd1
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/crash-133500314833c9037b34a4c34ad779dd78a0fa8d b/test/core/end2end/fuzzers/client_fuzzer_corpus/crash-133500314833c9037b34a4c34ad779dd78a0fa8d
new file mode 100644
index 0000000..e475765
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/crash-133500314833c9037b34a4c34ad779dd78a0fa8d
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/d3141a71cdaa7a0915c8c55abf2c940b0fd00172 b/test/core/end2end/fuzzers/client_fuzzer_corpus/d3141a71cdaa7a0915c8c55abf2c940b0fd00172
new file mode 100644
index 0000000..e2881da
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/d3141a71cdaa7a0915c8c55abf2c940b0fd00172
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/de9b9a35e1e7040e842b597b8d2fe4cd602e67f1 b/test/core/end2end/fuzzers/client_fuzzer_corpus/de9b9a35e1e7040e842b597b8d2fe4cd602e67f1
new file mode 100644
index 0000000..7b8d43e
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/de9b9a35e1e7040e842b597b8d2fe4cd602e67f1
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/e5319a8570a762bbdd67135b49579097324e369a b/test/core/end2end/fuzzers/client_fuzzer_corpus/e5319a8570a762bbdd67135b49579097324e369a
new file mode 100644
index 0000000..128aef0
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/e5319a8570a762bbdd67135b49579097324e369a
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/e5e276acd665ccb47f868fe3bc36e647bbb840bd b/test/core/end2end/fuzzers/client_fuzzer_corpus/e5e276acd665ccb47f868fe3bc36e647bbb840bd
new file mode 100644
index 0000000..05b14a7
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/e5e276acd665ccb47f868fe3bc36e647bbb840bd
Binary files differ
diff --git a/test/core/end2end/fuzzers/client_fuzzer_corpus/f69aa5666fd60c1b6814198f12d049abb0e9f148 b/test/core/end2end/fuzzers/client_fuzzer_corpus/f69aa5666fd60c1b6814198f12d049abb0e9f148
new file mode 100644
index 0000000..380bac2
--- /dev/null
+++ b/test/core/end2end/fuzzers/client_fuzzer_corpus/f69aa5666fd60c1b6814198f12d049abb0e9f148
Binary files differ
diff --git a/test/core/end2end/fuzzers/hpack.dictionary b/test/core/end2end/fuzzers/hpack.dictionary
index 12db0ff..81a2419 100644
--- a/test/core/end2end/fuzzers/hpack.dictionary
+++ b/test/core/end2end/fuzzers/hpack.dictionary
@@ -1,24 +1,62 @@
 # hpack fuzzing dictionary
+"\x05:path"
+"\x07:method"
+"\x07:status"
+"\x0A:authority"
+"\x07:scheme"
+"\x02te"
+"\x0Cgrpc-message"
+"\x0Bgrpc-status"
+"\x10grpc-payload-bin"
+"\x0Dgrpc-encoding"
+"\x14grpc-accept-encoding"
+"\x0Ccontent-type"
+"\x1Egrpc-internal-encoding-request"
+"\x0Auser-agent"
+"\x04host"
+"\x08lb-token"
+"\x0Blb-cost-bin"
+"\x0Cgrpc-timeout"
+"\x10grpc-tracing-bin"
+"\x0Egrpc-stats-bin"
+"\x00"
+"\x13grpc.wait_for_ready"
+"\x0Cgrpc.timeout"
+"\x1Egrpc.max_request_message_bytes"
+"\x1Fgrpc.max_response_message_bytes"
+"$/grpc.lb.v1.LoadBalancer/BalanceLoad"
 "\x010"
 "\x011"
 "\x012"
+"\x08identity"
+"\x04gzip"
+"\x07deflate"
+"\x08trailers"
+"\x10application/grpc"
+"\x04POST"
 "\x03200"
+"\x03404"
+"\x04http"
+"\x05https"
+"\x04grpc"
+"\x03GET"
+"\x03PUT"
+"\x01/"
+"\x0B/index.html"
 "\x03204"
 "\x03206"
 "\x03304"
 "\x03400"
-"\x03404"
 "\x03500"
-"\x06accept"
 "\x0Eaccept-charset"
 "\x0Faccept-encoding"
+"\x0Dgzip, deflate"
 "\x0Faccept-language"
 "\x0Daccept-ranges"
+"\x06accept"
 "\x1Baccess-control-allow-origin"
 "\x03age"
 "\x05allow"
-"\x10application/grpc"
-"\x0A:authority"
 "\x0Dauthorization"
 "\x0Dcache-control"
 "\x13content-disposition"
@@ -27,81 +65,71 @@
 "\x0Econtent-length"
 "\x10content-location"
 "\x0Dcontent-range"
-"\x0Ccontent-type"
 "\x06cookie"
 "\x04date"
-"\x07deflate"
-"\x0Cdeflate,gzip"
-"\x00"
 "\x04etag"
 "\x06expect"
 "\x07expires"
 "\x04from"
-"\x03GET"
-"\x04grpc"
-"\x14grpc-accept-encoding"
-"\x0Dgrpc-encoding"
-"\x1Egrpc-internal-encoding-request"
-"\x0Cgrpc-message"
-"\x10grpc-payload-bin"
-"\x0Egrpc-stats-bin"
-"\x0Bgrpc-status"
-"\x0Cgrpc-timeout"
-"\x10grpc-tracing-bin"
-"\x04gzip"
-"\x0Dgzip, deflate"
-"\x04host"
-"\x04http"
-"\x05https"
-"\x08identity"
-"\x10identity,deflate"
-"\x15identity,deflate,gzip"
-"\x0Didentity,gzip"
 "\x08if-match"
 "\x11if-modified-since"
 "\x0Dif-none-match"
 "\x08if-range"
 "\x13if-unmodified-since"
 "\x0Dlast-modified"
-"\x0Blb-cost-bin"
-"\x08lb-token"
 "\x04link"
 "\x08location"
 "\x0Cmax-forwards"
-"\x07:method"
-"\x05:path"
-"\x04POST"
 "\x12proxy-authenticate"
 "\x13proxy-authorization"
-"\x03PUT"
 "\x05range"
 "\x07referer"
 "\x07refresh"
 "\x0Bretry-after"
-"\x07:scheme"
 "\x06server"
 "\x0Aset-cookie"
-"\x01/"
-"\x0B/index.html"
-"\x07:status"
 "\x19strict-transport-security"
-"\x02te"
-"\x08trailers"
 "\x11transfer-encoding"
-"\x0Auser-agent"
 "\x04vary"
 "\x03via"
 "\x10www-authenticate"
+"\x10identity,deflate"
+"\x0Didentity,gzip"
+"\x0Cdeflate,gzip"
+"\x15identity,deflate,gzip"
+"\x00\x0Bgrpc-status\x010"
+"\x00\x0Bgrpc-status\x011"
+"\x00\x0Bgrpc-status\x012"
+"\x00\x0Dgrpc-encoding\x08identity"
+"\x00\x0Dgrpc-encoding\x04gzip"
+"\x00\x0Dgrpc-encoding\x07deflate"
+"\x00\x02te\x08trailers"
+"\x00\x0Ccontent-type\x10application/grpc"
+"\x00\x07:method\x04POST"
+"\x00\x07:status\x03200"
+"\x00\x07:status\x03404"
+"\x00\x07:scheme\x04http"
+"\x00\x07:scheme\x05https"
+"\x00\x07:scheme\x04grpc"
+"\x00\x0A:authority\x00"
+"\x00\x07:method\x03GET"
+"\x00\x07:method\x03PUT"
+"\x00\x05:path\x01/"
+"\x00\x05:path\x0B/index.html"
+"\x00\x07:status\x03204"
+"\x00\x07:status\x03206"
+"\x00\x07:status\x03304"
+"\x00\x07:status\x03400"
+"\x00\x07:status\x03500"
 "\x00\x0Eaccept-charset\x00"
-"\x00\x06accept\x00"
 "\x00\x0Faccept-encoding\x00"
 "\x00\x0Faccept-encoding\x0Dgzip, deflate"
 "\x00\x0Faccept-language\x00"
 "\x00\x0Daccept-ranges\x00"
+"\x00\x06accept\x00"
 "\x00\x1Baccess-control-allow-origin\x00"
 "\x00\x03age\x00"
 "\x00\x05allow\x00"
-"\x00\x0A:authority\x00"
 "\x00\x0Dauthorization\x00"
 "\x00\x0Dcache-control\x00"
 "\x00\x13content-disposition\x00"
@@ -110,7 +138,6 @@
 "\x00\x0Econtent-length\x00"
 "\x00\x10content-location\x00"
 "\x00\x0Dcontent-range\x00"
-"\x00\x0Ccontent-type\x10application/grpc"
 "\x00\x0Ccontent-type\x00"
 "\x00\x06cookie\x00"
 "\x00\x04date\x00"
@@ -118,19 +145,6 @@
 "\x00\x06expect\x00"
 "\x00\x07expires\x00"
 "\x00\x04from\x00"
-"\x00\x14grpc-accept-encoding\x07deflate"
-"\x00\x14grpc-accept-encoding\x0Cdeflate,gzip"
-"\x00\x14grpc-accept-encoding\x04gzip"
-"\x00\x14grpc-accept-encoding\x08identity"
-"\x00\x14grpc-accept-encoding\x10identity,deflate"
-"\x00\x14grpc-accept-encoding\x15identity,deflate,gzip"
-"\x00\x14grpc-accept-encoding\x0Didentity,gzip"
-"\x00\x0Dgrpc-encoding\x07deflate"
-"\x00\x0Dgrpc-encoding\x04gzip"
-"\x00\x0Dgrpc-encoding\x08identity"
-"\x00\x0Bgrpc-status\x010"
-"\x00\x0Bgrpc-status\x011"
-"\x00\x0Bgrpc-status\x012"
 "\x00\x04host\x00"
 "\x00\x08if-match\x00"
 "\x00\x11if-modified-since\x00"
@@ -138,38 +152,29 @@
 "\x00\x08if-range\x00"
 "\x00\x13if-unmodified-since\x00"
 "\x00\x0Dlast-modified\x00"
-"\x00\x0Blb-cost-bin\x00"
 "\x00\x08lb-token\x00"
+"\x00\x0Blb-cost-bin\x00"
 "\x00\x04link\x00"
 "\x00\x08location\x00"
 "\x00\x0Cmax-forwards\x00"
-"\x00\x07:method\x03GET"
-"\x00\x07:method\x04POST"
-"\x00\x07:method\x03PUT"
-"\x00\x05:path\x01/"
-"\x00\x05:path\x0B/index.html"
 "\x00\x12proxy-authenticate\x00"
 "\x00\x13proxy-authorization\x00"
 "\x00\x05range\x00"
 "\x00\x07referer\x00"
 "\x00\x07refresh\x00"
 "\x00\x0Bretry-after\x00"
-"\x00\x07:scheme\x04grpc"
-"\x00\x07:scheme\x04http"
-"\x00\x07:scheme\x05https"
 "\x00\x06server\x00"
 "\x00\x0Aset-cookie\x00"
-"\x00\x07:status\x03200"
-"\x00\x07:status\x03204"
-"\x00\x07:status\x03206"
-"\x00\x07:status\x03304"
-"\x00\x07:status\x03400"
-"\x00\x07:status\x03404"
-"\x00\x07:status\x03500"
 "\x00\x19strict-transport-security\x00"
-"\x00\x02te\x08trailers"
 "\x00\x11transfer-encoding\x00"
 "\x00\x0Auser-agent\x00"
 "\x00\x04vary\x00"
 "\x00\x03via\x00"
 "\x00\x10www-authenticate\x00"
+"\x00\x14grpc-accept-encoding\x08identity"
+"\x00\x14grpc-accept-encoding\x07deflate"
+"\x00\x14grpc-accept-encoding\x10identity,deflate"
+"\x00\x14grpc-accept-encoding\x04gzip"
+"\x00\x14grpc-accept-encoding\x0Didentity,gzip"
+"\x00\x14grpc-accept-encoding\x0Cdeflate,gzip"
+"\x00\x14grpc-accept-encoding\x15identity,deflate,gzip"
diff --git a/test/core/end2end/fuzzers/server_fuzzer.c b/test/core/end2end/fuzzers/server_fuzzer.c
index 115fb06..186542d 100644
--- a/test/core/end2end/fuzzers/server_fuzzer.c
+++ b/test/core/end2end/fuzzers/server_fuzzer.c
@@ -34,6 +34,7 @@
 #include <grpc/grpc.h>
 
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/surface/server.h"
 #include "test/core/util/memory_counters.h"
 #include "test/core/util/mock_endpoint.h"
@@ -49,7 +50,7 @@
 static void dont_log(gpr_log_func_args *args) {}
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
-  grpc_test_only_set_metadata_hash_seed(0);
+  grpc_test_only_set_slice_hash_seed(0);
   struct grpc_memory_counters counters;
   if (squelch) gpr_set_log_function(dont_log);
   if (leak_check) grpc_memory_counters_init();
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/084e9e02b8f2ed41ff9c22fef80e522e491227c5 b/test/core/end2end/fuzzers/server_fuzzer_corpus/084e9e02b8f2ed41ff9c22fef80e522e491227c5
new file mode 100644
index 0000000..dceac83
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/084e9e02b8f2ed41ff9c22fef80e522e491227c5
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/129382aa11fa2922053fa47ba691dddb98901260 b/test/core/end2end/fuzzers/server_fuzzer_corpus/129382aa11fa2922053fa47ba691dddb98901260
new file mode 100644
index 0000000..aaab740
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/129382aa11fa2922053fa47ba691dddb98901260
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/1353e447b7b10fb3eabb02863a1fc5bc9bb50460 b/test/core/end2end/fuzzers/server_fuzzer_corpus/1353e447b7b10fb3eabb02863a1fc5bc9bb50460
new file mode 100644
index 0000000..80c0509
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/1353e447b7b10fb3eabb02863a1fc5bc9bb50460
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/47563391b73b6ef4bf987014c3e631fe2555a377 b/test/core/end2end/fuzzers/server_fuzzer_corpus/47563391b73b6ef4bf987014c3e631fe2555a377
new file mode 100644
index 0000000..ed9f1c8
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/47563391b73b6ef4bf987014c3e631fe2555a377
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/6c0ec181e81b915071766762f5d78e9b1ccc9128 b/test/core/end2end/fuzzers/server_fuzzer_corpus/6c0ec181e81b915071766762f5d78e9b1ccc9128
new file mode 100644
index 0000000..e373c1e
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/6c0ec181e81b915071766762f5d78e9b1ccc9128
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/6c66271b74a11f4e7065a6acbc86e4611e7e0dcc b/test/core/end2end/fuzzers/server_fuzzer_corpus/6c66271b74a11f4e7065a6acbc86e4611e7e0dcc
new file mode 100644
index 0000000..c4a5dd5
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/6c66271b74a11f4e7065a6acbc86e4611e7e0dcc
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/72db978194609ac138bca393650740a3eae8f448 b/test/core/end2end/fuzzers/server_fuzzer_corpus/72db978194609ac138bca393650740a3eae8f448
new file mode 100644
index 0000000..8d3023a
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/72db978194609ac138bca393650740a3eae8f448
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/7df75412d12894fc501bd2d8493fe9e5a753ad88 b/test/core/end2end/fuzzers/server_fuzzer_corpus/7df75412d12894fc501bd2d8493fe9e5a753ad88
new file mode 100644
index 0000000..d2200a2
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/7df75412d12894fc501bd2d8493fe9e5a753ad88
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/947f53c0978672e59be452395670fa949a33cb4f b/test/core/end2end/fuzzers/server_fuzzer_corpus/947f53c0978672e59be452395670fa949a33cb4f
new file mode 100644
index 0000000..1c1f163
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/947f53c0978672e59be452395670fa949a33cb4f
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/a8305e9bb4a544eaef27e8bd21b4faabf524a84e b/test/core/end2end/fuzzers/server_fuzzer_corpus/a8305e9bb4a544eaef27e8bd21b4faabf524a84e
new file mode 100644
index 0000000..9476948
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/a8305e9bb4a544eaef27e8bd21b4faabf524a84e
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/ac4e19c9fe290afc54bbb1e5f6cfe84a9c1f3c29 b/test/core/end2end/fuzzers/server_fuzzer_corpus/ac4e19c9fe290afc54bbb1e5f6cfe84a9c1f3c29
new file mode 100644
index 0000000..6fb218b
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/ac4e19c9fe290afc54bbb1e5f6cfe84a9c1f3c29
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/ae297426638eb5a0611324f403c3189515274f1b b/test/core/end2end/fuzzers/server_fuzzer_corpus/ae297426638eb5a0611324f403c3189515274f1b
new file mode 100644
index 0000000..9540cb5
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/ae297426638eb5a0611324f403c3189515274f1b
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/b74dcd9c2780f145e09a27d2e03119576889a301 b/test/core/end2end/fuzzers/server_fuzzer_corpus/b74dcd9c2780f145e09a27d2e03119576889a301
new file mode 100644
index 0000000..ffe5b3b
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/b74dcd9c2780f145e09a27d2e03119576889a301
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/c39c0192d0d4f2b24f05c25a9e669cc091bff9e9 b/test/core/end2end/fuzzers/server_fuzzer_corpus/c39c0192d0d4f2b24f05c25a9e669cc091bff9e9
new file mode 100644
index 0000000..0da0378
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/c39c0192d0d4f2b24f05c25a9e669cc091bff9e9
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/crash-901723090ff2042ecc5a008ccd7ae0845c1681cb b/test/core/end2end/fuzzers/server_fuzzer_corpus/crash-901723090ff2042ecc5a008ccd7ae0845c1681cb
new file mode 100644
index 0000000..9f90983
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/crash-901723090ff2042ecc5a008ccd7ae0845c1681cb
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/d297b3f84e3dee9f74bf1162718aff66a11a7f5c b/test/core/end2end/fuzzers/server_fuzzer_corpus/d297b3f84e3dee9f74bf1162718aff66a11a7f5c
new file mode 100644
index 0000000..e619ae5
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/d297b3f84e3dee9f74bf1162718aff66a11a7f5c
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/e021c5b9815f02ee9d1e5a003cbf3500e19db13c b/test/core/end2end/fuzzers/server_fuzzer_corpus/e021c5b9815f02ee9d1e5a003cbf3500e19db13c
new file mode 100644
index 0000000..a3ae822
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/e021c5b9815f02ee9d1e5a003cbf3500e19db13c
Binary files differ
diff --git a/test/core/end2end/fuzzers/server_fuzzer_corpus/fc162db40f87facb73cff20751e23af2e1f5296e b/test/core/end2end/fuzzers/server_fuzzer_corpus/fc162db40f87facb73cff20751e23af2e1f5296e
new file mode 100644
index 0000000..d63d69c
--- /dev/null
+++ b/test/core/end2end/fuzzers/server_fuzzer_corpus/fc162db40f87facb73cff20751e23af2e1f5296e
Binary files differ
diff --git a/test/core/end2end/goaway_server_test.c b/test/core/end2end/goaway_server_test.c
index cd68b39..65fcb95 100644
--- a/test/core/end2end/goaway_server_test.c
+++ b/test/core/end2end/goaway_server_test.c
@@ -101,8 +101,7 @@
   grpc_metadata_array request_metadata1;
   grpc_call_details request_details1;
   grpc_status_code status1;
-  char *details1 = NULL;
-  size_t details_capacity1 = 0;
+  grpc_slice details1;
   grpc_metadata_array_init(&trailing_metadata_recv1);
   grpc_metadata_array_init(&request_metadata1);
   grpc_call_details_init(&request_details1);
@@ -111,8 +110,7 @@
   grpc_metadata_array request_metadata2;
   grpc_call_details request_details2;
   grpc_status_code status2;
-  char *details2 = NULL;
-  size_t details_capacity2 = 0;
+  grpc_slice details2;
   grpc_metadata_array_init(&trailing_metadata_recv2);
   grpc_metadata_array_init(&request_metadata2);
   grpc_call_details_init(&request_details2);
@@ -137,9 +135,11 @@
   /* create a channel that picks first amongst the servers */
   grpc_channel *chan = grpc_insecure_channel_create("test", &client_args, NULL);
   /* and an initial call to them */
-  grpc_call *call1 = grpc_channel_create_call(
-      chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/foo", "127.0.0.1",
-      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(20), NULL);
+  grpc_slice host = grpc_slice_from_static_string("127.0.0.1");
+  grpc_call *call1 =
+      grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+                               grpc_slice_from_static_string("/foo"), &host,
+                               GRPC_TIMEOUT_SECONDS_TO_DEADLINE(20), NULL);
   /* send initial metadata to probe connectivity */
   memset(ops, 0, sizeof(ops));
   op = ops;
@@ -158,7 +158,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv1;
   op->data.recv_status_on_client.status = &status1;
   op->data.recv_status_on_client.status_details = &details1;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity1;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -213,9 +212,10 @@
   cq_verify_empty(cqv);
 
   /* and a new call: should go through to server2 when we start it */
-  grpc_call *call2 = grpc_channel_create_call(
-      chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/foo", "127.0.0.1",
-      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(20), NULL);
+  grpc_call *call2 =
+      grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+                               grpc_slice_from_static_string("/foo"), &host,
+                               GRPC_TIMEOUT_SECONDS_TO_DEADLINE(20), NULL);
   /* send initial metadata to probe connectivity */
   memset(ops, 0, sizeof(ops));
   op = ops;
@@ -234,7 +234,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv2;
   op->data.recv_status_on_client.status = &status2;
   op->data.recv_status_on_client.status_details = &details2;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity2;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -300,11 +299,11 @@
   grpc_metadata_array_destroy(&trailing_metadata_recv1);
   grpc_metadata_array_destroy(&request_metadata1);
   grpc_call_details_destroy(&request_details1);
-  gpr_free(details1);
+  grpc_slice_unref(details1);
   grpc_metadata_array_destroy(&trailing_metadata_recv2);
   grpc_metadata_array_destroy(&request_metadata2);
   grpc_call_details_destroy(&request_details2);
-  gpr_free(details2);
+  grpc_slice_unref(details2);
 
   cq_verifier_destroy(cqv);
   grpc_completion_queue_destroy(cq);
diff --git a/test/core/end2end/invalid_call_argument_test.c b/test/core/end2end/invalid_call_argument_test.c
index d974d2c..eb6d23c 100644
--- a/test/core/end2end/invalid_call_argument_test.c
+++ b/test/core/end2end/invalid_call_argument_test.c
@@ -56,8 +56,7 @@
   grpc_metadata_array initial_metadata_recv;
   grpc_metadata_array trailing_metadata_recv;
   grpc_status_code status;
-  char *details;
-  size_t details_capacity;
+  grpc_slice details;
   grpc_call *server_call;
   grpc_server *server;
   grpc_metadata_array server_initial_metadata_recv;
@@ -76,17 +75,17 @@
   g_state.deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(2);
   g_state.cq = grpc_completion_queue_create(NULL);
   g_state.cqv = cq_verifier_create(g_state.cq);
-  g_state.details = NULL;
-  g_state.details_capacity = 0;
+  g_state.details = grpc_empty_slice();
   memset(g_state.ops, 0, sizeof(g_state.ops));
 
   if (is_client) {
     /* create a call, channel to a non existant server */
     g_state.chan =
         grpc_insecure_channel_create("nonexistant:54321", NULL, NULL);
+    grpc_slice host = grpc_slice_from_static_string("nonexistant");
     g_state.call = grpc_channel_create_call(
-        g_state.chan, NULL, GRPC_PROPAGATE_DEFAULTS, g_state.cq, "/Foo",
-        "nonexistant", g_state.deadline, NULL);
+        g_state.chan, NULL, GRPC_PROPAGATE_DEFAULTS, g_state.cq,
+        grpc_slice_from_static_string("/Foo"), &host, g_state.deadline, NULL);
   } else {
     g_state.server = grpc_server_create(NULL, NULL);
     grpc_server_register_completion_queue(g_state.server, g_state.cq, NULL);
@@ -97,9 +96,10 @@
     gpr_join_host_port(&server_hostport, "localhost", port);
     g_state.chan = grpc_insecure_channel_create(server_hostport, NULL, NULL);
     gpr_free(server_hostport);
+    grpc_slice host = grpc_slice_from_static_string("bar");
     g_state.call = grpc_channel_create_call(
-        g_state.chan, NULL, GRPC_PROPAGATE_DEFAULTS, g_state.cq, "/Foo", "bar",
-        g_state.deadline, NULL);
+        g_state.chan, NULL, GRPC_PROPAGATE_DEFAULTS, g_state.cq,
+        grpc_slice_from_static_string("/Foo"), &host, g_state.deadline, NULL);
     grpc_metadata_array_init(&g_state.server_initial_metadata_recv);
     grpc_call_details_init(&g_state.call_details);
     op = g_state.ops;
@@ -126,7 +126,7 @@
   grpc_call_destroy(g_state.call);
   cq_verifier_destroy(g_state.cqv);
   grpc_channel_destroy(g_state.chan);
-  gpr_free(g_state.details);
+  grpc_slice_unref(g_state.details);
   grpc_metadata_array_destroy(&g_state.initial_metadata_recv);
   grpc_metadata_array_destroy(&g_state.trailing_metadata_recv);
 
@@ -237,7 +237,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = NULL;
+  op->data.send_message.send_message = NULL;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -263,12 +263,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = tag(2);
+  op->data.send_message.send_message = tag(2);
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -289,7 +289,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -306,7 +307,8 @@
   prepare_test(1);
   op = g_state.ops;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &g_state.initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &g_state.initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -317,7 +319,8 @@
   cq_verify(g_state.cqv);
   op = g_state.ops;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &g_state.initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &g_state.initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -335,7 +338,7 @@
   prepare_test(1);
   op = g_state.ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &payload;
+  op->data.recv_message.recv_message = &payload;
   op->flags = 1;
   op->reserved = NULL;
   op++;
@@ -353,12 +356,12 @@
   prepare_test(1);
   op = g_state.ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &payload;
+  op->data.recv_message.recv_message = &payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &payload;
+  op->data.recv_message.recv_message = &payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -398,8 +401,6 @@
       &g_state.trailing_metadata_recv;
   op->data.recv_status_on_client.status = &g_state.status;
   op->data.recv_status_on_client.status_details = &g_state.details;
-  op->data.recv_status_on_client.status_details_capacity =
-      &g_state.details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -414,7 +415,6 @@
   op->data.recv_status_on_client.trailing_metadata = NULL;
   op->data.recv_status_on_client.status = NULL;
   op->data.recv_status_on_client.status_details = NULL;
-  op->data.recv_status_on_client.status_details_capacity = NULL;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -453,8 +453,6 @@
       &g_state.trailing_metadata_recv;
   op->data.recv_status_on_client.status = &g_state.status;
   op->data.recv_status_on_client.status_details = &g_state.details;
-  op->data.recv_status_on_client.status_details_capacity =
-      &g_state.details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -474,7 +472,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 1;
   op->reserved = NULL;
   op++;
@@ -495,7 +494,8 @@
   op->data.send_status_from_server.trailing_metadata_count =
       (size_t)INT_MAX + 1;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -515,14 +515,15 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -577,9 +578,8 @@
   gpr_log(GPR_INFO, "test_invalid_initial_metadata_reserved_key");
 
   grpc_metadata metadata;
-  metadata.key = ":start_with_colon";
-  metadata.value = "value";
-  metadata.value_length = 6;
+  metadata.key = grpc_slice_from_static_string(":start_with_colon");
+  metadata.value = grpc_slice_from_static_string("value");
 
   grpc_op *op;
   prepare_test(1);
@@ -599,6 +599,7 @@
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   grpc_init();
+  test_invalid_initial_metadata_reserved_key();
   test_non_null_reserved_on_start_batch();
   test_non_null_reserved_on_op();
   test_send_initial_metadata_more_than_once();
@@ -618,7 +619,6 @@
   test_send_server_status_twice();
   test_recv_close_on_server_with_invalid_flags();
   test_recv_close_on_server_twice();
-  test_invalid_initial_metadata_reserved_key();
   grpc_shutdown();
 
   return 0;
diff --git a/test/core/end2end/no_server_test.c b/test/core/end2end/no_server_test.c
index 03ff70a..5c0f7a0 100644
--- a/test/core/end2end/no_server_test.c
+++ b/test/core/end2end/no_server_test.c
@@ -52,8 +52,7 @@
   grpc_op *op;
   grpc_metadata_array trailing_metadata_recv;
   grpc_status_code status;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
   grpc_test_init(argc, argv);
   grpc_init();
@@ -65,8 +64,10 @@
 
   /* create a call, channel to a non existant server */
   chan = grpc_insecure_channel_create("nonexistant:54321", NULL, NULL);
+  grpc_slice host = grpc_slice_from_static_string("nonexistant");
   call = grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
-                                  "/Foo", "nonexistant", deadline, NULL);
+                                  grpc_slice_from_static_string("/Foo"), &host,
+                                  deadline, NULL);
 
   memset(ops, 0, sizeof(ops));
   op = ops;
@@ -79,7 +80,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -101,7 +101,7 @@
   grpc_channel_destroy(chan);
   cq_verifier_destroy(cqv);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
 
   grpc_shutdown();
diff --git a/test/core/end2end/tests/authority_not_supported.c b/test/core/end2end/tests/authority_not_supported.c
index 705970f..4ff8acb 100644
--- a/test/core/end2end/tests/authority_not_supported.c
+++ b/test/core/end2end/tests/authority_not_supported.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -103,9 +103,14 @@
   grpc_byte_buffer *request_payload =
       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
   gpr_timespec deadline = five_seconds_time();
-  grpc_metadata meta_c[2] = {
-      {"key1", "val1", 4, 0, {{NULL, NULL, NULL, NULL}}},
-      {"key2", "val2", 4, 0, {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"),
+                              grpc_slice_from_static_string("val1"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}},
+                             {grpc_slice_from_static_string("key2"),
+                              grpc_slice_from_static_string("val2"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}}};
   grpc_end2end_test_fixture f =
       begin_test(config, "test_with_authority_header", NULL, NULL);
   cq_verifier *cqv = cq_verifier_create(f.cq);
@@ -116,11 +121,12 @@
   grpc_byte_buffer *response_payload_recv = NULL;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
+  grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr");
   c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
-                               "/foo", "foo.test.google.fr", deadline, NULL);
+                               grpc_slice_from_static_string("/foo"), &host,
+                               deadline, NULL);
   GPR_ASSERT(c);
 
   grpc_metadata_array_init(&initial_metadata_recv);
@@ -135,7 +141,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -144,12 +150,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -157,7 +163,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -169,7 +174,7 @@
 
   GPR_ASSERT(status == GRPC_STATUS_CANCELLED);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
 
diff --git a/test/core/end2end/tests/bad_hostname.c b/test/core/end2end/tests/bad_hostname.c
index e0c7ac7..6eada3b 100644
--- a/test/core/end2end/tests/bad_hostname.c
+++ b/test/core/end2end/tests/bad_hostname.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_client(&f, client_args);
   config.init_server(&f, server_args);
@@ -109,11 +109,12 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
+  grpc_slice host = grpc_slice_from_static_string("slartibartfast.local");
   c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
-                               "/foo", "slartibartfast.local", deadline, NULL);
+                               grpc_slice_from_static_string("/foo"), &host,
+                               deadline, NULL);
   GPR_ASSERT(c);
 
   grpc_metadata_array_init(&initial_metadata_recv);
@@ -133,7 +134,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -141,7 +142,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -153,7 +153,7 @@
 
   GPR_ASSERT(status == GRPC_STATUS_INTERNAL);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/binary_metadata.c b/test/core/end2end/tests/binary_metadata.c
index dd7a8a9..9df98ea 100644
--- a/test/core/end2end/tests/binary_metadata.c
+++ b/test/core/end2end/tests/binary_metadata.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -110,25 +110,25 @@
       grpc_raw_byte_buffer_create(&response_payload_slice, 1);
   gpr_timespec deadline = five_seconds_time();
   grpc_metadata meta_c[2] = {
-      {"key1-bin",
-       "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc",
-       13,
+      {grpc_slice_from_static_string("key1-bin"),
+       grpc_slice_from_static_string(
+           "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc"),
        0,
        {{NULL, NULL, NULL, NULL}}},
-      {"key2-bin",
-       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d",
-       14,
+      {grpc_slice_from_static_string("key2-bin"),
+       grpc_slice_from_static_string(
+           "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d"),
        0,
        {{NULL, NULL, NULL, NULL}}}};
   grpc_metadata meta_s[2] = {
-      {"key3-bin",
-       "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee",
-       15,
+      {grpc_slice_from_static_string("key3-bin"),
+       grpc_slice_from_static_string(
+           "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee"),
        0,
        {{NULL, NULL, NULL, NULL}}},
-      {"key4-bin",
-       "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
-       16,
+      {grpc_slice_from_static_string("key4-bin"),
+       grpc_slice_from_static_string(
+           "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"),
        0,
        {{NULL, NULL, NULL, NULL}}}};
   grpc_end2end_test_fixture f = begin_test(
@@ -144,13 +144,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -168,7 +168,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -177,12 +177,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -190,7 +190,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -213,7 +212,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -231,14 +230,14 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details =
+  grpc_slice status_string = grpc_slice_from_static_string(
       "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12"
       "\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24"
       "\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36"
@@ -253,7 +252,8 @@
       "\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8"
       "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea"
       "\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc"
-      "\xfd\xfe\xff";
+      "\xfd\xfe\xff");
+  op->data.send_status_from_server.status_details = &status_string;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -267,24 +267,25 @@
   GPR_ASSERT(status == GRPC_STATUS_OK);
   GPR_ASSERT(
       0 ==
-      strcmp(details,
-             "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
-             "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
-             "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
-             "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
-             "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
-             "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
-             "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
-             "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
-             "\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
-             "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
-             "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
-             "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
-             "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
-             "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
-             "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
-             "\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+      grpc_slice_str_cmp(
+          details,
+          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+          "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
+          "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
+          "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
+          "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
+          "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
+          "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
+          "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
+          "\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
+          "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
+          "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
+          "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
+          "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
+          "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
+          "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
+          "\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
@@ -303,7 +304,7 @@
       &initial_metadata_recv, "key4-bin",
       "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/call_creds.c b/test/core/end2end/tests/call_creds.c
index 606938f..81309bb 100644
--- a/test/core/end2end/tests/call_creds.c
+++ b/test/core/end2end/tests/call_creds.c
@@ -59,7 +59,7 @@
                                             const char *test_name,
                                             int fail_server_auth_check) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(NULL, NULL);
   config.init_client(&f, NULL);
   if (fail_server_auth_check) {
@@ -156,8 +156,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   grpc_call_credentials *creds = NULL;
   grpc_auth_context *s_auth_context = NULL;
@@ -167,8 +166,9 @@
   cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
   creds = grpc_google_iam_credentials_create(iam_token, iam_selector, NULL);
@@ -203,7 +203,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -212,12 +212,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -225,7 +225,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -259,7 +258,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -277,14 +276,15 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -296,8 +296,8 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
@@ -337,7 +337,7 @@
       break;
   }
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -391,8 +391,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   grpc_byte_buffer *response_payload_recv = NULL;
   grpc_slice request_payload_slice =
       grpc_slice_from_copied_string("hello world");
@@ -404,8 +403,9 @@
   cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -425,7 +425,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -435,7 +434,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -444,12 +443,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -468,7 +467,7 @@
 
   grpc_byte_buffer_destroy(request_payload);
   grpc_byte_buffer_destroy(response_payload_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
 
   grpc_call_destroy(c);
 
diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c
index a8e310c..a8127c0 100644
--- a/test/core/end2end/tests/cancel_after_accept.c
+++ b/test/core/end2end/tests/cancel_after_accept.c
@@ -43,6 +43,7 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/service_config.h"
 
@@ -56,7 +57,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -118,8 +119,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   grpc_byte_buffer *request_payload_recv = NULL;
   grpc_byte_buffer *response_payload_recv = NULL;
   grpc_slice request_payload_slice =
@@ -154,8 +154,9 @@
   cq_verifier *cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/service/method",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -170,7 +171,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -180,17 +180,17 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -206,7 +206,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -216,7 +216,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -246,7 +246,7 @@
   grpc_byte_buffer_destroy(response_payload);
   grpc_byte_buffer_destroy(request_payload_recv);
   grpc_byte_buffer_destroy(response_payload_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
 
   grpc_call_destroy(c);
   grpc_call_destroy(s);
diff --git a/test/core/end2end/tests/cancel_after_client_done.c b/test/core/end2end/tests/cancel_after_client_done.c
index 7742f9d..d3beb04 100644
--- a/test/core/end2end/tests/cancel_after_client_done.c
+++ b/test/core/end2end/tests/cancel_after_client_done.c
@@ -51,7 +51,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -113,8 +113,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   grpc_byte_buffer *request_payload_recv = NULL;
   grpc_byte_buffer *response_payload_recv = NULL;
   grpc_slice request_payload_slice =
@@ -128,8 +127,9 @@
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -144,7 +144,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -154,7 +153,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -163,12 +162,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -184,7 +183,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -199,7 +198,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -224,7 +223,7 @@
   grpc_byte_buffer_destroy(response_payload);
   grpc_byte_buffer_destroy(request_payload_recv);
   grpc_byte_buffer_destroy(response_payload_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
 
   grpc_call_destroy(c);
   grpc_call_destroy(s);
diff --git a/test/core/end2end/tests/cancel_after_invoke.c b/test/core/end2end/tests/cancel_after_invoke.c
index c3c5418..2f091cb 100644
--- a/test/core/end2end/tests/cancel_after_invoke.c
+++ b/test/core/end2end/tests/cancel_after_invoke.c
@@ -52,7 +52,8 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s/%s", test_name, config.name, mode.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s/%s", test_name, config.name,
+          mode.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -113,8 +114,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   grpc_byte_buffer *response_payload_recv = NULL;
   grpc_slice request_payload_slice =
       grpc_slice_from_copied_string("hello world");
@@ -122,8 +122,9 @@
       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -138,7 +139,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -148,7 +148,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -157,12 +157,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -183,7 +183,7 @@
 
   grpc_byte_buffer_destroy(request_payload);
   grpc_byte_buffer_destroy(response_payload_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
 
   grpc_call_destroy(c);
 
diff --git a/test/core/end2end/tests/cancel_before_invoke.c b/test/core/end2end/tests/cancel_before_invoke.c
index d484282..e61a838 100644
--- a/test/core/end2end/tests/cancel_before_invoke.c
+++ b/test/core/end2end/tests/cancel_before_invoke.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -111,8 +111,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   grpc_byte_buffer *response_payload_recv = NULL;
   grpc_slice request_payload_slice =
       grpc_slice_from_copied_string("hello world");
@@ -120,8 +119,9 @@
       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -138,7 +138,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -148,7 +147,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -157,12 +156,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -181,7 +180,7 @@
 
   grpc_byte_buffer_destroy(request_payload);
   grpc_byte_buffer_destroy(response_payload_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
 
   grpc_call_destroy(c);
 
diff --git a/test/core/end2end/tests/cancel_in_a_vacuum.c b/test/core/end2end/tests/cancel_in_a_vacuum.c
index 5be850b..98d02a5 100644
--- a/test/core/end2end/tests/cancel_in_a_vacuum.c
+++ b/test/core/end2end/tests/cancel_in_a_vacuum.c
@@ -51,7 +51,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -106,8 +106,9 @@
   cq_verifier *v_client = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
diff --git a/test/core/end2end/tests/cancel_with_status.c b/test/core/end2end/tests/cancel_with_status.c
index 3aecaf7..a538844 100644
--- a/test/core/end2end/tests/cancel_with_status.c
+++ b/test/core/end2end/tests/cancel_with_status.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -108,14 +108,14 @@
   grpc_metadata_array trailing_metadata_recv;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
   gpr_log(GPR_DEBUG, "test with %" PRIuPTR " ops", num_ops);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -128,12 +128,11 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -156,9 +155,9 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
 
diff --git a/test/core/end2end/tests/compressed_payload.c b/test/core/end2end/tests/compressed_payload.c
index d7efe77..62cdb41 100644
--- a/test/core/end2end/tests/compressed_payload.c
+++ b/test/core/end2end/tests/compressed_payload.c
@@ -48,6 +48,7 @@
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/call_test_only.h"
+#include "src/core/lib/transport/static_metadata.h"
 #include "test/core/end2end/cq_verifier.h"
 
 static void *tag(intptr_t t) { return (void *)t; }
@@ -57,7 +58,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -125,8 +126,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   cq_verifier *cqv;
   char str[1024];
@@ -151,8 +151,9 @@
   cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -174,7 +175,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = send_flags_bitmask;
   op->reserved = NULL;
   op++;
@@ -183,7 +184,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -191,7 +192,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -212,7 +212,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -245,13 +245,13 @@
   gpr_asprintf(&expected_details, "Compression algorithm '%s' is disabled.",
                algo_name);
   /* and we expect a specific reason for it */
-  GPR_ASSERT(0 == strcmp(details, expected_details));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, expected_details));
   gpr_free(expected_details);
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -305,8 +305,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   cq_verifier *cqv;
   char request_str[1024];
@@ -331,8 +330,9 @@
   cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -354,7 +354,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -362,7 +362,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -412,12 +411,12 @@
     memset(ops, 0, sizeof(ops));
     op = ops;
     op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message = request_payload;
+    op->data.send_message.send_message = request_payload;
     op->flags = client_send_flags_bitmask;
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_RECV_MESSAGE;
-    op->data.recv_message = &response_payload_recv;
+    op->data.recv_message.recv_message = &response_payload_recv;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -427,7 +426,7 @@
     memset(ops, 0, sizeof(ops));
     op = ops;
     op->op = GRPC_OP_RECV_MESSAGE;
-    op->data.recv_message = &request_payload_recv;
+    op->data.recv_message.recv_message = &request_payload_recv;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -444,7 +443,7 @@
     memset(ops, 0, sizeof(ops));
     op = ops;
     op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message = response_payload;
+    op->data.send_message.send_message = response_payload;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -489,7 +488,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -503,13 +503,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -569,17 +569,14 @@
   grpc_metadata gzip_compression_override;
   grpc_metadata identity_compression_override;
 
-  gzip_compression_override.key = GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY;
-  gzip_compression_override.value = "gzip";
-  gzip_compression_override.value_length =
-      strlen(gzip_compression_override.value);
+  gzip_compression_override.key = GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST;
+  gzip_compression_override.value = grpc_slice_from_static_string("gzip");
   memset(&gzip_compression_override.internal_data, 0,
          sizeof(gzip_compression_override.internal_data));
 
-  identity_compression_override.key = GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY;
-  identity_compression_override.value = "identity";
-  identity_compression_override.value_length =
-      strlen(identity_compression_override.value);
+  identity_compression_override.key = GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST;
+  identity_compression_override.value =
+      grpc_slice_from_static_string("identity");
   memset(&identity_compression_override.internal_data, 0,
          sizeof(identity_compression_override.internal_data));
 
diff --git a/test/core/end2end/tests/default_host.c b/test/core/end2end/tests/default_host.c
index 208e316..b84eaf2 100644
--- a/test/core/end2end/tests/default_host.c
+++ b/test/core/end2end/tests/default_host.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_client(&f, client_args);
   config.init_server(&f, server_args);
@@ -110,13 +110,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   char *peer;
 
   c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
-                               "/foo", NULL, deadline, NULL);
+                               grpc_slice_from_static_string("/foo"), NULL,
+                               deadline, NULL);
   GPR_ASSERT(c);
 
   peer = grpc_call_get_peer(c);
@@ -141,7 +141,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -149,7 +149,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -182,7 +181,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -199,12 +199,12 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
-  GPR_ASSERT(0 == strncmp(call_details.host, "localhost", 9));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
+  GPR_ASSERT(grpc_slice_buf_start_eq(call_details.host, "localhost", 9));
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/disappearing_server.c b/test/core/end2end/tests/disappearing_server.c
index 8ebf7e6..6d61c66 100644
--- a/test/core/end2end/tests/disappearing_server.c
+++ b/test/core/end2end/tests/disappearing_server.c
@@ -93,13 +93,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f->client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f->client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -120,7 +120,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -128,7 +128,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -156,7 +155,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -174,13 +174,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -194,7 +194,8 @@
   grpc_end2end_test_fixture f = config.create_fixture(NULL, NULL);
   cq_verifier *cqv = cq_verifier_create(f.cq);
 
-  gpr_log(GPR_INFO, "%s/%s", "disappearing_server_test", config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", "disappearing_server_test",
+          config.name);
 
   config.init_client(&f, NULL);
   config.init_server(&f, NULL);
diff --git a/test/core/end2end/tests/empty_batch.c b/test/core/end2end/tests/empty_batch.c
index dc8e52a..1ac6ea8 100644
--- a/test/core/end2end/tests/empty_batch.c
+++ b/test/core/end2end/tests/empty_batch.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -106,8 +106,9 @@
   grpc_op *op = NULL;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
diff --git a/test/core/end2end/tests/filter_call_init_fails.c b/test/core/end2end/tests/filter_call_init_fails.c
index ac9d2dd..02add7a 100644
--- a/test/core/end2end/tests/filter_call_init_fails.c
+++ b/test/core/end2end/tests/filter_call_init_fails.c
@@ -58,7 +58,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -125,12 +125,12 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -148,7 +148,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -157,7 +157,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -165,7 +165,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -181,9 +180,9 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED);
-  GPR_ASSERT(0 == strcmp(details, "access denied"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "access denied"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/filter_causes_close.c b/test/core/end2end/tests/filter_causes_close.c
index 45f623c..a91b3dc 100644
--- a/test/core/end2end/tests/filter_causes_close.c
+++ b/test/core/end2end/tests/filter_causes_close.c
@@ -55,7 +55,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -121,12 +121,12 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -144,7 +144,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -153,7 +153,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -161,7 +161,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -177,9 +176,10 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED);
-  GPR_ASSERT(0 == strcmp(details, "Failure that's not preventable."));
+  GPR_ASSERT(0 ==
+             grpc_slice_str_cmp(details, "Failure that's not preventable."));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -208,18 +208,12 @@
                           grpc_error *error) {
   grpc_call_element *elem = arg;
   call_data *calld = elem->call_data;
-  if (error == GRPC_ERROR_NONE) {
-    // close the stream with an error.
-    grpc_slice message =
-        grpc_slice_from_copied_string("Failure that's not preventable.");
-    grpc_transport_stream_op *op = grpc_make_transport_stream_op(NULL);
-    grpc_transport_stream_op_add_close(exec_ctx, op,
-                                       GRPC_STATUS_PERMISSION_DENIED, &message);
-    grpc_call_next_op(exec_ctx, elem, op);
-  }
   grpc_closure_sched(
       exec_ctx, calld->recv_im_ready,
-      GRPC_ERROR_CREATE_REFERENCING("Forced call to close", &error, 1));
+      grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING(
+                             "Failure that's not preventable.", &error, 1),
+                         GRPC_ERROR_INT_GRPC_STATUS,
+                         GRPC_STATUS_PERMISSION_DENIED));
 }
 
 static void start_transport_stream_op(grpc_exec_ctx *exec_ctx,
diff --git a/test/core/end2end/tests/filter_latency.c b/test/core/end2end/tests/filter_latency.c
index dbb5c8d..5bc6313 100644
--- a/test/core/end2end/tests/filter_latency.c
+++ b/test/core/end2end/tests/filter_latency.c
@@ -62,7 +62,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -128,8 +128,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   gpr_mu_lock(&g_mu);
@@ -138,8 +137,10 @@
   gpr_mu_unlock(&g_mu);
   const gpr_timespec start_time = gpr_now(GPR_CLOCK_MONOTONIC);
 
-  c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
-                               "/foo", "foo.test.google.fr", deadline, NULL);
+  c = grpc_channel_create_call(
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr", config), deadline, NULL);
   GPR_ASSERT(c);
 
   grpc_metadata_array_init(&initial_metadata_recv);
@@ -156,7 +157,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -165,7 +166,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -173,7 +174,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -198,7 +198,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_string = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_string;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -215,9 +216,9 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/graceful_server_shutdown.c b/test/core/end2end/tests/graceful_server_shutdown.c
index 5fecadb..c5e3667 100644
--- a/test/core/end2end/tests/graceful_server_shutdown.c
+++ b/test/core/end2end/tests/graceful_server_shutdown.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -107,13 +107,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -135,7 +135,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -143,7 +143,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -171,7 +170,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -191,12 +191,12 @@
   grpc_call_destroy(s);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/high_initial_seqno.c b/test/core/end2end/tests/high_initial_seqno.c
index 01a4909..c9eae13 100644
--- a/test/core/end2end/tests/high_initial_seqno.c
+++ b/test/core/end2end/tests/high_initial_seqno.c
@@ -54,7 +54,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -113,13 +113,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -140,7 +140,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -148,7 +148,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -172,7 +171,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -189,13 +189,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -233,7 +233,7 @@
   f = begin_test(config, name, &client_args, NULL);
   for (i = 0; i < 10; i++) {
     simple_request_body(config, f);
-    gpr_log(GPR_INFO, "Passed simple request %d", i);
+    gpr_log(GPR_INFO, "Running test: Passed simple request %d", i);
   }
   end_test(&f);
   config.tear_down_data(&f);
diff --git a/test/core/end2end/tests/hpack_size.c b/test/core/end2end/tests/hpack_size.c
index cec8b2f..1376ba0 100644
--- a/test/core/end2end/tests/hpack_size.c
+++ b/test/core/end2end/tests/hpack_size.c
@@ -194,7 +194,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -254,24 +254,24 @@
   grpc_status_code status;
   grpc_call_error error;
   grpc_metadata extra_metadata[3];
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   memset(extra_metadata, 0, sizeof(extra_metadata));
-  extra_metadata[0].key = "hobbit-first-name";
-  extra_metadata[0].value = hobbits[index % GPR_ARRAY_SIZE(hobbits)][0];
-  extra_metadata[0].value_length = strlen(extra_metadata[0].value);
-  extra_metadata[1].key = "hobbit-second-name";
-  extra_metadata[1].value = hobbits[index % GPR_ARRAY_SIZE(hobbits)][1];
-  extra_metadata[1].value_length = strlen(extra_metadata[1].value);
-  extra_metadata[2].key = "dragon";
-  extra_metadata[2].value = dragons[index % GPR_ARRAY_SIZE(dragons)];
-  extra_metadata[2].value_length = strlen(extra_metadata[2].value);
+  extra_metadata[0].key = grpc_slice_from_static_string("hobbit-first-name");
+  extra_metadata[0].value = grpc_slice_from_static_string(
+      hobbits[index % GPR_ARRAY_SIZE(hobbits)][0]);
+  extra_metadata[1].key = grpc_slice_from_static_string("hobbit-second-name");
+  extra_metadata[1].value = grpc_slice_from_static_string(
+      hobbits[index % GPR_ARRAY_SIZE(hobbits)][1]);
+  extra_metadata[2].key = grpc_slice_from_static_string("dragon");
+  extra_metadata[2].value =
+      grpc_slice_from_static_string(dragons[index % GPR_ARRAY_SIZE(dragons)]);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -293,7 +293,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -301,7 +301,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -325,7 +324,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -342,13 +342,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/idempotent_request.c b/test/core/end2end/tests/idempotent_request.c
index 4f6d3bb..65accbb 100644
--- a/test/core/end2end/tests/idempotent_request.c
+++ b/test/core/end2end/tests/idempotent_request.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -111,14 +111,14 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   char *peer;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -144,7 +144,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -152,7 +152,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -185,7 +184,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -202,14 +202,14 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST == call_details.flags);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c
index 24abfa2..ed16904 100644
--- a/test/core/end2end/tests/invoke_large_request.c
+++ b/test/core/end2end/tests/invoke_large_request.c
@@ -51,7 +51,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -140,13 +140,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -163,7 +163,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -172,12 +172,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -185,7 +185,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -207,7 +206,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -225,14 +224,15 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -244,13 +244,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/large_metadata.c b/test/core/end2end/tests/large_metadata.c
index 69b4b24..dac9c3b 100644
--- a/test/core/end2end/tests/large_metadata.c
+++ b/test/core/end2end/tests/large_metadata.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -122,21 +122,19 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
-  meta.key = "key";
-  meta.value = gpr_malloc(large_size + 1);
-  memset((char *)meta.value, 'a', large_size);
-  ((char *)meta.value)[large_size] = 0;
-  meta.value_length = large_size;
+  meta.key = grpc_slice_from_static_string("key");
+  meta.value = grpc_slice_malloc(large_size);
+  memset(GRPC_SLICE_START_PTR(meta.value), 'a', large_size);
 
   grpc_metadata_array_init(&initial_metadata_recv);
   grpc_metadata_array_init(&trailing_metadata_recv);
@@ -153,7 +151,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -162,7 +160,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -170,7 +168,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -194,7 +191,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -216,7 +213,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -228,15 +226,17 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
   GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world"));
-  GPR_ASSERT(contains_metadata(&request_metadata_recv, "key", meta.value));
+  GPR_ASSERT(contains_metadata_slices(&request_metadata_recv,
+                                      grpc_slice_from_static_string("key"),
+                                      meta.value));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -250,7 +250,7 @@
   grpc_byte_buffer_destroy(request_payload);
   grpc_byte_buffer_destroy(request_payload_recv);
 
-  gpr_free((char *)meta.value);
+  grpc_slice_unref(meta.value);
 
   end_test(&f);
   config.tear_down_data(&f);
diff --git a/test/core/end2end/tests/load_reporting_hook.c b/test/core/end2end/tests/load_reporting_hook.c
index ae5c270..3db5114 100644
--- a/test/core/end2end/tests/load_reporting_hook.c
+++ b/test/core/end2end/tests/load_reporting_hook.c
@@ -74,7 +74,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
 
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
@@ -146,13 +146,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, method_name,
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string(method_name),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -171,7 +171,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -180,12 +180,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -193,7 +193,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -215,7 +214,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -233,7 +232,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -242,7 +241,8 @@
   op->data.send_status_from_server.trailing_metadata_count = 1;
   op->data.send_status_from_server.trailing_metadata = trailing_lr_metadata;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -255,7 +255,7 @@
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -295,15 +295,13 @@
   grpc_metadata initial_lr_metadata;
   grpc_metadata trailing_lr_metadata;
 
-  initial_lr_metadata.key = GRPC_LB_TOKEN_MD_KEY;
-  initial_lr_metadata.value = "client-token";
-  initial_lr_metadata.value_length = strlen(initial_lr_metadata.value);
+  initial_lr_metadata.key = GRPC_MDSTR_LB_TOKEN;
+  initial_lr_metadata.value = grpc_slice_from_static_string("client-token");
   memset(&initial_lr_metadata.internal_data, 0,
          sizeof(initial_lr_metadata.internal_data));
 
-  trailing_lr_metadata.key = GRPC_LB_COST_MD_KEY;
-  trailing_lr_metadata.value = "server-token";
-  trailing_lr_metadata.value_length = strlen(trailing_lr_metadata.value);
+  trailing_lr_metadata.key = GRPC_MDSTR_LB_COST_BIN;
+  trailing_lr_metadata.value = grpc_slice_from_static_string("server-token");
   memset(&trailing_lr_metadata.internal_data, 0,
          sizeof(trailing_lr_metadata.internal_data));
 
diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c
index 9338bc5..37d044c 100644
--- a/test/core/end2end/tests/max_concurrent_streams.c
+++ b/test/core/end2end/tests/max_concurrent_streams.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -109,13 +109,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -136,7 +136,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -144,7 +144,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -168,7 +167,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -185,13 +185,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -223,11 +223,9 @@
   grpc_metadata_array trailing_metadata_recv2;
   grpc_status_code status1;
   grpc_call_error error;
-  char *details1 = NULL;
-  size_t details_capacity1 = 0;
+  grpc_slice details1;
   grpc_status_code status2;
-  char *details2 = NULL;
-  size_t details_capacity2 = 0;
+  grpc_slice details2;
   grpc_op ops[6];
   grpc_op *op;
   int was_cancelled;
@@ -261,13 +259,15 @@
      the first completes */
   deadline = n_seconds_time(1000);
   c1 = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/alpha",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/alpha"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c1);
   c2 = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/beta",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/beta"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c2);
 
@@ -295,12 +295,12 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv1;
   op->data.recv_status_on_client.status = &status1;
   op->data.recv_status_on_client.status_details = &details1;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity1;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv1;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &initial_metadata_recv1;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -327,12 +327,12 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv2;
   op->data.recv_status_on_client.status = &status2;
   op->data.recv_status_on_client.status_details = &details2;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity2;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv1;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &initial_metadata_recv1;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -378,7 +378,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -392,6 +393,8 @@
   CQ_EXPECT_COMPLETION(cqv, tag(live_call + 1), 1);
   cq_verify(cqv);
 
+  grpc_call_details_destroy(&call_details);
+
   GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
                                  f.server, &s2, &call_details,
                                  &request_metadata_recv, f.cq, f.cq, tag(201)));
@@ -413,7 +416,7 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -431,8 +434,8 @@
   grpc_call_destroy(c2);
   grpc_call_destroy(s2);
 
-  gpr_free(details1);
-  gpr_free(details2);
+  grpc_slice_unref(details1);
+  grpc_slice_unref(details2);
   grpc_metadata_array_destroy(&initial_metadata_recv1);
   grpc_metadata_array_destroy(&trailing_metadata_recv1);
   grpc_metadata_array_destroy(&initial_metadata_recv2);
diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c
index 4a6ef44..4c9c218 100644
--- a/test/core/end2end/tests/max_message_length.c
+++ b/test/core/end2end/tests/max_message_length.c
@@ -43,6 +43,7 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/transport/metadata.h"
 #include "src/core/lib/transport/service_config.h"
 
@@ -55,7 +56,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   // We intentionally do not pass the client and server args to
   // create_fixture(), since we don't want the limit enforced on the
   // proxy, only on the backend server.
@@ -108,9 +109,12 @@
 // recv limit on server.
 static void test_max_message_length_on_request(grpc_end2end_test_config config,
                                                bool send_limit,
-                                               bool use_service_config) {
-  gpr_log(GPR_INFO, "testing request with send_limit=%d use_service_config=%d",
-          send_limit, use_service_config);
+                                               bool use_service_config,
+                                               bool use_string_json_value) {
+  gpr_log(GPR_INFO,
+          "testing request with send_limit=%d use_service_config=%d "
+          "use_string_json_value=%d",
+          send_limit, use_service_config, use_string_json_value);
 
   grpc_end2end_test_fixture f;
   grpc_call *c = NULL;
@@ -129,8 +133,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   grpc_channel_args *client_args = NULL;
@@ -142,14 +145,23 @@
     arg.type = GRPC_ARG_STRING;
     arg.key = GRPC_ARG_SERVICE_CONFIG;
     arg.value.string =
-        "{\n"
-        "  \"methodConfig\": [ {\n"
-        "    \"name\": [\n"
-        "      { \"service\": \"service\", \"method\": \"method\" }\n"
-        "    ],\n"
-        "    \"maxRequestMessageBytes\": \"5\"\n"
-        "  } ]\n"
-        "}";
+        use_string_json_value
+            ? "{\n"
+              "  \"methodConfig\": [ {\n"
+              "    \"name\": [\n"
+              "      { \"service\": \"service\", \"method\": \"method\" }\n"
+              "    ],\n"
+              "    \"maxRequestMessageBytes\": \"5\"\n"
+              "  } ]\n"
+              "}"
+            : "{\n"
+              "  \"methodConfig\": [ {\n"
+              "    \"name\": [\n"
+              "      { \"service\": \"service\", \"method\": \"method\" }\n"
+              "    ],\n"
+              "    \"maxRequestMessageBytes\": 5\n"
+              "  } ]\n"
+              "}";
     client_args = grpc_channel_args_copy_and_add(NULL, &arg, 1);
   } else {
     // Set limit via channel args.
@@ -178,8 +190,9 @@
   cqv = cq_verifier_create(f.cq);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/service/method",
-      get_host_override_string("foo.test.google.fr:1234", config),
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config),
       gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
   GPR_ASSERT(c);
 
@@ -196,7 +209,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -205,7 +218,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -213,7 +226,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -241,7 +253,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &recv_payload;
+  op->data.recv_message.recv_message = &recv_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -252,19 +264,20 @@
   CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
-  GPR_ASSERT(0 == strcmp(call_details.method, "/service/method"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
 done:
   GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT);
-  GPR_ASSERT(strcmp(details,
-                    send_limit
-                        ? "Sent message larger than max (11 vs. 5)"
-                        : "Received message larger than max (11 vs. 5)") == 0);
+  GPR_ASSERT(
+      grpc_slice_str_cmp(
+          details, send_limit
+                       ? "Sent message larger than max (11 vs. 5)"
+                       : "Received message larger than max (11 vs. 5)") == 0);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -286,9 +299,12 @@
 // recv limit on client.
 static void test_max_message_length_on_response(grpc_end2end_test_config config,
                                                 bool send_limit,
-                                                bool use_service_config) {
-  gpr_log(GPR_INFO, "testing response with send_limit=%d use_service_config=%d",
-          send_limit, use_service_config);
+                                                bool use_service_config,
+                                                bool use_string_json_value) {
+  gpr_log(GPR_INFO,
+          "testing response with send_limit=%d use_service_config=%d "
+          "use_string_json_value=%d",
+          send_limit, use_service_config, use_string_json_value);
 
   grpc_end2end_test_fixture f;
   grpc_call *c = NULL;
@@ -307,8 +323,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   grpc_channel_args *client_args = NULL;
@@ -320,14 +335,23 @@
     arg.type = GRPC_ARG_STRING;
     arg.key = GRPC_ARG_SERVICE_CONFIG;
     arg.value.string =
-        "{\n"
-        "  \"methodConfig\": [ {\n"
-        "    \"name\": [\n"
-        "      { \"service\": \"service\", \"method\": \"method\" }\n"
-        "    ],\n"
-        "    \"maxResponseMessageBytes\": \"5\"\n"
-        "  } ]\n"
-        "}";
+        use_string_json_value
+            ? "{\n"
+              "  \"methodConfig\": [ {\n"
+              "    \"name\": [\n"
+              "      { \"service\": \"service\", \"method\": \"method\" }\n"
+              "    ],\n"
+              "    \"maxResponseMessageBytes\": \"5\"\n"
+              "  } ]\n"
+              "}"
+            : "{\n"
+              "  \"methodConfig\": [ {\n"
+              "    \"name\": [\n"
+              "      { \"service\": \"service\", \"method\": \"method\" }\n"
+              "    ],\n"
+              "    \"maxResponseMessageBytes\": 5\n"
+              "  } ]\n"
+              "}";
     client_args = grpc_channel_args_copy_and_add(NULL, &arg, 1);
   } else {
     // Set limit via channel args.
@@ -354,9 +378,11 @@
   }
   cqv = cq_verifier_create(f.cq);
 
-  c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
-                               "/service/method", "foo.test.google.fr:1234",
-                               gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  c = grpc_channel_create_call(
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/service/method"),
+      get_host_override_slice("foo.test.google.fr:1234", config),
+      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
   GPR_ASSERT(c);
 
   grpc_metadata_array_init(&initial_metadata_recv);
@@ -376,12 +402,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &recv_payload;
+  op->data.recv_message.recv_message = &recv_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -389,7 +415,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -416,14 +441,15 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -434,16 +460,18 @@
   CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
-  GPR_ASSERT(0 == strcmp(call_details.method, "/service/method"));
-  GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr:1234"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/service/method"));
+  GPR_ASSERT(0 ==
+             grpc_slice_str_cmp(call_details.host, "foo.test.google.fr:1234"));
 
   GPR_ASSERT(status == GRPC_STATUS_INVALID_ARGUMENT);
-  GPR_ASSERT(strcmp(details,
-                    send_limit
-                        ? "Sent message larger than max (11 vs. 5)"
-                        : "Received message larger than max (11 vs. 5)") == 0);
+  GPR_ASSERT(
+      grpc_slice_str_cmp(
+          details, send_limit
+                       ? "Sent message larger than max (11 vs. 5)"
+                       : "Received message larger than max (11 vs. 5)") == 0);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -462,17 +490,29 @@
 
 void max_message_length(grpc_end2end_test_config config) {
   test_max_message_length_on_request(config, false /* send_limit */,
-                                     false /* use_service_config */);
+                                     false /* use_service_config */,
+                                     false /* use_string_json_value */);
   test_max_message_length_on_request(config, true /* send_limit */,
-                                     false /* use_service_config */);
+                                     false /* use_service_config */,
+                                     false /* use_string_json_value */);
   test_max_message_length_on_response(config, false /* send_limit */,
-                                      false /* use_service_config */);
+                                      false /* use_service_config */,
+                                      false /* use_string_json_value */);
   test_max_message_length_on_response(config, true /* send_limit */,
-                                      false /* use_service_config */);
+                                      false /* use_service_config */,
+                                      false /* use_string_json_value */);
   test_max_message_length_on_request(config, true /* send_limit */,
-                                     true /* use_service_config */);
+                                     true /* use_service_config */,
+                                     false /* use_string_json_value */);
+  test_max_message_length_on_request(config, true /* send_limit */,
+                                     true /* use_service_config */,
+                                     true /* use_string_json_value */);
   test_max_message_length_on_response(config, false /* send_limit */,
-                                      true /* use_service_config */);
+                                      true /* use_service_config */,
+                                      false /* use_string_json_value */);
+  test_max_message_length_on_response(config, false /* send_limit */,
+                                      true /* use_service_config */,
+                                      true /* use_string_json_value */);
 }
 
 void max_message_length_pre_init(void) {}
diff --git a/test/core/end2end/tests/negative_deadline.c b/test/core/end2end/tests/negative_deadline.c
index 929777d..d6271d7 100644
--- a/test/core/end2end/tests/negative_deadline.c
+++ b/test/core/end2end/tests/negative_deadline.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -108,14 +108,14 @@
   grpc_metadata_array trailing_metadata_recv;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
 
   gpr_log(GPR_DEBUG, "test with %" PRIuPTR " ops", num_ops);
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -128,12 +128,11 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -155,7 +154,7 @@
 
   GPR_ASSERT(status == GRPC_STATUS_DEADLINE_EXCEEDED);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
 
diff --git a/test/core/end2end/tests/network_status_change.c b/test/core/end2end/tests/network_status_change.c
index 2ebda2c..fe3afb7 100644
--- a/test/core/end2end/tests/network_status_change.c
+++ b/test/core/end2end/tests/network_status_change.c
@@ -53,7 +53,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -119,13 +119,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -142,7 +142,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -151,7 +151,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -159,7 +159,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -179,7 +178,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -201,7 +200,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -214,11 +214,11 @@
 
   // Expected behavior of a RPC when network is lost.
   GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE);
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/no_logging.c b/test/core/end2end/tests/no_logging.c
index 54614cb..72a5235 100644
--- a/test/core/end2end/tests/no_logging.c
+++ b/test/core/end2end/tests/no_logging.c
@@ -80,7 +80,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -139,14 +139,14 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   char *peer;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -171,7 +171,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -179,7 +179,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -210,7 +209,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -227,14 +227,14 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(0 == call_details.flags);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/no_op.c b/test/core/end2end/tests/no_op.c
index 8b29c21..09294bf 100644
--- a/test/core/end2end/tests/no_op.c
+++ b/test/core/end2end/tests/no_op.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
diff --git a/test/core/end2end/tests/payload.c b/test/core/end2end/tests/payload.c
index 4a88c5f..e23013d 100644
--- a/test/core/end2end/tests/payload.c
+++ b/test/core/end2end/tests/payload.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -138,13 +138,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -161,7 +161,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -170,12 +170,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -183,7 +183,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -205,7 +204,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -223,14 +222,15 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -242,8 +242,8 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
@@ -251,7 +251,7 @@
   GPR_ASSERT(
       byte_buffer_eq_slice(response_payload_recv, response_payload_slice));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/ping_pong_streaming.c b/test/core/end2end/tests/ping_pong_streaming.c
index 0a1566e..1aa23ea 100644
--- a/test/core/end2end/tests/ping_pong_streaming.c
+++ b/test/core/end2end/tests/ping_pong_streaming.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -112,8 +112,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   grpc_byte_buffer *request_payload;
   grpc_byte_buffer *request_payload_recv;
@@ -126,8 +125,9 @@
       grpc_slice_from_copied_string("hello you");
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -144,7 +144,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -152,7 +152,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -188,12 +187,12 @@
     memset(ops, 0, sizeof(ops));
     op = ops;
     op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message = request_payload;
+    op->data.send_message.send_message = request_payload;
     op->flags = 0;
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_RECV_MESSAGE;
-    op->data.recv_message = &response_payload_recv;
+    op->data.recv_message.recv_message = &response_payload_recv;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -203,7 +202,7 @@
     memset(ops, 0, sizeof(ops));
     op = ops;
     op->op = GRPC_OP_RECV_MESSAGE;
-    op->data.recv_message = &request_payload_recv;
+    op->data.recv_message.recv_message = &request_payload_recv;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -215,7 +214,7 @@
     memset(ops, 0, sizeof(ops));
     op = ops;
     op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message = response_payload;
+    op->data.send_message.send_message = response_payload;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -248,7 +247,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -270,7 +270,7 @@
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
   grpc_call_details_destroy(&call_details);
-  gpr_free(details);
+  grpc_slice_unref(details);
 
   end_test(&f);
   config.tear_down_data(&f);
diff --git a/test/core/end2end/tests/registered_call.c b/test/core/end2end/tests/registered_call.c
index 6594b42..9844e3d 100644
--- a/test/core/end2end/tests/registered_call.c
+++ b/test/core/end2end/tests/registered_call.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -111,8 +111,7 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_registered_call(
@@ -136,7 +135,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -144,7 +143,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -168,7 +166,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -185,13 +184,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/request_with_flags.c b/test/core/end2end/tests/request_with_flags.c
index 9c18e15..4a296a6 100644
--- a/test/core/end2end/tests/request_with_flags.c
+++ b/test/core/end2end/tests/request_with_flags.c
@@ -51,7 +51,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -117,13 +117,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   grpc_call_error expectation;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -140,7 +140,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = flags_for_op[op->op];
   op->reserved = NULL;
   op++;
@@ -149,7 +149,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = flags_for_op[op->op];
   op->reserved = NULL;
   op++;
@@ -157,7 +157,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = flags_for_op[op->op];
   op->reserved = NULL;
   op++;
@@ -168,9 +167,9 @@
   if (expectation == GRPC_CALL_OK) {
     CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
     cq_verify(cqv);
+    grpc_slice_unref(details);
   }
 
-  gpr_free(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/request_with_payload.c b/test/core/end2end/tests/request_with_payload.c
index c84e3ac..15c790b 100644
--- a/test/core/end2end/tests/request_with_payload.c
+++ b/test/core/end2end/tests/request_with_payload.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -116,13 +116,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -139,7 +139,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -148,7 +148,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -156,7 +156,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -177,7 +176,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -197,7 +196,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -209,14 +209,14 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
   GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/resource_quota_server.c b/test/core/end2end/tests/resource_quota_server.c
index c919fae..1000b63 100644
--- a/test/core/end2end/tests/resource_quota_server.c
+++ b/test/core/end2end/tests/resource_quota_server.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -149,8 +149,7 @@
   grpc_call_details *call_details =
       malloc(sizeof(grpc_call_details) * NUM_CALLS);
   grpc_status_code *status = malloc(sizeof(grpc_status_code) * NUM_CALLS);
-  char **details = malloc(sizeof(char *) * NUM_CALLS);
-  size_t *details_capacity = malloc(sizeof(size_t) * NUM_CALLS);
+  grpc_slice *details = malloc(sizeof(grpc_slice) * NUM_CALLS);
   grpc_byte_buffer **request_payload_recv =
       malloc(sizeof(grpc_byte_buffer *) * NUM_CALLS);
   int *was_cancelled = malloc(sizeof(int) * NUM_CALLS);
@@ -173,8 +172,6 @@
     grpc_metadata_array_init(&trailing_metadata_recv[i]);
     grpc_metadata_array_init(&request_metadata_recv[i]);
     grpc_call_details_init(&call_details[i]);
-    details[i] = NULL;
-    details_capacity[i] = 0;
     request_payload_recv[i] = NULL;
     was_cancelled[i] = 0;
   }
@@ -190,8 +187,10 @@
 
   for (int i = 0; i < NUM_CALLS; i++) {
     client_calls[i] = grpc_channel_create_call(
-        f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-        "foo.test.google.fr", n_seconds_time(60), NULL);
+        f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+        grpc_slice_from_static_string("/foo"),
+        get_host_override_slice("foo.test.google.fr", config),
+        n_seconds_time(60), NULL);
 
     memset(ops, 0, sizeof(ops));
     op = ops;
@@ -201,7 +200,7 @@
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message = request_payload;
+    op->data.send_message.send_message = request_payload;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -210,7 +209,8 @@
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_RECV_INITIAL_METADATA;
-    op->data.recv_initial_metadata = &initial_metadata_recv[i];
+    op->data.recv_initial_metadata.recv_initial_metadata =
+        &initial_metadata_recv[i];
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -219,8 +219,6 @@
         &trailing_metadata_recv[i];
     op->data.recv_status_on_client.status = &status[i];
     op->data.recv_status_on_client.status_details = &details[i];
-    op->data.recv_status_on_client.status_details_capacity =
-        &details_capacity[i];
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -260,7 +258,7 @@
       grpc_metadata_array_destroy(&initial_metadata_recv[call_id]);
       grpc_metadata_array_destroy(&trailing_metadata_recv[call_id]);
       grpc_call_destroy(client_calls[call_id]);
-      gpr_free(details[call_id]);
+      grpc_slice_unref(details[call_id]);
 
       pending_client_calls--;
     } else if (ev_tag < SERVER_RECV_BASE_TAG) {
@@ -277,7 +275,7 @@
       op->reserved = NULL;
       op++;
       op->op = GRPC_OP_RECV_MESSAGE;
-      op->data.recv_message = &request_payload_recv[call_id];
+      op->data.recv_message.recv_message = &request_payload_recv[call_id];
       op->flags = 0;
       op->reserved = NULL;
       op++;
@@ -317,7 +315,8 @@
       op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
       op->data.send_status_from_server.trailing_metadata_count = 0;
       op->data.send_status_from_server.status = GRPC_STATUS_OK;
-      op->data.send_status_from_server.status_details = "xyz";
+      grpc_slice status_details = grpc_slice_from_static_string("xyz");
+      op->data.send_status_from_server.status_details = &status_details;
       op->flags = 0;
       op->reserved = NULL;
       op++;
@@ -353,10 +352,6 @@
    * the client has received it. This means that we should see strictly more
    * failures on the client than on the server. */
   GPR_ASSERT(cancelled_calls_on_client >= cancelled_calls_on_server);
-  /* However, we shouldn't see radically more... 0.9 is a guessed bound on what
-   * we'd want that ratio to be... to at least trigger some investigation should
-   * that ratio become much higher. */
-  GPR_ASSERT(cancelled_calls_on_server >= 0.9 * cancelled_calls_on_client);
 
   grpc_byte_buffer_destroy(request_payload);
   grpc_slice_unref(request_payload_slice);
@@ -370,7 +365,6 @@
   free(call_details);
   free(status);
   free(details);
-  free(details_capacity);
   free(request_payload_recv);
   free(was_cancelled);
 
diff --git a/test/core/end2end/tests/server_finishes_request.c b/test/core/end2end/tests/server_finishes_request.c
index 3bb25fd..57c46d3 100644
--- a/test/core/end2end/tests/server_finishes_request.c
+++ b/test/core/end2end/tests/server_finishes_request.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -111,13 +111,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -134,7 +134,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -142,7 +142,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -166,7 +165,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -183,13 +183,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/shutdown_finishes_calls.c b/test/core/end2end/tests/shutdown_finishes_calls.c
index b80a2e3..b8c8dc9 100644
--- a/test/core/end2end/tests/shutdown_finishes_calls.c
+++ b/test/core/end2end/tests/shutdown_finishes_calls.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -100,13 +100,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -128,7 +128,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -136,7 +136,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -172,12 +171,12 @@
   grpc_server_destroy(f.server);
 
   GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE);
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/shutdown_finishes_tags.c b/test/core/end2end/tests/shutdown_finishes_tags.c
index 1d110a7..06f7e75 100644
--- a/test/core/end2end/tests/shutdown_finishes_tags.c
+++ b/test/core/end2end/tests/shutdown_finishes_tags.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
diff --git a/test/core/end2end/tests/simple_cacheable_request.c b/test/core/end2end/tests/simple_cacheable_request.c
index 2c229b0..3fbc35d 100644
--- a/test/core/end2end/tests/simple_cacheable_request.c
+++ b/test/core/end2end/tests/simple_cacheable_request.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -111,12 +111,22 @@
   grpc_byte_buffer *response_payload =
       grpc_raw_byte_buffer_create(&response_payload_slice, 1);
   gpr_timespec deadline = five_seconds_time();
-  grpc_metadata meta_c[2] = {
-      {"key1", "val1", 4, 0, {{NULL, NULL, NULL, NULL}}},
-      {"key2", "val2", 4, 0, {{NULL, NULL, NULL, NULL}}}};
-  grpc_metadata meta_s[2] = {
-      {"key3", "val3", 4, 0, {{NULL, NULL, NULL, NULL}}},
-      {"key4", "val4", 4, 0, {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"),
+                              grpc_slice_from_static_string("val1"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}},
+                             {grpc_slice_from_static_string("key2"),
+                              grpc_slice_from_static_string("val2"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_s[2] = {{grpc_slice_from_static_string("key3"),
+                              grpc_slice_from_static_string("val3"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}},
+                             {grpc_slice_from_static_string("key4"),
+                              grpc_slice_from_static_string("val4"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}}};
   grpc_end2end_test_fixture f = begin_test(
       config, "test_cacheable_request_response_with_metadata_and_payload", NULL,
       NULL);
@@ -131,13 +141,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -155,7 +165,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -164,12 +174,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -177,7 +187,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -200,7 +209,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -218,14 +227,15 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -237,8 +247,8 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   if (config.feature_mask & FEATURE_MASK_SUPPORTS_REQUEST_PROXYING) {
@@ -254,7 +264,7 @@
   GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key3", "val3"));
   GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key4", "val4"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c
index 414a03d..50ee7d3 100644
--- a/test/core/end2end/tests/simple_delayed_request.c
+++ b/test/core/end2end/tests/simple_delayed_request.c
@@ -100,15 +100,15 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   config.init_client(f, client_args);
 
   c = grpc_channel_create_call(
-      f->client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f->client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -129,7 +129,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -137,7 +137,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -163,7 +162,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -180,13 +180,13 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -208,7 +208,8 @@
   client_args.args = arg_array;
   client_args.num_args = 1;
 
-  gpr_log(GPR_INFO, "%s/%s", "test_simple_delayed_request_short", config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", "test_simple_delayed_request_short",
+          config.name);
   f = config.create_fixture(NULL, NULL);
 
   simple_delayed_request_body(config, &f, &client_args, NULL, 100000);
@@ -226,7 +227,8 @@
   client_args.args = arg_array;
   client_args.num_args = 1;
 
-  gpr_log(GPR_INFO, "%s/%s", "test_simple_delayed_request_long", config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", "test_simple_delayed_request_long",
+          config.name);
   f = config.create_fixture(NULL, NULL);
   /* This timeout should be longer than a single retry */
   simple_delayed_request_body(config, &f, &client_args, NULL, 1500000);
diff --git a/test/core/end2end/tests/simple_metadata.c b/test/core/end2end/tests/simple_metadata.c
index 5490cc2..7086d52 100644
--- a/test/core/end2end/tests/simple_metadata.c
+++ b/test/core/end2end/tests/simple_metadata.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -109,12 +109,22 @@
   grpc_byte_buffer *response_payload =
       grpc_raw_byte_buffer_create(&response_payload_slice, 1);
   gpr_timespec deadline = five_seconds_time();
-  grpc_metadata meta_c[2] = {
-      {"key1", "val1", 4, 0, {{NULL, NULL, NULL, NULL}}},
-      {"key2", "val2", 4, 0, {{NULL, NULL, NULL, NULL}}}};
-  grpc_metadata meta_s[2] = {
-      {"key3", "val3", 4, 0, {{NULL, NULL, NULL, NULL}}},
-      {"key4", "val4", 4, 0, {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"),
+                              grpc_slice_from_static_string("val1"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}},
+                             {grpc_slice_from_static_string("key2"),
+                              grpc_slice_from_static_string("val2"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_s[2] = {{grpc_slice_from_static_string("key3"),
+                              grpc_slice_from_static_string("val3"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}},
+                             {grpc_slice_from_static_string("key4"),
+                              grpc_slice_from_static_string("val4"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}}};
   grpc_end2end_test_fixture f = begin_test(
       config, "test_request_response_with_metadata_and_payload", NULL, NULL);
   cq_verifier *cqv = cq_verifier_create(f.cq);
@@ -128,13 +138,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -152,7 +162,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -161,12 +171,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -174,7 +184,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -197,7 +206,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -215,14 +224,15 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -234,8 +244,8 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
@@ -246,7 +256,7 @@
   GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key3", "val3"));
   GPR_ASSERT(contains_metadata(&initial_metadata_recv, "key4", "val4"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c
index 2dea5d6..7580ad1 100644
--- a/test/core/end2end/tests/simple_request.c
+++ b/test/core/end2end/tests/simple_request.c
@@ -52,7 +52,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -111,14 +111,14 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
   char *peer;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -144,7 +144,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -152,7 +152,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -185,7 +184,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -202,14 +202,14 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(0 == call_details.flags);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
@@ -236,7 +236,7 @@
       begin_test(config, "test_invoke_10_simple_requests", NULL, NULL);
   for (i = 0; i < 10; i++) {
     simple_request_body(config, f);
-    gpr_log(GPR_INFO, "Passed simple request %d", i);
+    gpr_log(GPR_INFO, "Running test: Passed simple request %d", i);
   }
   end_test(&f);
   config.tear_down_data(&f);
diff --git a/test/core/end2end/tests/streaming_error_response.c b/test/core/end2end/tests/streaming_error_response.c
index 583bc92..d5fefd1 100644
--- a/test/core/end2end/tests/streaming_error_response.c
+++ b/test/core/end2end/tests/streaming_error_response.c
@@ -51,8 +51,8 @@
                                             grpc_channel_args *server_args,
                                             bool request_status_early) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s/request_status_early=%s", test_name, config.name,
-          request_status_early ? "true" : "false");
+  gpr_log(GPR_INFO, "Running test: %s/%s/request_status_early=%s", test_name,
+          config.name, request_status_early ? "true" : "false");
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -121,13 +121,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -144,17 +144,16 @@
   op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload1_recv;
+  op->data.recv_message.recv_message = &response_payload1_recv;
   op++;
   if (request_status_early) {
     op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
     op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
     op->data.recv_status_on_client.status = &status;
     op->data.recv_status_on_client.status_details = &details;
-    op->data.recv_status_on_client.status_details_capacity = &details_capacity;
     op++;
   }
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
@@ -172,7 +171,7 @@
   op->data.send_initial_metadata.count = 0;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload1;
+  op->data.send_message.send_message = response_payload1;
   op++;
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
@@ -183,7 +182,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload2;
+  op->data.send_message.send_message = response_payload2;
   op++;
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
@@ -202,7 +201,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_FAILED_PRECONDITION;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op++;
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
@@ -211,7 +211,7 @@
     memset(ops, 0, sizeof(ops));
     op = ops;
     op->op = GRPC_OP_RECV_MESSAGE;
-    op->data.recv_message = &response_payload2_recv;
+    op->data.recv_message.recv_message = &response_payload2_recv;
     op++;
     error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(2), NULL);
     GPR_ASSERT(GRPC_CALL_OK == error);
@@ -232,7 +232,6 @@
     op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
     op->data.recv_status_on_client.status = &status;
     op->data.recv_status_on_client.status_details = &details;
-    op->data.recv_status_on_client.status_details_capacity = &details_capacity;
     op++;
     error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), NULL);
     GPR_ASSERT(GRPC_CALL_OK == error);
@@ -245,13 +244,13 @@
   }
 
   GPR_ASSERT(status == GRPC_STATUS_FAILED_PRECONDITION);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 1);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/trailing_metadata.c b/test/core/end2end/tests/trailing_metadata.c
index 9fd4fbc..ca5cf2c 100644
--- a/test/core/end2end/tests/trailing_metadata.c
+++ b/test/core/end2end/tests/trailing_metadata.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -109,15 +109,30 @@
   grpc_byte_buffer *response_payload =
       grpc_raw_byte_buffer_create(&response_payload_slice, 1);
   gpr_timespec deadline = five_seconds_time();
-  grpc_metadata meta_c[2] = {
-      {"key1", "val1", 4, 0, {{NULL, NULL, NULL, NULL}}},
-      {"key2", "val2", 4, 0, {{NULL, NULL, NULL, NULL}}}};
-  grpc_metadata meta_s[2] = {
-      {"key3", "val3", 4, 0, {{NULL, NULL, NULL, NULL}}},
-      {"key4", "val4", 4, 0, {{NULL, NULL, NULL, NULL}}}};
-  grpc_metadata meta_t[2] = {
-      {"key5", "val5", 4, 0, {{NULL, NULL, NULL, NULL}}},
-      {"key6", "val6", 4, 0, {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"),
+                              grpc_slice_from_static_string("val1"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}},
+                             {grpc_slice_from_static_string("key2"),
+                              grpc_slice_from_static_string("val2"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_s[2] = {{grpc_slice_from_static_string("key3"),
+                              grpc_slice_from_static_string("val3"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}},
+                             {grpc_slice_from_static_string("key4"),
+                              grpc_slice_from_static_string("val4"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}}};
+  grpc_metadata meta_t[2] = {{grpc_slice_from_static_string("key5"),
+                              grpc_slice_from_static_string("val5"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}},
+                             {grpc_slice_from_static_string("key6"),
+                              grpc_slice_from_static_string("val6"),
+                              0,
+                              {{NULL, NULL, NULL, NULL}}}};
   grpc_end2end_test_fixture f = begin_test(
       config, "test_request_response_with_metadata_and_payload", NULL, NULL);
   cq_verifier *cqv = cq_verifier_create(f.cq);
@@ -131,13 +146,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -155,7 +170,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -164,12 +179,12 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -177,7 +192,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -200,7 +214,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -218,7 +232,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = response_payload;
+  op->data.send_message.send_message = response_payload;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -226,7 +240,8 @@
   op->data.send_status_from_server.trailing_metadata_count = 2;
   op->data.send_status_from_server.trailing_metadata = meta_t;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -238,8 +253,8 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world"));
@@ -251,7 +266,7 @@
   GPR_ASSERT(contains_metadata(&trailing_metadata_recv, "key5", "val5"));
   GPR_ASSERT(contains_metadata(&trailing_metadata_recv, "key6", "val6"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/write_buffering.c b/test/core/end2end/tests/write_buffering.c
index 856e9f0..50c0ed7 100644
--- a/test/core/end2end/tests/write_buffering.c
+++ b/test/core/end2end/tests/write_buffering.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -120,13 +120,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details = grpc_empty_slice();
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -146,7 +146,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -163,7 +163,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload1;
+  op->data.send_message.send_message = request_payload1;
   op->flags = GRPC_WRITE_BUFFER_HINT;
   op++;
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), NULL);
@@ -181,7 +181,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv1;
+  op->data.recv_message.recv_message = &request_payload_recv1;
   op++;
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
@@ -195,7 +195,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload2;
+  op->data.send_message.send_message = request_payload2;
   op->flags = 0;
   op++;
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(4), NULL);
@@ -210,7 +210,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv2;
+  op->data.recv_message.recv_message = &request_payload_recv2;
   op++;
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
@@ -228,7 +228,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -244,7 +243,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -256,15 +256,15 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
   GPR_ASSERT(byte_buffer_eq_string(request_payload_recv1, "hello world"));
   GPR_ASSERT(byte_buffer_eq_string(request_payload_recv2, "abc123"));
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/end2end/tests/write_buffering_at_end.c b/test/core/end2end/tests/write_buffering_at_end.c
index 43aefcb..5f68ed0 100644
--- a/test/core/end2end/tests/write_buffering_at_end.c
+++ b/test/core/end2end/tests/write_buffering_at_end.c
@@ -50,7 +50,7 @@
                                             grpc_channel_args *client_args,
                                             grpc_channel_args *server_args) {
   grpc_end2end_test_fixture f;
-  gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+  gpr_log(GPR_INFO, "Running test: %s/%s", test_name, config.name);
   f = config.create_fixture(client_args, server_args);
   config.init_server(&f, server_args);
   config.init_client(&f, client_args);
@@ -117,13 +117,13 @@
   grpc_call_details call_details;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details = grpc_empty_slice();
   int was_cancelled = 2;
 
   c = grpc_channel_create_call(
-      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo",
-      get_host_override_string("foo.test.google.fr:1234", config), deadline,
+      f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq,
+      grpc_slice_from_static_string("/foo"),
+      get_host_override_slice("foo.test.google.fr:1234", config), deadline,
       NULL);
   GPR_ASSERT(c);
 
@@ -143,7 +143,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -160,7 +160,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = request_payload;
+  op->data.send_message.send_message = request_payload;
   op->flags = GRPC_WRITE_BUFFER_HINT;
   op++;
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(3), NULL);
@@ -178,7 +178,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv1;
+  op->data.recv_message.recv_message = &request_payload_recv1;
   op++;
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
@@ -205,7 +205,7 @@
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv2;
+  op->data.recv_message.recv_message = &request_payload_recv2;
   op++;
   error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(104), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
@@ -219,7 +219,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -235,7 +234,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -247,15 +247,15 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_OK);
-  GPR_ASSERT(0 == strcmp(details, "xyz"));
-  GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
+  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
   validate_host_override_string("foo.test.google.fr:1234", call_details.host,
                                 config);
   GPR_ASSERT(was_cancelled == 0);
   GPR_ASSERT(byte_buffer_eq_string(request_payload_recv1, "hello world"));
   GPR_ASSERT(request_payload_recv2 == NULL);
 
-  gpr_free(details);
+  grpc_slice_unref(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
   grpc_metadata_array_destroy(&request_metadata_recv);
diff --git a/test/core/fling/client.c b/test/core/fling/client.c
index e717b7f..c4de949 100644
--- a/test/core/fling/client.c
+++ b/test/core/fling/client.c
@@ -57,8 +57,7 @@
 static grpc_metadata_array trailing_metadata_recv;
 static grpc_byte_buffer *response_payload_recv = NULL;
 static grpc_status_code status;
-static char *details = NULL;
-static size_t details_capacity = 0;
+static grpc_slice details;
 static grpc_op *op;
 
 static void init_ping_pong_request(void) {
@@ -72,29 +71,30 @@
   op->data.send_initial_metadata.count = 0;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
-  op->data.send_message = the_buffer;
+  op->data.send_message.send_message = the_buffer;
   op++;
   op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op++;
   op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op++;
 }
 
 static void step_ping_pong_request(void) {
   GPR_TIMER_BEGIN("ping_pong", 1);
-  call = grpc_channel_create_call(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
-                                  "/Reflector/reflectUnary", "localhost",
-                                  gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  grpc_slice host = grpc_slice_from_static_string("localhost");
+  call = grpc_channel_create_call(
+      channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+      grpc_slice_from_static_string("/Reflector/reflectUnary"), &host,
+      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call, ops,
                                                    (size_t)(op - ops),
                                                    (void *)1, NULL));
@@ -109,13 +109,16 @@
   grpc_metadata_array_init(&initial_metadata_recv);
 
   grpc_call_error error;
-  call = grpc_channel_create_call(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
-                                  "/Reflector/reflectStream", "localhost",
-                                  gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+  grpc_slice host = grpc_slice_from_static_string("localhost");
+  call = grpc_channel_create_call(
+      channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+      grpc_slice_from_static_string("/Reflector/reflectStream"), &host,
+      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
   stream_init_ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
   stream_init_ops[0].data.send_initial_metadata.count = 0;
   stream_init_ops[1].op = GRPC_OP_RECV_INITIAL_METADATA;
-  stream_init_ops[1].data.recv_initial_metadata = &initial_metadata_recv;
+  stream_init_ops[1].data.recv_initial_metadata.recv_initial_metadata =
+      &initial_metadata_recv;
   error = grpc_call_start_batch(call, stream_init_ops, 2, (void *)1, NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
   grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
@@ -123,9 +126,9 @@
   grpc_metadata_array_init(&initial_metadata_recv);
 
   stream_step_ops[0].op = GRPC_OP_SEND_MESSAGE;
-  stream_step_ops[0].data.send_message = the_buffer;
+  stream_step_ops[0].data.send_message.send_message = the_buffer;
   stream_step_ops[1].op = GRPC_OP_RECV_MESSAGE;
-  stream_step_ops[1].data.recv_message = &response_payload_recv;
+  stream_step_ops[1].data.recv_message.recv_message = &response_payload_recv;
 }
 
 static void step_ping_pong_stream(void) {
diff --git a/test/core/fling/server.c b/test/core/fling/server.c
index fd446f1..11742ce 100644
--- a/test/core/fling/server.c
+++ b/test/core/fling/server.c
@@ -107,18 +107,18 @@
   op->data.send_initial_metadata.count = 0;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &terminal_buffer;
+  op->data.recv_message.recv_message = &terminal_buffer;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
   if (payload_buffer == NULL) {
     gpr_log(GPR_INFO, "NULL payload buffer !!!");
   }
-  op->data.send_message = payload_buffer;
+  op->data.send_message.send_message = payload_buffer;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
   op->data.send_status_from_server.trailing_metadata_count = 0;
-  op->data.send_status_from_server.status_details = "";
+  op->data.send_status_from_server.status_details = NULL;
   op++;
   op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
   op->data.recv_close_on_server.cancelled = &was_cancelled;
@@ -144,7 +144,7 @@
   grpc_call_error error;
   /* Starting read at server */
   read_op.op = GRPC_OP_RECV_MESSAGE;
-  read_op.data.recv_message = &payload_buffer;
+  read_op.data.recv_message.recv_message = &payload_buffer;
   error = grpc_call_start_batch(call, &read_op, 1, tag(t), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 }
@@ -157,7 +157,7 @@
   if (payload_buffer == NULL) {
     gpr_log(GPR_INFO, "NULL payload buffer !!!");
   }
-  write_op.data.send_message = payload_buffer;
+  write_op.data.send_message.send_message = payload_buffer;
   error = grpc_call_start_batch(call, &write_op, 1, tagarg, NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 }
@@ -168,7 +168,7 @@
   status_op[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   status_op[0].data.send_status_from_server.status = GRPC_STATUS_OK;
   status_op[0].data.send_status_from_server.trailing_metadata_count = 0;
-  status_op[0].data.send_status_from_server.status_details = "";
+  status_op[0].data.send_status_from_server.status_details = NULL;
   status_op[1].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
   status_op[1].data.recv_close_on_server.cancelled = &was_cancelled;
 
@@ -259,8 +259,8 @@
         switch ((intptr_t)s) {
           case FLING_SERVER_NEW_REQUEST:
             if (call != NULL) {
-              if (0 ==
-                  strcmp(call_details.method, "/Reflector/reflectStream")) {
+              if (0 == grpc_slice_str_cmp(call_details.method,
+                                          "/Reflector/reflectStream")) {
                 /* Received streaming call. Send metadata here. */
                 start_read_op(FLING_SERVER_READ_FOR_STREAMING);
                 send_initial_metadata();
diff --git a/test/core/iomgr/ev_epoll_linux_test.c b/test/core/iomgr/ev_epoll_linux_test.c
index d95d552..a10be7f 100644
--- a/test/core/iomgr/ev_epoll_linux_test.c
+++ b/test/core/iomgr/ev_epoll_linux_test.c
@@ -315,6 +315,7 @@
             "strategy. and the current strategy is: '%s'",
             poll_strategy);
   }
+
   {
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     grpc_iomgr_shutdown(&exec_ctx);
diff --git a/test/core/memory_usage/client.c b/test/core/memory_usage/client.c
index f4432bf..09f0e2d 100644
--- a/test/core/memory_usage/client.c
+++ b/test/core/memory_usage/client.c
@@ -58,8 +58,7 @@
   grpc_call *call;
   grpc_metadata_array initial_metadata_recv;
   grpc_status_code status;
-  char *details;
-  size_t details_capacity;
+  grpc_slice details;
   grpc_metadata_array trailing_metadata_recv;
 } fling_call;
 
@@ -82,12 +81,15 @@
   op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &calls[call_idx].initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &calls[call_idx].initial_metadata_recv;
   op++;
 
+  grpc_slice hostname = grpc_slice_from_static_string("localhost");
   calls[call_idx].call = grpc_channel_create_call(
-      channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/Reflector/reflectUnary",
-      "localhost", gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
+      channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
+      grpc_slice_from_static_string("/Reflector/reflectUnary"), &hostname,
+      gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
 
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
                                                    metadata_ops,
@@ -107,8 +109,6 @@
       &calls[call_idx].trailing_metadata_recv;
   op->data.recv_status_on_client.status = &calls[call_idx].status;
   op->data.recv_status_on_client.status_details = &calls[call_idx].details;
-  op->data.recv_status_on_client.status_details_capacity =
-      &calls[call_idx].details_capacity;
   op++;
 
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call,
@@ -118,13 +118,13 @@
   grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
   grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv);
   grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv);
-  gpr_free(calls[call_idx].details);
+  grpc_slice_unref(calls[call_idx].details);
   grpc_call_destroy(calls[call_idx].call);
   calls[call_idx].call = NULL;
 }
 
-static struct grpc_memory_counters send_snapshot_request(
-    int call_idx, const char *call_type) {
+static struct grpc_memory_counters send_snapshot_request(int call_idx,
+                                                         grpc_slice call_type) {
   grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv);
   grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv);
 
@@ -139,22 +139,22 @@
   op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &calls[call_idx].initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata =
+      &calls[call_idx].initial_metadata_recv;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &response_payload_recv;
+  op->data.recv_message.recv_message = &response_payload_recv;
   op++;
   op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
   op->data.recv_status_on_client.trailing_metadata =
       &calls[call_idx].trailing_metadata_recv;
   op->data.recv_status_on_client.status = &calls[call_idx].status;
   op->data.recv_status_on_client.status_details = &calls[call_idx].details;
-  op->data.recv_status_on_client.status_details_capacity =
-      &calls[call_idx].details_capacity;
   op++;
 
+  grpc_slice hostname = grpc_slice_from_static_string("localhost");
   calls[call_idx].call = grpc_channel_create_call(
-      channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, call_type, "localhost",
+      channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, call_type, &hostname,
       gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(
                                  calls[call_idx].call, snapshot_ops,
@@ -184,9 +184,8 @@
   grpc_slice_unref(response);
   grpc_byte_buffer_reader_destroy(&reader);
   grpc_byte_buffer_destroy(response_payload_recv);
-  gpr_free(calls[call_idx].details);
-  calls[call_idx].details = NULL;
-  calls[call_idx].details_capacity = 0;
+  grpc_slice_unref(calls[call_idx].details);
+  calls[call_idx].details = grpc_empty_slice();
   grpc_call_destroy(calls[call_idx].call);
   calls[call_idx].call = NULL;
 
@@ -219,9 +218,8 @@
   gpr_cmdline_parse(cl, argc, argv);
   gpr_cmdline_destroy(cl);
 
-  for (int k = 0; k < (int)(sizeof(calls) / sizeof(fling_call)); k++) {
-    calls[k].details = NULL;
-    calls[k].details_capacity = 0;
+  for (size_t k = 0; k < GPR_ARRAY_SIZE(calls); k++) {
+    calls[k].details = grpc_empty_slice();
   }
 
   cq = grpc_completion_queue_create(NULL);
@@ -232,10 +230,10 @@
 
   int call_idx = 0;
 
-  struct grpc_memory_counters before_server_create =
-      send_snapshot_request(0, "Reflector/GetBeforeSvrCreation");
-  struct grpc_memory_counters after_server_create =
-      send_snapshot_request(0, "Reflector/GetAfterSvrCreation");
+  struct grpc_memory_counters before_server_create = send_snapshot_request(
+      0, grpc_slice_from_static_string("Reflector/GetBeforeSvrCreation"));
+  struct grpc_memory_counters after_server_create = send_snapshot_request(
+      0, grpc_slice_from_static_string("Reflector/GetAfterSvrCreation"));
 
   // warmup period
   for (call_idx = 0; call_idx < warmup_iterations; ++call_idx) {
@@ -243,7 +241,8 @@
   }
 
   struct grpc_memory_counters server_benchmark_calls_start =
-      send_snapshot_request(0, "Reflector/SimpleSnapshot");
+      send_snapshot_request(
+          0, grpc_slice_from_static_string("Reflector/SimpleSnapshot"));
 
   struct grpc_memory_counters client_benchmark_calls_start =
       grpc_memory_counters_snapshot();
@@ -256,8 +255,8 @@
   struct grpc_memory_counters client_calls_inflight =
       grpc_memory_counters_snapshot();
 
-  struct grpc_memory_counters server_calls_inflight =
-      send_snapshot_request(0, "Reflector/DestroyCalls");
+  struct grpc_memory_counters server_calls_inflight = send_snapshot_request(
+      0, grpc_slice_from_static_string("Reflector/DestroyCalls"));
 
   do {
     event = grpc_completion_queue_next(
@@ -272,8 +271,8 @@
     finish_ping_pong_request(call_idx + 1);
   }
 
-  struct grpc_memory_counters server_calls_end =
-      send_snapshot_request(0, "Reflector/SimpleSnapshot");
+  struct grpc_memory_counters server_calls_end = send_snapshot_request(
+      0, grpc_slice_from_static_string("Reflector/SimpleSnapshot"));
 
   struct grpc_memory_counters client_channel_end =
       grpc_memory_counters_snapshot();
diff --git a/test/core/memory_usage/server.c b/test/core/memory_usage/server.c
index c071093..1cae4f4 100644
--- a/test/core/memory_usage/server.c
+++ b/test/core/memory_usage/server.c
@@ -110,7 +110,8 @@
   status_op.op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   status_op.data.send_status_from_server.status = GRPC_STATUS_OK;
   status_op.data.send_status_from_server.trailing_metadata_count = 0;
-  status_op.data.send_status_from_server.status_details = "";
+  grpc_slice details = grpc_slice_from_static_string("");
+  status_op.data.send_status_from_server.status_details = &details;
 
   GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch((*(fling_call *)tag).call,
                                                    &status_op, 1, tag, NULL));
@@ -129,18 +130,19 @@
   op->data.send_initial_metadata.count = 0;
   op++;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &terminal_buffer;
+  op->data.recv_message.recv_message = &terminal_buffer;
   op++;
   op->op = GRPC_OP_SEND_MESSAGE;
   if (payload_buffer == NULL) {
     gpr_log(GPR_INFO, "NULL payload buffer !!!");
   }
-  op->data.send_message = payload_buffer;
+  op->data.send_message.send_message = payload_buffer;
   op++;
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
   op->data.send_status_from_server.trailing_metadata_count = 0;
-  op->data.send_status_from_server.status_details = "";
+  grpc_slice details = grpc_slice_from_static_string("");
+  op->data.send_status_from_server.status_details = &details;
   op++;
   op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
   op->data.recv_close_on_server.cancelled = &was_cancelled;
@@ -245,25 +247,27 @@
         switch (s->state) {
           case FLING_SERVER_NEW_REQUEST:
             request_call_unary(++next_call_idx);
-            if (0 ==
-                strcmp(s->call_details.method, "/Reflector/reflectUnary")) {
+            if (0 == grpc_slice_str_cmp(s->call_details.method,
+                                        "/Reflector/reflectUnary")) {
               s->state = FLING_SERVER_SEND_INIT_METADATA;
               send_initial_metadata_unary(s);
-            } else if (0 == strcmp(s->call_details.method,
-                                   "Reflector/GetBeforeSvrCreation")) {
+            } else if (0 ==
+                       grpc_slice_str_cmp(s->call_details.method,
+                                          "Reflector/GetBeforeSvrCreation")) {
               s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT;
               send_snapshot(s, &before_server_create);
-            } else if (0 == strcmp(s->call_details.method,
-                                   "Reflector/GetAfterSvrCreation")) {
+            } else if (0 ==
+                       grpc_slice_str_cmp(s->call_details.method,
+                                          "Reflector/GetAfterSvrCreation")) {
               s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT;
               send_snapshot(s, &after_server_create);
-            } else if (0 == strcmp(s->call_details.method,
-                                   "Reflector/SimpleSnapshot")) {
+            } else if (0 == grpc_slice_str_cmp(s->call_details.method,
+                                               "Reflector/SimpleSnapshot")) {
               s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT;
               current_snapshot = grpc_memory_counters_snapshot();
               send_snapshot(s, &current_snapshot);
-            } else if (0 == strcmp(s->call_details.method,
-                                   "Reflector/DestroyCalls")) {
+            } else if (0 == grpc_slice_str_cmp(s->call_details.method,
+                                               "Reflector/DestroyCalls")) {
               s->state = FLING_SERVER_BATCH_SEND_STATUS_FLING_CALL;
               current_snapshot = grpc_memory_counters_snapshot();
               send_snapshot(s, &current_snapshot);
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c
index 0a79292..c0933c6 100644
--- a/test/core/security/credentials_test.c
+++ b/test/core/security/credentials_test.c
@@ -196,8 +196,8 @@
   grpc_slice value = grpc_slice_from_copied_string(value_str);
   grpc_credentials_md_store_add(store, key, value);
   GPR_ASSERT(store->num_entries == 1);
-  GPR_ASSERT(grpc_slice_cmp(key, store->entries[0].key) == 0);
-  GPR_ASSERT(grpc_slice_cmp(value, store->entries[0].value) == 0);
+  GPR_ASSERT(grpc_slice_eq(key, store->entries[0].key));
+  GPR_ASSERT(grpc_slice_eq(value, store->entries[0].value));
   grpc_slice_unref(key);
   grpc_slice_unref(value);
   grpc_credentials_md_store_unref(&exec_ctx, store);
@@ -1065,9 +1065,8 @@
   *s = PLUGIN_GET_METADATA_CALLED_STATE;
   for (i = 0; i < GPR_ARRAY_SIZE(plugin_md); i++) {
     memset(&md[i], 0, sizeof(grpc_metadata));
-    md[i].key = plugin_md[i].key;
-    md[i].value = plugin_md[i].value;
-    md[i].value_length = strlen(plugin_md[i].value);
+    md[i].key = grpc_slice_from_copied_string(plugin_md[i].key);
+    md[i].value = grpc_slice_from_copied_string(plugin_md[i].value);
   }
   cb(user_data, md, GPR_ARRAY_SIZE(md), GRPC_STATUS_OK, NULL);
 }
diff --git a/test/core/security/print_google_default_creds_token.c b/test/core/security/print_google_default_creds_token.c
index 2b74690..ea136fb 100644
--- a/test/core/security/print_google_default_creds_token.c
+++ b/test/core/security/print_google_default_creds_token.c
@@ -63,7 +63,7 @@
   } else {
     char *token;
     GPR_ASSERT(num_md == 1);
-    token = grpc_dump_slice(md_elems[0].value, GPR_DUMP_ASCII);
+    token = grpc_slice_to_c_string(md_elems[0].value);
     printf("\nGot token: %s\n\n", token);
     gpr_free(token);
   }
diff --git a/test/core/security/secure_endpoint_test.c b/test/core/security/secure_endpoint_test.c
index e1d6ff9..bcc50d0 100644
--- a/test/core/security/secure_endpoint_test.c
+++ b/test/core/security/secure_endpoint_test.c
@@ -164,7 +164,7 @@
   grpc_exec_ctx_finish(&exec_ctx);
   GPR_ASSERT(n == 1);
   GPR_ASSERT(incoming.count == 1);
-  GPR_ASSERT(0 == grpc_slice_cmp(s, incoming.slices[0]));
+  GPR_ASSERT(grpc_slice_eq(s, incoming.slices[0]));
 
   grpc_endpoint_shutdown(&exec_ctx, f.client_ep);
   grpc_endpoint_shutdown(&exec_ctx, f.server_ep);
diff --git a/test/core/security/security_connector_test.c b/test/core/security/security_connector_test.c
index 8872cbe..4219b13 100644
--- a/test/core/security/security_connector_test.c
+++ b/test/core/security/security_connector_test.c
@@ -370,7 +370,7 @@
   gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, "");
   grpc_set_ssl_roots_override_callback(override_roots_success);
   grpc_slice roots = grpc_get_default_ssl_roots_for_testing();
-  char *roots_contents = grpc_dump_slice(roots, GPR_DUMP_ASCII);
+  char *roots_contents = grpc_slice_to_c_string(roots);
   grpc_slice_unref(roots);
   GPR_ASSERT(strcmp(roots_contents, roots_for_override_api) == 0);
   gpr_free(roots_contents);
@@ -379,7 +379,7 @@
      instead. */
   gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_env_var_file_path);
   roots = grpc_get_default_ssl_roots_for_testing();
-  roots_contents = grpc_dump_slice(roots, GPR_DUMP_ASCII);
+  roots_contents = grpc_slice_to_c_string(roots);
   grpc_slice_unref(roots);
   GPR_ASSERT(strcmp(roots_contents, roots_for_env_var) == 0);
   gpr_free(roots_contents);
@@ -388,7 +388,7 @@
      the api. */
   gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, "");
   roots = grpc_get_default_ssl_roots_for_testing();
-  roots_contents = grpc_dump_slice(roots, GPR_DUMP_ASCII);
+  roots_contents = grpc_slice_to_c_string(roots);
   grpc_slice_unref(roots);
   GPR_ASSERT(strcmp(roots_contents, roots_for_override_api) == 0);
   gpr_free(roots_contents);
diff --git a/test/core/slice/percent_encode_fuzzer.c b/test/core/slice/percent_encode_fuzzer.c
index 9698e79..0d440c5 100644
--- a/test/core/slice/percent_encode_fuzzer.c
+++ b/test/core/slice/percent_encode_fuzzer.c
@@ -55,8 +55,8 @@
   grpc_slice permissive_decoded_output =
       grpc_permissive_percent_decode_slice(output);
   // and decoded output must always match the input
-  GPR_ASSERT(grpc_slice_cmp(input, decoded_output) == 0);
-  GPR_ASSERT(grpc_slice_cmp(input, permissive_decoded_output) == 0);
+  GPR_ASSERT(grpc_slice_eq(input, decoded_output));
+  GPR_ASSERT(grpc_slice_eq(input, permissive_decoded_output));
   grpc_slice_unref(input);
   grpc_slice_unref(output);
   grpc_slice_unref(decoded_output);
diff --git a/test/core/slice/percent_encoding_test.c b/test/core/slice/percent_encoding_test.c
index d71c99f..222e695 100644
--- a/test/core/slice/percent_encoding_test.c
+++ b/test/core/slice/percent_encoding_test.c
@@ -81,9 +81,9 @@
   gpr_free(encoded2raw_msg);
   gpr_free(encoded2raw_permissive_msg);
 
-  GPR_ASSERT(0 == grpc_slice_cmp(raw_slice, encoded2raw_slice));
-  GPR_ASSERT(0 == grpc_slice_cmp(raw_slice, encoded2raw_permissive_slice));
-  GPR_ASSERT(0 == grpc_slice_cmp(encoded_slice, raw2encoded_slice));
+  GPR_ASSERT(grpc_slice_eq(raw_slice, encoded2raw_slice));
+  GPR_ASSERT(grpc_slice_eq(raw_slice, encoded2raw_permissive_slice));
+  GPR_ASSERT(grpc_slice_eq(encoded_slice, raw2encoded_slice));
 
   grpc_slice_unref(encoded2raw_slice);
   grpc_slice_unref(encoded2raw_permissive_slice);
@@ -123,8 +123,8 @@
           encoded2raw_permissive_msg);
   gpr_free(encoded2raw_permissive_msg);
 
-  GPR_ASSERT(0 == grpc_slice_cmp(permissive_unencoded_slice,
-                                 encoded2raw_permissive_slice));
+  GPR_ASSERT(
+      grpc_slice_eq(permissive_unencoded_slice, encoded2raw_permissive_slice));
 
   grpc_slice_unref(permissive_unencoded_slice);
   grpc_slice_unref(encoded2raw_permissive_slice);
diff --git a/test/core/slice/slice_test.c b/test/core/slice/slice_test.c
index ca44bec..f68a953 100644
--- a/test/core/slice/slice_test.c
+++ b/test/core/slice/slice_test.c
@@ -35,8 +35,12 @@
 
 #include <string.h>
 
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/transport/static_metadata.h"
 #include "test/core/util/test_config.h"
 
 #define LOG_TEST_NAME(x) gpr_log(GPR_INFO, "%s", x);
@@ -61,8 +65,10 @@
     GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == length);
     /* If the slice has a refcount, it must be destroyable. */
     if (slice.refcount) {
-      GPR_ASSERT(slice.refcount->ref != NULL);
-      GPR_ASSERT(slice.refcount->unref != NULL);
+      GPR_ASSERT(slice.refcount->vtable != NULL);
+      GPR_ASSERT(slice.refcount->vtable->ref != NULL);
+      GPR_ASSERT(slice.refcount->vtable->unref != NULL);
+      GPR_ASSERT(slice.refcount->vtable->hash != NULL);
     }
     /* We must be able to write to every byte of the data */
     for (i = 0; i < length; i++) {
@@ -249,6 +255,55 @@
   grpc_slice_unref(slice);
 }
 
+static void test_slice_interning(void) {
+  LOG_TEST_NAME("test_slice_interning");
+
+  grpc_init();
+  grpc_slice src1 = grpc_slice_from_copied_string("hello123456789123456789");
+  grpc_slice src2 = grpc_slice_from_copied_string("hello123456789123456789");
+  GPR_ASSERT(GRPC_SLICE_START_PTR(src1) != GRPC_SLICE_START_PTR(src2));
+  grpc_slice interned1 = grpc_slice_intern(src1);
+  grpc_slice interned2 = grpc_slice_intern(src2);
+  GPR_ASSERT(GRPC_SLICE_START_PTR(interned1) ==
+             GRPC_SLICE_START_PTR(interned2));
+  GPR_ASSERT(GRPC_SLICE_START_PTR(interned1) != GRPC_SLICE_START_PTR(src1));
+  GPR_ASSERT(GRPC_SLICE_START_PTR(interned2) != GRPC_SLICE_START_PTR(src2));
+  grpc_slice_unref(src1);
+  grpc_slice_unref(src2);
+  grpc_slice_unref(interned1);
+  grpc_slice_unref(interned2);
+  grpc_shutdown();
+}
+
+static void test_static_slice_interning(void) {
+  LOG_TEST_NAME("test_static_slice_interning");
+
+  // grpc_init/grpc_shutdown deliberately omitted: they should not be necessary
+  // to intern a static slice
+
+  for (size_t i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
+    GPR_ASSERT(grpc_slice_is_equivalent(
+        grpc_static_slice_table[i],
+        grpc_slice_intern(grpc_static_slice_table[i])));
+  }
+}
+
+static void test_static_slice_copy_interning(void) {
+  LOG_TEST_NAME("test_static_slice_copy_interning");
+
+  grpc_init();
+
+  for (size_t i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
+    grpc_slice copy = grpc_slice_dup(grpc_static_slice_table[i]);
+    GPR_ASSERT(grpc_static_slice_table[i].refcount != copy.refcount);
+    GPR_ASSERT(grpc_static_slice_table[i].refcount ==
+               grpc_slice_intern(copy).refcount);
+    grpc_slice_unref(copy);
+  }
+
+  grpc_shutdown();
+}
+
 int main(int argc, char **argv) {
   unsigned length;
   grpc_test_init(argc, argv);
@@ -262,5 +317,8 @@
     test_slice_split_tail_works(length);
   }
   test_slice_from_copied_string_works();
+  test_slice_interning();
+  test_static_slice_interning();
+  test_static_slice_copy_interning();
   return 0;
 }
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
index b6db6a6..b5b42c1 100644
--- a/test/core/surface/lame_client_test.c
+++ b/test/core/surface/lame_client_test.c
@@ -90,8 +90,7 @@
   grpc_metadata_array trailing_metadata_recv;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   char *peer;
 
   grpc_test_init(argc, argv);
@@ -111,8 +110,9 @@
 
   cq = grpc_completion_queue_create(NULL);
 
+  grpc_slice host = grpc_slice_from_static_string("anywhere");
   call = grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
-                                  "/Foo", "anywhere",
+                                  grpc_slice_from_static_string("/Foo"), &host,
                                   GRPC_TIMEOUT_SECONDS_TO_DEADLINE(100), NULL);
   GPR_ASSERT(call);
   cqv = cq_verifier_create(cq);
@@ -125,7 +125,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -142,7 +142,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -164,7 +163,7 @@
 
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
 
   grpc_shutdown();
 
diff --git a/test/core/transport/chttp2/bin_decoder_test.c b/test/core/transport/chttp2/bin_decoder_test.c
index 221112a..a8b0a75 100644
--- a/test/core/transport/chttp2/bin_decoder_test.c
+++ b/test/core/transport/chttp2/bin_decoder_test.c
@@ -46,7 +46,7 @@
 
 static void expect_slice_eq(grpc_exec_ctx *exec_ctx, grpc_slice expected,
                             grpc_slice slice, char *debug, int line) {
-  if (0 != grpc_slice_cmp(slice, expected)) {
+  if (!grpc_slice_eq(slice, expected)) {
     char *hs = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     char *he = grpc_dump_slice(expected, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     gpr_log(GPR_ERROR, "FAILED:%d: %s\ngot:  %s\nwant: %s", line, debug, hs,
diff --git a/test/core/transport/chttp2/bin_encoder_test.c b/test/core/transport/chttp2/bin_encoder_test.c
index 53b55a3..bd10a1e 100644
--- a/test/core/transport/chttp2/bin_encoder_test.c
+++ b/test/core/transport/chttp2/bin_encoder_test.c
@@ -48,7 +48,7 @@
 
 static void expect_slice_eq(grpc_slice expected, grpc_slice slice, char *debug,
                             int line) {
-  if (0 != grpc_slice_cmp(slice, expected)) {
+  if (!grpc_slice_eq(slice, expected)) {
     char *hs = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     char *he = grpc_dump_slice(expected, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     gpr_log(GPR_ERROR, "FAILED:%d: %s\ngot:  %s\nwant: %s", line, debug, hs,
@@ -84,8 +84,8 @@
   grpc_slice input = grpc_slice_from_copied_buffer(s, len);
   grpc_slice base64 = grpc_chttp2_base64_encode(input);
   grpc_slice expect = grpc_chttp2_huffman_compress(base64);
-  grpc_slice got = grpc_chttp2_base64_encode_and_huffman_compress_impl(input);
-  if (0 != grpc_slice_cmp(expect, got)) {
+  grpc_slice got = grpc_chttp2_base64_encode_and_huffman_compress(input);
+  if (!grpc_slice_eq(expect, got)) {
     char *t = grpc_dump_slice(input, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     char *e = grpc_dump_slice(expect, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     char *g = grpc_dump_slice(got, GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -106,7 +106,7 @@
   expect_combined_equiv(x, sizeof(x) - 1, __LINE__)
 
 static void expect_binary_header(const char *hdr, int binary) {
-  if (grpc_is_binary_header(hdr, strlen(hdr)) != binary) {
+  if (grpc_is_binary_header(grpc_slice_from_static_string(hdr)) != binary) {
     gpr_log(GPR_ERROR, "FAILED: expected header '%s' to be %s", hdr,
             binary ? "binary" : "not binary");
     all_ok = 0;
diff --git a/test/core/transport/chttp2/hpack_encoder_test.c b/test/core/transport/chttp2/hpack_encoder_test.c
index 1fd2540..d572d79 100644
--- a/test/core/transport/chttp2/hpack_encoder_test.c
+++ b/test/core/transport/chttp2/hpack_encoder_test.c
@@ -81,7 +81,9 @@
       e[i - 1].next = &e[i];
       e[i].prev = &e[i - 1];
     }
-    e[i].md = grpc_mdelem_from_strings(exec_ctx, key, value);
+    e[i].md = grpc_mdelem_from_slices(
+        exec_ctx, grpc_slice_intern(grpc_slice_from_static_string(key)),
+        grpc_slice_intern(grpc_slice_from_static_string(value)));
   }
   e[0].prev = NULL;
   e[nheaders - 1].next = NULL;
@@ -89,6 +91,7 @@
 
   b.list.head = &e[0];
   b.list.tail = &e[nheaders - 1];
+  b.list.count = nheaders;
 
   if (cap_to_delete == num_to_delete) {
     cap_to_delete = GPR_MAX(2 * cap_to_delete, 1000);
@@ -106,7 +109,7 @@
   grpc_slice_buffer_destroy_internal(exec_ctx, &output);
   grpc_metadata_batch_destroy(exec_ctx, &b);
 
-  if (0 != grpc_slice_cmp(merged, expect)) {
+  if (!grpc_slice_eq(merged, expect)) {
     char *expect_str = grpc_dump_slice(expect, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     char *got_str = grpc_dump_slice(merged, GPR_DUMP_HEX | GPR_DUMP_ASCII);
     gpr_log(GPR_ERROR, "mismatched output for %s", expected);
@@ -193,7 +196,9 @@
                                                      const char *key,
                                                      const char *value) {
   grpc_slice_buffer output;
-  grpc_mdelem *elem = grpc_mdelem_from_strings(exec_ctx, key, value);
+  grpc_mdelem elem = grpc_mdelem_from_slices(
+      exec_ctx, grpc_slice_intern(grpc_slice_from_static_string(key)),
+      grpc_slice_intern(grpc_slice_from_static_string(value)));
   size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem);
   size_t initial_table_size = g_compressor.table_size;
   grpc_linked_mdelem *e = gpr_malloc(sizeof(*e));
@@ -204,6 +209,7 @@
   e[0].next = NULL;
   b.list.head = &e[0];
   b.list.tail = &e[0];
+  b.list.count = 1;
   grpc_slice_buffer_init(&output);
 
   grpc_transport_one_way_stats stats;
@@ -233,7 +239,7 @@
 
 int main(int argc, char **argv) {
   size_t i;
-  grpc_test_only_set_metadata_hash_seed(0);
+  grpc_test_only_set_slice_hash_seed(0);
   grpc_test_init(argc, argv);
   grpc_init();
   TEST(test_basic_headers);
diff --git a/test/core/transport/chttp2/hpack_parser_corpus/051268ade45dbed0aab896d7d9f4d10ba89d3b09 b/test/core/transport/chttp2/hpack_parser_corpus/051268ade45dbed0aab896d7d9f4d10ba89d3b09
new file mode 100644
index 0000000..70967ff
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_corpus/051268ade45dbed0aab896d7d9f4d10ba89d3b09
@@ -0,0 +1 @@
+*¤¤¤ð¤-bin‹c[)(ð¤-bin-'bin	!!?¤Ûð!ð{(-binð	!\	!å7é;?Gí([(!!\ð*¤¤Ûð
\ No newline at end of file
diff --git a/test/core/transport/chttp2/hpack_parser_corpus/5278e3581c069624157fd9176eddf52c0e58df67 b/test/core/transport/chttp2/hpack_parser_corpus/5278e3581c069624157fd9176eddf52c0e58df67
new file mode 100644
index 0000000..8f3d4a9
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_corpus/5278e3581c069624157fd9176eddf52c0e58df67
Binary files differ
diff --git a/test/core/transport/chttp2/hpack_parser_corpus/bc4263a92952d56168b354f723eaa43a33cd0b35 b/test/core/transport/chttp2/hpack_parser_corpus/bc4263a92952d56168b354f723eaa43a33cd0b35
new file mode 100644
index 0000000..d62e767
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_corpus/bc4263a92952d56168b354f723eaa43a33cd0b35
Binary files differ
diff --git a/test/core/transport/chttp2/hpack_parser_corpus/crash-5ac3e1ea7764cfb6383629574262f82dc7b3cada b/test/core/transport/chttp2/hpack_parser_corpus/crash-5ac3e1ea7764cfb6383629574262f82dc7b3cada
new file mode 100644
index 0000000..44984f3
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_corpus/crash-5ac3e1ea7764cfb6383629574262f82dc7b3cada
Binary files differ
diff --git a/test/core/transport/chttp2/hpack_parser_corpus/d6d8b478e6d13945f7a6f7d27f424ff57ca12f7f b/test/core/transport/chttp2/hpack_parser_corpus/d6d8b478e6d13945f7a6f7d27f424ff57ca12f7f
new file mode 100644
index 0000000..ace84af
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_corpus/d6d8b478e6d13945f7a6f7d27f424ff57ca12f7f
Binary files differ
diff --git a/test/core/transport/chttp2/hpack_parser_corpus/e67c79d0ed89ab2d5e8d81127df22876e636ac44 b/test/core/transport/chttp2/hpack_parser_corpus/e67c79d0ed89ab2d5e8d81127df22876e636ac44
new file mode 100644
index 0000000..600b6cd
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_corpus/e67c79d0ed89ab2d5e8d81127df22876e636ac44
Binary files differ
diff --git a/test/core/transport/chttp2/hpack_parser_corpus/f4b48c4f3f310ed767755267210f212dd62bd715 b/test/core/transport/chttp2/hpack_parser_corpus/f4b48c4f3f310ed767755267210f212dd62bd715
new file mode 100644
index 0000000..49ea908
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_corpus/f4b48c4f3f310ed767755267210f212dd62bd715
Binary files differ
diff --git a/test/core/transport/chttp2/hpack_parser_corpus/fcc06696e641a7743bfd2f3b7ecd88e7b727e00a b/test/core/transport/chttp2/hpack_parser_corpus/fcc06696e641a7743bfd2f3b7ecd88e7b727e00a
new file mode 100644
index 0000000..f79c824
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_corpus/fcc06696e641a7743bfd2f3b7ecd88e7b727e00a
@@ -0,0 +1 @@
+;?'cð[(!	ð[N!\	!åGý!acceptý.:¤cA)(!)í!¼ª)ågÝI)(Ù;)Š
\ No newline at end of file
diff --git a/test/core/transport/chttp2/hpack_parser_fuzzer_test.c b/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
index 4e00f49..e9ac16d 100644
--- a/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
+++ b/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
@@ -39,25 +39,26 @@
 #include <grpc/support/log.h>
 
 #include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
+#include "src/core/lib/slice/slice_internal.h"
 
 bool squelch = true;
 bool leak_check = true;
 
-static void onhdr(grpc_exec_ctx *exec_ctx, void *ud, grpc_mdelem *md) {
+static void onhdr(grpc_exec_ctx *exec_ctx, void *ud, grpc_mdelem md) {
   GRPC_MDELEM_UNREF(exec_ctx, md);
 }
 static void dont_log(gpr_log_func_args *args) {}
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
-  grpc_test_only_set_metadata_hash_seed(0);
+  grpc_test_only_set_slice_hash_seed(0);
   if (squelch) gpr_set_log_function(dont_log);
   grpc_init();
   grpc_chttp2_hpack_parser parser;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_chttp2_hpack_parser_init(&exec_ctx, &parser);
   parser.on_header = onhdr;
-  GRPC_ERROR_UNREF(
-      grpc_chttp2_hpack_parser_parse(&exec_ctx, &parser, data, data + size));
+  GRPC_ERROR_UNREF(grpc_chttp2_hpack_parser_parse(
+      &exec_ctx, &parser, grpc_slice_from_static_buffer(data, size)));
   grpc_chttp2_hpack_parser_destroy(&exec_ctx, &parser);
   grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
index 8f48849..01789c4 100644
--- a/test/core/transport/chttp2/hpack_parser_test.c
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -45,15 +45,15 @@
 
 typedef struct { va_list args; } test_checker;
 
-static void onhdr(grpc_exec_ctx *exec_ctx, void *ud, grpc_mdelem *md) {
+static void onhdr(grpc_exec_ctx *exec_ctx, void *ud, grpc_mdelem md) {
   const char *ekey, *evalue;
   test_checker *chk = ud;
   ekey = va_arg(chk->args, char *);
   GPR_ASSERT(ekey);
   evalue = va_arg(chk->args, char *);
   GPR_ASSERT(evalue);
-  GPR_ASSERT(grpc_slice_str_cmp(md->key->slice, ekey) == 0);
-  GPR_ASSERT(grpc_slice_str_cmp(md->value->slice, evalue) == 0);
+  GPR_ASSERT(grpc_slice_str_cmp(GRPC_MDKEY(md), ekey) == 0);
+  GPR_ASSERT(grpc_slice_str_cmp(GRPC_MDVALUE(md), evalue) == 0);
   GRPC_MDELEM_UNREF(exec_ctx, md);
 }
 
@@ -76,9 +76,8 @@
 
   for (i = 0; i < nslices; i++) {
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    GPR_ASSERT(grpc_chttp2_hpack_parser_parse(
-                   &exec_ctx, parser, GRPC_SLICE_START_PTR(slices[i]),
-                   GRPC_SLICE_END_PTR(slices[i])) == GRPC_ERROR_NONE);
+    GPR_ASSERT(grpc_chttp2_hpack_parser_parse(&exec_ctx, parser, slices[i]) ==
+               GRPC_ERROR_NONE);
     grpc_exec_ctx_finish(&exec_ctx);
   }
 
diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c
index ef2ad66..d2f6d5d 100644
--- a/test/core/transport/chttp2/hpack_table_test.c
+++ b/test/core/transport/chttp2/hpack_table_test.c
@@ -46,16 +46,16 @@
 
 #define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
 
-static void assert_str(const grpc_chttp2_hptbl *tbl, grpc_mdstr *mdstr,
+static void assert_str(const grpc_chttp2_hptbl *tbl, grpc_slice mdstr,
                        const char *str) {
-  GPR_ASSERT(grpc_slice_str_cmp(mdstr->slice, str) == 0);
+  GPR_ASSERT(grpc_slice_str_cmp(mdstr, str) == 0);
 }
 
 static void assert_index(const grpc_chttp2_hptbl *tbl, uint32_t idx,
                          const char *key, const char *value) {
-  grpc_mdelem *md = grpc_chttp2_hptbl_lookup(tbl, idx);
-  assert_str(tbl, md->key, key);
-  assert_str(tbl, md->value, value);
+  grpc_mdelem md = grpc_chttp2_hptbl_lookup(tbl, idx);
+  assert_str(tbl, GRPC_MDKEY(md), key);
+  assert_str(tbl, GRPC_MDVALUE(md), value);
 }
 
 static void test_static_lookup(void) {
@@ -143,10 +143,12 @@
   grpc_chttp2_hptbl_init(&exec_ctx, &tbl);
 
   for (i = 0; i < 100000; i++) {
-    grpc_mdelem *elem;
+    grpc_mdelem elem;
     gpr_asprintf(&key, "K:%d", i);
     gpr_asprintf(&value, "VALUE:%d", i);
-    elem = grpc_mdelem_from_strings(&exec_ctx, key, value);
+    elem =
+        grpc_mdelem_from_slices(&exec_ctx, grpc_slice_from_copied_string(key),
+                                grpc_slice_from_copied_string(value));
     GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
     GRPC_MDELEM_UNREF(&exec_ctx, elem);
     assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
@@ -169,7 +171,9 @@
                                                  const char *key,
                                                  const char *value) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_mdelem *md = grpc_mdelem_from_strings(&exec_ctx, key, value);
+  grpc_mdelem md =
+      grpc_mdelem_from_slices(&exec_ctx, grpc_slice_from_copied_string(key),
+                              grpc_slice_from_copied_string(value));
   grpc_chttp2_hptbl_find_result r = grpc_chttp2_hptbl_find(tbl, md);
   GRPC_MDELEM_UNREF(&exec_ctx, md);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -181,19 +185,24 @@
   grpc_chttp2_hptbl tbl;
   uint32_t i;
   char buffer[32];
-  grpc_mdelem *elem;
+  grpc_mdelem elem;
   grpc_chttp2_hptbl_find_result r;
 
   LOG_TEST("test_find");
 
   grpc_chttp2_hptbl_init(&exec_ctx, &tbl);
-  elem = grpc_mdelem_from_strings(&exec_ctx, "abc", "xyz");
+  elem =
+      grpc_mdelem_from_slices(&exec_ctx, grpc_slice_from_static_string("abc"),
+                              grpc_slice_from_static_string("xyz"));
   GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
   GRPC_MDELEM_UNREF(&exec_ctx, elem);
-  elem = grpc_mdelem_from_strings(&exec_ctx, "abc", "123");
+  elem =
+      grpc_mdelem_from_slices(&exec_ctx, grpc_slice_from_static_string("abc"),
+                              grpc_slice_from_static_string("123"));
   GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
   GRPC_MDELEM_UNREF(&exec_ctx, elem);
-  elem = grpc_mdelem_from_strings(&exec_ctx, "x", "1");
+  elem = grpc_mdelem_from_slices(&exec_ctx, grpc_slice_from_static_string("x"),
+                                 grpc_slice_from_static_string("1"));
   GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
   GRPC_MDELEM_UNREF(&exec_ctx, elem);
 
@@ -244,7 +253,9 @@
   /* overflow the string buffer, check find still works */
   for (i = 0; i < 10000; i++) {
     int64_ttoa(i, buffer);
-    elem = grpc_mdelem_from_strings(&exec_ctx, "test", buffer);
+    elem = grpc_mdelem_from_slices(&exec_ctx,
+                                   grpc_slice_from_static_string("test"),
+                                   grpc_slice_from_copied_string(buffer));
     GPR_ASSERT(grpc_chttp2_hptbl_add(&exec_ctx, &tbl, elem) == GRPC_ERROR_NONE);
     GRPC_MDELEM_UNREF(&exec_ctx, elem);
   }
diff --git a/test/core/transport/chttp2/varint_test.c b/test/core/transport/chttp2/varint_test.c
index e29be4b..f12c340 100644
--- a/test/core/transport/chttp2/varint_test.c
+++ b/test/core/transport/chttp2/varint_test.c
@@ -49,7 +49,7 @@
   slice = grpc_slice_malloc(nbytes);
   GRPC_CHTTP2_WRITE_VARINT(value, prefix_bits, prefix_or,
                            GRPC_SLICE_START_PTR(slice), nbytes);
-  GPR_ASSERT(grpc_slice_cmp(expect, slice) == 0);
+  GPR_ASSERT(grpc_slice_eq(expect, slice));
   grpc_slice_unref(expect);
   grpc_slice_unref(slice);
 }
diff --git a/test/core/transport/metadata_test.c b/test/core/transport/metadata_test.c
index 3625043..92e58f2 100644
--- a/test/core/transport/metadata_test.c
+++ b/test/core/transport/metadata_test.c
@@ -47,55 +47,51 @@
 #include "src/core/lib/transport/static_metadata.h"
 #include "test/core/util/test_config.h"
 
-#define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
-
 /* a large number */
 #define MANY 10000
 
 static void test_no_op(void) {
-  LOG_TEST("test_no_op");
+  gpr_log(GPR_INFO, "test_no_op");
   grpc_init();
   grpc_shutdown();
 }
 
-static void test_create_string(void) {
-  grpc_mdstr *s1, *s2, *s3;
-
-  LOG_TEST("test_create_string");
-
-  grpc_init();
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  s1 = grpc_mdstr_from_string("hello");
-  s2 = grpc_mdstr_from_string("hello");
-  s3 = grpc_mdstr_from_string("very much not hello");
-  GPR_ASSERT(s1 == s2);
-  GPR_ASSERT(s3 != s1);
-  GPR_ASSERT(grpc_slice_str_cmp(s1->slice, "hello") == 0);
-  GPR_ASSERT(grpc_slice_str_cmp(s3->slice, "very much not hello") == 0);
-  GRPC_MDSTR_UNREF(&exec_ctx, s1);
-  GRPC_MDSTR_UNREF(&exec_ctx, s2);
-  GRPC_MDSTR_UNREF(&exec_ctx, s3);
-  grpc_exec_ctx_finish(&exec_ctx);
-  grpc_shutdown();
+static grpc_slice maybe_intern(grpc_slice in, bool intern) {
+  grpc_slice out = intern ? grpc_slice_intern(in) : grpc_slice_ref(in);
+  grpc_slice_unref(in);
+  return out;
 }
 
-static void test_create_metadata(void) {
-  grpc_mdelem *m1, *m2, *m3;
+static grpc_slice maybe_dup(grpc_slice in, bool dup) {
+  grpc_slice out = dup ? grpc_slice_dup(in) : grpc_slice_ref(in);
+  grpc_slice_unref(in);
+  return out;
+}
 
-  LOG_TEST("test_create_metadata");
+static void test_create_metadata(bool intern_keys, bool intern_values) {
+  grpc_mdelem m1, m2, m3;
+
+  gpr_log(GPR_INFO, "test_create_metadata: intern_keys=%d intern_values=%d",
+          intern_keys, intern_values);
 
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  m1 = grpc_mdelem_from_strings(&exec_ctx, "a", "b");
-  m2 = grpc_mdelem_from_strings(&exec_ctx, "a", "b");
-  m3 = grpc_mdelem_from_strings(&exec_ctx, "a", "c");
-  GPR_ASSERT(m1 == m2);
-  GPR_ASSERT(m3 != m1);
-  GPR_ASSERT(m3->key == m1->key);
-  GPR_ASSERT(m3->value != m1->value);
-  GPR_ASSERT(grpc_slice_str_cmp(m1->key->slice, "a") == 0);
-  GPR_ASSERT(grpc_slice_str_cmp(m1->value->slice, "b") == 0);
-  GPR_ASSERT(grpc_slice_str_cmp(m3->value->slice, "c") == 0);
+  m1 = grpc_mdelem_from_slices(
+      &exec_ctx, maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+      maybe_intern(grpc_slice_from_static_string("b"), intern_values));
+  m2 = grpc_mdelem_from_slices(
+      &exec_ctx, maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+      maybe_intern(grpc_slice_from_static_string("b"), intern_values));
+  m3 = grpc_mdelem_from_slices(
+      &exec_ctx, maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+      maybe_intern(grpc_slice_from_static_string("c"), intern_values));
+  GPR_ASSERT(grpc_mdelem_eq(m1, m2));
+  GPR_ASSERT(!grpc_mdelem_eq(m3, m1));
+  GPR_ASSERT(grpc_slice_eq(GRPC_MDKEY(m3), GRPC_MDKEY(m1)));
+  GPR_ASSERT(!grpc_slice_eq(GRPC_MDVALUE(m3), GRPC_MDVALUE(m1)));
+  GPR_ASSERT(grpc_slice_str_cmp(GRPC_MDKEY(m1), "a") == 0);
+  GPR_ASSERT(grpc_slice_str_cmp(GRPC_MDVALUE(m1), "b") == 0);
+  GPR_ASSERT(grpc_slice_str_cmp(GRPC_MDVALUE(m3), "c") == 0);
   GRPC_MDELEM_UNREF(&exec_ctx, m1);
   GRPC_MDELEM_UNREF(&exec_ctx, m2);
   GRPC_MDELEM_UNREF(&exec_ctx, m3);
@@ -103,19 +99,28 @@
   grpc_shutdown();
 }
 
-static void test_create_many_ephemeral_metadata(void) {
+static void test_create_many_ephemeral_metadata(bool intern_keys,
+                                                bool intern_values) {
   char buffer[GPR_LTOA_MIN_BUFSIZE];
   long i;
 
-  LOG_TEST("test_create_many_ephemeral_metadata");
+  gpr_log(
+      GPR_INFO,
+      "test_create_many_ephemeral_metadata: intern_keys=%d intern_values=%d",
+      intern_keys, intern_values);
 
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   /* add, and immediately delete a bunch of different elements */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    GRPC_MDELEM_UNREF(&exec_ctx,
-                      grpc_mdelem_from_strings(&exec_ctx, "a", buffer));
+    GRPC_MDELEM_UNREF(
+        &exec_ctx,
+        grpc_mdelem_from_slices(
+            &exec_ctx,
+            maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+            maybe_intern(grpc_slice_from_copied_string(buffer),
+                         intern_values)));
   }
   grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
@@ -124,23 +129,27 @@
 static void test_create_many_persistant_metadata(void) {
   char buffer[GPR_LTOA_MIN_BUFSIZE];
   long i;
-  grpc_mdelem **created = gpr_malloc(sizeof(grpc_mdelem *) * MANY);
-  grpc_mdelem *md;
+  grpc_mdelem *created = gpr_malloc(sizeof(grpc_mdelem) * MANY);
+  grpc_mdelem md;
 
-  LOG_TEST("test_create_many_persistant_metadata");
+  gpr_log(GPR_INFO, "test_create_many_persistant_metadata");
 
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   /* add phase */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    created[i] = grpc_mdelem_from_strings(&exec_ctx, "a", buffer);
+    created[i] = grpc_mdelem_from_slices(
+        &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("a")),
+        grpc_slice_intern(grpc_slice_from_static_string(buffer)));
   }
   /* verify phase */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    md = grpc_mdelem_from_strings(&exec_ctx, "a", buffer);
-    GPR_ASSERT(md == created[i]);
+    md = grpc_mdelem_from_slices(
+        &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("a")),
+        grpc_slice_intern(grpc_slice_from_static_string(buffer)));
+    GPR_ASSERT(grpc_mdelem_eq(md, created[i]));
     GRPC_MDELEM_UNREF(&exec_ctx, md);
   }
   /* cleanup phase */
@@ -153,14 +162,77 @@
   gpr_free(created);
 }
 
-static void test_spin_creating_the_same_thing(void) {
-  LOG_TEST("test_spin_creating_the_same_thing");
+static void test_spin_creating_the_same_thing(bool intern_keys,
+                                              bool intern_values) {
+  gpr_log(GPR_INFO,
+          "test_spin_creating_the_same_thing: intern_keys=%d intern_values=%d",
+          intern_keys, intern_values);
 
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_from_strings(&exec_ctx, "a", "b"));
-  GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_from_strings(&exec_ctx, "a", "b"));
-  GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_from_strings(&exec_ctx, "a", "b"));
+  grpc_mdelem a, b, c;
+  GRPC_MDELEM_UNREF(
+      &exec_ctx,
+      a = grpc_mdelem_from_slices(
+          &exec_ctx,
+          maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+          maybe_intern(grpc_slice_from_static_string("b"), intern_values)));
+  GRPC_MDELEM_UNREF(
+      &exec_ctx,
+      b = grpc_mdelem_from_slices(
+          &exec_ctx,
+          maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+          maybe_intern(grpc_slice_from_static_string("b"), intern_values)));
+  GRPC_MDELEM_UNREF(
+      &exec_ctx,
+      c = grpc_mdelem_from_slices(
+          &exec_ctx,
+          maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+          maybe_intern(grpc_slice_from_static_string("b"), intern_values)));
+  if (intern_keys && intern_values) {
+    GPR_ASSERT(a.payload == b.payload);
+    GPR_ASSERT(a.payload == c.payload);
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_shutdown();
+}
+
+static void test_identity_laws(bool intern_keys, bool intern_values) {
+  gpr_log(GPR_INFO, "test_identity_laws: intern_keys=%d intern_values=%d",
+          intern_keys, intern_values);
+
+  grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_mdelem a, b, c;
+  a = grpc_mdelem_from_slices(
+      &exec_ctx, maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+      maybe_intern(grpc_slice_from_static_string("b"), intern_values));
+  b = grpc_mdelem_from_slices(
+      &exec_ctx, maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+      maybe_intern(grpc_slice_from_static_string("b"), intern_values));
+  c = grpc_mdelem_from_slices(
+      &exec_ctx, maybe_intern(grpc_slice_from_static_string("a"), intern_keys),
+      maybe_intern(grpc_slice_from_static_string("b"), intern_values));
+  GPR_ASSERT(grpc_mdelem_eq(a, a));
+  GPR_ASSERT(grpc_mdelem_eq(b, b));
+  GPR_ASSERT(grpc_mdelem_eq(c, c));
+  GPR_ASSERT(grpc_mdelem_eq(a, b));
+  GPR_ASSERT(grpc_mdelem_eq(b, c));
+  GPR_ASSERT(grpc_mdelem_eq(a, c));
+  GPR_ASSERT(grpc_mdelem_eq(b, a));
+  GPR_ASSERT(grpc_mdelem_eq(c, b));
+  GPR_ASSERT(grpc_mdelem_eq(c, a));
+  if (intern_keys && intern_values) {
+    GPR_ASSERT(a.payload == b.payload);
+    GPR_ASSERT(a.payload == c.payload);
+  } else {
+    GPR_ASSERT(a.payload != b.payload);
+    GPR_ASSERT(a.payload != c.payload);
+    GPR_ASSERT(b.payload != c.payload);
+  }
+  GRPC_MDELEM_UNREF(&exec_ctx, a);
+  GRPC_MDELEM_UNREF(&exec_ctx, b);
+  GRPC_MDELEM_UNREF(&exec_ctx, c);
   grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
 }
@@ -169,25 +241,25 @@
   size_t i, j;
   char *buffer;
   size_t nstrs = 1000;
-  grpc_mdstr **strs = gpr_malloc(sizeof(grpc_mdstr *) * nstrs);
+  grpc_slice *strs = gpr_malloc(sizeof(grpc_slice) * nstrs);
   size_t *shuf = gpr_malloc(sizeof(size_t) * nstrs);
-  grpc_mdstr *test;
+  grpc_slice test;
 
-  LOG_TEST("test_things_stick_around");
+  gpr_log(GPR_INFO, "test_things_stick_around");
 
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   for (i = 0; i < nstrs; i++) {
     gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%" PRIuPTR "x", i);
-    strs[i] = grpc_mdstr_from_string(buffer);
+    strs[i] = grpc_slice_intern(grpc_slice_from_static_string(buffer));
     shuf[i] = i;
     gpr_free(buffer);
   }
 
   for (i = 0; i < nstrs; i++) {
-    GRPC_MDSTR_REF(strs[i]);
-    GRPC_MDSTR_UNREF(&exec_ctx, strs[i]);
+    grpc_slice_ref_internal(strs[i]);
+    grpc_slice_unref_internal(&exec_ctx, strs[i]);
   }
 
   for (i = 0; i < nstrs; i++) {
@@ -199,13 +271,13 @@
   }
 
   for (i = 0; i < nstrs; i++) {
-    GRPC_MDSTR_UNREF(&exec_ctx, strs[shuf[i]]);
+    grpc_slice_unref_internal(&exec_ctx, strs[shuf[i]]);
     for (j = i + 1; j < nstrs; j++) {
       gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%" PRIuPTR "x",
                    shuf[j]);
-      test = grpc_mdstr_from_string(buffer);
-      GPR_ASSERT(test == strs[shuf[j]]);
-      GRPC_MDSTR_UNREF(&exec_ctx, test);
+      test = grpc_slice_intern(grpc_slice_from_static_string(buffer));
+      GPR_ASSERT(grpc_slice_is_equivalent(test, strs[shuf[j]]));
+      grpc_slice_unref_internal(&exec_ctx, test);
       gpr_free(buffer);
     }
   }
@@ -216,57 +288,11 @@
   gpr_free(shuf);
 }
 
-static void test_slices_work(void) {
-  /* ensure no memory leaks when switching representation from mdstr to slice */
-  grpc_mdstr *str;
-  grpc_slice slice;
-
-  LOG_TEST("test_slices_work");
-
-  grpc_init();
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-
-  str = grpc_mdstr_from_string(
-      "123456789012345678901234567890123456789012345678901234567890");
-  slice = grpc_slice_ref(str->slice);
-  GRPC_MDSTR_UNREF(&exec_ctx, str);
-  grpc_slice_unref_internal(&exec_ctx, slice);
-
-  str = grpc_mdstr_from_string(
-      "123456789012345678901234567890123456789012345678901234567890");
-  slice = grpc_slice_ref(str->slice);
-  grpc_slice_unref_internal(&exec_ctx, slice);
-  GRPC_MDSTR_UNREF(&exec_ctx, str);
-
-  grpc_exec_ctx_finish(&exec_ctx);
-  grpc_shutdown();
-}
-
-static void test_base64_and_huffman_works(void) {
-  grpc_mdstr *str;
-  grpc_slice slice1;
-  grpc_slice slice2;
-
-  LOG_TEST("test_base64_and_huffman_works");
-
-  grpc_init();
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  str = grpc_mdstr_from_string("abcdefg");
-  slice1 = grpc_mdstr_as_base64_encoded_and_huffman_compressed(str);
-  slice2 = grpc_chttp2_base64_encode_and_huffman_compress(str->slice);
-  GPR_ASSERT(0 == grpc_slice_cmp(slice1, slice2));
-
-  grpc_slice_unref_internal(&exec_ctx, slice2);
-  GRPC_MDSTR_UNREF(&exec_ctx, str);
-  grpc_exec_ctx_finish(&exec_ctx);
-  grpc_shutdown();
-}
-
 static void test_user_data_works(void) {
   int *ud1;
   int *ud2;
-  grpc_mdelem *md;
-  LOG_TEST("test_user_data_works");
+  grpc_mdelem md;
+  gpr_log(GPR_INFO, "test_user_data_works");
 
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
@@ -274,7 +300,9 @@
   *ud1 = 1;
   ud2 = gpr_malloc(sizeof(int));
   *ud2 = 2;
-  md = grpc_mdelem_from_strings(&exec_ctx, "abc", "123");
+  md = grpc_mdelem_from_slices(
+      &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")),
+      grpc_slice_intern(grpc_slice_from_static_string("123")));
   grpc_mdelem_set_user_data(md, gpr_free, ud1);
   grpc_mdelem_set_user_data(md, gpr_free, ud2);
   GPR_ASSERT(grpc_mdelem_get_user_data(md, gpr_free) == ud1);
@@ -284,8 +312,11 @@
 }
 
 static void verify_ascii_header_size(grpc_exec_ctx *exec_ctx, const char *key,
-                                     const char *value) {
-  grpc_mdelem *elem = grpc_mdelem_from_strings(exec_ctx, key, value);
+                                     const char *value, bool intern_key,
+                                     bool intern_value) {
+  grpc_mdelem elem = grpc_mdelem_from_slices(
+      exec_ctx, maybe_intern(grpc_slice_from_static_string(key), intern_key),
+      maybe_intern(grpc_slice_from_static_string(value), intern_value));
   size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem);
   size_t expected_size = 32 + strlen(key) + strlen(value);
   GPR_ASSERT(expected_size == elem_size);
@@ -293,10 +324,13 @@
 }
 
 static void verify_binary_header_size(grpc_exec_ctx *exec_ctx, const char *key,
-                                      const uint8_t *value, size_t value_len) {
-  grpc_mdelem *elem =
-      grpc_mdelem_from_string_and_buffer(exec_ctx, key, value, value_len);
-  GPR_ASSERT(grpc_is_binary_header(key, strlen(key)));
+                                      const uint8_t *value, size_t value_len,
+                                      bool intern_key, bool intern_value) {
+  grpc_mdelem elem = grpc_mdelem_from_slices(
+      exec_ctx, maybe_intern(grpc_slice_from_static_string(key), intern_key),
+      maybe_intern(grpc_slice_from_static_buffer(value, value_len),
+                   intern_value));
+  GPR_ASSERT(grpc_is_binary_header(GRPC_MDKEY(elem)));
   size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem);
   grpc_slice value_slice =
       grpc_slice_from_copied_buffer((const char *)value, value_len);
@@ -309,8 +343,9 @@
 }
 
 #define BUFFER_SIZE 64
-static void test_mdelem_sizes_in_hpack(void) {
-  LOG_TEST("test_mdelem_size");
+static void test_mdelem_sizes_in_hpack(bool intern_key, bool intern_value) {
+  gpr_log(GPR_INFO, "test_mdelem_size: intern_key=%d intern_value=%d",
+          intern_key, intern_value);
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
@@ -319,19 +354,44 @@
     binary_value[i] = i;
   }
 
-  verify_ascii_header_size(&exec_ctx, "hello", "world");
+  verify_ascii_header_size(&exec_ctx, "hello", "world", intern_key,
+                           intern_value);
   verify_ascii_header_size(&exec_ctx, "hello",
-                           "worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
-  verify_ascii_header_size(&exec_ctx, ":scheme", "http");
+                           "worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", intern_key,
+                           intern_value);
+  verify_ascii_header_size(&exec_ctx, ":scheme", "http", intern_key,
+                           intern_value);
 
   for (uint8_t i = 0; i < BUFFER_SIZE; i++) {
-    verify_binary_header_size(&exec_ctx, "hello-bin", binary_value, i);
+    verify_binary_header_size(&exec_ctx, "hello-bin", binary_value, i,
+                              intern_key, intern_value);
   }
 
-  const char *static_metadata = grpc_static_metadata_strings[0];
-  memcpy(binary_value, static_metadata, strlen(static_metadata));
-  verify_binary_header_size(&exec_ctx, "hello-bin", binary_value,
-                            strlen(static_metadata));
+  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_shutdown();
+}
+
+static void test_copied_static_metadata(bool dup_key, bool dup_value) {
+  gpr_log(GPR_INFO, "test_static_metadata: dup_key=%d dup_value=%d", dup_key,
+          dup_value);
+  grpc_init();
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+  for (size_t i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
+    grpc_mdelem p = GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[i],
+                                     GRPC_MDELEM_STORAGE_STATIC);
+    grpc_mdelem q =
+        grpc_mdelem_from_slices(&exec_ctx, maybe_dup(GRPC_MDKEY(p), dup_key),
+                                maybe_dup(GRPC_MDVALUE(p), dup_value));
+    GPR_ASSERT(grpc_mdelem_eq(p, q));
+    if (dup_key || dup_value) {
+      GPR_ASSERT(p.payload != q.payload);
+    } else {
+      GPR_ASSERT(p.payload == q.payload);
+    }
+    GRPC_MDELEM_UNREF(&exec_ctx, p);
+    GRPC_MDELEM_UNREF(&exec_ctx, q);
+  }
 
   grpc_exec_ctx_finish(&exec_ctx);
   grpc_shutdown();
@@ -340,15 +400,18 @@
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
   test_no_op();
-  test_create_string();
-  test_create_metadata();
-  test_create_many_ephemeral_metadata();
+  for (int k = 0; k <= 1; k++) {
+    for (int v = 0; v <= 1; v++) {
+      test_create_metadata(k, v);
+      test_create_many_ephemeral_metadata(k, v);
+      test_identity_laws(k, v);
+      test_spin_creating_the_same_thing(k, v);
+      test_mdelem_sizes_in_hpack(k, v);
+      test_copied_static_metadata(k, v);
+    }
+  }
   test_create_many_persistant_metadata();
-  test_spin_creating_the_same_thing();
   test_things_stick_around();
-  test_slices_work();
-  test_base64_and_huffman_works();
   test_user_data_works();
-  test_mdelem_sizes_in_hpack();
   return 0;
 }
diff --git a/test/core/transport/chttp2/status_conversion_test.c b/test/core/transport/status_conversion_test.c
similarity index 65%
rename from test/core/transport/chttp2/status_conversion_test.c
rename to test/core/transport/status_conversion_test.c
index f5a5cd1..65f840b 100644
--- a/test/core/transport/chttp2/status_conversion_test.c
+++ b/test/core/transport/status_conversion_test.c
@@ -31,50 +31,50 @@
  *
  */
 
-#include "src/core/ext/transport/chttp2/transport/status_conversion.h"
+#include "src/core/lib/transport/status_conversion.h"
 #include <grpc/support/log.h>
 #include "test/core/util/test_config.h"
 
 #define GRPC_STATUS_TO_HTTP2_ERROR(a, b) \
-  GPR_ASSERT(grpc_chttp2_grpc_status_to_http2_error(a) == (b))
+  GPR_ASSERT(grpc_status_to_http2_error(a) == (b))
 #define HTTP2_ERROR_TO_GRPC_STATUS(a, deadline, b) \
-  GPR_ASSERT(grpc_chttp2_http2_error_to_grpc_status(a, deadline) == (b))
+  GPR_ASSERT(grpc_http2_error_to_grpc_status(a, deadline) == (b))
 #define GRPC_STATUS_TO_HTTP2_STATUS(a, b) \
-  GPR_ASSERT(grpc_chttp2_grpc_status_to_http2_status(a) == (b))
+  GPR_ASSERT(grpc_status_to_http2_status(a) == (b))
 #define HTTP2_STATUS_TO_GRPC_STATUS(a, b) \
-  GPR_ASSERT(grpc_chttp2_http2_status_to_grpc_status(a) == (b))
+  GPR_ASSERT(grpc_http2_status_to_grpc_status(a) == (b))
 
 int main(int argc, char **argv) {
   int i;
 
   grpc_test_init(argc, argv);
 
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_OK, GRPC_CHTTP2_NO_ERROR);
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_CANCELLED, GRPC_CHTTP2_CANCEL);
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNKNOWN, GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_OK, GRPC_HTTP2_NO_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_CANCELLED, GRPC_HTTP2_CANCEL);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNKNOWN, GRPC_HTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_INVALID_ARGUMENT,
-                             GRPC_CHTTP2_INTERNAL_ERROR);
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DEADLINE_EXCEEDED, GRPC_CHTTP2_CANCEL);
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_NOT_FOUND, GRPC_CHTTP2_INTERNAL_ERROR);
+                             GRPC_HTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DEADLINE_EXCEEDED, GRPC_HTTP2_CANCEL);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_NOT_FOUND, GRPC_HTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_ALREADY_EXISTS,
-                             GRPC_CHTTP2_INTERNAL_ERROR);
+                             GRPC_HTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_PERMISSION_DENIED,
-                             GRPC_CHTTP2_INADEQUATE_SECURITY);
+                             GRPC_HTTP2_INADEQUATE_SECURITY);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNAUTHENTICATED,
-                             GRPC_CHTTP2_INTERNAL_ERROR);
+                             GRPC_HTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_RESOURCE_EXHAUSTED,
-                             GRPC_CHTTP2_ENHANCE_YOUR_CALM);
+                             GRPC_HTTP2_ENHANCE_YOUR_CALM);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_FAILED_PRECONDITION,
-                             GRPC_CHTTP2_INTERNAL_ERROR);
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_ABORTED, GRPC_CHTTP2_INTERNAL_ERROR);
+                             GRPC_HTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_ABORTED, GRPC_HTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_OUT_OF_RANGE,
-                             GRPC_CHTTP2_INTERNAL_ERROR);
+                             GRPC_HTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNIMPLEMENTED,
-                             GRPC_CHTTP2_INTERNAL_ERROR);
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_INTERNAL, GRPC_CHTTP2_INTERNAL_ERROR);
+                             GRPC_HTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_INTERNAL, GRPC_HTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNAVAILABLE,
-                             GRPC_CHTTP2_REFUSED_STREAM);
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DATA_LOSS, GRPC_CHTTP2_INTERNAL_ERROR);
+                             GRPC_HTTP2_REFUSED_STREAM);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DATA_LOSS, GRPC_HTTP2_INTERNAL_ERROR);
 
   GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_OK, 200);
   GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_CANCELLED, 200);
@@ -95,59 +95,59 @@
   GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_DATA_LOSS, 200);
 
   const gpr_timespec before_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_NO_ERROR, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_NO_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_PROTOCOL_ERROR, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_PROTOCOL_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INTERNAL_ERROR, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_INTERNAL_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FLOW_CONTROL_ERROR, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_FLOW_CONTROL_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_SETTINGS_TIMEOUT, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_SETTINGS_TIMEOUT, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_STREAM_CLOSED, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_STREAM_CLOSED, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FRAME_SIZE_ERROR, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_FRAME_SIZE_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_REFUSED_STREAM, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_REFUSED_STREAM, before_deadline,
                              GRPC_STATUS_UNAVAILABLE);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CANCEL, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_CANCEL, before_deadline,
                              GRPC_STATUS_CANCELLED);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_COMPRESSION_ERROR, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_COMPRESSION_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CONNECT_ERROR, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_CONNECT_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_ENHANCE_YOUR_CALM, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_ENHANCE_YOUR_CALM, before_deadline,
                              GRPC_STATUS_RESOURCE_EXHAUSTED);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INADEQUATE_SECURITY, before_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_INADEQUATE_SECURITY, before_deadline,
                              GRPC_STATUS_PERMISSION_DENIED);
 
   const gpr_timespec after_deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_NO_ERROR, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_NO_ERROR, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_PROTOCOL_ERROR, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_PROTOCOL_ERROR, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INTERNAL_ERROR, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_INTERNAL_ERROR, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FLOW_CONTROL_ERROR, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_FLOW_CONTROL_ERROR, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_SETTINGS_TIMEOUT, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_SETTINGS_TIMEOUT, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_STREAM_CLOSED, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_STREAM_CLOSED, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FRAME_SIZE_ERROR, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_FRAME_SIZE_ERROR, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_REFUSED_STREAM, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_REFUSED_STREAM, after_deadline,
                              GRPC_STATUS_UNAVAILABLE);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CANCEL, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_CANCEL, after_deadline,
                              GRPC_STATUS_DEADLINE_EXCEEDED);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_COMPRESSION_ERROR, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_COMPRESSION_ERROR, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CONNECT_ERROR, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_CONNECT_ERROR, after_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_ENHANCE_YOUR_CALM, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_ENHANCE_YOUR_CALM, after_deadline,
                              GRPC_STATUS_RESOURCE_EXHAUSTED);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INADEQUATE_SECURITY, after_deadline,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_INADEQUATE_SECURITY, after_deadline,
                              GRPC_STATUS_PERMISSION_DENIED);
 
   HTTP2_STATUS_TO_GRPC_STATUS(200, GRPC_STATUS_OK);
@@ -165,7 +165,7 @@
 
   /* check all status values can be converted */
   for (i = 0; i <= 999; i++) {
-    grpc_chttp2_http2_status_to_grpc_status(i);
+    grpc_http2_status_to_grpc_status(i);
   }
 
   return 0;
diff --git a/test/core/transport/timeout_encoding_test.c b/test/core/transport/timeout_encoding_test.c
index b6004af..10e1804 100644
--- a/test/core/transport/timeout_encoding_test.c
+++ b/test/core/transport/timeout_encoding_test.c
@@ -88,7 +88,8 @@
 static void assert_decodes_as(const char *buffer, gpr_timespec expected) {
   gpr_timespec got;
   gpr_log(GPR_INFO, "check decoding '%s'", buffer);
-  GPR_ASSERT(1 == grpc_http2_decode_timeout(buffer, &got));
+  GPR_ASSERT(1 == grpc_http2_decode_timeout(
+                      grpc_slice_from_static_string(buffer), &got));
   GPR_ASSERT(0 == gpr_time_cmp(got, expected));
 }
 
@@ -134,18 +135,23 @@
   assert_decodes_as("9999999999S", gpr_inf_future(GPR_TIMESPAN));
 }
 
-void test_decoding_fails(void) {
+static void assert_decoding_fails(const char *s) {
   gpr_timespec x;
+  GPR_ASSERT(0 ==
+             grpc_http2_decode_timeout(grpc_slice_from_static_string(s), &x));
+}
+
+void test_decoding_fails(void) {
   LOG_TEST("test_decoding_fails");
-  GPR_ASSERT(0 == grpc_http2_decode_timeout("", &x));
-  GPR_ASSERT(0 == grpc_http2_decode_timeout(" ", &x));
-  GPR_ASSERT(0 == grpc_http2_decode_timeout("x", &x));
-  GPR_ASSERT(0 == grpc_http2_decode_timeout("1", &x));
-  GPR_ASSERT(0 == grpc_http2_decode_timeout("1x", &x));
-  GPR_ASSERT(0 == grpc_http2_decode_timeout("1ux", &x));
-  GPR_ASSERT(0 == grpc_http2_decode_timeout("!", &x));
-  GPR_ASSERT(0 == grpc_http2_decode_timeout("n1", &x));
-  GPR_ASSERT(0 == grpc_http2_decode_timeout("-1u", &x));
+  assert_decoding_fails("");
+  assert_decoding_fails(" ");
+  assert_decoding_fails("x");
+  assert_decoding_fails("1");
+  assert_decoding_fails("1x");
+  assert_decoding_fails("1ux");
+  assert_decoding_fails("!");
+  assert_decoding_fails("n1");
+  assert_decoding_fails("-1u");
 }
 
 int main(int argc, char **argv) {
diff --git a/test/core/util/port_server_client.c b/test/core/util/port_server_client.c
index 4baf63d..118708d 100644
--- a/test/core/util/port_server_client.c
+++ b/test/core/util/port_server_client.c
@@ -152,7 +152,7 @@
     failed = 1;
     const char *msg = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "failed port pick from server: retrying [%s]", msg);
-    grpc_error_free_string(msg);
+
   } else if (response->status != 200) {
     failed = 1;
     gpr_log(GPR_DEBUG, "failed port pick from server: status=%d",
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index 2ce3f2f..f536012 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -228,12 +228,7 @@
       : disable_blocking(non_block),
         credentials_type(creds_type),
         message_content(content) {}
-  void Log() const {
-    gpr_log(
-        GPR_INFO,
-        "Scenario: disable_blocking %d, credentials %s, message size %" PRIuPTR,
-        disable_blocking, credentials_type.c_str(), message_content.size());
-  }
+  void Log() const;
   bool disable_blocking;
   // Although the below grpc::string's are logically const, we can't declare
   // them const because of a limitation in the way old compilers (e.g., gcc-4.4)
@@ -242,6 +237,20 @@
   grpc::string message_content;
 };
 
+static std::ostream& operator<<(std::ostream& out,
+                                const TestScenario& scenario) {
+  return out << "TestScenario{disable_blocking="
+             << (scenario.disable_blocking ? "true" : "false")
+             << ", credentials='" << scenario.credentials_type
+             << "', message_size=" << scenario.message_content.size() << "}";
+}
+
+void TestScenario::Log() const {
+  std::ostringstream out;
+  out << *this;
+  gpr_log(GPR_DEBUG, "%s", out.str().c_str());
+}
+
 class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> {
  protected:
   AsyncEnd2endTest() { GetParam().Log(); }
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 1a1a94e..47e5c5b 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -209,10 +209,7 @@
  public:
   TestScenario(bool proxy, const grpc::string& creds_type)
       : use_proxy(proxy), credentials_type(creds_type) {}
-  void Log() const {
-    gpr_log(GPR_INFO, "Scenario: proxy %d, credentials %s", use_proxy,
-            credentials_type.c_str());
-  }
+  void Log() const;
   bool use_proxy;
   // Although the below grpc::string is logically const, we can't declare
   // them const because of a limitation in the way old compilers (e.g., gcc-4.4)
@@ -220,6 +217,19 @@
   grpc::string credentials_type;
 };
 
+static std::ostream& operator<<(std::ostream& out,
+                                const TestScenario& scenario) {
+  return out << "TestScenario{use_proxy="
+             << (scenario.use_proxy ? "true" : "false") << ", credentials='"
+             << scenario.credentials_type << "'}";
+}
+
+void TestScenario::Log() const {
+  std::ostringstream out;
+  out << *this;
+  gpr_log(GPR_DEBUG, "%s", out.str().c_str());
+}
+
 class End2endTest : public ::testing::TestWithParam<TestScenario> {
  protected:
   End2endTest()
@@ -636,7 +646,7 @@
   TestBidiStreamServerCancel(CANCEL_AFTER_PROCESSING, 5);
 }
 
-TEST_P(End2endTest, SimpleRpcWithCustomeUserAgentPrefix) {
+TEST_P(End2endTest, SimpleRpcWithCustomUserAgentPrefix) {
   user_agent_prefix_ = "custom_prefix";
   ResetStub();
   EchoRequest request;
@@ -891,6 +901,8 @@
   EchoRequest request;
   EchoResponse response;
   request.set_message(string(kMaxMessageSize_ * 2, 'a'));
+  // cancelled is not guaranteed to appear before the end of the service handler
+  request.mutable_param()->set_skip_cancelled_check(true);
 
   ClientContext context;
   Status s = stub_->Echo(&context, request, &response);
@@ -1293,6 +1305,7 @@
   EXPECT_FALSE(s.ok());
   EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
 }
+
 TEST_P(SecureEnd2endTest, SetPerCallCredentials) {
   ResetStub();
   EchoRequest request;
diff --git a/test/cpp/grpclb/grpclb_test.cc b/test/cpp/grpclb/grpclb_test.cc
index de304b9..a10cda7 100644
--- a/test/cpp/grpclb/grpclb_test.cc
+++ b/test/cpp/grpclb/grpclb_test.cc
@@ -213,7 +213,7 @@
   // receive request for backends
   op = ops;
   op->op = GRPC_OP_RECV_MESSAGE;
-  op->data.recv_message = &request_payload_recv;
+  op->data.recv_message.recv_message = &request_payload_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -268,7 +268,7 @@
     response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1);
     op = ops;
     op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message = response_payload;
+    op->data.send_message.send_message = response_payload;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -288,7 +288,8 @@
   op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
   op->data.send_status_from_server.trailing_metadata_count = 0;
   op->data.send_status_from_server.status = GRPC_STATUS_OK;
-  op->data.send_status_from_server.status_details = "xyz";
+  grpc_slice status_details = grpc_slice_from_static_string("xyz");
+  op->data.send_status_from_server.status_details = &status_details;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -372,7 +373,7 @@
     while (!exit) {
       op = ops;
       op->op = GRPC_OP_RECV_MESSAGE;
-      op->data.recv_message = &request_payload_recv;
+      op->data.recv_message.recv_message = &request_payload_recv;
       op->flags = 0;
       op->reserved = NULL;
       op++;
@@ -401,7 +402,7 @@
             grpc_raw_byte_buffer_create(&response_payload_slice, 1);
         op = ops;
         op->op = GRPC_OP_SEND_MESSAGE;
-        op->data.send_message = response_payload;
+        op->data.send_message.send_message = response_payload;
         op->flags = 0;
         op->reserved = NULL;
         op++;
@@ -433,7 +434,9 @@
     op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
     op->data.send_status_from_server.trailing_metadata_count = 0;
     op->data.send_status_from_server.status = GRPC_STATUS_OK;
-    op->data.send_status_from_server.status_details = "Backend server out a-ok";
+    grpc_slice status_details =
+        grpc_slice_from_static_string("Backend server out a-ok");
+    op->data.send_status_from_server.status_details = &status_details;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -462,8 +465,7 @@
   grpc_metadata_array trailing_metadata_recv;
   grpc_status_code status;
   grpc_call_error error;
-  char *details = NULL;
-  size_t details_capacity = 0;
+  grpc_slice details;
   grpc_byte_buffer *request_payload;
   grpc_byte_buffer *response_payload_recv;
   int i;
@@ -472,9 +474,11 @@
   grpc_slice request_payload_slice =
       grpc_slice_from_copied_string("hello world");
 
+  grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr:1234");
   c = grpc_channel_create_call(cf->client, NULL, GRPC_PROPAGATE_DEFAULTS,
-                               cf->cq, "/foo", "foo.test.google.fr:1234",
-                               GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL);
+                               cf->cq, grpc_slice_from_static_string("/foo"),
+                               &host, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5),
+                               NULL);
   gpr_log(GPR_INFO, "Call 0x%" PRIxPTR " created", (intptr_t)c);
   GPR_ASSERT(c);
   char *peer;
@@ -489,7 +493,7 @@
   op->reserved = NULL;
   op++;
   op->op = GRPC_OP_RECV_INITIAL_METADATA;
-  op->data.recv_initial_metadata = &initial_metadata_recv;
+  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -497,7 +501,6 @@
   op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
   op->data.recv_status_on_client.status = &status;
   op->data.recv_status_on_client.status_details = &details;
-  op->data.recv_status_on_client.status_details_capacity = &details_capacity;
   op->flags = 0;
   op->reserved = NULL;
   op++;
@@ -509,12 +512,12 @@
 
     op = ops;
     op->op = GRPC_OP_SEND_MESSAGE;
-    op->data.send_message = request_payload;
+    op->data.send_message.send_message = request_payload;
     op->flags = 0;
     op->reserved = NULL;
     op++;
     op->op = GRPC_OP_RECV_MESSAGE;
-    op->data.recv_message = &response_payload_recv;
+    op->data.recv_message.recv_message = &response_payload_recv;
     op->flags = 0;
     op->reserved = NULL;
     op++;
@@ -553,7 +556,7 @@
 
   grpc_metadata_array_destroy(&initial_metadata_recv);
   grpc_metadata_array_destroy(&trailing_metadata_recv);
-  gpr_free(details);
+  grpc_slice_unref(details);
   gpr_log(GPR_INFO, "Client call (peer %s) DESTROYED.", peer);
   gpr_free(peer);
 }
diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc
index 3265554..1df2fc8 100644
--- a/test/cpp/interop/client.cc
+++ b/test/cpp/interop/client.cc
@@ -32,6 +32,7 @@
  */
 
 #include <memory>
+#include <unordered_map>
 
 #include <unistd.h>
 
@@ -108,119 +109,78 @@
   grpc::testing::InteropClient client(CreateChannelForTestCase(FLAGS_test_case),
                                       true,
                                       FLAGS_do_not_abort_on_transient_failures);
-  if (FLAGS_test_case == "empty_unary") {
-    client.DoEmpty();
-  } else if (FLAGS_test_case == "large_unary") {
-    client.DoLargeUnary();
-  } else if (FLAGS_test_case == "server_compressed_unary") {
-    client.DoServerCompressedUnary();
-  } else if (FLAGS_test_case == "client_compressed_unary") {
-    client.DoClientCompressedUnary();
-  } else if (FLAGS_test_case == "client_streaming") {
-    client.DoRequestStreaming();
-  } else if (FLAGS_test_case == "server_streaming") {
-    client.DoResponseStreaming();
-  } else if (FLAGS_test_case == "server_compressed_streaming") {
-    client.DoServerCompressedStreaming();
-  } else if (FLAGS_test_case == "client_compressed_streaming") {
-    client.DoClientCompressedStreaming();
-  } else if (FLAGS_test_case == "slow_consumer") {
-    client.DoResponseStreamingWithSlowConsumer();
-  } else if (FLAGS_test_case == "half_duplex") {
-    client.DoHalfDuplex();
-  } else if (FLAGS_test_case == "ping_pong") {
-    client.DoPingPong();
-  } else if (FLAGS_test_case == "cancel_after_begin") {
-    client.DoCancelAfterBegin();
-  } else if (FLAGS_test_case == "cancel_after_first_response") {
-    client.DoCancelAfterFirstResponse();
-  } else if (FLAGS_test_case == "timeout_on_sleeping_server") {
-    client.DoTimeoutOnSleepingServer();
-  } else if (FLAGS_test_case == "empty_stream") {
-    client.DoEmptyStream();
-  } else if (FLAGS_test_case == "compute_engine_creds") {
-    client.DoComputeEngineCreds(FLAGS_default_service_account,
-                                FLAGS_oauth_scope);
-  } else if (FLAGS_test_case == "jwt_token_creds") {
-    grpc::string json_key = GetServiceAccountJsonKey();
-    client.DoJwtTokenCreds(json_key);
-  } else if (FLAGS_test_case == "oauth2_auth_token") {
-    client.DoOauth2AuthToken(FLAGS_default_service_account, FLAGS_oauth_scope);
-  } else if (FLAGS_test_case == "per_rpc_creds") {
-    grpc::string json_key = GetServiceAccountJsonKey();
-    client.DoPerRpcCreds(json_key);
-  } else if (FLAGS_test_case == "status_code_and_message") {
-    client.DoStatusWithMessage();
-  } else if (FLAGS_test_case == "custom_metadata") {
-    client.DoCustomMetadata();
-  } else if (FLAGS_test_case == "unimplemented_method") {
-    client.DoUnimplementedMethod();
-  } else if (FLAGS_test_case == "unimplemented_service") {
-    client.DoUnimplementedService();
-  } else if (FLAGS_test_case == "cacheable_unary") {
-    client.DoCacheableUnary();
-  } else if (FLAGS_test_case == "all") {
-    client.DoEmpty();
-    client.DoLargeUnary();
-    client.DoClientCompressedUnary();
-    client.DoServerCompressedUnary();
-    client.DoRequestStreaming();
-    client.DoResponseStreaming();
-    client.DoClientCompressedStreaming();
-    client.DoServerCompressedStreaming();
-    client.DoHalfDuplex();
-    client.DoPingPong();
-    client.DoCancelAfterBegin();
-    client.DoCancelAfterFirstResponse();
-    client.DoTimeoutOnSleepingServer();
-    client.DoEmptyStream();
-    client.DoStatusWithMessage();
-    client.DoCustomMetadata();
-    client.DoUnimplementedMethod();
-    client.DoUnimplementedService();
-    client.DoCacheableUnary();
-    // service_account_creds and jwt_token_creds can only run with ssl.
-    if (FLAGS_use_tls) {
-      grpc::string json_key = GetServiceAccountJsonKey();
-      client.DoJwtTokenCreds(json_key);
-      client.DoOauth2AuthToken(FLAGS_default_service_account,
-                               FLAGS_oauth_scope);
-      client.DoPerRpcCreds(json_key);
-    }
-    // compute_engine_creds only runs in GCE.
-  } else {
-    const char* testcases[] = {"all",
-                               "cacheable_unary",
-                               "cancel_after_begin",
-                               "cancel_after_first_response",
-                               "client_compressed_streaming",
-                               "client_compressed_unary",
-                               "client_streaming",
-                               "compute_engine_creds",
-                               "custom_metadata",
-                               "empty_stream",
-                               "empty_unary",
-                               "half_duplex",
-                               "jwt_token_creds",
-                               "large_unary",
-                               "oauth2_auth_token",
-                               "oauth2_auth_token",
-                               "per_rpc_creds",
-                               "per_rpc_creds",
-                               "ping_pong",
-                               "server_compressed_streaming",
-                               "server_compressed_unary",
-                               "server_streaming",
-                               "status_code_and_message",
-                               "timeout_on_sleeping_server",
-                               "unimplemented_method",
-                               "unimplemented_service"};
-    char* joined_testcases =
-        gpr_strjoin_sep(testcases, GPR_ARRAY_SIZE(testcases), "\n", NULL);
 
+  std::unordered_map<grpc::string, std::function<bool()>> actions;
+  actions["empty_unary"] =
+      std::bind(&grpc::testing::InteropClient::DoEmpty, &client);
+  actions["large_unary"] =
+      std::bind(&grpc::testing::InteropClient::DoLargeUnary, &client);
+  actions["server_compressed_unary"] = std::bind(
+      &grpc::testing::InteropClient::DoServerCompressedUnary, &client);
+  actions["client_compressed_unary"] = std::bind(
+      &grpc::testing::InteropClient::DoClientCompressedUnary, &client);
+  actions["client_streaming"] =
+      std::bind(&grpc::testing::InteropClient::DoRequestStreaming, &client);
+  actions["server_streaming"] =
+      std::bind(&grpc::testing::InteropClient::DoResponseStreaming, &client);
+  actions["server_compressed_streaming"] = std::bind(
+      &grpc::testing::InteropClient::DoServerCompressedStreaming, &client);
+  actions["client_compressed_streaming"] = std::bind(
+      &grpc::testing::InteropClient::DoClientCompressedStreaming, &client);
+  actions["slow_consumer"] = std::bind(
+      &grpc::testing::InteropClient::DoResponseStreamingWithSlowConsumer,
+      &client);
+  actions["half_duplex"] =
+      std::bind(&grpc::testing::InteropClient::DoHalfDuplex, &client);
+  actions["ping_pong"] =
+      std::bind(&grpc::testing::InteropClient::DoPingPong, &client);
+  actions["cancel_after_begin"] =
+      std::bind(&grpc::testing::InteropClient::DoCancelAfterBegin, &client);
+  actions["cancel_after_first_response"] = std::bind(
+      &grpc::testing::InteropClient::DoCancelAfterFirstResponse, &client);
+  actions["timeout_on_sleeping_server"] = std::bind(
+      &grpc::testing::InteropClient::DoTimeoutOnSleepingServer, &client);
+  actions["empty_stream"] =
+      std::bind(&grpc::testing::InteropClient::DoEmptyStream, &client);
+  if (FLAGS_use_tls) {
+    actions["compute_engine_creds"] =
+        std::bind(&grpc::testing::InteropClient::DoComputeEngineCreds, &client,
+                  FLAGS_default_service_account, FLAGS_oauth_scope);
+    actions["jwt_token_creds"] =
+        std::bind(&grpc::testing::InteropClient::DoJwtTokenCreds, &client,
+                  GetServiceAccountJsonKey());
+    actions["oauth2_auth_token"] =
+        std::bind(&grpc::testing::InteropClient::DoOauth2AuthToken, &client,
+                  FLAGS_default_service_account, FLAGS_oauth_scope);
+    actions["per_rpc_creds"] =
+        std::bind(&grpc::testing::InteropClient::DoPerRpcCreds, &client,
+                  GetServiceAccountJsonKey());
+  }
+  actions["status_code_and_message"] =
+      std::bind(&grpc::testing::InteropClient::DoStatusWithMessage, &client);
+  actions["custom_metadata"] =
+      std::bind(&grpc::testing::InteropClient::DoCustomMetadata, &client);
+  actions["unimplemented_method"] =
+      std::bind(&grpc::testing::InteropClient::DoUnimplementedMethod, &client);
+  actions["unimplemented_service"] =
+      std::bind(&grpc::testing::InteropClient::DoUnimplementedService, &client);
+  // actions["cacheable_unary"] =
+  //    std::bind(&grpc::testing::InteropClient::DoCacheableUnary, &client);
+
+  if (FLAGS_test_case == "all") {
+    for (const auto& action : actions) {
+      action.second();
+    }
+  } else if (actions.find(FLAGS_test_case) != actions.end()) {
+    actions.find(FLAGS_test_case)->second();
+  } else {
+    grpc::string test_cases;
+    for (const auto& action : actions) {
+      if (!test_cases.empty()) test_cases += "\n";
+      test_cases += action.first;
+    }
     gpr_log(GPR_ERROR, "Unsupported test case %s. Valid options are\n%s",
-            FLAGS_test_case.c_str(), joined_testcases);
-    gpr_free(joined_testcases);
+            FLAGS_test_case.c_str(), test_cases.c_str());
     ret = 1;
   }
 
diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc
index d124262..aa34d94 100644
--- a/test/cpp/interop/interop_client.cc
+++ b/test/cpp/interop/interop_client.cc
@@ -109,7 +109,10 @@
 
 UnimplementedService::Stub*
 InteropClient::ServiceStub::GetUnimplementedServiceStub() {
-  return UnimplementedService::NewStub(channel_).get();
+  if (unimplemented_service_stub_ == nullptr) {
+    unimplemented_service_stub_ = UnimplementedService::NewStub(channel_);
+  }
+  return unimplemented_service_stub_.get();
 }
 
 void InteropClient::ServiceStub::Reset(std::shared_ptr<Channel> channel) {
@@ -943,7 +946,7 @@
     const auto& server_initial_metadata = context.GetServerInitialMetadata();
     auto iter = server_initial_metadata.find(kEchoInitialMetadataKey);
     GPR_ASSERT(iter != server_initial_metadata.end());
-    GPR_ASSERT(iter->second.data() == kInitialMetadataValue);
+    GPR_ASSERT(iter->second == kInitialMetadataValue);
     const auto& server_trailing_metadata = context.GetServerTrailingMetadata();
     iter = server_trailing_metadata.find(kEchoTrailingBinMetadataKey);
     GPR_ASSERT(iter != server_trailing_metadata.end());
@@ -994,7 +997,7 @@
     const auto& server_initial_metadata = context.GetServerInitialMetadata();
     auto iter = server_initial_metadata.find(kEchoInitialMetadataKey);
     GPR_ASSERT(iter != server_initial_metadata.end());
-    GPR_ASSERT(iter->second.data() == kInitialMetadataValue);
+    GPR_ASSERT(iter->second == kInitialMetadataValue);
     const auto& server_trailing_metadata = context.GetServerTrailingMetadata();
     iter = server_trailing_metadata.find(kEchoTrailingBinMetadataKey);
     GPR_ASSERT(iter != server_trailing_metadata.end());
diff --git a/test/cpp/interop/interop_client.h b/test/cpp/interop/interop_client.h
index 7ec7ebe..74f4db6 100644
--- a/test/cpp/interop/interop_client.h
+++ b/test/cpp/interop/interop_client.h
@@ -107,6 +107,7 @@
 
    private:
     std::unique_ptr<TestService::Stub> stub_;
+    std::unique_ptr<UnimplementedService::Stub> unimplemented_service_stub_;
     std::shared_ptr<Channel> channel_;
     bool new_stub_every_call_;  // If true, a new stub is returned by every
                                 // Get() call
diff --git a/test/cpp/interop/interop_server.cc b/test/cpp/interop/interop_server.cc
index 956840b..1810cd0 100644
--- a/test/cpp/interop/interop_server.cc
+++ b/test/cpp/interop/interop_server.cc
@@ -91,7 +91,9 @@
 
   auto iter = client_metadata.find(kEchoInitialMetadataKey);
   if (iter != client_metadata.end()) {
-    context->AddInitialMetadata(kEchoInitialMetadataKey, iter->second.data());
+    context->AddInitialMetadata(
+        kEchoInitialMetadataKey,
+        grpc::string(iter->second.begin(), iter->second.end()));
   }
   iter = client_metadata.find(kEchoTrailingBinMetadataKey);
   if (iter != client_metadata.end()) {
diff --git a/test/cpp/microbenchmarks/bm_fullstack.cc b/test/cpp/microbenchmarks/bm_fullstack.cc
index 6c0bf80..bd158db 100644
--- a/test/cpp/microbenchmarks/bm_fullstack.cc
+++ b/test/cpp/microbenchmarks/bm_fullstack.cc
@@ -443,6 +443,8 @@
                    Server_AddInitialMetadata<RandomAsciiMetadata<31>, 1>);
 BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
                    Server_AddInitialMetadata<RandomAsciiMetadata<100>, 1>);
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
+                   Server_AddInitialMetadata<RandomAsciiMetadata<10>, 100>);
 
 }  // namespace testing
 }  // namespace grpc
diff --git a/test/cpp/test/server_context_test_spouse_test.cc b/test/cpp/test/server_context_test_spouse_test.cc
index e0d6a2f..eb279e2 100644
--- a/test/cpp/test/server_context_test_spouse_test.cc
+++ b/test/cpp/test/server_context_test_spouse_test.cc
@@ -36,11 +36,14 @@
 #include <cstring>
 #include <vector>
 
+#include <grpc++/impl/grpc_library.h>
 #include <gtest/gtest.h>
 
 namespace grpc {
 namespace testing {
 
+static internal::GrpcLibraryInitializer g_initializer;
+
 const char key1[] = "metadata-key1";
 const char key2[] = "metadata-key2";
 const char val1[] = "metadata-val1";
diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py
index cf3762d..4703708 100755
--- a/tools/codegen/core/gen_static_metadata.py
+++ b/tools/codegen/core/gen_static_metadata.py
@@ -31,8 +31,12 @@
 
 import hashlib
 import itertools
+import collections
 import os
 import sys
+import subprocess
+import re
+import perfection
 
 # configuration: a list of either strings or 2-tuples of strings
 # a single string represents a static grpc_mdstr
@@ -40,6 +44,8 @@
 # also be created)
 
 CONFIG = [
+    # metadata strings
+    'host',
     'grpc-timeout',
     'grpc-internal-encoding-request',
     'grpc-payload-bin',
@@ -48,12 +54,19 @@
     'grpc-accept-encoding',
     'user-agent',
     ':authority',
-    'host',
     'grpc-message',
     'grpc-status',
     'grpc-tracing-bin',
     'grpc-stats-bin',
     '',
+    # channel arg keys
+    'grpc.wait_for_ready',
+    'grpc.timeout',
+    'grpc.max_request_message_bytes',
+    'grpc.max_response_message_bytes',
+    # well known method names
+    '/grpc.lb.v1.LoadBalancer/BalanceLoad',
+    # metadata elements
     ('grpc-status', '0'),
     ('grpc-status', '1'),
     ('grpc-status', '2'),
@@ -130,6 +143,26 @@
     ('www-authenticate', ''),
 ]
 
+METADATA_BATCH_CALLOUTS = [
+    ':path',
+    ':method',
+    ':status',
+    ':authority',
+    ':scheme',
+    'te',
+    'grpc-message',
+    'grpc-status',
+    'grpc-payload-bin',
+    'grpc-encoding',
+    'grpc-accept-encoding',
+    'content-type',
+    'grpc-internal-encoding-request',
+    'user-agent',
+    'host',
+    'lb-token',
+    'lb-cost-bin',
+]
+
 COMPRESSION_ALGORITHMS = [
     'identity',
     'deflate',
@@ -137,7 +170,7 @@
 ]
 
 # utility: mangle the name of a config
-def mangle(elem):
+def mangle(elem, name=None):
   xl = {
       '-': '_',
       ':': '',
@@ -162,10 +195,14 @@
         r += put
     if r[-1] == '_': r = r[:-1]
     return r
+  def n(default, name=name):
+    if name is None: return 'grpc_%s_' % default
+    if name == '': return ''
+    return 'grpc_%s_' % name
   if isinstance(elem, tuple):
-    return 'grpc_mdelem_%s_%s' % (m0(elem[0]), m0(elem[1]))
+    return '%s%s_%s' % (n('mdelem'), m0(elem[0]), m0(elem[1]))
   else:
-    return 'grpc_mdstr_%s' % (m0(elem))
+    return '%s%s' % (n('mdstr'), m0(elem))
 
 # utility: generate some hash value for a string
 def fake_hash(elem):
@@ -181,28 +218,37 @@
     print >>f
 
 # build a list of all the strings we need
-all_strs = set()
-all_elems = set()
+all_strs = list()
+all_elems = list()
 static_userdata = {}
+# put metadata batch callouts first, to make the check of if a static metadata
+# string is a callout trivial
+for elem in METADATA_BATCH_CALLOUTS:
+  if elem not in all_strs:
+    all_strs.append(elem)
 for elem in CONFIG:
   if isinstance(elem, tuple):
-    all_strs.add(elem[0])
-    all_strs.add(elem[1])
-    all_elems.add(elem)
+    if elem[0] not in all_strs:
+      all_strs.append(elem[0])
+    if elem[1] not in all_strs:
+      all_strs.append(elem[1])
+    if elem not in all_elems:
+      all_elems.append(elem)
   else:
-    all_strs.add(elem)
+    if elem not in all_strs:
+      all_strs.append(elem)
 compression_elems = []
 for mask in range(1, 1<<len(COMPRESSION_ALGORITHMS)):
   val = ','.join(COMPRESSION_ALGORITHMS[alg]
                  for alg in range(0, len(COMPRESSION_ALGORITHMS))
                  if (1 << alg) & mask)
   elem = ('grpc-accept-encoding', val)
-  all_strs.add(val)
-  all_elems.add(elem)
+  if val not in all_strs:
+    all_strs.append(val)
+  if elem not in all_elems:
+    all_elems.append(elem)
   compression_elems.append(elem)
   static_userdata[elem] = 1 + (mask | 1)
-all_strs = sorted(list(all_strs), key=mangle)
-all_elems = sorted(list(all_elems), key=mangle)
 
 # output configuration
 args = sys.argv[1:]
@@ -279,15 +325,52 @@
 
 print >>C, '#include "src/core/lib/transport/static_metadata.h"'
 print >>C
+print >>C, '#include "src/core/lib/slice/slice_internal.h"'
+print >>C
+
+str_ofs = 0
+id2strofs = {}
+for i, elem in enumerate(all_strs):
+  id2strofs[i] = str_ofs
+  str_ofs += len(elem);
+def slice_def(i):
+  return '{.refcount = &grpc_static_metadata_refcounts[%d], .data.refcounted = {g_bytes+%d, %d}}' % (i, id2strofs[i], len(all_strs[i]))
+
+# validate configuration
+for elem in METADATA_BATCH_CALLOUTS:
+  assert elem in all_strs
 
 print >>H, '#define GRPC_STATIC_MDSTR_COUNT %d' % len(all_strs)
-print >>H, 'extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];'
+print >>H, 'extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT];'
 for i, elem in enumerate(all_strs):
   print >>H, '/* "%s" */' % elem
-  print >>H, '#define %s (&grpc_static_mdstr_table[%d])' % (mangle(elem).upper(), i)
+  print >>H, '#define %s (grpc_static_slice_table[%d])' % (mangle(elem).upper(), i)
 print >>H
-print >>C, 'grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];'
+print >>C, 'static uint8_t g_bytes[] = {%s};' % (','.join('%d' % ord(c) for c in ''.join(all_strs)))
 print >>C
+print >>C, 'static void static_ref(void *unused) {}'
+print >>C, 'static void static_unref(grpc_exec_ctx *exec_ctx, void *unused) {}'
+print >>C, 'static const grpc_slice_refcount_vtable static_sub_vtable = {static_ref, static_unref, grpc_slice_default_eq_impl, grpc_slice_default_hash_impl};';
+print >>H, 'extern const grpc_slice_refcount_vtable grpc_static_metadata_vtable;';
+print >>C, 'const grpc_slice_refcount_vtable grpc_static_metadata_vtable = {static_ref, static_unref, grpc_static_slice_eq, grpc_static_slice_hash};';
+print >>C, 'static grpc_slice_refcount static_sub_refcnt = {&static_sub_vtable, &static_sub_refcnt};';
+print >>H, 'extern grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT];'
+print >>C, 'grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = {'
+for i, elem in enumerate(all_strs):
+  print >>C, '  {&grpc_static_metadata_vtable, &static_sub_refcnt},'
+print >>C, '};'
+print >>C
+print >>H, '#define GRPC_IS_STATIC_METADATA_STRING(slice) \\'
+print >>H, '  ((slice).refcount != NULL && (slice).refcount->vtable == &grpc_static_metadata_vtable)'
+print >>H
+print >>C, 'const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = {'
+for i, elem in enumerate(all_strs):
+  print >>C, slice_def(i) + ','
+print >>C, '};'
+print >>C
+print >>H, '#define GRPC_STATIC_METADATA_INDEX(static_slice) \\'
+print >>H, '  ((int)((static_slice).refcount - grpc_static_metadata_refcounts))'
+print >>H
 
 print >>D, '# hpack fuzzing dictionary'
 for i, elem in enumerate(all_strs):
@@ -297,13 +380,12 @@
                               [len(elem[1])] + [ord(c) for c in elem[1]]))
 
 print >>H, '#define GRPC_STATIC_MDELEM_COUNT %d' % len(all_elems)
-print >>H, 'extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];'
+print >>H, 'extern grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];'
 print >>H, 'extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];'
 for i, elem in enumerate(all_elems):
   print >>H, '/* "%s": "%s" */' % elem
-  print >>H, '#define %s (&grpc_static_mdelem_table[%d])' % (mangle(elem).upper(), i)
+  print >>H, '#define %s (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[%d], GRPC_MDELEM_STORAGE_STATIC))' % (mangle(elem).upper(), i)
 print >>H
-print >>C, 'grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];'
 print >>C, 'uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = {'
 print >>C, '  %s' % ','.join('%d' % static_userdata.get(elem, 0) for elem in all_elems)
 print >>C, '};'
@@ -319,25 +401,100 @@
     if m == m2:
       return i
 
-print >>H, 'extern const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT*2];'
-print >>C, 'const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT*2] = {'
-print >>C, ','.join('%d' % str_idx(x) for x in itertools.chain.from_iterable([a,b] for a, b in all_elems))
-print >>C, '};'
+def offset_trials(mink):
+  yield 0
+  for i in range(1, 100):
+    for mul in [-1, 1]:
+      yield mul * i
+
+def perfect_hash(keys, name):
+  p = perfection.hash_parameters(keys)
+  def f(i, p=p):
+    i += p.offset
+    x = i % p.t
+    y = i / p.t
+    return x + p.r[y]
+  return {
+    'PHASHRANGE': p.t - 1 + max(p.r),
+    'PHASHNKEYS': len(p.slots),
+    'pyfunc': f,
+    'code': """
+static const int8_t %(name)s_r[] = {%(r)s};
+static uint32_t %(name)s_phash(uint32_t i) {
+  i %(offset_sign)s= %(offset)d;
+  uint32_t x = i %% %(t)d;
+  uint32_t y = i / %(t)d;
+  uint32_t h = x;
+  if (y < GPR_ARRAY_SIZE(%(name)s_r)) {
+    uint32_t delta = (uint32_t)%(name)s_r[y];
+    h += delta;
+  }
+  return h;
+}
+    """ % {
+      'name': name,
+      'r': ','.join('%d' % (r if r is not None else 0) for r in p.r),
+      't': p.t,
+      'offset': abs(p.offset),
+      'offset_sign': '+' if p.offset > 0 else '-'
+    }
+  }
+
+
+elem_keys = [str_idx(elem[0]) * len(all_strs) + str_idx(elem[1]) for elem in all_elems]
+elem_hash = perfect_hash(elem_keys, "elems")
+print >>C, elem_hash['code']
+
+keys = [0] * int(elem_hash['PHASHRANGE'])
+idxs = [255] * int(elem_hash['PHASHNKEYS'])
+for i, k in enumerate(elem_keys):
+    h = elem_hash['pyfunc'](k)
+    assert keys[h] == 0
+    keys[h] = k
+    idxs[h] = i
+print >>C, 'static const uint16_t elem_keys[] = {%s};' % ','.join('%d' % k for k in keys)
+print >>C, 'static const uint8_t elem_idxs[] = {%s};' % ','.join('%d' % i for i in idxs)
 print >>C
 
-print >>H, 'extern const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];'
-print >>C, 'const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {'
-print >>C, '%s' % ',\n'.join('  "%s"' % s for s in all_strs)
-print >>C, '};'
+print >>H, 'grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b);'
+print >>C, 'grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) {'
+print >>C, '  if (a == -1 || b == -1) return GRPC_MDNULL;'
+print >>C, '  uint32_t k = (uint32_t)(a * %d + b);' % len(all_strs)
+print >>C, '  uint32_t h = elems_phash(k);'
+print >>C, '  return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k ? GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[elem_idxs[h]], GRPC_MDELEM_STORAGE_STATIC) : GRPC_MDNULL;'
+print >>C, '}'
 print >>C
 
+print >>C, 'grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = {'
+for a, b in all_elems:
+  print >>C, '{%s,%s},' % (slice_def(str_idx(a)), slice_def(str_idx(b)))
+print >>C, '};'
+
+print >>H, 'typedef enum {'
+for elem in METADATA_BATCH_CALLOUTS:
+  print >>H, '  %s,' % mangle(elem, 'batch').upper()
+print >>H, '  GRPC_BATCH_CALLOUTS_COUNT'
+print >>H, '} grpc_metadata_batch_callouts_index;'
+print >>H
+print >>H, 'typedef union {'
+print >>H, '  struct grpc_linked_mdelem *array[GRPC_BATCH_CALLOUTS_COUNT];'
+print >>H, '  struct {'
+for elem in METADATA_BATCH_CALLOUTS:
+  print >>H, '  struct grpc_linked_mdelem *%s;' % mangle(elem, '').lower()
+print >>H, '  } named;'
+print >>H, '} grpc_metadata_batch_callouts;'
+print >>H
+print >>H, '#define GRPC_BATCH_INDEX_OF(slice) \\'
+print >>H, '  (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)'
+print >>H
+
 print >>H, 'extern const uint8_t grpc_static_accept_encoding_metadata[%d];' % (1 << len(COMPRESSION_ALGORITHMS))
 print >>C, 'const uint8_t grpc_static_accept_encoding_metadata[%d] = {' % (1 << len(COMPRESSION_ALGORITHMS))
 print >>C, '0,%s' % ','.join('%d' % md_idx(elem) for elem in compression_elems)
 print >>C, '};'
 print >>C
 
-print >>H, '#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) (&grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]])'
+print >>H, '#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))'
 
 print >>H, '#endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */'
 
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index af844d6..128a409 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -822,6 +822,7 @@
 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/status_helper.h \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 2b2d8a3..5bd4323 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -823,6 +823,7 @@
 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/status_helper.h \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 30d4e8f..106817d 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -987,15 +987,12 @@
 src/core/ext/transport/chttp2/transport/hpack_parser.h \
 src/core/ext/transport/chttp2/transport/hpack_table.c \
 src/core/ext/transport/chttp2/transport/hpack_table.h \
-src/core/ext/transport/chttp2/transport/http2_errors.h \
 src/core/ext/transport/chttp2/transport/huffsyms.c \
 src/core/ext/transport/chttp2/transport/huffsyms.h \
 src/core/ext/transport/chttp2/transport/incoming_metadata.c \
 src/core/ext/transport/chttp2/transport/incoming_metadata.h \
 src/core/ext/transport/chttp2/transport/internal.h \
 src/core/ext/transport/chttp2/transport/parsing.c \
-src/core/ext/transport/chttp2/transport/status_conversion.c \
-src/core/ext/transport/chttp2/transport/status_conversion.h \
 src/core/ext/transport/chttp2/transport/stream_lists.c \
 src/core/ext/transport/chttp2/transport/stream_map.c \
 src/core/ext/transport/chttp2/transport/stream_map.h \
@@ -1055,6 +1052,7 @@
 src/core/lib/iomgr/endpoint_pair_windows.c \
 src/core/lib/iomgr/error.c \
 src/core/lib/iomgr/error.h \
+src/core/lib/iomgr/error_internal.h \
 src/core/lib/iomgr/ev_epoll_linux.c \
 src/core/lib/iomgr/ev_epoll_linux.h \
 src/core/lib/iomgr/ev_poll_posix.c \
@@ -1210,6 +1208,9 @@
 src/core/lib/slice/percent_encoding.h \
 src/core/lib/slice/slice.c \
 src/core/lib/slice/slice_buffer.c \
+src/core/lib/slice/slice_hash_table.c \
+src/core/lib/slice/slice_hash_table.h \
+src/core/lib/slice/slice_intern.c \
 src/core/lib/slice/slice_internal.h \
 src/core/lib/slice/slice_string_helpers.c \
 src/core/lib/slice/slice_string_helpers.h \
@@ -1297,14 +1298,16 @@
 src/core/lib/surface/server.c \
 src/core/lib/surface/server.h \
 src/core/lib/surface/validate_metadata.c \
+src/core/lib/surface/validate_metadata.h \
 src/core/lib/surface/version.c \
 src/core/lib/transport/README.md \
 src/core/lib/transport/byte_stream.c \
 src/core/lib/transport/byte_stream.h \
 src/core/lib/transport/connectivity_state.c \
 src/core/lib/transport/connectivity_state.h \
-src/core/lib/transport/mdstr_hash_table.c \
-src/core/lib/transport/mdstr_hash_table.h \
+src/core/lib/transport/error_utils.c \
+src/core/lib/transport/error_utils.h \
+src/core/lib/transport/http2_errors.h \
 src/core/lib/transport/metadata.c \
 src/core/lib/transport/metadata.h \
 src/core/lib/transport/metadata_batch.c \
@@ -1315,6 +1318,8 @@
 src/core/lib/transport/service_config.h \
 src/core/lib/transport/static_metadata.c \
 src/core/lib/transport/static_metadata.h \
+src/core/lib/transport/status_conversion.c \
+src/core/lib/transport/status_conversion.h \
 src/core/lib/transport/timeout_encoding.c \
 src/core/lib/transport/timeout_encoding.h \
 src/core/lib/transport/transport.c \
diff --git a/tools/jenkins/run_interop.sh b/tools/jenkins/run_interop.sh
index 4a7bff3..176ee18 100755
--- a/tools/jenkins/run_interop.sh
+++ b/tools/jenkins/run_interop.sh
@@ -37,3 +37,5 @@
 cd $(dirname $0)/../..
 
 tools/run_tests/run_interop_tests.py -l all -s all --cloud_to_prod --cloud_to_prod_auth --use_docker --http2_interop -t -j 12 $@ || true
+tools/run_tests/run_interop_tests.py -l java --use_docker --http2_badserver_interop $@ || true
+tools/run_tests/run_interop_tests.py -l python --use_docker --http2_badserver_interop $@ || true
diff --git a/tools/run_tests/generated/configs.json b/tools/run_tests/generated/configs.json
index b0839ef..f2c1107 100644
--- a/tools/run_tests/generated/configs.json
+++ b/tools/run_tests/generated/configs.json
@@ -3,37 +3,6 @@
     "config": "opt"
   }, 
   {
-    "config": "asan-trace-cmp", 
-    "environ": {
-      "ASAN_OPTIONS": "detect_leaks=1:color=always", 
-      "LSAN_OPTIONS": "suppressions=tools/lsan_suppressions.txt:report_objects=1"
-    }, 
-    "timeout_multiplier": 3
-  }, 
-  {
-    "config": "dbg"
-  }, 
-  {
-    "config": "easan", 
-    "environ": {
-      "ASAN_OPTIONS": "detect_leaks=1:color=always", 
-      "LSAN_OPTIONS": "suppressions=tools/lsan_suppressions.txt:report_objects=1"
-    }, 
-    "timeout_multiplier": 3
-  }, 
-  {
-    "config": "asan", 
-    "environ": {
-      "ASAN_OPTIONS": "detect_leaks=1:color=always", 
-      "LSAN_OPTIONS": "suppressions=tools/lsan_suppressions.txt:report_objects=1"
-    }, 
-    "timeout_multiplier": 3
-  }, 
-  {
-    "config": "msan", 
-    "timeout_multiplier": 4
-  }, 
-  {
     "config": "basicprof"
   }, 
   {
@@ -52,27 +21,21 @@
     "timeout_multiplier": 3
   }, 
   {
-    "config": "edbg"
+    "config": "asan-trace-cmp", 
+    "environ": {
+      "ASAN_OPTIONS": "detect_leaks=1:color=always", 
+      "LSAN_OPTIONS": "suppressions=tools/lsan_suppressions.txt:report_objects=1"
+    }, 
+    "timeout_multiplier": 3
   }, 
   {
-    "config": "ubsan", 
-    "environ": {
-      "UBSAN_OPTIONS": "halt_on_error=1:print_stacktrace=1"
-    }, 
-    "timeout_multiplier": 1.5
-  }, 
-  {
-    "config": "tsan", 
-    "environ": {
-      "TSAN_OPTIONS": "suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1"
-    }, 
-    "timeout_multiplier": 5
+    "config": "dbg"
   }, 
   {
     "config": "stapprof"
   }, 
   {
-    "config": "mutrace"
+    "config": "gcov"
   }, 
   {
     "config": "memcheck", 
@@ -84,13 +47,32 @@
     ]
   }, 
   {
-    "config": "etsan", 
+    "config": "asan", 
+    "environ": {
+      "ASAN_OPTIONS": "detect_leaks=1:color=always", 
+      "LSAN_OPTIONS": "suppressions=tools/lsan_suppressions.txt:report_objects=1"
+    }, 
+    "timeout_multiplier": 3
+  }, 
+  {
+    "config": "tsan", 
     "environ": {
       "TSAN_OPTIONS": "suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1"
     }, 
     "timeout_multiplier": 5
   }, 
   {
-    "config": "gcov"
+    "config": "ubsan", 
+    "environ": {
+      "UBSAN_OPTIONS": "halt_on_error=1:print_stacktrace=1"
+    }, 
+    "timeout_multiplier": 1.5
+  }, 
+  {
+    "config": "msan", 
+    "timeout_multiplier": 4
+  }, 
+  {
+    "config": "mutrace"
   }
 ]
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 08f4ba4..200a089 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -227,23 +227,6 @@
     "headers": [], 
     "is_filegroup": false, 
     "language": "c", 
-    "name": "chttp2_status_conversion_test", 
-    "src": [
-      "test/core/transport/chttp2/status_conversion_test.c"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
-  {
-    "deps": [
-      "gpr", 
-      "gpr_test_util", 
-      "grpc", 
-      "grpc_test_util"
-    ], 
-    "headers": [], 
-    "is_filegroup": false, 
-    "language": "c", 
     "name": "chttp2_stream_map_test", 
     "src": [
       "test/core/transport/chttp2/stream_map_test.c"
@@ -2027,6 +2010,23 @@
     "headers": [], 
     "is_filegroup": false, 
     "language": "c", 
+    "name": "status_conversion_test", 
+    "src": [
+      "test/core/transport/status_conversion_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
     "name": "tcp_client_posix_test", 
     "src": [
       "test/core/iomgr/tcp_client_posix_test.c"
@@ -7028,6 +7028,7 @@
       "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_epoll_linux.h", 
       "src/core/lib/iomgr/ev_poll_posix.h", 
       "src/core/lib/iomgr/ev_posix.h", 
@@ -7080,6 +7081,7 @@
       "src/core/lib/json/json_reader.h", 
       "src/core/lib/json/json_writer.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/surface/api_trace.h", 
@@ -7093,14 +7095,17 @@
       "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/byte_stream.h", 
       "src/core/lib/transport/connectivity_state.h", 
-      "src/core/lib/transport/mdstr_hash_table.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/timeout_encoding.h", 
       "src/core/lib/transport/transport.h", 
       "src/core/lib/transport/transport_impl.h"
@@ -7167,6 +7172,7 @@
       "src/core/lib/iomgr/endpoint_pair_windows.c", 
       "src/core/lib/iomgr/error.c", 
       "src/core/lib/iomgr/error.h", 
+      "src/core/lib/iomgr/error_internal.h", 
       "src/core/lib/iomgr/ev_epoll_linux.c", 
       "src/core/lib/iomgr/ev_epoll_linux.h", 
       "src/core/lib/iomgr/ev_poll_posix.c", 
@@ -7278,6 +7284,9 @@
       "src/core/lib/slice/percent_encoding.h", 
       "src/core/lib/slice/slice.c", 
       "src/core/lib/slice/slice_buffer.c", 
+      "src/core/lib/slice/slice_hash_table.c", 
+      "src/core/lib/slice/slice_hash_table.h", 
+      "src/core/lib/slice/slice_intern.c", 
       "src/core/lib/slice/slice_internal.h", 
       "src/core/lib/slice/slice_string_helpers.c", 
       "src/core/lib/slice/slice_string_helpers.h", 
@@ -7309,13 +7318,15 @@
       "src/core/lib/surface/server.c", 
       "src/core/lib/surface/server.h", 
       "src/core/lib/surface/validate_metadata.c", 
+      "src/core/lib/surface/validate_metadata.h", 
       "src/core/lib/surface/version.c", 
       "src/core/lib/transport/byte_stream.c", 
       "src/core/lib/transport/byte_stream.h", 
       "src/core/lib/transport/connectivity_state.c", 
       "src/core/lib/transport/connectivity_state.h", 
-      "src/core/lib/transport/mdstr_hash_table.c", 
-      "src/core/lib/transport/mdstr_hash_table.h", 
+      "src/core/lib/transport/error_utils.c", 
+      "src/core/lib/transport/error_utils.h", 
+      "src/core/lib/transport/http2_errors.h", 
       "src/core/lib/transport/metadata.c", 
       "src/core/lib/transport/metadata.h", 
       "src/core/lib/transport/metadata_batch.c", 
@@ -7326,6 +7337,8 @@
       "src/core/lib/transport/service_config.h", 
       "src/core/lib/transport/static_metadata.c", 
       "src/core/lib/transport/static_metadata.h", 
+      "src/core/lib/transport/status_conversion.c", 
+      "src/core/lib/transport/status_conversion.h", 
       "src/core/lib/transport/timeout_encoding.c", 
       "src/core/lib/transport/timeout_encoding.h", 
       "src/core/lib/transport/transport.c", 
@@ -7704,11 +7717,9 @@
       "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_errors.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/status_conversion.h", 
       "src/core/ext/transport/chttp2/transport/stream_map.h", 
       "src/core/ext/transport/chttp2/transport/varint.h"
     ], 
@@ -7742,15 +7753,12 @@
       "src/core/ext/transport/chttp2/transport/hpack_parser.h", 
       "src/core/ext/transport/chttp2/transport/hpack_table.c", 
       "src/core/ext/transport/chttp2/transport/hpack_table.h", 
-      "src/core/ext/transport/chttp2/transport/http2_errors.h", 
       "src/core/ext/transport/chttp2/transport/huffsyms.c", 
       "src/core/ext/transport/chttp2/transport/huffsyms.h", 
       "src/core/ext/transport/chttp2/transport/incoming_metadata.c", 
       "src/core/ext/transport/chttp2/transport/incoming_metadata.h", 
       "src/core/ext/transport/chttp2/transport/internal.h", 
       "src/core/ext/transport/chttp2/transport/parsing.c", 
-      "src/core/ext/transport/chttp2/transport/status_conversion.c", 
-      "src/core/ext/transport/chttp2/transport/status_conversion.h", 
       "src/core/ext/transport/chttp2/transport/stream_lists.c", 
       "src/core/ext/transport/chttp2/transport/stream_map.c", 
       "src/core/ext/transport/chttp2/transport/stream_map.h", 
@@ -8119,6 +8127,7 @@
       "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/status_helper.h", 
@@ -8152,6 +8161,7 @@
       "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/status_helper.h", 
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 815d5d3..174f8c8 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -281,28 +281,6 @@
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
-    "name": "chttp2_status_conversion_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ]
-  }, 
-  {
-    "args": [], 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
     "name": "chttp2_stream_map_test", 
     "platforms": [
       "linux", 
@@ -2056,6 +2034,28 @@
     "ci_platforms": [
       "linux", 
       "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "status_conversion_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
       "posix"
     ], 
     "cpu_cost": 0.5, 
@@ -39157,9 +39157,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39194,9 +39191,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39231,9 +39225,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39268,9 +39259,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39305,9 +39293,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39342,9 +39327,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39379,9 +39361,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39416,9 +39395,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39453,9 +39429,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39490,9 +39463,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39527,9 +39497,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39564,9 +39531,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39601,9 +39565,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39638,9 +39599,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39675,9 +39633,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39712,9 +39667,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39749,9 +39701,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39786,9 +39735,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39823,9 +39769,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39860,9 +39803,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39897,9 +39837,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39934,9 +39871,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -39971,9 +39905,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40008,9 +39939,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40045,9 +39973,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40082,9 +40007,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40119,9 +40041,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40156,9 +40075,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40193,9 +40109,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40230,9 +40143,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40267,9 +40177,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40304,9 +40211,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40341,9 +40245,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -40378,9 +40279,6 @@
       "asan-trace-cmp", 
       "basicprof", 
       "dbg", 
-      "easan", 
-      "edbg", 
-      "etsan", 
       "gcov", 
       "helgrind", 
       "memcheck", 
@@ -41413,6 +41311,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/070aca38b5cd06626dfc98126ef1225595800427"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/070c7005e63abba72c6bc1a0ee6d44e340f2d2be"
     ], 
     "ci_platforms": [
@@ -41743,6 +41663,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/08398518b9b4e98d4625dfb063ab0b6d9399a239"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/08455b3ef9d516deb8155d8db7d51c43ce0ff07a"
     ], 
     "ci_platforms": [
@@ -41919,6 +41861,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/096f9413b1bf712e092cf8bd70754014d6242f12"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/0976de1461fb037c6987d77d088416440b524dde"
     ], 
     "ci_platforms": [
@@ -42073,6 +42037,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/0a85889229b729883f4c0420d1a13d9f0a2ca7c2"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/0a90826e3173642be15ea005c2cbe8ca36ac1c3d"
     ], 
     "ci_platforms": [
@@ -44757,6 +44743,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/1c0dc85faed94bb89bc90f87799d7383cf9907cf"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/1c1a1980a1959423766c5a26ac79d07264224278"
     ], 
     "ci_platforms": [
@@ -46803,6 +46811,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/2a4fc709f61977f0f6c75760af2b33007ecd5941"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/2a600cae342e8e9e23406bb1e76133f48d936766"
     ], 
     "ci_platforms": [
@@ -48695,6 +48725,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/352599b2850ac5a96ec656788c897c4b36114cc4"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/35623259c0d5d73d23ea52efc3e4bd808c119440"
     ], 
     "ci_platforms": [
@@ -48783,6 +48835,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/35f172b0168cd0b2da4509c3463c2502274e01a6"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/3606c0748089f53e252b577ff7ab2df203d098ff"
     ], 
     "ci_platforms": [
@@ -50565,6 +50639,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/424b6a20be32318d920d83aa2a292a0aba013a1b"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/427392659bf3a945097c6c754a17d8c15e23816f"
     ], 
     "ci_platforms": [
@@ -52149,6 +52245,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/4b303d7f245a569f507013af0b3afb2f033b6741"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/4b538eda0a7ad5d38b9d95867b7c181cbe84589b"
     ], 
     "ci_platforms": [
@@ -53447,6 +53565,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/559d537675dd3fc5e0c6d40e94133e9016014f6c"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/55c1b1bd6e532928ad47cadd8e5c5363849d7df5"
     ], 
     "ci_platforms": [
@@ -55251,6 +55391,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/60b4c79213b97204d1e4f6819ad1ed5f6e191789"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/60e8618c075ec5fd47a1699271c6da1b5befd579"
     ], 
     "ci_platforms": [
@@ -56329,6 +56491,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/671d63b9968444308d4908eb6a26bccdf32e5e29"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/6722929b4924f4d50ccfb999460e9a31ca104b4c"
     ], 
     "ci_platforms": [
@@ -56725,6 +56909,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/6ad3a1170ef884a884a6f340101549df624bf5a4"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/6ad7afcf2d12025faf0e1812ee7a0a5d754620c6"
     ], 
     "ci_platforms": [
@@ -57825,6 +58031,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/71961e53698b07bfd57e9ea37067e0dd52298a3d"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/71c01818823d5c5fd8a3d1cb4c5db4aca51efdb2"
     ], 
     "ci_platforms": [
@@ -60267,6 +60495,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/807a48bd33ce375438dbc158c1531891c5b54a51"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/80a249d17248e0dc7dcc9fb64d8ac2dd0320a544"
     ], 
     "ci_platforms": [
@@ -62489,6 +62739,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/8dfdb04ef558fba84dcbfa65a6aa318b10988fa9"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/8e226a7f67b7c6e9d439c3627bfa5644af992593"
     ], 
     "ci_platforms": [
@@ -63831,6 +64103,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/9916b65305d28acf75a17495b9f44d7b839b948f"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/9989534524a212092e9d7fede16106b586c434f4"
     ], 
     "ci_platforms": [
@@ -66075,6 +66369,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/a8c0a166004ee637f22f5838960c8f4320738694"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/a8c9f7043c578e48be49661be5207ceb9ec1b61f"
     ], 
     "ci_platforms": [
@@ -66559,6 +66875,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/abd5de7b5e9218d19a57c47d2ccd6a1bc022c7fb"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/abdb7891569085e3df0f6c7a5348c12bf3dd1ae0"
     ], 
     "ci_platforms": [
@@ -67417,6 +67755,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/b36c3e5841c6aed7b0d439bd3b9c6d922d56c70b"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/b3776ef844b4910a2cd6d149dc13fb57bd523ac3"
     ], 
     "ci_platforms": [
@@ -68297,6 +68657,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/b8bc377e53ca54ae4e30f437b69e270040c35f65"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/b8bedb9c38fd149bc494a65674a4af5e61dfb311"
     ], 
     "ci_platforms": [
@@ -72983,6 +73365,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/crash-60a3f7ed4abddaa6a7c837ace86d048fa15e288b"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/crash-6db86c556caf542fe8c3345ef396467b1d609d32"
     ], 
     "ci_platforms": [
@@ -76085,6 +76489,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/db25df0fa146d02874114b8605000e0135ff12dd"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/db7c4b56e701832634e61cc0b3ab5206fabf518d"
     ], 
     "ci_platforms": [
@@ -80221,6 +80647,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/api_fuzzer_corpus/f503f8d526eaec9bab6cae97d09207deefa85d8d"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "api_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/api_fuzzer_corpus/f5867f7dbacd22878e2955f4be8fca147442aa9d"
     ], 
     "ci_platforms": [
@@ -83433,6 +83881,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/175f16901997fdee41ac9ec88f7e018d46d774e7"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/17b1758fc7cd69a00d140f113b1ac894023ff20b"
     ], 
     "ci_platforms": [
@@ -84665,6 +85135,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/26f250ae38865f030176a8801ce992536351a326"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/2717067bbc0e9bfc1d90d15cddf6154800a25ec6"
     ], 
     "ci_platforms": [
@@ -85589,6 +86081,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/3233f2ab3b6c9431289ef7dc4f40676d0128bcad"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/330dd22142ff48078b189f4533ccc56878d88f92"
     ], 
     "ci_platforms": [
@@ -85765,6 +86279,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/3656614c7b0dc11d4dd7e1826f77fa96a88c420f"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/368c75135a7341a96627d0dcfc4b2081003d8979"
     ], 
     "ci_platforms": [
@@ -86161,6 +86697,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/3dec1981e8677ec6ad2517feba29869d53f8d884"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/3e8f531043a07df2280bca73fe4a7987d82ce67e"
     ], 
     "ci_platforms": [
@@ -86997,6 +87555,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/4bed84590a55b06d7eb30d8faa4b00a881ba9b2c"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/4e05d6cf1c3f0c04f6ee92d09a53ee0fe35c085a"
     ], 
     "ci_platforms": [
@@ -87789,6 +88369,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/591d46b83d2e6babc0ce3c753c4606a10c46d7ce"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/591ef436ef8cc982b48fd827a4555b57cd9780e5"
     ], 
     "ci_platforms": [
@@ -87921,6 +88523,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/5a52d683c9342dd301b0e699db36175aad715e1d"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/5a8cbd42a033b7899383d48c3929e517dafbb995"
     ], 
     "ci_platforms": [
@@ -87943,6 +88567,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/5b7ff7c113c2bcf125271d22c937f758a0cd2ee4"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/5c14b48da74ab06b3cc20c4fe355e24f7dd7852a"
     ], 
     "ci_platforms": [
@@ -88229,6 +88875,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/616e55354ba3517ce6762d6cd56600f97915e646"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/622d46854c2d38b5fe632649d58a69b7da0803c0"
     ], 
     "ci_platforms": [
@@ -89439,6 +90107,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/7a9a12b15c798b88f1a599779f52d14edf9f4ef5"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/7af3156d286a32a6a6fede46d93ec12ded1ac138"
     ], 
     "ci_platforms": [
@@ -90517,6 +91207,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/8f05b8da748cc04e64b688c4b435fe64699bd481"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/8f980dd25f1c77e3536131c2c620aa32e8c13180"
     ], 
     "ci_platforms": [
@@ -90561,6 +91273,50 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/8fd758e2603f1e3772cfcd295ff0f951988d7648"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/9018925b5e791ae4b3ae9c84b8a4d47d86671c2d"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/90a9c3390752b94ca19a58cb2fe6267bc818f718"
     ], 
     "ci_platforms": [
@@ -91067,6 +91823,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/9912de197918ac0e305f0c0107f863823e8ae818"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/998a54dc94ab6e7d6a6066415fb0dd9b52356171"
     ], 
     "ci_platforms": [
@@ -92255,6 +93033,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/ab90bebd4c017827a6d5de61511445df81169eb6"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/abd52da5882855a63632a6917df3639538928cd3"
     ], 
     "ci_platforms": [
@@ -92629,6 +93429,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/b26a3a83d0b319ce13a9ae164e90fa6da1b05a57"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/b283eb8884c98dd50523995ce221aa1ecb3ca182"
     ], 
     "ci_platforms": [
@@ -93003,6 +93825,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/ba25be123c630bd3e4aeb9bda6304dd1a7e51365"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/ba3566735888b53712c6b2e6d52ff5f2197afd6a"
     ], 
     "ci_platforms": [
@@ -93575,6 +94419,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/c77ee0b6cb21ab7b7cf222c7f6563b3b1b1b6eed"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/c784ad2e205ba49b5bb1302746723dbc57320981"
     ], 
     "ci_platforms": [
@@ -93597,6 +94463,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/c7b17d6ae5f3b155532a5a9bf0239c098c35eec1"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/c84da54dacf04445b50448a70fb0ecdd08e9234a"
     ], 
     "ci_platforms": [
@@ -93905,6 +94793,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/ccc36ee2c6a0daad65a4d929599fda5cd38b8fd1"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/cceb4c620c02337138e489383db0d4f4e2c7a722"
     ], 
     "ci_platforms": [
@@ -94191,6 +95101,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/crash-133500314833c9037b34a4c34ad779dd78a0fa8d"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/crash-14ed70cd9ea7987cdd0c8f6e39398ee7c60ee2ff"
     ], 
     "ci_platforms": [
@@ -94697,6 +95629,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/d3141a71cdaa7a0915c8c55abf2c940b0fd00172"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/d3386702918881101368cdba2c4967e86ff3a7b9"
     ], 
     "ci_platforms": [
@@ -95577,6 +96531,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/de9b9a35e1e7040e842b597b8d2fe4cd602e67f1"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/df20bbbb854cb997a73285ef30d227aa12d90e4e"
     ], 
     "ci_platforms": [
@@ -95995,6 +96971,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/e5319a8570a762bbdd67135b49579097324e369a"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/e5a7c086208248a15ee6fa5195fc4ce22469de15"
     ], 
     "ci_platforms": [
@@ -96039,6 +97037,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/e5e276acd665ccb47f868fe3bc36e647bbb840bd"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/e61f728210ce72ed8b2c066bd1b1ecf9e6824b77"
     ], 
     "ci_platforms": [
@@ -96897,6 +97917,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/client_fuzzer_corpus/f69aa5666fd60c1b6814198f12d049abb0e9f148"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "client_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/client_fuzzer_corpus/f6af3f46aacee395877d7f7909f8e412a6538efb"
     ], 
     "ci_platforms": [
@@ -97887,6 +98929,28 @@
   }, 
   {
     "args": [
+      "test/core/transport/chttp2/hpack_parser_corpus/051268ade45dbed0aab896d7d9f4d10ba89d3b09"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "hpack_parser_fuzzer_test_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/transport/chttp2/hpack_parser_corpus/0696e7bf7837d98de01c915d3c9d80e5d21b30d2"
     ], 
     "ci_platforms": [
@@ -99977,6 +101041,28 @@
   }, 
   {
     "args": [
+      "test/core/transport/chttp2/hpack_parser_corpus/5278e3581c069624157fd9176eddf52c0e58df67"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "hpack_parser_fuzzer_test_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/transport/chttp2/hpack_parser_corpus/52fe8f0e1fa270ea16f66c93f2ffab265ce059e8"
     ], 
     "ci_platforms": [
@@ -102881,6 +103967,28 @@
   }, 
   {
     "args": [
+      "test/core/transport/chttp2/hpack_parser_corpus/bc4263a92952d56168b354f723eaa43a33cd0b35"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "hpack_parser_fuzzer_test_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/transport/chttp2/hpack_parser_corpus/bcaa71abf23b2e5130e0cc464755fe769bf4aaa7"
     ], 
     "ci_platforms": [
@@ -103585,6 +104693,28 @@
   }, 
   {
     "args": [
+      "test/core/transport/chttp2/hpack_parser_corpus/crash-5ac3e1ea7764cfb6383629574262f82dc7b3cada"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "hpack_parser_fuzzer_test_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/transport/chttp2/hpack_parser_corpus/d000502f32ca5620d7745f39ff6be3b547e26a6d"
     ], 
     "ci_platforms": [
@@ -103761,6 +104891,28 @@
   }, 
   {
     "args": [
+      "test/core/transport/chttp2/hpack_parser_corpus/d6d8b478e6d13945f7a6f7d27f424ff57ca12f7f"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "hpack_parser_fuzzer_test_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/transport/chttp2/hpack_parser_corpus/d76d0c7f24ae3cc3f530d5306b8dcc15290c7ff2"
     ], 
     "ci_platforms": [
@@ -104223,6 +105375,28 @@
   }, 
   {
     "args": [
+      "test/core/transport/chttp2/hpack_parser_corpus/e67c79d0ed89ab2d5e8d81127df22876e636ac44"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "hpack_parser_fuzzer_test_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/transport/chttp2/hpack_parser_corpus/e6fab7572fb2a1c6e107b6f83cffd103a233d021"
     ], 
     "ci_platforms": [
@@ -104729,6 +105903,28 @@
   }, 
   {
     "args": [
+      "test/core/transport/chttp2/hpack_parser_corpus/f4b48c4f3f310ed767755267210f212dd62bd715"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "hpack_parser_fuzzer_test_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/transport/chttp2/hpack_parser_corpus/f4d6ff635ae4fda497221da4bfa3e593df59a44e"
     ], 
     "ci_platforms": [
@@ -104883,6 +106079,28 @@
   }, 
   {
     "args": [
+      "test/core/transport/chttp2/hpack_parser_corpus/fcc06696e641a7743bfd2f3b7ecd88e7b727e00a"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "hpack_parser_fuzzer_test_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/transport/chttp2/hpack_parser_corpus/fd34ec90fe8f9218fd25c3eac151aec998cff6d8"
     ], 
     "ci_platforms": [
@@ -125057,6 +126275,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/084e9e02b8f2ed41ff9c22fef80e522e491227c5"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/08c42ef29eff83052c5887855f2fa3e07ebe470c"
     ], 
     "ci_platforms": [
@@ -125431,6 +126671,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/129382aa11fa2922053fa47ba691dddb98901260"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/13501419f349b7855d2e94060bd08b28923d1f37"
     ], 
     "ci_platforms": [
@@ -125453,6 +126715,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/1353e447b7b10fb3eabb02863a1fc5bc9bb50460"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/1421a8e9f045ac65a0f6938fae93fece1060c41d"
     ], 
     "ci_platforms": [
@@ -127587,6 +128871,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/47563391b73b6ef4bf987014c3e631fe2555a377"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/49cb33cbb60f041e8e99dd718993acd2c3354416"
     ], 
     "ci_platforms": [
@@ -128885,6 +130191,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/6c0ec181e81b915071766762f5d78e9b1ccc9128"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/6c5bb78b51cf5006c92258292de19550985c00ba"
     ], 
     "ci_platforms": [
@@ -128907,6 +130235,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/6c66271b74a11f4e7065a6acbc86e4611e7e0dcc"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/6db42d0c5471ac697d82e882c01867b73f71c71f"
     ], 
     "ci_platforms": [
@@ -129105,6 +130455,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/72db978194609ac138bca393650740a3eae8f448"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/7342b3febb07521e39abdf4ee976d16199d51239"
     ], 
     "ci_platforms": [
@@ -129303,6 +130675,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/7df75412d12894fc501bd2d8493fe9e5a753ad88"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/7f15bbce.bin"
     ], 
     "ci_platforms": [
@@ -130073,6 +131467,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/947f53c0978672e59be452395670fa949a33cb4f"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/9540d3ad3fa75bfb95c0d57cefd737611c7069a5"
     ], 
     "ci_platforms": [
@@ -130623,6 +132039,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/a8305e9bb4a544eaef27e8bd21b4faabf524a84e"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/a8b4049240b53947a8bc76cadf8d4ff9a802c783"
     ], 
     "ci_platforms": [
@@ -130909,6 +132347,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/ac4e19c9fe290afc54bbb1e5f6cfe84a9c1f3c29"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/ad810f7f.bin"
     ], 
     "ci_platforms": [
@@ -130931,6 +132391,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/ae297426638eb5a0611324f403c3189515274f1b"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/aedefcd9bd7fc10b7bf60372da54c43e953523bd"
     ], 
     "ci_platforms": [
@@ -131107,6 +132589,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/b74dcd9c2780f145e09a27d2e03119576889a301"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/b7ce4a4f6eea20c0b83d9f7fa8406a0730ee0040"
     ], 
     "ci_platforms": [
@@ -131503,6 +133007,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/c39c0192d0d4f2b24f05c25a9e669cc091bff9e9"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/c43d97f2.bin"
     ], 
     "ci_platforms": [
@@ -132009,6 +133535,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/crash-901723090ff2042ecc5a008ccd7ae0845c1681cb"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/crash-c1f66840627e3bfdedf2e4c225bc4de0c267ed37"
     ], 
     "ci_platforms": [
@@ -132185,6 +133733,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/d297b3f84e3dee9f74bf1162718aff66a11a7f5c"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/d44d94764e1761cb7278ffe5cb17871abab7ed89"
     ], 
     "ci_platforms": [
@@ -132515,6 +134085,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/e021c5b9815f02ee9d1e5a003cbf3500e19db13c"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/e0d9a9a7.bin"
     ], 
     "ci_platforms": [
@@ -133241,6 +134833,28 @@
   }, 
   {
     "args": [
+      "test/core/end2end/fuzzers/server_fuzzer_corpus/fc162db40f87facb73cff20751e23af2e1f5296e"
+    ], 
+    "ci_platforms": [
+      "linux"
+    ], 
+    "cpu_cost": 0.1, 
+    "exclude_configs": [
+      "tsan"
+    ], 
+    "exclude_iomgrs": [
+      "uv"
+    ], 
+    "flaky": false, 
+    "language": "c", 
+    "name": "server_fuzzer_one_entry", 
+    "platforms": [
+      "linux"
+    ], 
+    "uses_polling": false
+  }, 
+  {
+    "args": [
       "test/core/end2end/fuzzers/server_fuzzer_corpus/fd14bea45ecaf13af0053900edb2f17b71a0bf09"
     ], 
     "ci_platforms": [
diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln
index f484f29..aa38215 100644
--- a/vsprojects/buildtests_c.sln
+++ b/vsprojects/buildtests_c.sln
@@ -153,17 +153,6 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chttp2_status_conversion_test", "vcxproj\test\chttp2_status_conversion_test\chttp2_status_conversion_test.vcxproj", "{ABAD3D2C-078C-7850-B413-3352A07C6176}"
-	ProjectSection(myProperties) = preProject
-        	lib = "False"
-	EndProjectSection
-	ProjectSection(ProjectDependencies) = postProject
-		{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
-		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
-		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
-		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
-	EndProjectSection
-EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chttp2_stream_map_test", "vcxproj\test\chttp2_stream_map_test\chttp2_stream_map_test.vcxproj", "{12F9C5F8-1BDA-305F-5A0B-B0F9CC7AA7A4}"
 	ProjectSection(myProperties) = preProject
         	lib = "False"
@@ -1456,6 +1445,17 @@
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "status_conversion_test", "vcxproj\test\status_conversion_test\status_conversion_test.vcxproj", "{21E2A241-9D48-02CD-92E4-4EEC98424CF5}"
+	ProjectSection(myProperties) = preProject
+        	lib = "False"
+	EndProjectSection
+	ProjectSection(ProjectDependencies) = postProject
+		{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B} = {17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}
+		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
+		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
+		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
+	EndProjectSection
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_tcp_server", "vcxproj\.\test_tcp_server\test_tcp_server.vcxproj", "{E3110C46-A148-FF65-08FD-3324829BE7FE}"
 	ProjectSection(myProperties) = preProject
         	lib = "True"
@@ -1815,22 +1815,6 @@
 		{19F92966-3B0E-4FF8-CD7C-435D353E079E}.Release-DLL|Win32.Build.0 = Release|Win32
 		{19F92966-3B0E-4FF8-CD7C-435D353E079E}.Release-DLL|x64.ActiveCfg = Release|x64
 		{19F92966-3B0E-4FF8-CD7C-435D353E079E}.Release-DLL|x64.Build.0 = Release|x64
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Debug|Win32.ActiveCfg = Debug|Win32
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Debug|x64.ActiveCfg = Debug|x64
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Release|Win32.ActiveCfg = Release|Win32
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Release|x64.ActiveCfg = Release|x64
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Debug|Win32.Build.0 = Debug|Win32
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Debug|x64.Build.0 = Debug|x64
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Release|Win32.Build.0 = Release|Win32
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Release|x64.Build.0 = Release|x64
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Debug-DLL|Win32.Build.0 = Debug|Win32
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Debug-DLL|x64.ActiveCfg = Debug|x64
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Debug-DLL|x64.Build.0 = Debug|x64
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Release-DLL|Win32.ActiveCfg = Release|Win32
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Release-DLL|Win32.Build.0 = Release|Win32
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Release-DLL|x64.ActiveCfg = Release|x64
-		{ABAD3D2C-078C-7850-B413-3352A07C6176}.Release-DLL|x64.Build.0 = Release|x64
 		{12F9C5F8-1BDA-305F-5A0B-B0F9CC7AA7A4}.Debug|Win32.ActiveCfg = Debug|Win32
 		{12F9C5F8-1BDA-305F-5A0B-B0F9CC7AA7A4}.Debug|x64.ActiveCfg = Debug|x64
 		{12F9C5F8-1BDA-305F-5A0B-B0F9CC7AA7A4}.Release|Win32.ActiveCfg = Release|Win32
@@ -3767,6 +3751,22 @@
 		{529771F0-10B0-9B1A-1E7E-8A8E01870348}.Release-DLL|Win32.Build.0 = Release|Win32
 		{529771F0-10B0-9B1A-1E7E-8A8E01870348}.Release-DLL|x64.ActiveCfg = Release|x64
 		{529771F0-10B0-9B1A-1E7E-8A8E01870348}.Release-DLL|x64.Build.0 = Release|x64
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Debug|Win32.ActiveCfg = Debug|Win32
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Debug|x64.ActiveCfg = Debug|x64
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release|Win32.ActiveCfg = Release|Win32
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release|x64.ActiveCfg = Release|x64
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Debug|Win32.Build.0 = Debug|Win32
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Debug|x64.Build.0 = Debug|x64
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release|Win32.Build.0 = Release|Win32
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release|x64.Build.0 = Release|x64
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Debug-DLL|Win32.ActiveCfg = Debug|Win32
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Debug-DLL|Win32.Build.0 = Debug|Win32
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Debug-DLL|x64.ActiveCfg = Debug|x64
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Debug-DLL|x64.Build.0 = Debug|x64
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release-DLL|Win32.ActiveCfg = Release|Win32
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release-DLL|Win32.Build.0 = Release|Win32
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release-DLL|x64.ActiveCfg = Release|x64
+		{21E2A241-9D48-02CD-92E4-4EEC98424CF5}.Release-DLL|x64.Build.0 = Release|x64
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Debug|Win32.ActiveCfg = Debug|Win32
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Debug|x64.ActiveCfg = Debug|x64
 		{E3110C46-A148-FF65-08FD-3324829BE7FE}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
index bd9c546..86c2fe3 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj
@@ -321,6 +321,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_context.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
diff --git a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
index 93d8274..dffc70f 100644
--- a/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++/grpc++.vcxproj.filters
@@ -303,6 +303,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
index 7eba5c5..58b87ef 100644
--- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj
@@ -168,6 +168,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_context.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
diff --git a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
index 9a10e07..aabb45d 100644
--- a/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_test_util/grpc++_test_util.vcxproj.filters
@@ -96,6 +96,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
index 7f2e815..33a74ba 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj
@@ -321,6 +321,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_context.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
diff --git a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
index df62384..b87d789 100644
--- a/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
@@ -288,6 +288,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index b531c6f..13d825a 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -323,6 +323,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h" />
@@ -375,6 +376,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_writer.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" />
@@ -388,14 +390,17 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\init.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\lame_client.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\server.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\validate_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\http2_errors.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport_impl.h" />
@@ -412,11 +417,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\hpack_encoder.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\hpack_parser.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\hpack_table.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\http2_errors.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\huffsyms.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\incoming_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\internal.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.h" />
@@ -656,6 +659,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_buffer.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_intern.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\alarm.c">
@@ -698,7 +705,7 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata.c">
     </ClCompile>
@@ -710,6 +717,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
@@ -750,8 +759,6 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\parsing.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_lists.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index 79bbaec..de2e821 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -256,6 +256,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_buffer.c">
       <Filter>src\core\lib\slice</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.c">
+      <Filter>src\core\lib\slice</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_intern.c">
+      <Filter>src\core\lib\slice</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.c">
       <Filter>src\core\lib\slice</Filter>
     </ClCompile>
@@ -319,7 +325,7 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata.c">
@@ -337,6 +343,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
@@ -397,9 +406,6 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\parsing.c">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.c">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_lists.c">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClCompile>
@@ -827,6 +833,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error_internal.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
@@ -983,6 +992,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.h">
+      <Filter>src\core\lib\slice</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
@@ -1022,13 +1034,19 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\server.h">
       <Filter>src\core\lib\surface</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\validate_metadata.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\http2_errors.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h">
@@ -1046,6 +1064,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
@@ -1094,9 +1115,6 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\hpack_table.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\http2_errors.h">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\huffsyms.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
@@ -1106,9 +1124,6 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\internal.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.h">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
index 230c3eb..7d6685d 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj
@@ -216,6 +216,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h" />
@@ -268,6 +269,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_writer.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" />
@@ -281,14 +283,17 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\init.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\lame_client.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\server.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\validate_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\http2_errors.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport_impl.h" />
@@ -502,6 +507,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_buffer.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_intern.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\alarm.c">
@@ -544,7 +553,7 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata.c">
     </ClCompile>
@@ -556,6 +565,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
diff --git a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
index 46a3815..1c21ff3 100644
--- a/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_test_util/grpc_test_util.vcxproj.filters
@@ -313,6 +313,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_buffer.c">
       <Filter>src\core\lib\slice</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.c">
+      <Filter>src\core\lib\slice</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_intern.c">
+      <Filter>src\core\lib\slice</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.c">
       <Filter>src\core\lib\slice</Filter>
     </ClCompile>
@@ -376,7 +382,7 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata.c">
@@ -394,6 +400,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
@@ -608,6 +617,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error_internal.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
@@ -764,6 +776,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.h">
+      <Filter>src\core\lib\slice</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
@@ -803,13 +818,19 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\server.h">
       <Filter>src\core\lib\surface</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\validate_metadata.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\http2_errors.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h">
@@ -827,6 +848,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index 66dc430..826c065 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -313,6 +313,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h" />
@@ -365,6 +366,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_reader.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\json\json_writer.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\api_trace.h" />
@@ -378,14 +380,17 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\init.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\lame_client.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\server.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\validate_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\http2_errors.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata_batch.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\pid_controller.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\service_config.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\transport_impl.h" />
@@ -402,11 +407,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\hpack_encoder.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\hpack_parser.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\hpack_table.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\http2_errors.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\huffsyms.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\incoming_metadata.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\internal.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.h" />
@@ -624,6 +627,10 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_buffer.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_intern.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\alarm.c">
@@ -666,7 +673,7 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata.c">
     </ClCompile>
@@ -678,6 +685,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\transport.c">
@@ -720,8 +729,6 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\parsing.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.c">
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_lists.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 67592b2..06ddac9 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -259,6 +259,12 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_buffer.c">
       <Filter>src\core\lib\slice</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.c">
+      <Filter>src\core\lib\slice</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_intern.c">
+      <Filter>src\core\lib\slice</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\slice\slice_string_helpers.c">
       <Filter>src\core\lib\slice</Filter>
     </ClCompile>
@@ -322,7 +328,7 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\metadata.c">
@@ -340,6 +346,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.c">
+      <Filter>src\core\lib\transport</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.c">
       <Filter>src\core\lib\transport</Filter>
     </ClCompile>
@@ -403,9 +412,6 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\parsing.c">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.c">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_lists.c">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClCompile>
@@ -740,6 +746,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error_internal.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_epoll_linux.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
@@ -896,6 +905,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\percent_encoding.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_hash_table.h">
+      <Filter>src\core\lib\slice</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\slice\slice_internal.h">
       <Filter>src\core\lib\slice</Filter>
     </ClInclude>
@@ -935,13 +947,19 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\server.h">
       <Filter>src\core\lib\surface</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\surface\validate_metadata.h">
+      <Filter>src\core\lib\surface</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\byte_stream.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\connectivity_state.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\mdstr_hash_table.h">
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\error_utils.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\http2_errors.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\metadata.h">
@@ -959,6 +977,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\static_metadata.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\status_conversion.h">
+      <Filter>src\core\lib\transport</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\transport\timeout_encoding.h">
       <Filter>src\core\lib\transport</Filter>
     </ClInclude>
@@ -1007,9 +1028,6 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\hpack_table.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\http2_errors.h">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\huffsyms.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
@@ -1019,9 +1037,6 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\internal.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\status_conversion.h">
-      <Filter>src\core\ext\transport\chttp2\transport</Filter>
-    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\stream_map.h">
       <Filter>src\core\ext\transport\chttp2\transport</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/chttp2_status_conversion_test/chttp2_status_conversion_test.vcxproj.filters b/vsprojects/vcxproj/test/chttp2_status_conversion_test/chttp2_status_conversion_test.vcxproj.filters
deleted file mode 100644
index d94af50..0000000
--- a/vsprojects/vcxproj/test/chttp2_status_conversion_test/chttp2_status_conversion_test.vcxproj.filters
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\transport\chttp2\status_conversion_test.c">
-      <Filter>test\core\transport\chttp2</Filter>
-    </ClCompile>
-  </ItemGroup>
-
-  <ItemGroup>
-    <Filter Include="test">
-      <UniqueIdentifier>{29690dfb-a808-84b3-d82e-deadb5d04103}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core">
-      <UniqueIdentifier>{d17e51ca-73ac-6f31-d02c-631ac849c194}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\transport">
-      <UniqueIdentifier>{3f03cd74-998e-23ed-a372-a1b706d35bf4}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\transport\chttp2">
-      <UniqueIdentifier>{b65a9b0c-fa3d-1919-b70f-9d2d5cc28077}</UniqueIdentifier>
-    </Filter>
-  </ItemGroup>
-</Project>
-
diff --git a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
index ad6865a..d8d4a76 100644
--- a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
+++ b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj
@@ -181,6 +181,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_context.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
diff --git a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
index 1b367b1..d79f228 100644
--- a/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
+++ b/vsprojects/vcxproj/test/codegen_test_full/codegen_test_full.vcxproj.filters
@@ -84,6 +84,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
index 9df6702..595e673 100644
--- a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
+++ b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj
@@ -181,6 +181,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_context.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
diff --git a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
index c8f62ff..78de950 100644
--- a/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
+++ b/vsprojects/vcxproj/test/codegen_test_minimal/codegen_test_minimal.vcxproj.filters
@@ -87,6 +87,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj
index 88e4bf6..5381be5 100644
--- a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj
+++ b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj
@@ -182,6 +182,7 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_context.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\server_interface.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h" />
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_code_enum.h" />
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status_helper.h" />
diff --git a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters
index fece7f3..0526a07 100644
--- a/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters
+++ b/vsprojects/vcxproj/test/grpc_tool_test/grpc_tool_test.vcxproj.filters
@@ -78,6 +78,9 @@
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\service_type.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\slice.h">
+      <Filter>include\grpc++\impl\codegen</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\include\grpc++\impl\codegen\status.h">
       <Filter>include\grpc++\impl\codegen</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/chttp2_status_conversion_test/chttp2_status_conversion_test.vcxproj b/vsprojects/vcxproj/test/status_conversion_test/status_conversion_test.vcxproj
similarity index 97%
rename from vsprojects/vcxproj/test/chttp2_status_conversion_test/chttp2_status_conversion_test.vcxproj
rename to vsprojects/vcxproj/test/status_conversion_test/status_conversion_test.vcxproj
index f5b8838..ab1e3a7 100644
--- a/vsprojects/vcxproj/test/chttp2_status_conversion_test/chttp2_status_conversion_test.vcxproj
+++ b/vsprojects/vcxproj/test/status_conversion_test/status_conversion_test.vcxproj
@@ -20,7 +20,7 @@
     </ProjectConfiguration>
   </ItemGroup>
   <PropertyGroup Label="Globals">
-    <ProjectGuid>{ABAD3D2C-078C-7850-B413-3352A07C6176}</ProjectGuid>
+    <ProjectGuid>{21E2A241-9D48-02CD-92E4-4EEC98424CF5}</ProjectGuid>
     <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
     <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
   </PropertyGroup>
@@ -60,14 +60,14 @@
   </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)'=='Debug'">
-    <TargetName>chttp2_status_conversion_test</TargetName>
+    <TargetName>status_conversion_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib>
     <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
     <Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)'=='Release'">
-    <TargetName>chttp2_status_conversion_test</TargetName>
+    <TargetName>status_conversion_test</TargetName>
     <Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib>
     <Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib>
     <Linkage-grpc_dependencies_openssl>static</Linkage-grpc_dependencies_openssl>
@@ -158,7 +158,7 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\transport\chttp2\status_conversion_test.c">
+    <ClCompile Include="$(SolutionDir)\..\test\core\transport\status_conversion_test.c">
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
diff --git a/vsprojects/vcxproj/test/status_conversion_test/status_conversion_test.vcxproj.filters b/vsprojects/vcxproj/test/status_conversion_test/status_conversion_test.vcxproj.filters
new file mode 100644
index 0000000..72ebef5
--- /dev/null
+++ b/vsprojects/vcxproj/test/status_conversion_test/status_conversion_test.vcxproj.filters
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="$(SolutionDir)\..\test\core\transport\status_conversion_test.c">
+      <Filter>test\core\transport</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Filter Include="test">
+      <UniqueIdentifier>{4702a9eb-d239-c799-0712-b7af210665bd}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core">
+      <UniqueIdentifier>{2f2eed07-7d0f-4b4a-67c0-96e860bb6b16}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="test\core\transport">
+      <UniqueIdentifier>{269c6750-9126-9b6a-4196-b7cbe492ee9f}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>
+