Merge pull request #4231 from parkr/patch-1

php: remove callable type hint from BaseStub->_simpleRequest()
diff --git a/BUILD b/BUILD
index 2e2bd1f..f1395da 100644
--- a/BUILD
+++ b/BUILD
@@ -181,6 +181,7 @@
     "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h",
     "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h",
     "src/core/client_config/uri_parser.h",
+    "src/core/compression/algorithm_metadata.h",
     "src/core/compression/message_compress.h",
     "src/core/debug/trace.h",
     "src/core/httpcli/format_request.h",
@@ -263,6 +264,7 @@
     "src/core/transport/connectivity_state.h",
     "src/core/transport/metadata.h",
     "src/core/transport/metadata_batch.h",
+    "src/core/transport/static_metadata.h",
     "src/core/transport/transport.h",
     "src/core/transport/transport_impl.h",
     "src/core/census/aggregation.h",
@@ -413,6 +415,7 @@
     "src/core/transport/connectivity_state.c",
     "src/core/transport/metadata.c",
     "src/core/transport/metadata_batch.c",
+    "src/core/transport/static_metadata.c",
     "src/core/transport/transport.c",
     "src/core/transport/transport_op_string.c",
     "src/core/census/context.c",
@@ -474,6 +477,7 @@
     "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h",
     "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h",
     "src/core/client_config/uri_parser.h",
+    "src/core/compression/algorithm_metadata.h",
     "src/core/compression/message_compress.h",
     "src/core/debug/trace.h",
     "src/core/httpcli/format_request.h",
@@ -556,6 +560,7 @@
     "src/core/transport/connectivity_state.h",
     "src/core/transport/metadata.h",
     "src/core/transport/metadata_batch.h",
+    "src/core/transport/static_metadata.h",
     "src/core/transport/transport.h",
     "src/core/transport/transport_impl.h",
     "src/core/census/aggregation.h",
@@ -686,6 +691,7 @@
     "src/core/transport/connectivity_state.c",
     "src/core/transport/metadata.c",
     "src/core/transport/metadata_batch.c",
+    "src/core/transport/static_metadata.c",
     "src/core/transport/transport.c",
     "src/core/transport/transport_op_string.c",
     "src/core/census/context.c",
@@ -1219,6 +1225,7 @@
     "src/core/transport/connectivity_state.c",
     "src/core/transport/metadata.c",
     "src/core/transport/metadata_batch.c",
+    "src/core/transport/static_metadata.c",
     "src/core/transport/transport.c",
     "src/core/transport/transport_op_string.c",
     "src/core/census/context.c",
@@ -1277,6 +1284,7 @@
     "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h",
     "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h",
     "src/core/client_config/uri_parser.h",
+    "src/core/compression/algorithm_metadata.h",
     "src/core/compression/message_compress.h",
     "src/core/debug/trace.h",
     "src/core/httpcli/format_request.h",
@@ -1359,6 +1367,7 @@
     "src/core/transport/connectivity_state.h",
     "src/core/transport/metadata.h",
     "src/core/transport/metadata_batch.h",
+    "src/core/transport/static_metadata.h",
     "src/core/transport/transport.h",
     "src/core/transport/transport_impl.h",
     "src/core/census/aggregation.h",
diff --git a/Makefile b/Makefile
index 71d75e3..5363b4f 100644
--- a/Makefile
+++ b/Makefile
@@ -4644,6 +4644,7 @@
     src/core/transport/connectivity_state.c \
     src/core/transport/metadata.c \
     src/core/transport/metadata_batch.c \
+    src/core/transport/static_metadata.c \
     src/core/transport/transport.c \
     src/core/transport/transport_op_string.c \
     src/core/census/context.c \
@@ -4929,6 +4930,7 @@
     src/core/transport/connectivity_state.c \
     src/core/transport/metadata.c \
     src/core/transport/metadata_batch.c \
+    src/core/transport/static_metadata.c \
     src/core/transport/transport.c \
     src/core/transport/transport_op_string.c \
     src/core/census/context.c \
diff --git a/binding.gyp b/binding.gyp
index 69d7ca7..5205568 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -297,6 +297,7 @@
         'src/core/transport/connectivity_state.c',
         'src/core/transport/metadata.c',
         'src/core/transport/metadata_batch.c',
+        'src/core/transport/static_metadata.c',
         'src/core/transport/transport.c',
         'src/core/transport/transport_op_string.c',
         'src/core/census/context.c',
diff --git a/build.yaml b/build.yaml
index 82a023d..f1e44fe 100644
--- a/build.yaml
+++ b/build.yaml
@@ -135,6 +135,7 @@
   - src/core/client_config/subchannel_factory_decorators/add_channel_arg.h
   - src/core/client_config/subchannel_factory_decorators/merge_channel_args.h
   - src/core/client_config/uri_parser.h
+  - src/core/compression/algorithm_metadata.h
   - src/core/compression/message_compress.h
   - src/core/debug/trace.h
   - src/core/httpcli/format_request.h
@@ -217,6 +218,7 @@
   - src/core/transport/connectivity_state.h
   - src/core/transport/metadata.h
   - src/core/transport/metadata_batch.h
+  - src/core/transport/static_metadata.h
   - src/core/transport/transport.h
   - src/core/transport/transport_impl.h
   src:
@@ -344,6 +346,7 @@
   - src/core/transport/connectivity_state.c
   - src/core/transport/metadata.c
   - src/core/transport/metadata_batch.c
+  - src/core/transport/static_metadata.c
   - src/core/transport/transport.c
   - src/core/transport/transport_op_string.c
 - name: grpc_test_util_base
diff --git a/doc/grpc-auth-support.md b/doc/grpc-auth-support.md
deleted file mode 100644
index b210ff1..0000000
--- a/doc/grpc-auth-support.md
+++ /dev/null
@@ -1,289 +0,0 @@
-#gRPC Authentication support
-
-gRPC is designed to plug-in a number of authentication mechanisms. This document
-provides a quick overview of the various auth mechanisms supported, discusses
-the API with some examples, and concludes with a discussion of extensibility.
-More documentation and examples are coming soon!
-
-## Supported auth mechanisms
-
-###SSL/TLS
-gRPC has SSL/TLS integration and promotes the use of SSL/TLS to authenticate the
-server, and encrypt all the data exchanged between the client and the server.
-Optional mechanisms are available for clients to provide certificates to
-accomplish mutual authentication.
-
-###OAuth 2.0
-gRPC provides a generic mechanism (described below) to attach metadata to
-requests and responses. This mechanism can be used to attach OAuth 2.0 Access
-Tokens to RPCs being made at a client. Additional support for acquiring Access
-Tokens while accessing Google APIs through gRPC is provided for certain auth
-flows, demonstrated through code examples below.
-
-## API
-To reduce complexity and minimize API clutter, gRPC works with a unified concept
-of a Credentials object. Users construct gRPC credentials using corresponding
-bootstrap credentials (e.g., SSL client certs or Service Account Keys), and use
-the credentials while creating a gRPC channel to any server. Depending on the
-type of credential supplied, the channel uses the credentials during the initial
-SSL/TLS handshake with the server, or uses  the credential to generate and
-attach Access Tokens to each request being made on the channel.
-
-###SSL/TLS for server authentication and encryption
-This is the simplest authentication scenario, where a client just wants to
-authenticate the server and encrypt all data.
-
-```cpp
-SslCredentialsOptions ssl_opts;  // Options to override SSL params, empty by default
-// Create the credentials object by providing service account key in constructor
-std::shared_ptr<ChannelCredentials> creds = SslCredentials(ssl_opts);
-// Create a channel using the credentials created in the previous step
-std::shared_ptr<Channel> channel = CreateChannel(server_name, creds);
-// Create a stub on the channel
-std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
-// Make actual RPC calls on the stub.
-grpc::Status s = stub->sayHello(&context, *request, response);
-```
-
-For advanced use cases such as modifying the root CA or using client certs,
-the corresponding options can be set in the SslCredentialsOptions parameter
-passed to the factory method.
-
-
-###Authenticating with Google
-
-gRPC applications can use a simple API to create a credential that works in various deployment scenarios.
-
-```cpp
-std::shared_ptr<ChannelCredentials> creds = GoogleDefaultCredentials();
-// Create a channel, stub and make RPC calls (same as in the previous example)
-std::shared_ptr<Channel> channel = CreateChannel(server_name, creds);
-std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
-grpc::Status s = stub->sayHello(&context, *request, response);
-```
-
-This credential works for applications using Service Accounts as well as for
-applications running in [Google Compute Engine (GCE)](https://cloud.google.com/compute/). In the former case, the
-service account’s private keys are loaded from the file named in the environment
-variable `GOOGLE_APPLICATION_CREDENTIALS`. The
-keys are used to generate bearer tokens that are attached to each outgoing RPC
-on the corresponding channel.
-
-For applications running in GCE, a default service account and corresponding
-OAuth scopes can be configured during VM setup. At run-time, this credential
-handles communication with the authentication systems to obtain OAuth2 access
-tokens and attaches them to each outgoing RPC on the corresponding channel.
-Extending gRPC to support other authentication mechanisms
-The gRPC protocol is designed with a general mechanism for sending metadata
-associated with RPC. Clients can send metadata at the beginning of an RPC and
-servers can send back metadata at the beginning and end of the RPC. This
-provides a natural mechanism to support OAuth2 and other authentication
-mechanisms that need attach bearer tokens to individual request.
-
-In the simplest case, there is a single line of code required on the client
-to add a specific token as metadata to an RPC and a corresponding access on
-the server to retrieve this piece of metadata. The generation of the token
-on the client side and its verification at the server can be done separately.
-
-A deeper integration can be achieved by plugging in a gRPC credentials implementation for any custom authentication mechanism that needs to attach per-request tokens. gRPC internals also allow switching out SSL/TLS with other encryption mechanisms.
-
-## Examples
-
-These authentication mechanisms will be available in all gRPC's supported languages.
-The following sections demonstrate how authentication and authorization features described above appear in each language: more languages are coming soon.
-
-###SSL/TLS for server authentication and encryption (Ruby)
-```ruby
-# Base case - No encryption
-stub = Helloworld::Greeter::Stub.new('localhost:50051')
-...
-
-# With server authentication SSL/TLS
-creds = GRPC::Core::Credentials.new(load_certs)  # load_certs typically loads a CA roots file
-stub = Helloworld::Greeter::Stub.new('localhost:50051', creds: creds)
-```
-
-###SSL/TLS for server authentication and encryption (C#)
-```csharp
-// Base case - No encryption
-var channel = new Channel("localhost:50051");
-var client = new Greeter.GreeterClient(channel);
-...
-
-// With server authentication SSL/TLS
-var credentials = new SslCredentials(File.ReadAllText("ca.pem"));  // Load a CA file
-var channel = new Channel("localhost:50051", credentials);
-var client = new Greeter.GreeterClient(channel);
-```
-
-###SSL/TLS for server authentication and encryption (Objective-C)
-
-The default for Objective-C is to use SSL/TLS, as that's the most common use case when accessing
-remote APIs.
-
-```objective-c
-// Base case - With server authentication SSL/TLS
-HLWGreeter *client = [[HLWGreeter alloc] initWithHost:@"localhost:50051"];
-// Same as using @"https://localhost:50051".
-...
-
-// No encryption
-HLWGreeter *client = [[HLWGreeter alloc] initWithHost:@"http://localhost:50051"];
-// Specifying the HTTP scheme explicitly forces no encryption.
-```
-
-###SSL/TLS for server authentication and encryption (Python)
-```python
-# Base case - No encryption
-stub = early_adopter_create_GreeterService_stub('localhost', 50051)
-...
-
-# With server authentication SSL/TLS
-stub = early_adopter_create_GreeterService_stub(
-  'localhost', 50051, secure=True, root_certificates=open('ca.pem').read())
-...
-```
-n.b.: the beta API will look different
-
-###Authenticating with Google (Ruby)
-```ruby
-# Base case - No encryption/authorization
-stub = Helloworld::Greeter::Stub.new('localhost:50051')
-...
-
-# Authenticating with Google
-require 'googleauth'  # from http://www.rubydoc.info/gems/googleauth/0.1.0
-...
-creds = GRPC::Core::Credentials.new(load_certs)  # load_certs typically loads a CA roots file
-scope = 'https://www.googleapis.com/auth/grpc-testing'
-authorization = Google::Auth.get_application_default(scope)
-stub = Helloworld::Greeter::Stub.new('localhost:50051',
-                                     creds: creds,
-                                     update_metadata: authorization.updater_proc)
-```
-
-###Authenticating with Google (Node.js)
-
-```node
-// Base case - No encryption/authorization
-var stub = new helloworld.Greeter('localhost:50051');
-...
-// Authenticating with Google
-var GoogleAuth = require('google-auth-library'); // from https://www.npmjs.com/package/google-auth-library
-...
-var creds = grpc.Credentials.createSsl(load_certs); // load_certs typically loads a CA roots file
-var scope = 'https://www.googleapis.com/auth/grpc-testing';
-(new GoogleAuth()).getApplicationDefault(function(err, auth) {
-  if (auth.createScopeRequired()) {
-    auth = auth.createScoped(scope);
-  }
-  var stub = new helloworld.Greeter('localhost:50051',
-                                    {credentials: creds},
-                                    grpc.getGoogleAuthDelegate(auth));
-});
-```
-
-###Authenticating with Google (C#)
-```csharp
-// Base case - No encryption/authorization
-var channel = new Channel("localhost:50051");
-var client = new Greeter.GreeterClient(channel);
-...
-
-// Authenticating with Google
-using Grpc.Auth;  // from Grpc.Auth NuGet package
-...
-var credentials = new SslCredentials(File.ReadAllText("ca.pem"));  // Load a CA file
-var channel = new Channel("localhost:50051", credentials);
-
-string scope = "https://www.googleapis.com/auth/grpc-testing";
-var authorization = GoogleCredential.GetApplicationDefault();
-if (authorization.IsCreateScopedRequired)
-{
-    authorization = credential.CreateScoped(new[] { scope });
-}
-var client = new Greeter.GreeterClient(channel,
-        new StubConfiguration(OAuth2InterceptorFactory.Create(credential)));
-```
-
-###Authenticating with Google (PHP)
-```php
-// Base case - No encryption/authorization
-$client = new helloworld\GreeterClient(
-  new Grpc\BaseStub('localhost:50051', []));
-...
-
-// Authenticating with Google
-// the environment variable "GOOGLE_APPLICATION_CREDENTIALS" needs to be set
-$scope = "https://www.googleapis.com/auth/grpc-testing";
-$auth = Google\Auth\ApplicationDefaultCredentials::getCredentials($scope);
-$opts = [
-  'credentials' => Grpc\Credentials::createSsl(file_get_contents('ca.pem'));
-  'update_metadata' => $auth->getUpdateMetadataFunc(),
-];
-
-$client = new helloworld\GreeterClient(
-  new Grpc\BaseStub('localhost:50051', $opts));
-
-```
-
-###Authenticating with Google (Objective-C)
-
-This example uses the [Google iOS Sign-In library](https://developers.google.com/identity/sign-in/ios/),
-but it's easily extrapolated to any other OAuth2 library.
-
-```objective-c
-// Base case - No authentication
-[client sayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) {
-  ...
-}];
-
-...
-
-// Authenticating with Google
-
-// When signing the user in, ask her for the relevant scopes.
-GIDSignIn.sharedInstance.scopes = @[@"https://www.googleapis.com/auth/grpc-testing"];
-
-...
-
-#import <ProtoRPC/ProtoRPC.h>
-
-// Create a not-yet-started RPC. We want to set the request headers on this object before starting
-// it.
-ProtoRPC *call =
-    [client RPCToSayHelloWithRequest:request handler:^(HLWHelloReply *response, NSError *error) {
-      ...
-    }];
-
-// Set the access token to be used.
-NSString *accessToken = GIDSignIn.sharedInstance.currentUser.authentication.accessToken;
-call.requestMetadata[@"Authorization"] = [@"Bearer " stringByAppendingString:accessToken]}];
-
-// Start the RPC.
-[call start];
-```
-
-You can see a working example app, with a more detailed explanation, [here](examples/objective-c/auth_sample).
-
-### Authenticating with Google (Python)
-```python
-# Base case - No encryption
-stub = early_adopter_create_GreeterService_stub('localhost', 50051)
-...
-
-# With server authentication SSL/TLS
-import oauth2client.client
-credentials = oauth2client.GoogleCredentials.get_application_default()
-scope = 'https://www.googleapis.com/auth/grpc-testing'
-scoped_credentials = credentials.create_scoped([scope])
-access_token = scoped_credentials.get_access_token().access_token
-metadata_transformer = (
-    lambda x: [('Authorization', 'Bearer {}'.format(access_token))])
-
-stub = early_adopter_create_GreeterService_stub(
-  'localhost', 50051, secure=True, root_certificates=open('ca.pem').read(),
-  metadata_transformer=metadata_transformer)
-...
-```
-n.b.: the beta API will look different
diff --git a/gRPC.podspec b/gRPC.podspec
index 2797943..0262479 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -185,6 +185,7 @@
                       'src/core/client_config/subchannel_factory_decorators/add_channel_arg.h',
                       'src/core/client_config/subchannel_factory_decorators/merge_channel_args.h',
                       'src/core/client_config/uri_parser.h',
+                      'src/core/compression/algorithm_metadata.h',
                       'src/core/compression/message_compress.h',
                       'src/core/debug/trace.h',
                       'src/core/httpcli/format_request.h',
@@ -267,6 +268,7 @@
                       'src/core/transport/connectivity_state.h',
                       'src/core/transport/metadata.h',
                       'src/core/transport/metadata_batch.h',
+                      'src/core/transport/static_metadata.h',
                       'src/core/transport/transport.h',
                       'src/core/transport/transport_impl.h',
                       'src/core/census/aggregation.h',
@@ -424,6 +426,7 @@
                       'src/core/transport/connectivity_state.c',
                       'src/core/transport/metadata.c',
                       'src/core/transport/metadata_batch.c',
+                      'src/core/transport/static_metadata.c',
                       'src/core/transport/transport.c',
                       'src/core/transport/transport_op_string.c',
                       'src/core/census/context.c',
@@ -484,6 +487,7 @@
                               'src/core/client_config/subchannel_factory_decorators/add_channel_arg.h',
                               'src/core/client_config/subchannel_factory_decorators/merge_channel_args.h',
                               'src/core/client_config/uri_parser.h',
+                              'src/core/compression/algorithm_metadata.h',
                               'src/core/compression/message_compress.h',
                               'src/core/debug/trace.h',
                               'src/core/httpcli/format_request.h',
@@ -566,6 +570,7 @@
                               'src/core/transport/connectivity_state.h',
                               'src/core/transport/metadata.h',
                               'src/core/transport/metadata_batch.h',
+                              'src/core/transport/static_metadata.h',
                               'src/core/transport/transport.h',
                               'src/core/transport/transport_impl.h',
                               'src/core/census/aggregation.h',
diff --git a/include/grpc/support/slice.h b/include/grpc/support/slice.h
index 3abb1b7..507cb19 100644
--- a/include/grpc/support/slice.h
+++ b/include/grpc/support/slice.h
@@ -144,6 +144,9 @@
      memcpy(slice->data, source, len); */
 gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len);
 
+/* Create a slice pointing to constant memory */
+gpr_slice gpr_slice_from_static_string(const char *source);
+
 /* Return a result slice derived from s, which shares a ref count with s, where
    result.data==s.data+begin, and result.length==end-begin.
    The ref count of s is increased by one.
diff --git a/src/core/census/grpc_filter.c b/src/core/census/grpc_filter.c
index daa1a7a..61a95ec 100644
--- a/src/core/census/grpc_filter.c
+++ b/src/core/census/grpc_filter.c
@@ -36,16 +36,18 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "src/core/channel/channel_stack.h"
-#include "src/core/channel/noop_filter.h"
-#include "src/core/statistics/census_interface.h"
-#include "src/core/statistics/census_rpc_stats.h"
 #include <grpc/census.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/slice.h>
 #include <grpc/support/time.h>
 
+#include "src/core/channel/channel_stack.h"
+#include "src/core/channel/noop_filter.h"
+#include "src/core/statistics/census_interface.h"
+#include "src/core/statistics/census_rpc_stats.h"
+#include "src/core/transport/static_metadata.h"
+
 typedef struct call_data {
   census_op_id op_id;
   census_context *ctxt;
@@ -59,7 +61,7 @@
 } call_data;
 
 typedef struct channel_data {
-  grpc_mdstr *path_str; /* pointer to meta data str with key == ":path" */
+  gpr_uint8 unused;
 } channel_data;
 
 static void extract_and_annotate_method_tag(grpc_metadata_batch *md,
@@ -67,7 +69,7 @@
                                             channel_data *chand) {
   grpc_linked_mdelem *m;
   for (m = md->list.head; m != NULL; m = m->next) {
-    if (m->md->key == chand->path_str) {
+    if (m->md->key == GRPC_MDSTR_PATH) {
       gpr_log(GPR_DEBUG, "%s",
               (const char *)GPR_SLICE_START_PTR(m->md->value->slice));
       /* Add method tag here */
@@ -161,16 +163,12 @@
                               grpc_channel_element_args *args) {
   channel_data *chand = elem->channel_data;
   GPR_ASSERT(chand != NULL);
-  chand->path_str = grpc_mdstr_from_string(args->metadata_context, ":path");
 }
 
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
   channel_data *chand = elem->channel_data;
   GPR_ASSERT(chand != NULL);
-  if (chand->path_str != NULL) {
-    GRPC_MDSTR_UNREF(chand->path_str);
-  }
 }
 
 const grpc_channel_filter grpc_client_census_filter = {
diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c
index 02e33a0..7f7fbf4 100644
--- a/src/core/channel/channel_stack.c
+++ b/src/core/channel/channel_stack.c
@@ -105,7 +105,6 @@
                              const grpc_channel_filter **filters,
                              size_t filter_count, grpc_channel *master,
                              const grpc_channel_args *channel_args,
-                             grpc_mdctx *metadata_context,
                              grpc_channel_stack *stack) {
   size_t call_size =
       ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) +
@@ -125,7 +124,6 @@
   for (i = 0; i < filter_count; i++) {
     args.master = master;
     args.channel_args = channel_args;
-    args.metadata_context = metadata_context;
     args.is_first = i == 0;
     args.is_last = i == (filter_count - 1);
     elems[i].filter = filters[i];
diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h
index 5d33ab5..1db12ea 100644
--- a/src/core/channel/channel_stack.h
+++ b/src/core/channel/channel_stack.h
@@ -54,7 +54,6 @@
 typedef struct {
   grpc_channel *master;
   const grpc_channel_args *channel_args;
-  grpc_mdctx *metadata_context;
   int is_first;
   int is_last;
 } grpc_channel_element_args;
@@ -180,7 +179,6 @@
                              const grpc_channel_filter **filters,
                              size_t filter_count, grpc_channel *master,
                              const grpc_channel_args *args,
-                             grpc_mdctx *metadata_context,
                              grpc_channel_stack *stack);
 /* Destroy a channel stack */
 void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index 16d91d4..020138b 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -55,8 +55,6 @@
 typedef grpc_subchannel_call_holder call_data;
 
 typedef struct client_channel_channel_data {
-  /** metadata context for this channel */
-  grpc_mdctx *mdctx;
   /** resolver for this channel */
   grpc_resolver *resolver;
   /** have we started resolving this channel */
@@ -387,7 +385,6 @@
   GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
 
   gpr_mu_init(&chand->mu_config);
-  chand->mdctx = args->metadata_context;
   chand->master = args->master;
   grpc_pollset_set_init(&chand->pollset_set);
   grpc_closure_init(&chand->on_config_changed, cc_on_config_changed, chand);
diff --git a/src/core/channel/client_uchannel.c b/src/core/channel/client_uchannel.c
index ec6b023..456ffb7 100644
--- a/src/core/channel/client_uchannel.c
+++ b/src/core/channel/client_uchannel.c
@@ -54,9 +54,6 @@
  * load-balancing mechanisms meant for communication from within the core. */
 
 typedef struct client_uchannel_channel_data {
-  /** metadata context for this channel */
-  grpc_mdctx *mdctx;
-
   /** master channel - the grpc_channel instance that ultimately owns
       this channel_data via its channel stack.
       We occasionally use this to bump the refcount on the master channel
@@ -161,7 +158,6 @@
   grpc_closure_init(&chand->connectivity_cb, monitor_subchannel, chand);
   GPR_ASSERT(args->is_last);
   GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter);
-  chand->mdctx = args->metadata_context;
   chand->master = args->master;
   grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
                                "client_uchannel");
@@ -252,13 +248,11 @@
   grpc_channel *channel = NULL;
 #define MAX_FILTERS 3
   const grpc_channel_filter *filters[MAX_FILTERS];
-  grpc_mdctx *mdctx = grpc_subchannel_get_mdctx(subchannel);
   grpc_channel *master = grpc_subchannel_get_master(subchannel);
   char *target = grpc_channel_get_target(master);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   size_t n = 0;
 
-  grpc_mdctx_ref(mdctx);
   if (grpc_channel_args_is_census_enabled(args)) {
     filters[n++] = &grpc_client_census_filter;
   }
@@ -266,8 +260,8 @@
   filters[n++] = &grpc_client_uchannel_filter;
   GPR_ASSERT(n <= MAX_FILTERS);
 
-  channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
-                                             args, mdctx, 1);
+  channel =
+      grpc_channel_create_from_filters(&exec_ctx, target, filters, n, args, 1);
 
   gpr_free(target);
   return channel;
diff --git a/src/core/channel/compress_filter.c b/src/core/channel/compress_filter.c
index f23d805..fc8b425 100644
--- a/src/core/channel/compress_filter.c
+++ b/src/core/channel/compress_filter.c
@@ -42,8 +42,10 @@
 #include "src/core/channel/compress_filter.h"
 #include "src/core/channel/channel_args.h"
 #include "src/core/profiling/timers.h"
+#include "src/core/compression/algorithm_metadata.h"
 #include "src/core/compression/message_compress.h"
 #include "src/core/support/string.h"
+#include "src/core/transport/static_metadata.h"
 
 typedef struct call_data {
   gpr_slice_buffer slices; /**< Buffers up input slices to be compressed */
@@ -67,20 +69,12 @@
 } call_data;
 
 typedef struct channel_data {
-  /** Metadata key for the incoming (requested) compression algorithm */
-  grpc_mdstr *mdstr_request_compression_algorithm_key;
-  /** Metadata key for the outgoing (used) compression algorithm */
-  grpc_mdstr *mdstr_outgoing_compression_algorithm_key;
-  /** Metadata key for the accepted encodings */
-  grpc_mdstr *mdstr_compression_capabilities_key;
-  /** Precomputed metadata elements for all available compression algorithms */
-  grpc_mdelem *mdelem_compression_algorithms[GRPC_COMPRESS_ALGORITHMS_COUNT];
-  /** Precomputed metadata elements for the accepted encodings */
-  grpc_mdelem *mdelem_accept_encoding;
   /** The default, channel-level, compression algorithm */
   grpc_compression_algorithm default_compression_algorithm;
   /** Compression options for the channel */
   grpc_compression_options compression_options;
+  /** Supported compression algorithms */
+  gpr_uint32 supported_compression_algorithms;
 } channel_data;
 
 /** For each \a md element from the incoming metadata, filter out the entry for
@@ -91,7 +85,7 @@
   call_data *calld = elem->call_data;
   channel_data *channeld = elem->channel_data;
 
-  if (md->key == channeld->mdstr_request_compression_algorithm_key) {
+  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)) {
@@ -147,14 +141,13 @@
   /* hint compression algorithm */
   grpc_metadata_batch_add_tail(
       initial_metadata, &calld->compression_algorithm_storage,
-      GRPC_MDELEM_REF(
-          channeld
-              ->mdelem_compression_algorithms[calld->compression_algorithm]));
+      grpc_compression_encoding_mdelem(calld->compression_algorithm));
 
   /* convey supported compression algorithms */
-  grpc_metadata_batch_add_tail(
-      initial_metadata, &calld->accept_encoding_storage,
-      GRPC_MDELEM_REF(channeld->mdelem_accept_encoding));
+  grpc_metadata_batch_add_tail(initial_metadata,
+                               &calld->accept_encoding_storage,
+                               GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(
+                                   channeld->supported_compression_algorithms));
 }
 
 static void continue_send_message(grpc_exec_ctx *exec_ctx,
@@ -266,10 +259,6 @@
                               grpc_channel_element_args *args) {
   channel_data *channeld = elem->channel_data;
   grpc_compression_algorithm algo_idx;
-  const char *supported_algorithms_names[GRPC_COMPRESS_ALGORITHMS_COUNT - 1];
-  size_t supported_algorithms_idx = 0;
-  char *accept_encoding_str;
-  size_t accept_encoding_str_len;
 
   grpc_compression_options_init(&channeld->compression_options);
   channeld->compression_options.enabled_algorithms_bitset =
@@ -284,61 +273,22 @@
   channeld->compression_options.default_compression_algorithm =
       channeld->default_compression_algorithm;
 
-  channeld->mdstr_request_compression_algorithm_key = grpc_mdstr_from_string(
-      args->metadata_context, GRPC_COMPRESS_REQUEST_ALGORITHM_KEY);
-
-  channeld->mdstr_outgoing_compression_algorithm_key =
-      grpc_mdstr_from_string(args->metadata_context, "grpc-encoding");
-
-  channeld->mdstr_compression_capabilities_key =
-      grpc_mdstr_from_string(args->metadata_context, "grpc-accept-encoding");
-
+  channeld->supported_compression_algorithms = 0;
   for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) {
-    char *algorithm_name;
     /* skip disabled algorithms */
     if (grpc_compression_options_is_algorithm_enabled(
             &channeld->compression_options, algo_idx) == 0) {
       continue;
     }
-    GPR_ASSERT(grpc_compression_algorithm_name(algo_idx, &algorithm_name) != 0);
-    channeld->mdelem_compression_algorithms[algo_idx] =
-        grpc_mdelem_from_metadata_strings(
-            args->metadata_context,
-            GRPC_MDSTR_REF(channeld->mdstr_outgoing_compression_algorithm_key),
-            grpc_mdstr_from_string(args->metadata_context, algorithm_name));
-    if (algo_idx > 0) {
-      supported_algorithms_names[supported_algorithms_idx++] = algorithm_name;
-    }
+    channeld->supported_compression_algorithms |= 1u << algo_idx;
   }
 
-  /* TODO(dgq): gpr_strjoin_sep could be made to work with statically allocated
-   * arrays, as to avoid the heap allocs */
-  accept_encoding_str =
-      gpr_strjoin_sep(supported_algorithms_names, supported_algorithms_idx, ",",
-                      &accept_encoding_str_len);
-
-  channeld->mdelem_accept_encoding = grpc_mdelem_from_metadata_strings(
-      args->metadata_context,
-      GRPC_MDSTR_REF(channeld->mdstr_compression_capabilities_key),
-      grpc_mdstr_from_string(args->metadata_context, accept_encoding_str));
-  gpr_free(accept_encoding_str);
-
   GPR_ASSERT(!args->is_last);
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
-  channel_data *channeld = elem->channel_data;
-  grpc_compression_algorithm algo_idx;
-
-  GRPC_MDSTR_UNREF(channeld->mdstr_request_compression_algorithm_key);
-  GRPC_MDSTR_UNREF(channeld->mdstr_outgoing_compression_algorithm_key);
-  GRPC_MDSTR_UNREF(channeld->mdstr_compression_capabilities_key);
-  for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) {
-    GRPC_MDELEM_UNREF(channeld->mdelem_compression_algorithms[algo_idx]);
-  }
-  GRPC_MDELEM_UNREF(channeld->mdelem_accept_encoding);
 }
 
 const grpc_channel_filter grpc_compress_filter = {
diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c
index 3a0f68f..b9a30cd 100644
--- a/src/core/channel/http_client_filter.c
+++ b/src/core/channel/http_client_filter.c
@@ -37,6 +37,7 @@
 #include <grpc/support/string_util.h>
 #include "src/core/support/string.h"
 #include "src/core/profiling/timers.h"
+#include "src/core/transport/static_metadata.h"
 
 typedef struct call_data {
   grpc_linked_mdelem method;
@@ -57,12 +58,7 @@
 } call_data;
 
 typedef struct channel_data {
-  grpc_mdelem *te_trailers;
-  grpc_mdelem *method;
-  grpc_mdelem *scheme;
-  grpc_mdelem *content_type;
-  grpc_mdelem *status;
-  /** complete user agent mdelem */
+  grpc_mdelem *static_scheme;
   grpc_mdelem *user_agent;
 } channel_data;
 
@@ -73,14 +69,12 @@
 
 static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
   client_recv_filter_args *a = user_data;
-  grpc_call_element *elem = a->elem;
-  channel_data *channeld = elem->channel_data;
-  if (md == channeld->status) {
+  if (md == GRPC_MDELEM_STATUS_200) {
     return NULL;
-  } else if (md->key == channeld->status->key) {
-    grpc_call_element_send_cancel(a->exec_ctx, elem);
+  } else if (md->key == GRPC_MDSTR_STATUS) {
+    grpc_call_element_send_cancel(a->exec_ctx, a->elem);
     return NULL;
-  } else if (md->key == channeld->content_type->key) {
+  } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
     return NULL;
   }
   return md;
@@ -98,14 +92,12 @@
 }
 
 static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
-  grpc_call_element *elem = user_data;
-  channel_data *channeld = elem->channel_data;
   /* eat the things we'd like to set ourselves */
-  if (md->key == channeld->method->key) return NULL;
-  if (md->key == channeld->scheme->key) return NULL;
-  if (md->key == channeld->te_trailers->key) return NULL;
-  if (md->key == channeld->content_type->key) return NULL;
-  if (md->key == channeld->user_agent->key) return NULL;
+  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;
 }
 
@@ -120,14 +112,14 @@
     /* Send : prefixed headers, which have to be before any application
        layer headers. */
     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method,
-                                 GRPC_MDELEM_REF(channeld->method));
+                                 GRPC_MDELEM_METHOD_POST);
     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme,
-                                 GRPC_MDELEM_REF(channeld->scheme));
+                                 channeld->static_scheme);
     grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers,
-                                 GRPC_MDELEM_REF(channeld->te_trailers));
-    grpc_metadata_batch_add_tail(op->send_initial_metadata,
-                                 &calld->content_type,
-                                 GRPC_MDELEM_REF(channeld->content_type));
+                                 GRPC_MDELEM_TE_TRAILERS);
+    grpc_metadata_batch_add_tail(
+        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));
   }
@@ -162,21 +154,28 @@
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
                               grpc_call_element *elem) {}
 
-static const char *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};
   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) {
-        return args->args[i].value.string;
+        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)) {
+            return valid_schemes[j];
+          }
+        }
       }
     }
   }
-  return "http";
+  return GRPC_MDELEM_SCHEME_HTTP;
 }
 
-static grpc_mdstr *user_agent_from_args(grpc_mdctx *mdctx,
-                                        const grpc_channel_args *args) {
+static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args) {
   gpr_strvec v;
   size_t i;
   int is_first = 1;
@@ -218,7 +217,7 @@
 
   tmp = gpr_strvec_flatten(&v, NULL);
   gpr_strvec_destroy(&v);
-  result = grpc_mdstr_from_string(mdctx, tmp);
+  result = grpc_mdstr_from_string(tmp);
   gpr_free(tmp);
 
   return result;
@@ -228,43 +227,18 @@
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
                               grpc_channel_element *elem,
                               grpc_channel_element_args *args) {
-  /* grab pointers to our data from the channel element */
-  channel_data *channeld = elem->channel_data;
-
-  /* The first and the last filters tend to be implemented differently to
-     handle the case that there's no 'next' filter to call on the up or down
-     path */
+  channel_data *chand = elem->channel_data;
   GPR_ASSERT(!args->is_last);
-
-  /* initialize members */
-  channeld->te_trailers =
-      grpc_mdelem_from_strings(args->metadata_context, "te", "trailers");
-  channeld->method =
-      grpc_mdelem_from_strings(args->metadata_context, ":method", "POST");
-  channeld->scheme = grpc_mdelem_from_strings(
-      args->metadata_context, ":scheme", scheme_from_args(args->channel_args));
-  channeld->content_type = grpc_mdelem_from_strings(
-      args->metadata_context, "content-type", "application/grpc");
-  channeld->status =
-      grpc_mdelem_from_strings(args->metadata_context, ":status", "200");
-  channeld->user_agent = grpc_mdelem_from_metadata_strings(
-      args->metadata_context,
-      grpc_mdstr_from_string(args->metadata_context, "user-agent"),
-      user_agent_from_args(args->metadata_context, args->channel_args));
+  chand->static_scheme = scheme_from_args(args->channel_args);
+  chand->user_agent = grpc_mdelem_from_metadata_strings(
+      GRPC_MDSTR_USER_AGENT, user_agent_from_args(args->channel_args));
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
-  /* grab pointers to our data from the channel element */
-  channel_data *channeld = elem->channel_data;
-
-  GRPC_MDELEM_UNREF(channeld->te_trailers);
-  GRPC_MDELEM_UNREF(channeld->method);
-  GRPC_MDELEM_UNREF(channeld->scheme);
-  GRPC_MDELEM_UNREF(channeld->content_type);
-  GRPC_MDELEM_UNREF(channeld->status);
-  GRPC_MDELEM_UNREF(channeld->user_agent);
+  channel_data *chand = elem->channel_data;
+  GRPC_MDELEM_UNREF(chand->user_agent);
 }
 
 const grpc_channel_filter grpc_http_client_filter = {
diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c
index 2adfe2b..c1645c2 100644
--- a/src/core/channel/http_server_filter.c
+++ b/src/core/channel/http_server_filter.c
@@ -37,6 +37,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include "src/core/profiling/timers.h"
+#include "src/core/transport/static_metadata.h"
 
 typedef struct call_data {
   gpr_uint8 seen_path;
@@ -57,22 +58,7 @@
   grpc_closure hs_on_recv;
 } call_data;
 
-typedef struct channel_data {
-  grpc_mdelem *te_trailers;
-  grpc_mdelem *method_post;
-  grpc_mdelem *http_scheme;
-  grpc_mdelem *https_scheme;
-  /* TODO(klempner): Remove this once we stop using it */
-  grpc_mdelem *grpc_scheme;
-  grpc_mdelem *content_type;
-  grpc_mdelem *status_ok;
-  grpc_mdelem *status_not_found;
-  grpc_mdstr *path_key;
-  grpc_mdstr *authority_key;
-  grpc_mdstr *host_key;
-
-  grpc_mdctx *mdctx;
-} channel_data;
+typedef struct channel_data { gpr_uint8 unused; } channel_data;
 
 typedef struct {
   grpc_call_element *elem;
@@ -82,25 +68,24 @@
 static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
   server_filter_args *a = user_data;
   grpc_call_element *elem = a->elem;
-  channel_data *channeld = elem->channel_data;
   call_data *calld = elem->call_data;
 
   /* Check if it is one of the headers we care about. */
-  if (md == channeld->te_trailers || md == channeld->method_post ||
-      md == channeld->http_scheme || md == channeld->https_scheme ||
-      md == channeld->grpc_scheme || md == channeld->content_type) {
+  if (md == GRPC_MDELEM_TE_TRAILERS || md == GRPC_MDELEM_METHOD_POST ||
+      md == GRPC_MDELEM_SCHEME_HTTP || md == GRPC_MDELEM_SCHEME_HTTPS ||
+      md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
     /* swallow it */
-    if (md == channeld->method_post) {
+    if (md == GRPC_MDELEM_METHOD_POST) {
       calld->seen_post = 1;
-    } else if (md->key == channeld->http_scheme->key) {
+    } else if (md->key == GRPC_MDSTR_SCHEME) {
       calld->seen_scheme = 1;
-    } else if (md == channeld->te_trailers) {
+    } 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 == channeld->content_type->key) {
+  } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
     if (strncmp(grpc_mdstr_as_c_string(md->value), "application/grpc+", 17) ==
         0) {
       /* Although the C implementation doesn't (currently) generate them,
@@ -112,12 +97,11 @@
       /* 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",
-              channeld->content_type->key);
+              grpc_mdstr_as_c_string(md->value));
     }
     return NULL;
-  } else if (md->key == channeld->te_trailers->key ||
-             md->key == channeld->method_post->key ||
-             md->key == channeld->http_scheme->key) {
+  } 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. */
@@ -125,22 +109,21 @@
        on the wire here. */
     grpc_call_element_send_cancel(a->exec_ctx, elem);
     return NULL;
-  } else if (md->key == channeld->path_key) {
+  } else if (md->key == GRPC_MDSTR_PATH) {
     if (calld->seen_path) {
       gpr_log(GPR_ERROR, "Received :path twice");
       return NULL;
     }
     calld->seen_path = 1;
     return md;
-  } else if (md->key == channeld->authority_key) {
+  } else if (md->key == GRPC_MDSTR_AUTHORITY) {
     calld->seen_authority = 1;
     return md;
-  } else if (md->key == channeld->host_key) {
+  } else if (md->key == GRPC_MDSTR_HOST) {
     /* translate host to :authority since :authority may be
        omitted */
     grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
-        channeld->mdctx, GRPC_MDSTR_REF(channeld->authority_key),
-        GRPC_MDSTR_REF(md->value));
+        GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value));
     GRPC_MDELEM_UNREF(md);
     calld->seen_authority = 1;
     return authority;
@@ -191,15 +174,14 @@
                          grpc_transport_stream_op *op) {
   /* grab pointers to our data from the call element */
   call_data *calld = elem->call_data;
-  channel_data *channeld = elem->channel_data;
 
   if (op->send_initial_metadata != NULL && !calld->sent_status) {
     calld->sent_status = 1;
     grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->status,
-                                 GRPC_MDELEM_REF(channeld->status_ok));
-    grpc_metadata_batch_add_tail(op->send_initial_metadata,
-                                 &calld->content_type,
-                                 GRPC_MDELEM_REF(channeld->content_type));
+                                 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->recv_initial_metadata) {
@@ -238,56 +220,12 @@
 static void init_channel_elem(grpc_exec_ctx *exec_ctx,
                               grpc_channel_element *elem,
                               grpc_channel_element_args *args) {
-  /* grab pointers to our data from the channel element */
-  channel_data *channeld = elem->channel_data;
-
-  /* The first and the last filters tend to be implemented differently to
-     handle the case that there's no 'next' filter to call on the up or down
-     path */
   GPR_ASSERT(!args->is_last);
-
-  /* initialize members */
-  channeld->te_trailers =
-      grpc_mdelem_from_strings(args->metadata_context, "te", "trailers");
-  channeld->status_ok =
-      grpc_mdelem_from_strings(args->metadata_context, ":status", "200");
-  channeld->status_not_found =
-      grpc_mdelem_from_strings(args->metadata_context, ":status", "404");
-  channeld->method_post =
-      grpc_mdelem_from_strings(args->metadata_context, ":method", "POST");
-  channeld->http_scheme =
-      grpc_mdelem_from_strings(args->metadata_context, ":scheme", "http");
-  channeld->https_scheme =
-      grpc_mdelem_from_strings(args->metadata_context, ":scheme", "https");
-  channeld->grpc_scheme =
-      grpc_mdelem_from_strings(args->metadata_context, ":scheme", "grpc");
-  channeld->path_key = grpc_mdstr_from_string(args->metadata_context, ":path");
-  channeld->authority_key =
-      grpc_mdstr_from_string(args->metadata_context, ":authority");
-  channeld->host_key = grpc_mdstr_from_string(args->metadata_context, "host");
-  channeld->content_type = grpc_mdelem_from_strings(
-      args->metadata_context, "content-type", "application/grpc");
-
-  channeld->mdctx = args->metadata_context;
 }
 
 /* Destructor for channel data */
 static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
                                  grpc_channel_element *elem) {
-  /* grab pointers to our data from the channel element */
-  channel_data *channeld = elem->channel_data;
-
-  GRPC_MDELEM_UNREF(channeld->te_trailers);
-  GRPC_MDELEM_UNREF(channeld->status_ok);
-  GRPC_MDELEM_UNREF(channeld->status_not_found);
-  GRPC_MDELEM_UNREF(channeld->method_post);
-  GRPC_MDELEM_UNREF(channeld->http_scheme);
-  GRPC_MDELEM_UNREF(channeld->https_scheme);
-  GRPC_MDELEM_UNREF(channeld->grpc_scheme);
-  GRPC_MDELEM_UNREF(channeld->content_type);
-  GRPC_MDSTR_UNREF(channeld->path_key);
-  GRPC_MDSTR_UNREF(channeld->authority_key);
-  GRPC_MDSTR_UNREF(channeld->host_key);
 }
 
 const grpc_channel_filter grpc_http_server_filter = {
diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c
index ff211bc..28496ac 100644
--- a/src/core/client_config/subchannel.c
+++ b/src/core/client_config/subchannel.c
@@ -90,8 +90,6 @@
   size_t addr_len;
   /** initial string to send to peer */
   gpr_slice initial_connect_string;
-  /** metadata context */
-  grpc_mdctx *mdctx;
   /** master channel - the grpc_channel instance that ultimately owns
       this channel_data via its channel stack.
       We occasionally use this to bump the refcount on the master channel
@@ -271,7 +269,6 @@
   grpc_channel_args_destroy(c->args);
   gpr_free(c->addr);
   gpr_slice_unref(c->initial_connect_string);
-  grpc_mdctx_unref(c->mdctx);
   grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker);
   grpc_connector_unref(exec_ctx, c->connector);
   gpr_free(c);
@@ -312,11 +309,9 @@
   grpc_set_initial_connect_string(&c->addr, &c->addr_len,
                                   &c->initial_connect_string);
   c->args = grpc_channel_args_copy(args->args);
-  c->mdctx = args->mdctx;
   c->master = args->master;
   c->pollset_set = grpc_client_channel_get_connecting_pollset_set(parent_elem);
   c->random = random_seed();
-  grpc_mdctx_ref(c->mdctx);
   grpc_closure_init(&c->connected, subchannel_connected, c);
   grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE,
                                "subchannel");
@@ -631,7 +626,7 @@
   con->refs = 0;
   con->subchannel = c;
   grpc_channel_stack_init(exec_ctx, filters, num_filters, c->master, c->args,
-                          c->mdctx, stk);
+                          stk);
   grpc_connected_channel_bind_transport(stk, c->connecting_result.transport);
   gpr_free((void *)c->connecting_result.filters);
   memset(&c->connecting_result, 0, sizeof(c->connecting_result));
@@ -878,10 +873,6 @@
   return call;
 }
 
-grpc_mdctx *grpc_subchannel_get_mdctx(grpc_subchannel *subchannel) {
-  return subchannel->mdctx;
-}
-
 grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel) {
   return subchannel->master;
 }
diff --git a/src/core/client_config/subchannel.h b/src/core/client_config/subchannel.h
index 381b768..85ea373 100644
--- a/src/core/client_config/subchannel.h
+++ b/src/core/client_config/subchannel.h
@@ -147,8 +147,6 @@
   /** Address to connect to */
   struct sockaddr *addr;
   size_t addr_len;
-  /** metadata context to use */
-  grpc_mdctx *mdctx;
   /** master channel */
   grpc_channel *master;
 };
@@ -157,9 +155,6 @@
 grpc_subchannel *grpc_subchannel_create(grpc_connector *connector,
                                         grpc_subchannel_args *args);
 
-/** Return the metadata context associated with the subchannel */
-grpc_mdctx *grpc_subchannel_get_mdctx(grpc_subchannel *subchannel);
-
 /** Return the master channel associated with the subchannel */
 grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel);
 
diff --git a/src/core/compression/algorithm.c b/src/core/compression/algorithm.c
index fd95a3c..73d91fa 100644
--- a/src/core/compression/algorithm.c
+++ b/src/core/compression/algorithm.c
@@ -37,7 +37,9 @@
 #include <grpc/compression.h>
 #include <grpc/support/useful.h>
 
+#include "src/core/compression/algorithm_metadata.h"
 #include "src/core/surface/api_trace.h"
+#include "src/core/transport/static_metadata.h"
 
 int grpc_compression_algorithm_parse(const char *name, size_t name_length,
                                      grpc_compression_algorithm *algorithm) {
@@ -72,17 +74,55 @@
   switch (algorithm) {
     case GRPC_COMPRESS_NONE:
       *name = "identity";
-      break;
+      return 1;
     case GRPC_COMPRESS_DEFLATE:
       *name = "deflate";
-      break;
+      return 1;
     case GRPC_COMPRESS_GZIP:
       *name = "gzip";
-      break;
-    default:
+      return 1;
+    case GRPC_COMPRESS_ALGORITHMS_COUNT:
       return 0;
   }
-  return 1;
+  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;
+  return GRPC_COMPRESS_ALGORITHMS_COUNT;
+}
+
+grpc_mdstr *grpc_compression_algorithm_mdstr(
+    grpc_compression_algorithm algorithm) {
+  switch (algorithm) {
+    case GRPC_COMPRESS_NONE:
+      return GRPC_MDSTR_IDENTITY;
+    case GRPC_COMPRESS_DEFLATE:
+      return GRPC_MDSTR_DEFLATE;
+    case GRPC_COMPRESS_GZIP:
+      return GRPC_MDSTR_GZIP;
+    case GRPC_COMPRESS_ALGORITHMS_COUNT:
+      return NULL;
+  }
+  return NULL;
+}
+
+grpc_mdelem *grpc_compression_encoding_mdelem(
+    grpc_compression_algorithm algorithm) {
+  switch (algorithm) {
+    case GRPC_COMPRESS_NONE:
+      return GRPC_MDELEM_GRPC_ENCODING_IDENTITY;
+    case GRPC_COMPRESS_DEFLATE:
+      return GRPC_MDELEM_GRPC_ENCODING_DEFLATE;
+    case GRPC_COMPRESS_GZIP:
+      return GRPC_MDELEM_GRPC_ENCODING_GZIP;
+    case GRPC_COMPRESS_ALGORITHMS_COUNT:
+      return NULL;
+  }
+  return NULL;
 }
 
 /* TODO(dgq): Add the ability to specify parameters to the individual
diff --git a/src/core/compression/algorithm_metadata.h b/src/core/compression/algorithm_metadata.h
new file mode 100644
index 0000000..882633c
--- /dev/null
+++ b/src/core/compression/algorithm_metadata.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_METADATA_H
+#define GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_METADATA_H
+
+#include <grpc/compression.h>
+#include "src/core/transport/metadata.h"
+
+/** Return compression algorithm based metadata value */
+grpc_mdstr *grpc_compression_algorithm_mdstr(
+    grpc_compression_algorithm algorithm);
+
+/** Return compression algorithm based metadata element (grpc-encoding: xxx) */
+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);
+
+#endif /* GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_METADATA_H */
diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c
index 7ff80e6..2be0ea2 100644
--- a/src/core/iomgr/fd_posix.c
+++ b/src/core/iomgr/fd_posix.c
@@ -207,14 +207,21 @@
 }
 
 void grpc_fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done,
-                    const char *reason) {
+                    int *release_fd, const char *reason) {
   fd->on_done_closure = on_done;
-  shutdown(fd->fd, SHUT_RDWR);
+  fd->released = release_fd != NULL;
+  if (!fd->released) {
+    shutdown(fd->fd, SHUT_RDWR);
+  } else {
+    *release_fd = fd->fd;
+  }
   gpr_mu_lock(&fd->mu);
   REF_BY(fd, 1, reason); /* remove active status, but keep referenced */
   if (!has_watchers(fd)) {
     fd->closed = 1;
-    close(fd->fd);
+    if (!fd->released) {
+      close(fd->fd);
+    }
     grpc_exec_ctx_enqueue(exec_ctx, fd->on_done_closure, 1);
   } else {
     wake_all_watchers_locked(fd);
@@ -406,7 +413,9 @@
   }
   if (grpc_fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) {
     fd->closed = 1;
-    close(fd->fd);
+    if (!fd->released) {
+      close(fd->fd);
+    }
     grpc_exec_ctx_enqueue(exec_ctx, fd->on_done_closure, 1);
   }
   gpr_mu_unlock(&fd->mu);
diff --git a/src/core/iomgr/fd_posix.h b/src/core/iomgr/fd_posix.h
index dc917eb..d628ef3 100644
--- a/src/core/iomgr/fd_posix.h
+++ b/src/core/iomgr/fd_posix.h
@@ -62,6 +62,7 @@
   gpr_mu mu;
   int shutdown;
   int closed;
+  int released;
 
   /* The watcher list.
 
@@ -107,11 +108,12 @@
 /* Releases fd to be asynchronously destroyed.
    on_done is called when the underlying file descriptor is definitely close()d.
    If on_done is NULL, no callback will be made.
+   If release_fd is not NULL, it's set to fd and fd will not be closed.
    Requires: *fd initialized; no outstanding notify_on_read or
    notify_on_write.
    MUST NOT be called with a pollset lock taken */
 void grpc_fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done,
-                    const char *reason);
+                    int *release_fd, const char *reason);
 
 /* Begin polling on an fd.
    Registers that the given pollset is interested in this fd - so that if read
diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c
index abd6315..d9d24ee 100644
--- a/src/core/iomgr/tcp_client_posix.c
+++ b/src/core/iomgr/tcp_client_posix.c
@@ -196,7 +196,7 @@
 finish:
   if (fd != NULL) {
     grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd);
-    grpc_fd_orphan(exec_ctx, fd, NULL, "tcp_client_orphan");
+    grpc_fd_orphan(exec_ctx, fd, NULL, NULL, "tcp_client_orphan");
     fd = NULL;
   }
   done = (--ac->refs == 0);
@@ -265,7 +265,7 @@
 
   if (errno != EWOULDBLOCK && errno != EINPROGRESS) {
     gpr_log(GPR_ERROR, "connect error to '%s': %s", addr_str, strerror(errno));
-    grpc_fd_orphan(exec_ctx, fdobj, NULL, "tcp_client_connect_error");
+    grpc_fd_orphan(exec_ctx, fdobj, NULL, NULL, "tcp_client_connect_error");
     grpc_exec_ctx_enqueue(exec_ctx, closure, 0);
     goto done;
   }
diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c
index 915553d..f3be41a 100644
--- a/src/core/iomgr/tcp_posix.c
+++ b/src/core/iomgr/tcp_posix.c
@@ -90,6 +90,8 @@
 
   grpc_closure *read_cb;
   grpc_closure *write_cb;
+  grpc_closure *release_fd_cb;
+  int *release_fd;
 
   grpc_closure read_closure;
   grpc_closure write_closure;
@@ -108,7 +110,8 @@
 }
 
 static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
-  grpc_fd_orphan(exec_ctx, tcp->em_fd, NULL, "tcp_unref_orphan");
+  grpc_fd_orphan(exec_ctx, tcp->em_fd, tcp->release_fd_cb, tcp->release_fd,
+                 "tcp_unref_orphan");
   gpr_slice_buffer_destroy(&tcp->last_read_buffer);
   gpr_free(tcp->peer_string);
   gpr_free(tcp);
@@ -452,6 +455,8 @@
   tcp->fd = em_fd->fd;
   tcp->read_cb = NULL;
   tcp->write_cb = NULL;
+  tcp->release_fd_cb = NULL;
+  tcp->release_fd = NULL;
   tcp->incoming_buffer = NULL;
   tcp->slice_size = slice_size;
   tcp->iov_size = 1;
@@ -468,4 +473,13 @@
   return &tcp->base;
 }
 
+void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
+                                     int *fd, grpc_closure *done) {
+  grpc_tcp *tcp = (grpc_tcp *)ep;
+  GPR_ASSERT(ep->vtable == &vtable);
+  tcp->release_fd = fd;
+  tcp->release_fd_cb = done;
+  TCP_UNREF(exec_ctx, tcp, "destroy");
+}
+
 #endif
diff --git a/src/core/iomgr/tcp_posix.h b/src/core/iomgr/tcp_posix.h
index 40b3ae2..b554983 100644
--- a/src/core/iomgr/tcp_posix.h
+++ b/src/core/iomgr/tcp_posix.h
@@ -56,4 +56,10 @@
 grpc_endpoint *grpc_tcp_create(grpc_fd *fd, size_t read_slice_size,
                                const char *peer_string);
 
+/* Destroy the tcp endpoint without closing its fd. *fd will be set and done
+ * will be called when the endpoint is destroyed.
+ * Requires: ep must be a tcp endpoint and fd must not be NULL. */
+void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
+                                     int *fd, grpc_closure *done);
+
 #endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_POSIX_H */
diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c
index 0ece77c..a89ee02 100644
--- a/src/core/iomgr/tcp_server_posix.c
+++ b/src/core/iomgr/tcp_server_posix.c
@@ -193,7 +193,7 @@
       }
       sp->destroyed_closure.cb = destroyed_port;
       sp->destroyed_closure.cb_arg = s;
-      grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure,
+      grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL,
                      "tcp_listener_shutdown");
     }
     gpr_mu_unlock(&s->mu);
diff --git a/src/core/iomgr/udp_server.c b/src/core/iomgr/udp_server.c
index 9903e97..782fbd9 100644
--- a/src/core/iomgr/udp_server.c
+++ b/src/core/iomgr/udp_server.c
@@ -179,7 +179,7 @@
       }
       sp->destroyed_closure.cb = destroyed_port;
       sp->destroyed_closure.cb_arg = s;
-      grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure,
+      grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL,
                      "udp_listener_shutdown");
     }
     gpr_mu_unlock(&s->mu);
diff --git a/src/core/iomgr/workqueue_posix.c b/src/core/iomgr/workqueue_posix.c
index c087b88..2e30178 100644
--- a/src/core/iomgr/workqueue_posix.c
+++ b/src/core/iomgr/workqueue_posix.c
@@ -115,7 +115,7 @@
     /* HACK: let wakeup_fd code know that we stole the fd */
     workqueue->wakeup_fd.read_fd = 0;
     grpc_wakeup_fd_destroy(&workqueue->wakeup_fd);
-    grpc_fd_orphan(exec_ctx, workqueue->wakeup_read_fd, NULL, "destroy");
+    grpc_fd_orphan(exec_ctx, workqueue->wakeup_read_fd, NULL, NULL, "destroy");
     gpr_free(workqueue);
   } else {
     gpr_mu_lock(&workqueue->mu);
diff --git a/src/core/security/client_auth_filter.c b/src/core/security/client_auth_filter.c
index d3d948a..4e5be03 100644
--- a/src/core/security/client_auth_filter.c
+++ b/src/core/security/client_auth_filter.c
@@ -45,6 +45,7 @@
 #include "src/core/security/security_connector.h"
 #include "src/core/security/credentials.h"
 #include "src/core/surface/call.h"
+#include "src/core/transport/static_metadata.h"
 
 #define MAX_CREDENTIALS_METADATA_COUNT 4
 
@@ -67,11 +68,6 @@
 /* We can have a per-channel credentials. */
 typedef struct {
   grpc_channel_security_connector *security_connector;
-  grpc_mdctx *md_ctx;
-  grpc_mdstr *authority_string;
-  grpc_mdstr *path_string;
-  grpc_mdstr *error_msg_key;
-  grpc_mdstr *status_key;
 } channel_data;
 
 static void reset_auth_metadata_context(
@@ -104,7 +100,6 @@
                                     grpc_credentials_status status) {
   grpc_call_element *elem = (grpc_call_element *)user_data;
   call_data *calld = elem->call_data;
-  channel_data *chand = elem->channel_data;
   grpc_transport_stream_op *op = &calld->op;
   grpc_metadata_batch *mdb;
   size_t i;
@@ -120,7 +115,7 @@
   for (i = 0; i < num_md; i++) {
     grpc_metadata_batch_add_tail(
         mdb, &calld->md_links[i],
-        grpc_mdelem_from_slices(chand->md_ctx, gpr_slice_ref(md_elems[i].key),
+        grpc_mdelem_from_slices(gpr_slice_ref(md_elems[i].key),
                                 gpr_slice_ref(md_elems[i].value)));
   }
   grpc_call_next_op(exec_ctx, elem, op);
@@ -243,10 +238,10 @@
       grpc_mdelem *md = l->md;
       /* Pointer comparison is OK for md_elems created from the same context.
        */
-      if (md->key == chand->authority_string) {
+      if (md->key == GRPC_MDSTR_AUTHORITY) {
         if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host);
         calld->host = GRPC_MDSTR_REF(md->value);
-      } else if (md->key == chand->path_string) {
+      } else if (md->key == GRPC_MDSTR_PATH) {
         if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method);
         calld->method = GRPC_MDSTR_REF(md->value);
       }
@@ -326,11 +321,6 @@
   chand->security_connector =
       (grpc_channel_security_connector *)GRPC_SECURITY_CONNECTOR_REF(
           sc, "client_auth_filter");
-  chand->md_ctx = args->metadata_context;
-  chand->authority_string = grpc_mdstr_from_string(chand->md_ctx, ":authority");
-  chand->path_string = grpc_mdstr_from_string(chand->md_ctx, ":path");
-  chand->error_msg_key = grpc_mdstr_from_string(chand->md_ctx, "grpc-message");
-  chand->status_key = grpc_mdstr_from_string(chand->md_ctx, "grpc-status");
 }
 
 /* Destructor for channel data */
@@ -339,19 +329,8 @@
   /* grab pointers to our data from the channel element */
   channel_data *chand = elem->channel_data;
   grpc_channel_security_connector *ctx = chand->security_connector;
-  if (ctx != NULL)
+  if (ctx != NULL) {
     GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter");
-  if (chand->authority_string != NULL) {
-    GRPC_MDSTR_UNREF(chand->authority_string);
-  }
-  if (chand->error_msg_key != NULL) {
-    GRPC_MDSTR_UNREF(chand->error_msg_key);
-  }
-  if (chand->status_key != NULL) {
-    GRPC_MDSTR_UNREF(chand->status_key);
-  }
-  if (chand->path_string != NULL) {
-    GRPC_MDSTR_UNREF(chand->path_string);
   }
 }
 
diff --git a/src/core/security/server_auth_filter.c b/src/core/security/server_auth_filter.c
index fee962b..5cfee6d 100644
--- a/src/core/security/server_auth_filter.c
+++ b/src/core/security/server_auth_filter.c
@@ -58,7 +58,6 @@
 typedef struct channel_data {
   grpc_auth_context *auth_context;
   grpc_server_credentials *creds;
-  grpc_mdctx *mdctx;
 } channel_data;
 
 static grpc_metadata_array metadata_batch_to_md_array(
@@ -247,7 +246,6 @@
   chand->auth_context =
       GRPC_AUTH_CONTEXT_REF(auth_context, "server_auth_filter");
   chand->creds = grpc_server_credentials_ref(creds);
-  chand->mdctx = args->metadata_context;
 }
 
 /* Destructor for channel data */
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index 1ea269b..14a5d0f 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -87,7 +87,7 @@
 }
 
 static void setup_transport(grpc_exec_ctx *exec_ctx, void *statep,
-                            grpc_transport *transport, grpc_mdctx *mdctx) {
+                            grpc_transport *transport) {
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_server_auth_filter, &grpc_http_server_filter};
   grpc_server_secure_state *state = statep;
@@ -99,7 +99,7 @@
       grpc_server_get_channel_args(state->server), args_to_add,
       GPR_ARRAY_SIZE(args_to_add));
   grpc_server_setup_transport(exec_ctx, state->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx, args_copy);
+                              GPR_ARRAY_SIZE(extra_filters), args_copy);
   grpc_channel_args_destroy(args_copy);
 }
 
@@ -130,16 +130,14 @@
                                      grpc_endpoint *secure_endpoint) {
   grpc_server_secure_state *state = statep;
   grpc_transport *transport;
-  grpc_mdctx *mdctx;
   if (status == GRPC_SECURITY_OK) {
     gpr_mu_lock(&state->mu);
     remove_tcp_from_list_locked(state, wrapped_endpoint);
     if (!state->is_shutdown) {
-      mdctx = grpc_mdctx_create();
       transport = grpc_create_chttp2_transport(
           exec_ctx, grpc_server_get_channel_args(state->server),
-          secure_endpoint, mdctx, 0);
-      setup_transport(exec_ctx, state, transport, mdctx);
+          secure_endpoint, 0);
+      setup_transport(exec_ctx, state, transport);
       grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
     } else {
       /* We need to consume this here, because the server may already have gone
diff --git a/src/core/support/slice.c b/src/core/support/slice.c
index 53024e8..5b091f1 100644
--- a/src/core/support/slice.c
+++ b/src/core/support/slice.c
@@ -57,6 +57,21 @@
   }
 }
 
+/* gpr_slice_from_static_string support structure - a refcount that does
+   nothing */
+static void noop_ref_or_unref(void *unused) {}
+
+static gpr_slice_refcount noop_refcount = {noop_ref_or_unref,
+                                           noop_ref_or_unref};
+
+gpr_slice gpr_slice_from_static_string(const char *s) {
+  gpr_slice slice;
+  slice.refcount = &noop_refcount;
+  slice.data.refcounted.bytes = (gpr_uint8 *)s;
+  slice.data.refcounted.length = strlen(s);
+  return slice;
+}
+
 /* gpr_slice_new support structures - we create a refcount object extended
    with the user provided data pointer & destroy function */
 typedef struct new_slice_refcount {
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 86d5fca..49a0c7e 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -43,6 +43,7 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/channel/channel_stack.h"
+#include "src/core/compression/algorithm_metadata.h"
 #include "src/core/iomgr/timer.h"
 #include "src/core/profiling/timers.h"
 #include "src/core/support/string.h"
@@ -50,6 +51,7 @@
 #include "src/core/surface/call.h"
 #include "src/core/surface/channel.h"
 #include "src/core/surface/completion_queue.h"
+#include "src/core/transport/static_metadata.h"
 
 /** The maximum number of concurrent batches possible.
     Based upon the maximum number of individually queueable ops in the batch
@@ -135,7 +137,6 @@
   grpc_channel *channel;
   grpc_call *parent;
   grpc_call *first_child;
-  grpc_mdctx *metadata_context;
   /* TODO(ctiller): share with cq if possible? */
   gpr_mu mu;
 
@@ -267,7 +268,6 @@
   }
   call->send_deadline = send_deadline;
   GRPC_CHANNEL_INTERNAL_REF(channel, "call");
-  call->metadata_context = grpc_channel_get_metadata_context(channel);
   /* initial refcount dropped by grpc_call_destroy */
   grpc_call_stack_init(&exec_ctx, channel_stack, 1, destroy_call, call,
                        call->context, server_transport_data,
@@ -567,9 +567,8 @@
     grpc_metadata *md = &metadata[i];
     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(call->metadata_context, md->key,
-                                               (const gpr_uint8 *)md->value,
-                                               md->value_length);
+    l->md = grpc_mdelem_from_string_and_buffer(
+        md->key, (const gpr_uint8 *)md->value, md->value_length);
     if (!grpc_mdstr_is_legal_header(l->md->key)) {
       gpr_log(GPR_ERROR, "attempt to send invalid metadata key: %s",
               grpc_mdstr_as_c_string(l->md->key));
@@ -712,8 +711,7 @@
                                           grpc_status_code status,
                                           const char *description) {
   grpc_mdstr *details =
-      description ? grpc_mdstr_from_string(c->metadata_context, description)
-                  : NULL;
+      description ? grpc_mdstr_from_string(description) : NULL;
   cancel_closure *cc = gpr_malloc(sizeof(*cc));
 
   GPR_ASSERT(status != GRPC_STATUS_OK);
@@ -788,8 +786,12 @@
 
 static gpr_uint32 decode_status(grpc_mdelem *md) {
   gpr_uint32 status;
-  void *user_data = grpc_mdelem_get_user_data(md, destroy_status);
-  if (user_data) {
+  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;
+  user_data = grpc_mdelem_get_user_data(md, destroy_status);
+  if (user_data != NULL) {
     status = ((gpr_uint32)(gpr_intptr)user_data) - STATUS_OFFSET;
   } else {
     if (!gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value),
@@ -803,38 +805,23 @@
   return status;
 }
 
-/* just as for status above, we need to offset: metadata userdata can't hold a
- * zero (null), which in this case is used to signal no compression */
-#define COMPRESS_OFFSET 1
-static void destroy_compression(void *ignored) {}
-
 static gpr_uint32 decode_compression(grpc_mdelem *md) {
-  grpc_compression_algorithm algorithm;
-  void *user_data = grpc_mdelem_get_user_data(md, destroy_compression);
-  if (user_data) {
-    algorithm =
-        ((grpc_compression_algorithm)(gpr_intptr)user_data) - COMPRESS_OFFSET;
-  } else {
+  grpc_compression_algorithm algorithm =
+      grpc_compression_algorithm_from_mdstr(md->value);
+  if (algorithm == GRPC_COMPRESS_ALGORITHMS_COUNT) {
     const char *md_c_str = grpc_mdstr_as_c_string(md->value);
-    if (!grpc_compression_algorithm_parse(md_c_str, strlen(md_c_str),
-                                          &algorithm)) {
-      gpr_log(GPR_ERROR, "Invalid compression algorithm: '%s'", md_c_str);
-      assert(0);
-    }
-    grpc_mdelem_set_user_data(
-        md, destroy_compression,
-        (void *)(gpr_intptr)(algorithm + COMPRESS_OFFSET));
+    gpr_log(GPR_ERROR, "Invalid compression algorithm: '%s'", md_c_str);
   }
   return algorithm;
 }
 
 static grpc_mdelem *recv_common_filter(grpc_call *call, grpc_mdelem *elem) {
-  if (elem->key == grpc_channel_get_status_string(call->channel)) {
+  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_channel_get_message_string(call->channel)) {
+  } else if (elem->key == GRPC_MDSTR_GRPC_MESSAGE) {
     GPR_TIMER_BEGIN("status-details", 0);
     set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(elem->value));
     GPR_TIMER_END("status-details", 0);
@@ -867,14 +854,12 @@
   elem = recv_common_filter(call, elem);
   if (elem == NULL) {
     return NULL;
-  } else if (elem->key ==
-             grpc_channel_get_compression_algorithm_string(call->channel)) {
+  } else if (elem->key == GRPC_MDSTR_GRPC_ENCODING) {
     GPR_TIMER_BEGIN("compression_algorithm", 0);
     set_compression_algorithm(call, decode_compression(elem));
     GPR_TIMER_END("compression_algorithm", 0);
     return NULL;
-  } else if (elem->key == grpc_channel_get_encodings_accepted_by_peer_string(
-                              call->channel)) {
+  } else if (elem->key == GRPC_MDSTR_GRPC_ACCEPT_ENCODING) {
     GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0);
     set_encodings_accepted_by_peer(call, elem);
     GPR_TIMER_END("encodings_accepted_by_peer", 0);
@@ -1241,10 +1226,8 @@
             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(
-              call->metadata_context,
-              GRPC_MDSTR_REF(grpc_channel_get_message_string(call->channel)),
+              GRPC_MDSTR_GRPC_MESSAGE,
               grpc_mdstr_from_string(
-                  call->metadata_context,
                   op->data.send_status_from_server.status_details));
           call->send_extra_metadata_count++;
           set_status_details(
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index a9a5f82..8591974 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -46,6 +46,7 @@
 #include "src/core/surface/api_trace.h"
 #include "src/core/surface/call.h"
 #include "src/core/surface/init.h"
+#include "src/core/transport/static_metadata.h"
 
 /** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS.
  *  Avoids needing to take a metadata context lock for sending status
@@ -64,17 +65,7 @@
   int is_client;
   gpr_refcount refs;
   gpr_uint32 max_message_length;
-  grpc_mdctx *metadata_context;
-  /** mdstr for the grpc-status key */
-  grpc_mdstr *grpc_status_string;
-  grpc_mdstr *grpc_compression_algorithm_string;
-  grpc_mdstr *grpc_encodings_accepted_by_peer_string;
-  grpc_mdstr *grpc_message_string;
-  grpc_mdstr *path_string;
-  grpc_mdstr *authority_string;
   grpc_mdelem *default_authority;
-  /** mdelem for grpc-status: 0 thru grpc-status: 2 */
-  grpc_mdelem *grpc_status_elem[NUM_CACHED_STATUS_ELEMS];
 
   gpr_mu registered_call_mu;
   registered_call *registered_calls;
@@ -93,7 +84,7 @@
 grpc_channel *grpc_channel_create_from_filters(
     grpc_exec_ctx *exec_ctx, const char *target,
     const grpc_channel_filter **filters, size_t num_filters,
-    const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client) {
+    const grpc_channel_args *args, int is_client) {
   size_t i;
   size_t size =
       sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters);
@@ -104,22 +95,6 @@
   channel->is_client = is_client;
   /* decremented by grpc_channel_destroy */
   gpr_ref_init(&channel->refs, 1);
-  channel->metadata_context = mdctx;
-  channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status");
-  channel->grpc_compression_algorithm_string =
-      grpc_mdstr_from_string(mdctx, "grpc-encoding");
-  channel->grpc_encodings_accepted_by_peer_string =
-      grpc_mdstr_from_string(mdctx, "grpc-accept-encoding");
-  channel->grpc_message_string = grpc_mdstr_from_string(mdctx, "grpc-message");
-  for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) {
-    char buf[GPR_LTOA_MIN_BUFSIZE];
-    gpr_ltoa((long)i, buf);
-    channel->grpc_status_elem[i] = grpc_mdelem_from_metadata_strings(
-        mdctx, GRPC_MDSTR_REF(channel->grpc_status_string),
-        grpc_mdstr_from_string(mdctx, buf));
-  }
-  channel->path_string = grpc_mdstr_from_string(mdctx, ":path");
-  channel->authority_string = grpc_mdstr_from_string(mdctx, ":authority");
   gpr_mu_init(&channel->registered_call_mu);
   channel->registered_calls = NULL;
 
@@ -146,7 +121,7 @@
             GRPC_MDELEM_UNREF(channel->default_authority);
           }
           channel->default_authority = grpc_mdelem_from_strings(
-              mdctx, ":authority", args->args[i].value.string);
+              ":authority", args->args[i].value.string);
         }
       } else if (0 ==
                  strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) {
@@ -160,7 +135,7 @@
                     GRPC_ARG_DEFAULT_AUTHORITY);
           } else {
             channel->default_authority = grpc_mdelem_from_strings(
-                mdctx, ":authority", args->args[i].value.string);
+                ":authority", args->args[i].value.string);
           }
         }
       }
@@ -171,14 +146,13 @@
       target != NULL) {
     char *default_authority = grpc_get_default_authority(target);
     if (default_authority) {
-      channel->default_authority = grpc_mdelem_from_strings(
-          channel->metadata_context, ":authority", default_authority);
+      channel->default_authority =
+          grpc_mdelem_from_strings(":authority", default_authority);
     }
     gpr_free(default_authority);
   }
 
   grpc_channel_stack_init(exec_ctx, filters, num_filters, channel, args,
-                          channel->metadata_context,
                           CHANNEL_STACK_FROM_CHANNEL(channel));
 
   return channel;
@@ -227,13 +201,10 @@
   GPR_ASSERT(!reserved);
   return grpc_channel_create_call_internal(
       channel, parent_call, propagation_mask, cq,
-      grpc_mdelem_from_metadata_strings(
-          channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
-          grpc_mdstr_from_string(channel->metadata_context, method)),
-      host ? grpc_mdelem_from_metadata_strings(
-                 channel->metadata_context,
-                 GRPC_MDSTR_REF(channel->authority_string),
-                 grpc_mdstr_from_string(channel->metadata_context, host))
+      grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
+                                        grpc_mdstr_from_string(method)),
+      host ? grpc_mdelem_from_metadata_strings(GRPC_MDSTR_AUTHORITY,
+                                               grpc_mdstr_from_string(host))
            : NULL,
       deadline);
 }
@@ -245,15 +216,11 @@
       "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)",
       4, (channel, method, host, reserved));
   GPR_ASSERT(!reserved);
-  rc->path = grpc_mdelem_from_metadata_strings(
-      channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
-      grpc_mdstr_from_string(channel->metadata_context, method));
-  rc->authority =
-      host ? grpc_mdelem_from_metadata_strings(
-                 channel->metadata_context,
-                 GRPC_MDSTR_REF(channel->authority_string),
-                 grpc_mdstr_from_string(channel->metadata_context, host))
-           : NULL;
+  rc->path = grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
+                                               grpc_mdstr_from_string(method));
+  rc->authority = host ? grpc_mdelem_from_metadata_strings(
+                             GRPC_MDSTR_AUTHORITY, grpc_mdstr_from_string(host))
+                       : NULL;
   gpr_mu_lock(&channel->registered_call_mu);
   rc->next = channel->registered_calls;
   channel->registered_calls = rc;
@@ -293,17 +260,7 @@
 }
 
 static void destroy_channel(grpc_exec_ctx *exec_ctx, grpc_channel *channel) {
-  size_t i;
   grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel));
-  for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) {
-    GRPC_MDELEM_UNREF(channel->grpc_status_elem[i]);
-  }
-  GRPC_MDSTR_UNREF(channel->grpc_status_string);
-  GRPC_MDSTR_UNREF(channel->grpc_compression_algorithm_string);
-  GRPC_MDSTR_UNREF(channel->grpc_encodings_accepted_by_peer_string);
-  GRPC_MDSTR_UNREF(channel->grpc_message_string);
-  GRPC_MDSTR_UNREF(channel->path_string);
-  GRPC_MDSTR_UNREF(channel->authority_string);
   while (channel->registered_calls) {
     registered_call *rc = channel->registered_calls;
     channel->registered_calls = rc->next;
@@ -316,7 +273,6 @@
   if (channel->default_authority != NULL) {
     GRPC_MDELEM_UNREF(channel->default_authority);
   }
-  grpc_mdctx_unref(channel->metadata_context);
   gpr_mu_destroy(&channel->registered_call_mu);
   gpr_free(channel->target);
   gpr_free(channel);
@@ -355,38 +311,19 @@
   return CHANNEL_STACK_FROM_CHANNEL(channel);
 }
 
-grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel) {
-  return channel->metadata_context;
-}
-
-grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel) {
-  return channel->grpc_status_string;
-}
-
-grpc_mdstr *grpc_channel_get_compression_algorithm_string(
-    grpc_channel *channel) {
-  return channel->grpc_compression_algorithm_string;
-}
-
-grpc_mdstr *grpc_channel_get_encodings_accepted_by_peer_string(
-    grpc_channel *channel) {
-  return channel->grpc_encodings_accepted_by_peer_string;
-}
-
 grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
-  if (i >= 0 && i < NUM_CACHED_STATUS_ELEMS) {
-    return GRPC_MDELEM_REF(channel->grpc_status_elem[i]);
-  } else {
-    char tmp[GPR_LTOA_MIN_BUFSIZE];
-    gpr_ltoa(i, tmp);
-    return grpc_mdelem_from_metadata_strings(
-        channel->metadata_context, GRPC_MDSTR_REF(channel->grpc_status_string),
-        grpc_mdstr_from_string(channel->metadata_context, tmp));
+  char tmp[GPR_LTOA_MIN_BUFSIZE];
+  switch (i) {
+    case 0:
+      return GRPC_MDELEM_GRPC_STATUS_0;
+    case 1:
+      return GRPC_MDELEM_GRPC_STATUS_1;
+    case 2:
+      return GRPC_MDELEM_GRPC_STATUS_2;
   }
-}
-
-grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel) {
-  return channel->grpc_message_string;
+  gpr_ltoa(i, tmp);
+  return grpc_mdelem_from_metadata_strings(GRPC_MDSTR_GRPC_STATUS,
+                                           grpc_mdstr_from_string(tmp));
 }
 
 gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel) {
diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h
index e5030d5..7dea609 100644
--- a/src/core/surface/channel.h
+++ b/src/core/surface/channel.h
@@ -40,26 +40,17 @@
 grpc_channel *grpc_channel_create_from_filters(
     grpc_exec_ctx *exec_ctx, const char *target,
     const grpc_channel_filter **filters, size_t count,
-    const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client);
+    const grpc_channel_args *args, int is_client);
 
 /** Get a (borrowed) pointer to this channels underlying channel stack */
 grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
 
-/** Get a (borrowed) pointer to the channel wide metadata context */
-grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel);
-
 /** Get a grpc_mdelem of grpc-status: X where X is the numeric value of
     status_code.
 
     The returned elem is owned by the caller. */
 grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel,
                                                  int status_code);
-grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel);
-grpc_mdstr *grpc_channel_get_compression_algorithm_string(
-    grpc_channel *channel);
-grpc_mdstr *grpc_channel_get_encodings_accepted_by_peer_string(
-    grpc_channel *channel);
-grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel);
 gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel);
 
 #ifdef GRPC_CHANNEL_REF_COUNT_DEBUG
diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c
index 921f8c2..6d40780 100644
--- a/src/core/surface/channel_create.c
+++ b/src/core/surface/channel_create.c
@@ -63,8 +63,6 @@
 
   grpc_endpoint *tcp;
 
-  grpc_mdctx *mdctx;
-
   grpc_closure connected;
 } connector;
 
@@ -76,7 +74,6 @@
 static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
   connector *c = (connector *)con;
   if (gpr_unref(&c->refs)) {
-    grpc_mdctx_unref(c->mdctx);
     /* c->initial_string_buffer does not need to be destroyed */
     gpr_free(c);
   }
@@ -103,7 +100,7 @@
                           &c->initial_string_sent);
     }
     c->result->transport = grpc_create_chttp2_transport(
-        exec_ctx, c->args.channel_args, tcp, c->mdctx, 1);
+        exec_ctx, c->args.channel_args, tcp, 1);
     grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL,
                                         0);
     GPR_ASSERT(c->result->transport);
@@ -143,7 +140,6 @@
 typedef struct {
   grpc_subchannel_factory base;
   gpr_refcount refs;
-  grpc_mdctx *mdctx;
   grpc_channel_args *merge_args;
   grpc_channel *master;
 } subchannel_factory;
@@ -159,7 +155,6 @@
   if (gpr_unref(&f->refs)) {
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory");
     grpc_channel_args_destroy(f->merge_args);
-    grpc_mdctx_unref(f->mdctx);
     gpr_free(f);
   }
 }
@@ -174,10 +169,7 @@
   grpc_subchannel *s;
   memset(c, 0, sizeof(*c));
   c->base.vtable = &connector_vtable;
-  c->mdctx = f->mdctx;
-  grpc_mdctx_ref(c->mdctx);
   gpr_ref_init(&c->refs, 1);
-  args->mdctx = f->mdctx;
   args->args = final_args;
   args->master = f->master;
   s = grpc_subchannel_create(&c->base, args);
@@ -202,7 +194,6 @@
   const grpc_channel_filter *filters[MAX_FILTERS];
   grpc_resolver *resolver;
   subchannel_factory *f;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   size_t n = 0;
   GRPC_API_TRACE(
@@ -216,14 +207,12 @@
   filters[n++] = &grpc_client_channel_filter;
   GPR_ASSERT(n <= MAX_FILTERS);
 
-  channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
-                                             args, mdctx, 1);
+  channel =
+      grpc_channel_create_from_filters(&exec_ctx, target, filters, n, args, 1);
 
   f = gpr_malloc(sizeof(*f));
   f->base.vtable = &subchannel_factory_vtable;
   gpr_ref_init(&f->refs, 1);
-  grpc_mdctx_ref(mdctx);
-  f->mdctx = mdctx;
   f->merge_args = grpc_channel_args_copy(args);
   f->master = channel;
   GRPC_CHANNEL_INTERNAL_REF(f->master, "subchannel_factory");
diff --git a/src/core/surface/init.c b/src/core/surface/init.c
index f8cba01..04d6862 100644
--- a/src/core/surface/init.c
+++ b/src/core/surface/init.c
@@ -93,6 +93,7 @@
   gpr_mu_lock(&g_init_mu);
   if (++g_initializations == 1) {
     gpr_time_init();
+    grpc_mdctx_global_init();
     grpc_lb_policy_registry_init(grpc_pick_first_lb_factory_create());
     grpc_register_lb_policy(grpc_pick_first_lb_factory_create());
     grpc_register_lb_policy(grpc_round_robin_lb_factory_create());
@@ -147,6 +148,7 @@
         g_all_of_the_plugins[i].destroy();
       }
     }
+    grpc_mdctx_global_shutdown();
   }
   gpr_mu_unlock(&g_init_mu);
 }
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index fc458a6..0247116 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -49,7 +49,6 @@
 } call_data;
 
 typedef struct {
-  grpc_mdctx *mdctx;
   grpc_channel *master;
   grpc_status_code error_code;
   const char *error_message;
@@ -60,9 +59,9 @@
   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(chand->mdctx, "grpc-status", tmp);
-  calld->details.md = grpc_mdelem_from_strings(chand->mdctx, "grpc-message",
-                                               chand->error_message);
+  calld->status.md = grpc_mdelem_from_strings("grpc-status", tmp);
+  calld->details.md =
+      grpc_mdelem_from_strings("grpc-message", chand->error_message);
   calld->status.prev = calld->details.next = NULL;
   calld->status.next = &calld->details;
   calld->details.prev = &calld->status;
@@ -104,7 +103,8 @@
 }
 
 static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
-                           grpc_call_element_args *args) {}
+                           grpc_call_element_args *args) {
+}
 
 static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
                               grpc_call_element *elem) {}
@@ -115,7 +115,6 @@
   channel_data *chand = elem->channel_data;
   GPR_ASSERT(args->is_first);
   GPR_ASSERT(args->is_last);
-  chand->mdctx = args->metadata_context;
   chand->master = args->master;
 }
 
@@ -139,8 +138,8 @@
   channel_data *chand;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   static const grpc_channel_filter *filters[] = {&lame_filter};
-  channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, 1,
-                                             NULL, grpc_mdctx_create(), 1);
+  channel =
+      grpc_channel_create_from_filters(&exec_ctx, target, filters, 1, NULL, 1);
   elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
   GRPC_API_TRACE(
       "grpc_lame_client_channel_create(target=%s, error_code=%d, "
diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c
index e8afe1a..374b44b 100644
--- a/src/core/surface/secure_channel_create.c
+++ b/src/core/surface/secure_channel_create.c
@@ -71,8 +71,6 @@
   grpc_endpoint *newly_connecting_endpoint;
 
   grpc_closure connected_closure;
-
-  grpc_mdctx *mdctx;
 } connector;
 
 static void connector_ref(grpc_connector *con) {
@@ -83,7 +81,6 @@
 static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
   connector *c = (connector *)con;
   if (gpr_unref(&c->refs)) {
-    grpc_mdctx_unref(c->mdctx);
     /* c->initial_string_buffer does not need to be destroyed */
     gpr_free(c);
   }
@@ -110,7 +107,7 @@
     c->connecting_endpoint = NULL;
     gpr_mu_unlock(&c->mu);
     c->result->transport = grpc_create_chttp2_transport(
-        exec_ctx, c->args.channel_args, secure_endpoint, c->mdctx, 1);
+        exec_ctx, c->args.channel_args, secure_endpoint, 1);
     grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL,
                                         0);
     c->result->filters = gpr_malloc(sizeof(grpc_channel_filter *) * 2);
@@ -198,7 +195,6 @@
 typedef struct {
   grpc_subchannel_factory base;
   gpr_refcount refs;
-  grpc_mdctx *mdctx;
   grpc_channel_args *merge_args;
   grpc_channel_security_connector *security_connector;
   grpc_channel *master;
@@ -217,7 +213,6 @@
                                   "subchannel_factory");
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory");
     grpc_channel_args_destroy(f->merge_args);
-    grpc_mdctx_unref(f->mdctx);
     gpr_free(f);
   }
 }
@@ -233,13 +228,10 @@
   memset(c, 0, sizeof(*c));
   c->base.vtable = &connector_vtable;
   c->security_connector = f->security_connector;
-  c->mdctx = f->mdctx;
   gpr_mu_init(&c->mu);
-  grpc_mdctx_ref(c->mdctx);
   gpr_ref_init(&c->refs, 1);
   args->args = final_args;
   args->master = f->master;
-  args->mdctx = f->mdctx;
   s = grpc_subchannel_create(&c->base, args);
   grpc_connector_unref(exec_ctx, &c->base);
   grpc_channel_args_destroy(final_args);
@@ -263,7 +255,6 @@
   grpc_channel_args *args_copy;
   grpc_channel_args *new_args_from_connector;
   grpc_channel_security_connector *security_connector;
-  grpc_mdctx *mdctx;
   grpc_resolver *resolver;
   subchannel_factory *f;
 #define MAX_FILTERS 3
@@ -293,7 +284,6 @@
         target, GRPC_STATUS_INVALID_ARGUMENT,
         "Failed to create security connector.");
   }
-  mdctx = grpc_mdctx_create();
 
   connector_arg = grpc_security_connector_to_arg(&security_connector->base);
   args_copy = grpc_channel_args_copy_and_add(
@@ -307,13 +297,11 @@
   GPR_ASSERT(n <= MAX_FILTERS);
 
   channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
-                                             args_copy, mdctx, 1);
+                                             args_copy, 1);
 
   f = gpr_malloc(sizeof(*f));
   f->base.vtable = &subchannel_factory_vtable;
   gpr_ref_init(&f->refs, 1);
-  grpc_mdctx_ref(mdctx);
-  f->mdctx = mdctx;
   GRPC_SECURITY_CONNECTOR_REF(&security_connector->base, "subchannel_factory");
   f->security_connector = security_connector;
   f->merge_args = grpc_channel_args_copy(args_copy);
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index e9f3800..cdbd542 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -54,6 +54,7 @@
 #include "src/core/surface/completion_queue.h"
 #include "src/core/surface/init.h"
 #include "src/core/transport/metadata.h"
+#include "src/core/transport/static_metadata.h"
 
 typedef struct listener {
   void *arg;
@@ -108,8 +109,6 @@
   grpc_server *server;
   grpc_connectivity_state connectivity_state;
   grpc_channel *channel;
-  grpc_mdstr *path_key;
-  grpc_mdstr *authority_key;
   /* linked list of all channels on a server */
   channel_data *next;
   channel_data *prev;
@@ -558,12 +557,11 @@
 
 static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
   grpc_call_element *elem = user_data;
-  channel_data *chand = elem->channel_data;
   call_data *calld = elem->call_data;
-  if (md->key == chand->path_key) {
+  if (md->key == GRPC_MDSTR_PATH) {
     calld->path = GRPC_MDSTR_REF(md->value);
     return NULL;
-  } else if (md->key == chand->authority_key) {
+  } else if (md->key == GRPC_MDSTR_AUTHORITY) {
     calld->host = GRPC_MDSTR_REF(md->value);
     return NULL;
   }
@@ -718,9 +716,6 @@
   GPR_ASSERT(!args->is_last);
   chand->server = NULL;
   chand->channel = NULL;
-  chand->path_key = grpc_mdstr_from_string(args->metadata_context, ":path");
-  chand->authority_key =
-      grpc_mdstr_from_string(args->metadata_context, ":authority");
   chand->next = chand->prev = chand;
   chand->registered_methods = NULL;
   chand->connectivity_state = GRPC_CHANNEL_IDLE;
@@ -750,8 +745,6 @@
     chand->next = chand->prev = chand;
     maybe_finish_shutdown(exec_ctx, chand->server);
     gpr_mu_unlock(&chand->server->mu_global);
-    GRPC_MDSTR_UNREF(chand->path_key);
-    GRPC_MDSTR_UNREF(chand->authority_key);
     server_unref(exec_ctx, chand->server);
   }
 }
@@ -894,7 +887,7 @@
 void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s,
                                  grpc_transport *transport,
                                  grpc_channel_filter const **extra_filters,
-                                 size_t num_extra_filters, grpc_mdctx *mdctx,
+                                 size_t num_extra_filters,
                                  const grpc_channel_args *args) {
   size_t num_filters = s->channel_filter_count + num_extra_filters + 1;
   grpc_channel_filter const **filters =
@@ -929,7 +922,7 @@
   }
 
   channel = grpc_channel_create_from_filters(exec_ctx, NULL, filters,
-                                             num_filters, args, mdctx, 0);
+                                             num_filters, args, 0);
   chand = (channel_data *)grpc_channel_stack_element(
               grpc_channel_get_channel_stack(channel), 0)->channel_data;
   chand->server = s;
@@ -948,8 +941,8 @@
     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(mdctx, rm->host) : NULL;
-      method = grpc_mdstr_from_string(mdctx, rm->method);
+      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);
       for (probes = 0; chand->registered_methods[(hash + probes) % slots]
                                .server_registered_method != NULL;
diff --git a/src/core/surface/server.h b/src/core/surface/server.h
index 4c46d07..a957fdb 100644
--- a/src/core/surface/server.h
+++ b/src/core/surface/server.h
@@ -57,7 +57,7 @@
 void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *server,
                                  grpc_transport *transport,
                                  grpc_channel_filter const **extra_filters,
-                                 size_t num_extra_filters, grpc_mdctx *mdctx,
+                                 size_t num_extra_filters,
                                  const grpc_channel_args *args);
 
 const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server);
diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c
index 1408f9c..990bc4a 100644
--- a/src/core/surface/server_chttp2.c
+++ b/src/core/surface/server_chttp2.c
@@ -44,11 +44,11 @@
 #include <grpc/support/useful.h>
 
 static void setup_transport(grpc_exec_ctx *exec_ctx, void *server,
-                            grpc_transport *transport, grpc_mdctx *mdctx) {
+                            grpc_transport *transport) {
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_server_setup_transport(exec_ctx, server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(server));
 }
 
@@ -61,10 +61,9 @@
    * (as in server_secure_chttp2.c) needs to add synchronization to avoid this
    * case.
    */
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_transport *transport = grpc_create_chttp2_transport(
-      exec_ctx, grpc_server_get_channel_args(server), tcp, mdctx, 0);
-  setup_transport(exec_ctx, server, transport, mdctx);
+      exec_ctx, grpc_server_get_channel_args(server), tcp, 0);
+  setup_transport(exec_ctx, server, transport);
   grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
 }
 
diff --git a/src/core/transport/chttp2/hpack_encoder.c b/src/core/transport/chttp2/hpack_encoder.c
index 62b60ab..28d5433 100644
--- a/src/core/transport/chttp2/hpack_encoder.c
+++ b/src/core/transport/chttp2/hpack_encoder.c
@@ -44,6 +44,7 @@
 #include "src/core/transport/chttp2/hpack_table.h"
 #include "src/core/transport/chttp2/timeout_encoding.h"
 #include "src/core/transport/chttp2/varint.h"
+#include "src/core/transport/static_metadata.h"
 
 #define HASH_FRAGMENT_1(x) ((x)&255)
 #define HASH_FRAGMENT_2(x) ((x >> 8) & 255)
@@ -452,8 +453,7 @@
   grpc_chttp2_encode_timeout(
       gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str);
   mdelem = grpc_mdelem_from_metadata_strings(
-      c->mdctx, GRPC_MDSTR_REF(c->timeout_key_str),
-      grpc_mdstr_from_string(c->mdctx, timeout_str));
+      GRPC_MDSTR_GRPC_TIMEOUT, grpc_mdstr_from_string(timeout_str));
   hpack_enc(c, mdelem, st);
   GRPC_MDELEM_UNREF(mdelem);
 }
@@ -468,11 +468,8 @@
   return (bytes + 31) / 32;
 }
 
-void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
-                                       grpc_mdctx *ctx) {
+void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c) {
   memset(c, 0, sizeof(*c));
-  c->mdctx = ctx;
-  c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout");
   c->max_table_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE;
   c->cap_table_elems = elems_for_bytes(c->max_table_size);
   c->max_table_elems = c->cap_table_elems;
@@ -489,7 +486,6 @@
     if (c->entries_keys[i]) GRPC_MDSTR_UNREF(c->entries_keys[i]);
     if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]);
   }
-  GRPC_MDSTR_UNREF(c->timeout_key_str);
   gpr_free(c->table_elem_size);
 }
 
diff --git a/src/core/transport/chttp2/hpack_encoder.h b/src/core/transport/chttp2/hpack_encoder.h
index ce83d10..a360043 100644
--- a/src/core/transport/chttp2/hpack_encoder.h
+++ b/src/core/transport/chttp2/hpack_encoder.h
@@ -71,11 +71,6 @@
      been seen. When that count reaches max (255), all values are halved. */
   gpr_uint8 filter_elems[GRPC_CHTTP2_HPACKC_NUM_FILTERS];
 
-  /* metadata context */
-  grpc_mdctx *mdctx;
-  /* the string 'grpc-timeout' */
-  grpc_mdstr *timeout_key_str;
-
   /* 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];
@@ -86,8 +81,7 @@
   gpr_uint16 *table_elem_size;
 } grpc_chttp2_hpack_compressor;
 
-void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
-                                       grpc_mdctx *mdctx);
+void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c);
 void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c);
 void grpc_chttp2_hpack_compressor_set_max_table_size(
     grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size);
diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c
index 57a3a20..d38ff68 100644
--- a/src/core/transport/chttp2/hpack_parser.c
+++ b/src/core/transport/chttp2/hpack_parser.c
@@ -637,8 +637,7 @@
 
 static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p,
                                grpc_chttp2_hpack_parser_string *str) {
-  grpc_mdstr *s = grpc_mdstr_from_buffer(p->table.mdctx, (gpr_uint8 *)str->str,
-                                         str->length);
+  grpc_mdstr *s = grpc_mdstr_from_buffer((gpr_uint8 *)str->str, str->length);
   str->length = 0;
   return s;
 }
@@ -749,8 +748,7 @@
 static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
                                 const gpr_uint8 *cur, const gpr_uint8 *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                                     GRPC_MDSTR_REF(md->key),
+  return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
                                                      take_string(p, &p->value)),
                 1) &&
          parse_begin(p, cur, end);
@@ -759,8 +757,7 @@
 /* finish a literal header with incremental indexing with no index */
 static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
                                   const gpr_uint8 *cur, const gpr_uint8 *end) {
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                                     take_string(p, &p->key),
+  return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
                                                      take_string(p, &p->value)),
                 1) &&
          parse_begin(p, cur, end);
@@ -802,8 +799,7 @@
 static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
                                 const gpr_uint8 *cur, const gpr_uint8 *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                                     GRPC_MDSTR_REF(md->key),
+  return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
                                                      take_string(p, &p->value)),
                 0) &&
          parse_begin(p, cur, end);
@@ -812,8 +808,7 @@
 /* finish a literal header without incremental indexing with index = 0 */
 static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
                                   const gpr_uint8 *cur, const gpr_uint8 *end) {
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                                     take_string(p, &p->key),
+  return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
                                                      take_string(p, &p->value)),
                 0) &&
          parse_begin(p, cur, end);
@@ -855,8 +850,7 @@
 static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
                                 const gpr_uint8 *cur, const gpr_uint8 *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                                     GRPC_MDSTR_REF(md->key),
+  return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
                                                      take_string(p, &p->value)),
                 0) &&
          parse_begin(p, cur, end);
@@ -865,8 +859,7 @@
 /* finish a literal header that is never indexed with an extra value */
 static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
                                   const gpr_uint8 *cur, const gpr_uint8 *end) {
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
-                                                     take_string(p, &p->key),
+  return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
                                                      take_string(p, &p->value)),
                 0) &&
          parse_begin(p, cur, end);
@@ -1356,8 +1349,7 @@
   abort();
 }
 
-void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
-                                   grpc_mdctx *mdctx) {
+void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) {
   p->on_header = on_header_not_set;
   p->on_header_user_data = NULL;
   p->state = parse_begin;
@@ -1367,7 +1359,7 @@
   p->value.str = NULL;
   p->value.capacity = 0;
   p->value.length = 0;
-  grpc_chttp2_hptbl_init(&p->table, mdctx);
+  grpc_chttp2_hptbl_init(&p->table);
 }
 
 void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) {
diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h
index f568670..fb894b5 100644
--- a/src/core/transport/chttp2/hpack_parser.h
+++ b/src/core/transport/chttp2/hpack_parser.h
@@ -95,8 +95,7 @@
   grpc_chttp2_hptbl table;
 };
 
-void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
-                                   grpc_mdctx *mdctx);
+void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p);
 void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p);
 
 void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p);
diff --git a/src/core/transport/chttp2/hpack_table.c b/src/core/transport/chttp2/hpack_table.c
index f2362b0..59060da 100644
--- a/src/core/transport/chttp2/hpack_table.c
+++ b/src/core/transport/chttp2/hpack_table.c
@@ -176,11 +176,10 @@
          GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
 }
 
-void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) {
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl) {
   size_t i;
 
   memset(tbl, 0, sizeof(*tbl));
-  tbl->mdctx = mdctx;
   tbl->current_table_bytes = tbl->max_bytes =
       GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE;
   tbl->max_entries = tbl->cap_entries =
@@ -188,8 +187,8 @@
   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(
-        mdctx, static_table[i].key, static_table[i].value);
+    tbl->static_ents[i - 1] =
+        grpc_mdelem_from_strings(static_table[i].key, static_table[i].value);
   }
 }
 
diff --git a/src/core/transport/chttp2/hpack_table.h b/src/core/transport/chttp2/hpack_table.h
index e03b8c1..a173eec 100644
--- a/src/core/transport/chttp2/hpack_table.h
+++ b/src/core/transport/chttp2/hpack_table.h
@@ -59,7 +59,6 @@
 
 /* hpack decoder table */
 typedef struct {
-  grpc_mdctx *mdctx;
   /* the first used entry in ents */
   gpr_uint32 first_ent;
   /* how many entries are in the table */
@@ -84,7 +83,7 @@
 } grpc_chttp2_hptbl;
 
 /* initialize a hpack table */
-void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx);
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl);
 void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl);
 void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
                                      gpr_uint32 max_bytes);
diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h
index b2ac8ee..3952f8a 100644
--- a/src/core/transport/chttp2/internal.h
+++ b/src/core/transport/chttp2/internal.h
@@ -238,9 +238,6 @@
 
   /** data to write later - after parsing */
   gpr_slice_buffer qbuf;
-  /* metadata object cache */
-  grpc_mdstr *str_grpc_timeout;
-  grpc_mdelem *elem_grpc_status_ok;
   /** parser for headers */
   grpc_chttp2_hpack_parser hpack_parser;
   /** simple one shot parsers */
@@ -294,7 +291,6 @@
 struct grpc_chttp2_transport {
   grpc_transport base; /* must be first */
   grpc_endpoint *ep;
-  grpc_mdctx *metadata_context;
   gpr_refcount refs;
   char *peer_string;
 
diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c
index 1e06532..467e734 100644
--- a/src/core/transport/chttp2/parsing.c
+++ b/src/core/transport/chttp2/parsing.c
@@ -35,14 +35,15 @@
 
 #include <string.h>
 
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
 #include "src/core/profiling/timers.h"
 #include "src/core/transport/chttp2/http2_errors.h"
 #include "src/core/transport/chttp2/status_conversion.h"
 #include "src/core/transport/chttp2/timeout_encoding.h"
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/string_util.h>
+#include "src/core/transport/static_metadata.h"
 
 static int init_frame_parser(grpc_exec_ctx *exec_ctx,
                              grpc_chttp2_transport_parsing *transport_parsing);
@@ -592,13 +593,12 @@
       transport_parsing->is_client ? "CLI" : "SVR",
       grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)));
 
-  if (md->key == transport_parsing->elem_grpc_status_ok->key &&
-      md != transport_parsing->elem_grpc_status_ok) {
+  if (md->key == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) {
     /* TODO(ctiller): check for a status like " 0" */
     stream_parsing->seen_error = 1;
   }
 
-  if (md->key == transport_parsing->str_grpc_timeout) {
+  if (md->key == GRPC_MDSTR_GRPC_TIMEOUT) {
     gpr_timespec *cached_timeout = grpc_mdelem_get_user_data(md, free_timeout);
     if (!cached_timeout) {
       /* not already parsed: parse it now, and store the result away */
@@ -639,8 +639,7 @@
       transport_parsing->is_client ? "CLI" : "SVR",
       grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)));
 
-  if (md->key == transport_parsing->elem_grpc_status_ok->key &&
-      md != transport_parsing->elem_grpc_status_ok) {
+  if (md->key == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) {
     /* TODO(ctiller): check for a status like " 0" */
     stream_parsing->seen_error = 1;
   }
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index 77c68c9..97f2a1b 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -49,6 +49,7 @@
 #include "src/core/transport/chttp2/internal.h"
 #include "src/core/transport/chttp2/status_conversion.h"
 #include "src/core/transport/chttp2/timeout_encoding.h"
+#include "src/core/transport/static_metadata.h"
 #include "src/core/transport/transport_impl.h"
 
 #define DEFAULT_WINDOW 65535
@@ -155,9 +156,6 @@
   grpc_chttp2_hpack_parser_destroy(&t->parsing.hpack_parser);
   grpc_chttp2_goaway_parser_destroy(&t->parsing.goaway_parser);
 
-  GRPC_MDSTR_UNREF(t->parsing.str_grpc_timeout);
-  GRPC_MDELEM_UNREF(t->parsing.elem_grpc_status_ok);
-
   for (i = 0; i < STREAM_LIST_COUNT; i++) {
     GPR_ASSERT(t->lists[i].head == NULL);
     GPR_ASSERT(t->lists[i].tail == NULL);
@@ -183,8 +181,6 @@
     gpr_free(ping);
   }
 
-  grpc_mdctx_unref(t->metadata_context);
-
   gpr_free(t->peer_string);
   gpr_free(t);
 }
@@ -219,8 +215,7 @@
 
 static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                            const grpc_channel_args *channel_args,
-                           grpc_endpoint *ep, grpc_mdctx *mdctx,
-                           gpr_uint8 is_client) {
+                           grpc_endpoint *ep, gpr_uint8 is_client) {
   size_t i;
   int j;
 
@@ -236,9 +231,7 @@
   /* ref is dropped at transport close() */
   gpr_ref_init(&t->shutdown_ep_refs, 1);
   gpr_mu_init(&t->mu);
-  grpc_mdctx_ref(mdctx);
   t->peer_string = grpc_endpoint_get_peer(ep);
-  t->metadata_context = mdctx;
   t->endpoint_reading = 1;
   t->global.next_stream_id = is_client ? 1 : 2;
   t->global.is_client = is_client;
@@ -249,10 +242,6 @@
   t->global.ping_counter = 1;
   t->global.pings.next = t->global.pings.prev = &t->global.pings;
   t->parsing.is_client = is_client;
-  t->parsing.str_grpc_timeout =
-      grpc_mdstr_from_string(t->metadata_context, "grpc-timeout");
-  t->parsing.elem_grpc_status_ok =
-      grpc_mdelem_from_strings(t->metadata_context, "grpc-status", "0");
   t->parsing.deframe_state =
       is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0;
   t->writing.is_client = is_client;
@@ -263,12 +252,12 @@
   gpr_slice_buffer_init(&t->global.qbuf);
 
   gpr_slice_buffer_init(&t->writing.outbuf);
-  grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor, mdctx);
+  grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor);
   grpc_closure_init(&t->writing_action, writing_action, t);
 
   gpr_slice_buffer_init(&t->parsing.qbuf);
   grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser);
-  grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser, t->metadata_context);
+  grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser);
 
   grpc_closure_init(&t->writing.done_cb, grpc_chttp2_terminate_writing,
                     &t->writing);
@@ -762,11 +751,10 @@
 static int contains_non_ok_status(
     grpc_chttp2_transport_global *transport_global,
     grpc_metadata_batch *batch) {
-  grpc_mdelem *ok_elem =
-      TRANSPORT_FROM_GLOBAL(transport_global)->parsing.elem_grpc_status_ok;
   grpc_linked_mdelem *l;
   for (l = batch->list.head; l; l = l->next) {
-    if (l->md->key == ok_elem->key && l->md != ok_elem) {
+    if (l->md->key == GRPC_MDSTR_GRPC_STATUS &&
+        l->md != GRPC_MDELEM_GRPC_STATUS_0) {
       return 1;
     }
   }
@@ -1078,19 +1066,18 @@
      about the metadata yet */
   if (!stream_global->published_trailing_metadata ||
       stream_global->recv_trailing_metadata_finished != NULL) {
-    grpc_mdctx *mdctx =
-        TRANSPORT_FROM_GLOBAL(transport_global)->metadata_context;
     char status_string[GPR_LTOA_MIN_BUFSIZE];
     gpr_ltoa(status, status_string);
     grpc_chttp2_incoming_metadata_buffer_add(
         &stream_global->received_trailing_metadata,
-        grpc_mdelem_from_strings(mdctx, "grpc-status", status_string));
+        grpc_mdelem_from_metadata_strings(
+            GRPC_MDSTR_GRPC_STATUS, grpc_mdstr_from_string(status_string)));
     if (slice) {
       grpc_chttp2_incoming_metadata_buffer_add(
           &stream_global->received_trailing_metadata,
           grpc_mdelem_from_metadata_strings(
-              mdctx, grpc_mdstr_from_string(mdctx, "grpc-message"),
-              grpc_mdstr_from_slice(mdctx, gpr_slice_ref(*slice))));
+              GRPC_MDSTR_GRPC_MESSAGE,
+              grpc_mdstr_from_slice(gpr_slice_ref(*slice))));
     }
     stream_global->published_trailing_metadata = 1;
     grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
@@ -1641,9 +1628,9 @@
 
 grpc_transport *grpc_create_chttp2_transport(
     grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args,
-    grpc_endpoint *ep, grpc_mdctx *mdctx, int is_client) {
+    grpc_endpoint *ep, int is_client) {
   grpc_chttp2_transport *t = gpr_malloc(sizeof(grpc_chttp2_transport));
-  init_transport(exec_ctx, t, channel_args, ep, mdctx, is_client != 0);
+  init_transport(exec_ctx, t, channel_args, ep, is_client != 0);
   return &t->base;
 }
 
diff --git a/src/core/transport/chttp2_transport.h b/src/core/transport/chttp2_transport.h
index fce2b68..9552050 100644
--- a/src/core/transport/chttp2_transport.h
+++ b/src/core/transport/chttp2_transport.h
@@ -42,7 +42,7 @@
 
 grpc_transport *grpc_create_chttp2_transport(
     grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args,
-    grpc_endpoint *ep, grpc_mdctx *metadata_context, int is_client);
+    grpc_endpoint *ep, int is_client);
 
 void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx,
                                          grpc_transport *transport,
diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c
index a72dee4..a76d1ad 100644
--- a/src/core/transport/metadata.c
+++ b/src/core/transport/metadata.c
@@ -31,20 +31,32 @@
  *
  */
 
-#include "src/core/iomgr/sockaddr.h"
 #include "src/core/transport/metadata.h"
 
 #include <assert.h>
 #include <stddef.h>
 #include <string.h>
 
+#include <grpc/compression.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/time.h>
 #include "src/core/profiling/timers.h"
 #include "src/core/support/murmur_hash.h"
+#include "src/core/support/string.h"
 #include "src/core/transport/chttp2/bin_encoder.h"
+#include "src/core/transport/static_metadata.h"
+
+/* There are two kinds of mdelem and mdstr instances.
+ * Static instances are declared in static_metadata.{h,c} and
+ * are initialized by grpc_mdctx_global_init().
+ * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
+ * by internal_string and internal_element structures.
+ * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
+ * used to determine which kind of element a pointer refers to.
+ */
 
 #define INITIAL_STRTAB_CAPACITY 4
 #define INITIAL_MDTAB_CAPACITY 4
@@ -52,107 +64,186 @@
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
 #define DEBUG_ARGS , const char *file, int line
 #define FWD_DEBUG_ARGS , file, line
-#define INTERNAL_STRING_REF(s) internal_string_ref((s), __FILE__, __LINE__)
-#define INTERNAL_STRING_UNREF(s) internal_string_unref((s), __FILE__, __LINE__)
-#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
+#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
 #else
 #define DEBUG_ARGS
 #define FWD_DEBUG_ARGS
-#define INTERNAL_STRING_REF(s) internal_string_ref((s))
-#define INTERNAL_STRING_UNREF(s) internal_string_unref((s))
-#define REF_MD_LOCKED(s) ref_md_locked((s))
+#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))
+
 typedef void (*destroy_user_data_func)(void *user_data);
 
+/* Shadow structure for grpc_mdstr for non-static values */
 typedef struct internal_string {
   /* must be byte compatible with grpc_mdstr */
   gpr_slice slice;
   gpr_uint32 hash;
 
   /* private only data */
-  gpr_uint32 refs;
+  gpr_atm refcnt;
+
   gpr_uint8 has_base64_and_huffman_encoded;
   gpr_slice_refcount refcount;
 
   gpr_slice base64_and_huffman;
 
-  grpc_mdctx *context;
-
   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;
 
+  /* private only data */
   gpr_atm refcnt;
 
-  /* private only data */
   gpr_mu mu_user_data;
   gpr_atm destroy_user_data;
   gpr_atm user_data;
 
-  grpc_mdctx *context;
   struct internal_metadata *bucket_next;
 } internal_metadata;
 
-struct grpc_mdctx {
-  gpr_uint32 hash_seed;
-  int refs;
-
+typedef struct strtab_shard {
   gpr_mu mu;
+  internal_string **strs;
+  size_t count;
+  size_t capacity;
+} strtab_shard;
 
-  internal_string **strtab;
-  size_t strtab_count;
-  size_t strtab_capacity;
+typedef struct mdtab_shard {
+  gpr_mu mu;
+  internal_metadata **elems;
+  size_t count;
+  size_t capacity;
+  size_t free;
+} mdtab_shard;
 
-  internal_metadata **mdtab;
-  size_t mdtab_count;
-  size_t mdtab_free;
-  size_t mdtab_capacity;
-};
+#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))
 
-static void internal_string_ref(internal_string *s DEBUG_ARGS);
-static void internal_string_unref(internal_string *s DEBUG_ARGS);
-static void discard_metadata(grpc_mdctx *ctx);
-static void gc_mdtab(grpc_mdctx *ctx);
-static void metadata_context_destroy_locked(grpc_mdctx *ctx);
+/* hash seed: decided at initialization time */
+static gpr_uint32 g_hash_seed;
 
-static void lock(grpc_mdctx *ctx) { gpr_mu_lock(&ctx->mu); }
+/* 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 void unlock(grpc_mdctx *ctx) {
-  /* If the context has been orphaned we'd like to delete it soon. We check
-     conditions in unlock as it signals the end of mutations on a context.
+static strtab_shard g_strtab_shard[STRTAB_SHARD_COUNT];
+static mdtab_shard g_mdtab_shard[MDTAB_SHARD_COUNT];
 
-     We need to ensure all grpc_mdelem and grpc_mdstr elements have been deleted
-     first. This is equivalent to saying that both tables have zero counts,
-     which is equivalent to saying that strtab_count is zero (as mdelem's MUST
-     reference an mdstr for their key and value slots).
+static void gc_mdtab(mdtab_shard *shard);
 
-     To encourage that to happen, we start discarding zero reference count
-     mdelems on every unlock (instead of the usual 'I'm too loaded' trigger
-     case), since otherwise we can be stuck waiting for a garbage collection
-     that will never happen. */
-  if (ctx->refs == 0) {
-/* uncomment if you're having trouble diagnosing an mdelem leak to make
-   things clearer (slows down destruction a lot, however) */
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
-    gc_mdtab(ctx);
-#endif
-    if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) {
-      discard_metadata(ctx);
+void grpc_mdctx_global_init(void) {
+  size_t i, j;
+  g_hash_seed = (gpr_uint32)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];
+    gpr_uint32 hash = gpr_murmur_hash3(str, strlen(str), g_hash_seed);
+    *(gpr_slice *)&elem->slice = gpr_slice_from_static_string(str);
+    *(gpr_uint32 *)&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 (ctx->strtab_count == 0) {
-      metadata_context_destroy_locked(ctx);
-      return;
+    if (j > g_static_strtab_maxprobe) {
+      g_static_strtab_maxprobe = j;
     }
   }
-  gpr_mu_unlock(&ctx->mu);
+  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]];
+    gpr_uint32 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];
+    gpr_mu_init(&shard->mu);
+    shard->count = 0;
+    shard->free = 0;
+    shard->capacity = INITIAL_MDTAB_CAPACITY;
+    shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity);
+    memset(shard->elems, 0, sizeof(*shard->elems) * shard->capacity);
+  }
 }
 
-static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
+void grpc_mdctx_global_shutdown(void) {
+  size_t i;
+  for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
+    mdtab_shard *shard = &g_mdtab_shard[i];
+    gpr_mu_destroy(&shard->mu);
+    gc_mdtab(shard);
+    /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
+    if (shard->count != 0) {
+      gpr_log(GPR_DEBUG, "WARNING: %d metadata elements were leaked", shard->count);
+    }
+    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: %d metadata strings were leaked", shard->count);
+    }
+    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 void ref_md_locked(mdtab_shard *shard,
+                          internal_metadata *md DEBUG_ARGS) {
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
           "ELM   REF:%p:%d->%d: '%s' = '%s'", md,
@@ -162,96 +253,14 @@
           grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
 #endif
   if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) {
-    md->context->mdtab_free--;
+    shard->free--;
   } else {
     GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1));
   }
 }
 
-grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
-  grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
-
-  ctx->refs = 1;
-  ctx->hash_seed = seed;
-  gpr_mu_init(&ctx->mu);
-  ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
-  memset(ctx->strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY);
-  ctx->strtab_count = 0;
-  ctx->strtab_capacity = INITIAL_STRTAB_CAPACITY;
-  ctx->mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY);
-  memset(ctx->mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY);
-  ctx->mdtab_count = 0;
-  ctx->mdtab_capacity = INITIAL_MDTAB_CAPACITY;
-  ctx->mdtab_free = 0;
-
-  return ctx;
-}
-
-grpc_mdctx *grpc_mdctx_create(void) {
-  /* This seed is used to prevent remote connections from controlling hash table
-   * collisions. It needs to be somewhat unpredictable to a remote connection.
-   */
-  return grpc_mdctx_create_with_seed(
-      (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
-}
-
-static void discard_metadata(grpc_mdctx *ctx) {
-  size_t i;
-  internal_metadata *next, *cur;
-
-  for (i = 0; i < ctx->mdtab_capacity; i++) {
-    cur = ctx->mdtab[i];
-    while (cur) {
-      void *user_data = (void *)gpr_atm_no_barrier_load(&cur->user_data);
-      GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
-      next = cur->bucket_next;
-      INTERNAL_STRING_UNREF(cur->key);
-      INTERNAL_STRING_UNREF(cur->value);
-      if (user_data != NULL) {
-        ((destroy_user_data_func)gpr_atm_no_barrier_load(
-            &cur->destroy_user_data))(user_data);
-      }
-      gpr_mu_destroy(&cur->mu_user_data);
-      gpr_free(cur);
-      cur = next;
-      ctx->mdtab_free--;
-      ctx->mdtab_count--;
-    }
-    ctx->mdtab[i] = NULL;
-  }
-}
-
-static void metadata_context_destroy_locked(grpc_mdctx *ctx) {
-  GPR_ASSERT(ctx->strtab_count == 0);
-  GPR_ASSERT(ctx->mdtab_count == 0);
-  GPR_ASSERT(ctx->mdtab_free == 0);
-  gpr_free(ctx->strtab);
-  gpr_free(ctx->mdtab);
-  gpr_mu_unlock(&ctx->mu);
-  gpr_mu_destroy(&ctx->mu);
-  gpr_free(ctx);
-}
-
-void grpc_mdctx_ref(grpc_mdctx *ctx) {
-  GPR_TIMER_BEGIN("grpc_mdctx_ref", 0);
-  lock(ctx);
-  GPR_ASSERT(ctx->refs > 0);
-  ctx->refs++;
-  unlock(ctx);
-  GPR_TIMER_END("grpc_mdctx_ref", 0);
-}
-
-void grpc_mdctx_unref(grpc_mdctx *ctx) {
-  GPR_TIMER_BEGIN("grpc_mdctx_unref", 0);
-  lock(ctx);
-  GPR_ASSERT(ctx->refs > 0);
-  ctx->refs--;
-  unlock(ctx);
-  GPR_TIMER_END("grpc_mdctx_unref", 0);
-}
-
-static void grow_strtab(grpc_mdctx *ctx) {
-  size_t capacity = ctx->strtab_capacity * 2;
+static void grow_strtab(strtab_shard *shard) {
+  size_t capacity = shard->capacity * 2;
   size_t i;
   internal_string **strtab;
   internal_string *s, *next;
@@ -261,105 +270,95 @@
   strtab = gpr_malloc(sizeof(internal_string *) * capacity);
   memset(strtab, 0, sizeof(internal_string *) * capacity);
 
-  for (i = 0; i < ctx->strtab_capacity; i++) {
-    for (s = ctx->strtab[i]; s; s = next) {
+  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[s->hash % capacity];
-      strtab[s->hash % capacity] = s;
+      s->bucket_next = strtab[idx];
+      strtab[idx] = s;
     }
   }
 
-  gpr_free(ctx->strtab);
-  ctx->strtab = strtab;
-  ctx->strtab_capacity = capacity;
+  gpr_free(shard->strs);
+  shard->strs = strtab;
+  shard->capacity = capacity;
 
   GPR_TIMER_END("grow_strtab", 0);
 }
 
-static void internal_destroy_string(internal_string *is) {
+static void internal_destroy_string(strtab_shard *shard, internal_string *is) {
   internal_string **prev_next;
   internal_string *cur;
-  grpc_mdctx *ctx = is->context;
   GPR_TIMER_BEGIN("internal_destroy_string", 0);
   if (is->has_base64_and_huffman_encoded) {
     gpr_slice_unref(is->base64_and_huffman);
   }
-  for (prev_next = &ctx->strtab[is->hash % ctx->strtab_capacity],
+  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;
-  ctx->strtab_count--;
+  shard->count--;
   gpr_free(is);
   GPR_TIMER_END("internal_destroy_string", 0);
 }
 
-static void internal_string_ref(internal_string *s DEBUG_ARGS) {
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR   REF:%p:%d->%d: '%s'", s,
-          s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
-#endif
-  ++s->refs;
-}
-
-static void internal_string_unref(internal_string *s DEBUG_ARGS) {
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
-  gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s,
-          s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
-#endif
-  GPR_ASSERT(s->refs > 0);
-  if (0 == --s->refs) {
-    internal_destroy_string(s);
-  }
-}
-
 static void slice_ref(void *p) {
   internal_string *is =
       (internal_string *)((char *)p - offsetof(internal_string, refcount));
-  grpc_mdctx *ctx = is->context;
-  GPR_TIMER_BEGIN("slice_ref", 0);
-  lock(ctx);
-  INTERNAL_STRING_REF(is);
-  unlock(ctx);
-  GPR_TIMER_END("slice_ref", 0);
+  GRPC_MDSTR_REF((grpc_mdstr *)(is));
 }
 
 static void slice_unref(void *p) {
   internal_string *is =
       (internal_string *)((char *)p - offsetof(internal_string, refcount));
-  grpc_mdctx *ctx = is->context;
-  GPR_TIMER_BEGIN("slice_unref", 0);
-  lock(ctx);
-  INTERNAL_STRING_UNREF(is);
-  unlock(ctx);
-  GPR_TIMER_END("slice_unref", 0);
+  GRPC_MDSTR_UNREF((grpc_mdstr *)(is));
 }
 
-grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str) {
-  return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, strlen(str));
+grpc_mdstr *grpc_mdstr_from_string(const char *str) {
+  return grpc_mdstr_from_buffer((const gpr_uint8 *)str, strlen(str));
 }
 
-grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice) {
-  grpc_mdstr *result = grpc_mdstr_from_buffer(ctx, GPR_SLICE_START_PTR(slice),
+grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice) {
+  grpc_mdstr *result = grpc_mdstr_from_buffer(GPR_SLICE_START_PTR(slice),
                                               GPR_SLICE_LENGTH(slice));
   gpr_slice_unref(slice);
   return result;
 }
 
-grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
-                                   size_t length) {
-  gpr_uint32 hash = gpr_murmur_hash3(buf, length, ctx->hash_seed);
+grpc_mdstr *grpc_mdstr_from_buffer(const gpr_uint8 *buf, size_t length) {
+  gpr_uint32 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);
-  lock(ctx);
+
+  /* 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 && GPR_SLICE_LENGTH(ss->slice) == length &&
+        0 == memcmp(buf, GPR_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 */
-  for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) {
+  idx = TABLE_IDX(hash, LOG2_STRTAB_SHARD_COUNT, shard->capacity);
+  for (s = shard->strs[idx]; s; s = s->bucket_next) {
     if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
         0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
-      INTERNAL_STRING_REF(s);
-      unlock(ctx);
+      GRPC_MDSTR_REF((grpc_mdstr *)s);
+      gpr_mu_unlock(&shard->mu);
       GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
       return (grpc_mdstr *)s;
     }
@@ -369,7 +368,7 @@
   if (length + 1 < GPR_SLICE_INLINED_SIZE) {
     /* string data goes directly into the slice */
     s = gpr_malloc(sizeof(internal_string));
-    s->refs = 1;
+    gpr_atm_rel_store(&s->refcnt, 2);
     s->slice.refcount = NULL;
     memcpy(s->slice.data.inlined.bytes, buf, length);
     s->slice.data.inlined.bytes[length] = 0;
@@ -378,7 +377,7 @@
     /* string data goes after the internal_string header, and we +1 for null
        terminator */
     s = gpr_malloc(sizeof(internal_string) + length + 1);
-    s->refs = 1;
+    gpr_atm_rel_store(&s->refcnt, 2);
     s->refcount.ref = slice_ref;
     s->refcount.unref = slice_unref;
     s->slice.refcount = &s->refcount;
@@ -390,44 +389,43 @@
   }
   s->has_base64_and_huffman_encoded = 0;
   s->hash = hash;
-  s->context = ctx;
-  s->bucket_next = ctx->strtab[hash % ctx->strtab_capacity];
-  ctx->strtab[hash % ctx->strtab_capacity] = s;
+  s->bucket_next = shard->strs[idx];
+  shard->strs[idx] = s;
 
-  ctx->strtab_count++;
+  shard->count++;
 
-  if (ctx->strtab_count > ctx->strtab_capacity * 2) {
-    grow_strtab(ctx);
+  if (shard->count > shard->capacity * 2) {
+    grow_strtab(shard);
   }
 
-  unlock(ctx);
+  gpr_mu_unlock(&shard->mu);
   GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
 
   return (grpc_mdstr *)s;
 }
 
-static void gc_mdtab(grpc_mdctx *ctx) {
+static void gc_mdtab(mdtab_shard *shard) {
   size_t i;
   internal_metadata **prev_next;
   internal_metadata *md, *next;
 
   GPR_TIMER_BEGIN("gc_mdtab", 0);
-  for (i = 0; i < ctx->mdtab_capacity; i++) {
-    prev_next = &ctx->mdtab[i];
-    for (md = ctx->mdtab[i]; md; md = next) {
+  for (i = 0; i < shard->capacity; i++) {
+    prev_next = &shard->elems[i];
+    for (md = shard->elems[i]; md; md = next) {
       void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
       next = md->bucket_next;
       if (gpr_atm_acq_load(&md->refcnt) == 0) {
-        INTERNAL_STRING_UNREF(md->key);
-        INTERNAL_STRING_UNREF(md->value);
+        GRPC_MDSTR_UNREF((grpc_mdstr *)md->key);
+        GRPC_MDSTR_UNREF((grpc_mdstr *)md->value);
         if (md->user_data) {
           ((destroy_user_data_func)gpr_atm_no_barrier_load(
               &md->destroy_user_data))(user_data);
         }
         gpr_free(md);
         *prev_next = next;
-        ctx->mdtab_free--;
-        ctx->mdtab_count--;
+        shard->free--;
+        shard->count--;
       } else {
         prev_next = &md->bucket_next;
       }
@@ -436,8 +434,8 @@
   GPR_TIMER_END("gc_mdtab", 0);
 }
 
-static void grow_mdtab(grpc_mdctx *ctx) {
-  size_t capacity = ctx->mdtab_capacity * 2;
+static void grow_mdtab(mdtab_shard *shard) {
+  size_t capacity = shard->capacity * 2;
   size_t i;
   internal_metadata **mdtab;
   internal_metadata *md, *next;
@@ -448,52 +446,67 @@
   mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity);
   memset(mdtab, 0, sizeof(internal_metadata *) * capacity);
 
-  for (i = 0; i < ctx->mdtab_capacity; i++) {
-    for (md = ctx->mdtab[i]; md; md = next) {
+  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);
       next = md->bucket_next;
-      md->bucket_next = mdtab[hash % capacity];
-      mdtab[hash % capacity] = md;
+      idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, capacity);
+      md->bucket_next = mdtab[idx];
+      mdtab[idx] = md;
     }
   }
 
-  gpr_free(ctx->mdtab);
-  ctx->mdtab = mdtab;
-  ctx->mdtab_capacity = capacity;
+  gpr_free(shard->elems);
+  shard->elems = mdtab;
+  shard->capacity = capacity;
 
   GPR_TIMER_END("grow_mdtab", 0);
 }
 
-static void rehash_mdtab(grpc_mdctx *ctx) {
-  if (ctx->mdtab_free > ctx->mdtab_capacity / 4) {
-    gc_mdtab(ctx);
+static void rehash_mdtab(mdtab_shard *shard) {
+  if (shard->free > shard->capacity / 4) {
+    gc_mdtab(shard);
   } else {
-    grow_mdtab(ctx);
+    grow_mdtab(shard);
   }
 }
 
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
-                                               grpc_mdstr *mkey,
+grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
                                                grpc_mdstr *mvalue) {
   internal_string *key = (internal_string *)mkey;
   internal_string *value = (internal_string *)mvalue;
   gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
   internal_metadata *md;
-
-  GPR_ASSERT(key->context == ctx);
-  GPR_ASSERT(value->context == ctx);
+  mdtab_shard *shard = &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
+  size_t i;
+  size_t idx;
 
   GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
 
-  lock(ctx);
+  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);
   /* search for an existing pair */
-  for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) {
+  for (md = shard->elems[idx]; md; md = md->bucket_next) {
     if (md->key == key && md->value == value) {
-      REF_MD_LOCKED(md);
-      INTERNAL_STRING_UNREF(key);
-      INTERNAL_STRING_UNREF(value);
-      unlock(ctx);
+      REF_MD_LOCKED(shard, md);
+      GRPC_MDSTR_UNREF((grpc_mdstr *)key);
+      GRPC_MDSTR_UNREF((grpc_mdstr *)value);
+      gpr_mu_unlock(&shard->mu);
       GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
       return (grpc_mdelem *)md;
     }
@@ -502,12 +515,12 @@
   /* not found: create a new pair */
   md = gpr_malloc(sizeof(internal_metadata));
   gpr_atm_rel_store(&md->refcnt, 2);
-  md->context = ctx;
   md->key = key;
   md->value = value;
   md->user_data = 0;
   md->destroy_user_data = 0;
-  md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity];
+  md->bucket_next = shard->elems[idx];
+  shard->elems[idx] = md;
   gpr_mu_init(&md->mu_user_data);
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(GPR_DEBUG, "ELM   NEW:%p:%d: '%s' = '%s'", md,
@@ -515,44 +528,39 @@
           grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
           grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
 #endif
-  ctx->mdtab[hash % ctx->mdtab_capacity] = md;
-  ctx->mdtab_count++;
+  shard->count++;
 
-  if (ctx->mdtab_count > ctx->mdtab_capacity * 2) {
-    rehash_mdtab(ctx);
+  if (shard->count > shard->capacity * 2) {
+    rehash_mdtab(shard);
   }
 
-  unlock(ctx);
+  gpr_mu_unlock(&shard->mu);
 
   GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
 
   return (grpc_mdelem *)md;
 }
 
-grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
-                                      const char *value) {
-  return grpc_mdelem_from_metadata_strings(ctx,
-                                           grpc_mdstr_from_string(ctx, key),
-                                           grpc_mdstr_from_string(ctx, value));
+grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value) {
+  return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_string(key),
+                                           grpc_mdstr_from_string(value));
 }
 
-grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
-                                     gpr_slice value) {
-  return grpc_mdelem_from_metadata_strings(ctx, grpc_mdstr_from_slice(ctx, key),
-                                           grpc_mdstr_from_slice(ctx, value));
+grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value) {
+  return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_slice(key),
+                                           grpc_mdstr_from_slice(value));
 }
 
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
-                                                const char *key,
+grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
                                                 const gpr_uint8 *value,
                                                 size_t value_length) {
   return grpc_mdelem_from_metadata_strings(
-      ctx, grpc_mdstr_from_string(ctx, key),
-      grpc_mdstr_from_buffer(ctx, value, value_length));
+      grpc_mdstr_from_string(key), grpc_mdstr_from_buffer(value, value_length));
 }
 
 grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
   internal_metadata *md = (internal_metadata *)gmd;
+  if (is_mdelem_static(gmd)) return gmd;
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
           "ELM   REF:%p:%d->%d: '%s' = '%s'", md,
@@ -573,6 +581,7 @@
 void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
   internal_metadata *md = (internal_metadata *)gmd;
   if (!md) return;
+  if (is_mdelem_static(gmd)) return;
 #ifdef GRPC_METADATA_REFCOUNT_DEBUG
   gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
           "ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
@@ -582,14 +591,16 @@
           grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
 #endif
   if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
-    grpc_mdctx *ctx = md->context;
+    gpr_uint32 hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
+    mdtab_shard *shard =
+        &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
     GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0);
-    lock(ctx);
+    gpr_mu_lock(&shard->mu);
     if (1 == gpr_atm_no_barrier_load(&md->refcnt)) {
-      ctx->mdtab_free++;
+      shard->free++;
       gpr_atm_no_barrier_store(&md->refcnt, 0);
     }
-    unlock(ctx);
+    gpr_mu_unlock(&shard->mu);
     GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0);
   }
 }
@@ -600,36 +611,31 @@
 
 grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
   internal_string *s = (internal_string *)gs;
-  grpc_mdctx *ctx = s->context;
-  lock(ctx);
-  internal_string_ref(s FWD_DEBUG_ARGS);
-  unlock(ctx);
+  if (is_mdstr_static(gs)) return gs;
+  GPR_ASSERT(gpr_atm_full_fetch_add(&s->refcnt, 1) != 0);
   return gs;
 }
 
 void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
   internal_string *s = (internal_string *)gs;
-  grpc_mdctx *ctx = s->context;
-  lock(ctx);
-  internal_string_unref(s FWD_DEBUG_ARGS);
-  unlock(ctx);
-}
-
-size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *ctx) {
-  return ctx->mdtab_capacity;
-}
-
-size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *ctx) {
-  return ctx->mdtab_count;
-}
-
-size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) {
-  return ctx->mdtab_free;
+  if (is_mdstr_static(gs)) return;
+  if (2 == 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);
+    if (1 == gpr_atm_no_barrier_load(&s->refcnt)) {
+      internal_destroy_string(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 {
@@ -641,6 +647,7 @@
 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)) {
@@ -659,15 +666,16 @@
 gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
   internal_string *s = (internal_string *)gs;
   gpr_slice slice;
-  grpc_mdctx *ctx = s->context;
-  lock(ctx);
+  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;
   }
   slice = s->base64_and_huffman;
-  unlock(ctx);
+  gpr_mu_unlock(&shard->mu);
   return slice;
 }
 
diff --git a/src/core/transport/metadata.h b/src/core/transport/metadata.h
index 9a81640..c1071e4 100644
--- a/src/core/transport/metadata.h
+++ b/src/core/transport/metadata.h
@@ -59,10 +59,15 @@
 
    grpc_mdelem instances MAY live longer than their refcount implies, and are
    garbage collected periodically, meaning cached data can easily outlive a
-   single request. */
+   single request.
+
+   STATIC METADATA: in static_metadata.h we declare a set of static metadata.
+   These mdelems and mdstrs are available via pre-declared code generated macros
+   and are available to code anywhere between grpc_init() and grpc_shutdown().
+   They are not refcounted, but can be passed to _ref and _unref functions
+   declared here - in which case those functions are effectively no-ops. */
 
 /* Forward declarations */
-typedef struct grpc_mdctx grpc_mdctx;
 typedef struct grpc_mdstr grpc_mdstr;
 typedef struct grpc_mdelem grpc_mdelem;
 
@@ -81,25 +86,18 @@
   /* there is a private part to this in metadata.c */
 };
 
-/* Create/orphan a metadata context */
-grpc_mdctx *grpc_mdctx_create(void);
-grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed);
-void grpc_mdctx_ref(grpc_mdctx *mdctx);
-void grpc_mdctx_unref(grpc_mdctx *mdctx);
-
 /* Test only accessors to internal state - only for testing this code - do not
    rely on it outside of metadata_test.c */
-size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *mdctx);
-size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *mdctx);
-size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *mdctx);
+size_t grpc_mdctx_get_mdtab_capacity_test_only(void);
+size_t grpc_mdctx_get_mdtab_count_test_only(void);
+size_t grpc_mdctx_get_mdtab_free_test_only(void);
 
 /* Constructors for grpc_mdstr instances; take a variety of data types that
    clients may have handy */
-grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str);
+grpc_mdstr *grpc_mdstr_from_string(const char *str);
 /* Unrefs the slice. */
-grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice);
-grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *str,
-                                   size_t length);
+grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice);
+grpc_mdstr *grpc_mdstr_from_buffer(const gpr_uint8 *str, size_t length);
 
 /* Returns a borrowed slice from the mdstr with its contents base64 encoded
    and huffman compressed */
@@ -107,15 +105,12 @@
 
 /* Constructors for grpc_mdelem instances; take a variety of data types that
    clients may have handy */
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx, grpc_mdstr *key,
+grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *key,
                                                grpc_mdstr *value);
-grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
-                                      const char *value);
+grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value);
 /* Unrefs the slices. */
-grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
-                                     gpr_slice value);
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
-                                                const char *key,
+grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value);
+grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
                                                 const gpr_uint8 *value,
                                                 size_t value_length);
 
@@ -157,4 +152,7 @@
 
 #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(void);
+
 #endif /* GRPC_INTERNAL_CORE_TRANSPORT_METADATA_H */
diff --git a/src/core/transport/static_metadata.c b/src/core/transport/static_metadata.c
new file mode 100644
index 0000000..e7aff32
--- /dev/null
+++ b/src/core/transport/static_metadata.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * WARNING: Auto-generated code.
+ *
+ * To make changes to this file, change
+ * tools/codegen/core/gen_static_metadata.py,
+ * and then re-run it.
+ *
+ * See metadata.h for an explanation of the interface here, and metadata.c for
+ * an
+ * explanation of what's going on.
+ */
+
+#include "src/core/transport/static_metadata.h"
+
+grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
+
+grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+gpr_uintptr 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, 3, 7, 5, 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};
+
+const gpr_uint8
+    grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] = {
+        11, 33, 10, 33, 12, 33, 12, 47, 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, 46, 40, 51, 40, 52, 40, 53, 40, 54, 41, 31, 41, 46, 41, 51,
+        44, 0,  44, 1,  44, 2,  48, 33, 55, 33, 56, 33, 57, 33, 58, 33, 59, 33,
+        60, 33, 61, 33, 62, 33, 63, 33, 64, 38, 64, 66, 65, 76, 65, 77, 67, 33,
+        68, 33, 69, 33, 70, 33, 71, 33, 72, 33, 73, 39, 73, 49, 73, 50, 74, 33,
+        75, 33, 78, 3,  78, 4,  78, 5,  78, 6,  78, 7,  78, 8,  78, 9,  79, 33,
+        80, 81, 82, 33, 83, 33, 84, 33, 85, 33, 86, 33};
+
+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-status",
+    "grpc-timeout",
+    "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",
+    "link",
+    "location",
+    "max-forwards",
+    ":method",
+    ":path",
+    "POST",
+    "proxy-authenticate",
+    "proxy-authorization",
+    "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"};
+
+const gpr_uint8 grpc_static_accept_encoding_metadata[8] = {0,  29, 26, 30,
+                                                           28, 32, 27, 31};
diff --git a/src/core/transport/static_metadata.h b/src/core/transport/static_metadata.h
new file mode 100644
index 0000000..e9055fb
--- /dev/null
+++ b/src/core/transport/static_metadata.h
@@ -0,0 +1,402 @@
+/*
+ * 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.
+ */
+
+/*
+ * WARNING: Auto-generated code.
+ *
+ * To make changes to this file, change
+ * tools/codegen/core/gen_static_metadata.py,
+ * and then re-run it.
+ *
+ * See metadata.h for an explanation of the interface here, and metadata.c for
+ * an
+ * explanation of what's going on.
+ */
+
+#ifndef GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H
+#define GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H
+
+#include "src/core/transport/metadata.h"
+
+#define GRPC_STATIC_MDSTR_COUNT 87
+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])
+/* ":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])
+/* "grpc-message" */
+#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[43])
+/* "grpc-status" */
+#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[44])
+/* "grpc-timeout" */
+#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[45])
+/* "gzip" */
+#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[46])
+/* "gzip, deflate" */
+#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[47])
+/* "host" */
+#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[48])
+/* "http" */
+#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[49])
+/* "https" */
+#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[50])
+/* "identity" */
+#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[51])
+/* "identity,deflate" */
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[52])
+/* "identity,deflate,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
+  (&grpc_static_mdstr_table[53])
+/* "identity,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[54])
+/* "if-match" */
+#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[55])
+/* "if-modified-since" */
+#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[56])
+/* "if-none-match" */
+#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[57])
+/* "if-range" */
+#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[58])
+/* "if-unmodified-since" */
+#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[59])
+/* "last-modified" */
+#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[60])
+/* "link" */
+#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[61])
+/* "location" */
+#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[62])
+/* "max-forwards" */
+#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[63])
+/* ":method" */
+#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[64])
+/* ":path" */
+#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[65])
+/* "POST" */
+#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[66])
+/* "proxy-authenticate" */
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[67])
+/* "proxy-authorization" */
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[68])
+/* "range" */
+#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[69])
+/* "referer" */
+#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[70])
+/* "refresh" */
+#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[71])
+/* "retry-after" */
+#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[72])
+/* ":scheme" */
+#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[73])
+/* "server" */
+#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[74])
+/* "set-cookie" */
+#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[75])
+/* "/" */
+#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[76])
+/* "/index.html" */
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[77])
+/* ":status" */
+#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[78])
+/* "strict-transport-security" */
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[79])
+/* "te" */
+#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[80])
+/* "trailers" */
+#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[81])
+/* "transfer-encoding" */
+#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[82])
+/* "user-agent" */
+#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[83])
+/* "vary" */
+#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[84])
+/* "via" */
+#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[85])
+/* "www-authenticate" */
+#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[86])
+
+#define GRPC_STATIC_MDELEM_COUNT 78
+extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+extern gpr_uintptr 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])
+/* "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])
+/* "link": "" */
+#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[46])
+/* "location": "" */
+#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[47])
+/* "max-forwards": "" */
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[48])
+/* ":method": "GET" */
+#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[49])
+/* ":method": "POST" */
+#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[50])
+/* ":path": "/" */
+#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[51])
+/* ":path": "/index.html" */
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[52])
+/* "proxy-authenticate": "" */
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[53])
+/* "proxy-authorization": "" */
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[54])
+/* "range": "" */
+#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[55])
+/* "referer": "" */
+#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[56])
+/* "refresh": "" */
+#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[57])
+/* "retry-after": "" */
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[58])
+/* ":scheme": "grpc" */
+#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[59])
+/* ":scheme": "http" */
+#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[60])
+/* ":scheme": "https" */
+#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[61])
+/* "server": "" */
+#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[62])
+/* "set-cookie": "" */
+#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[63])
+/* ":status": "200" */
+#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[64])
+/* ":status": "204" */
+#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[65])
+/* ":status": "206" */
+#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[66])
+/* ":status": "304" */
+#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[67])
+/* ":status": "400" */
+#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[68])
+/* ":status": "404" */
+#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[69])
+/* ":status": "500" */
+#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[70])
+/* "strict-transport-security": "" */
+#define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
+  (&grpc_static_mdelem_table[71])
+/* "te": "trailers" */
+#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[72])
+/* "transfer-encoding": "" */
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[73])
+/* "user-agent": "" */
+#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[74])
+/* "vary": "" */
+#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[75])
+/* "via": "" */
+#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[76])
+/* "www-authenticate": "" */
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[77])
+
+extern const gpr_uint8
+    grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
+extern const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];
+extern const gpr_uint8 grpc_static_accept_encoding_metadata[8];
+#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) \
+  (&grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]])
+#endif /* GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H */
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index 7972272..db9385e 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -94,6 +94,10 @@
     end
     $CFLAGS << ' -I' + File.join(grpc_root, 'include')
     $LDFLAGS << ' -L' + grpc_lib_dir
+    if grpc_config == 'gcov'
+      $CFLAGS << ' -O0 -fprofile-arcs -ftest-coverage'
+      $LDFLAGS << ' -fprofile-arcs -ftest-coverage -rdynamic'
+    end
     raise 'gpr not found' unless have_library('gpr', 'gpr_now')
     raise 'grpc not found' unless have_library('grpc', 'grpc_channel_destroy')
   end
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index ed46e7b..e1a4b8e 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -64,14 +64,13 @@
   gpr_event_set(&a->done_write, (void *)1);
 }
 
-static void server_setup_transport(void *ts, grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+static void server_setup_transport(void *ts, grpc_transport *transport) {
   thd_args *a = ts;
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_server_setup_transport(&exec_ctx, a->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(a->server));
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -84,7 +83,6 @@
   gpr_thd_id id;
   char *hex;
   grpc_transport *transport;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   gpr_slice slice =
       gpr_slice_from_copied_buffer(client_payload, client_payload_length);
   gpr_slice_buffer outgoing;
@@ -113,9 +111,8 @@
   a.validator = validator;
   grpc_server_register_completion_queue(a.server, a.cq, NULL);
   grpc_server_start(a.server);
-  transport =
-      grpc_create_chttp2_transport(&exec_ctx, NULL, sfd.server, mdctx, 0);
-  server_setup_transport(&a, transport, mdctx);
+  transport = grpc_create_chttp2_transport(&exec_ctx, NULL, sfd.server, 0);
+  server_setup_transport(&a, transport);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
 
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index 5e1ba11..9dbb879 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -93,13 +93,10 @@
   grpc_call_element *call_elem;
   grpc_arg arg;
   grpc_channel_args chan_args;
-  grpc_mdctx *metadata_context;
   int *channel_data;
   int *call_data;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
-  metadata_context = grpc_mdctx_create();
-
   arg.type = GRPC_ARG_INTEGER;
   arg.key = "test_key";
   arg.value.integer = 42;
@@ -109,7 +106,7 @@
 
   channel_stack = gpr_malloc(grpc_channel_stack_size(&filters, 1));
   grpc_channel_stack_init(&exec_ctx, &filters, 1, NULL, &chan_args,
-                          metadata_context, channel_stack);
+                          channel_stack);
   GPR_ASSERT(channel_stack->count == 1);
   channel_elem = grpc_channel_stack_element(channel_stack, 0);
   channel_data = (int *)channel_elem->channel_data;
@@ -133,13 +130,13 @@
   grpc_channel_stack_destroy(&exec_ctx, channel_stack);
   gpr_free(channel_stack);
 
-  grpc_mdctx_unref(metadata_context);
-
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
+  grpc_init();
   test_create_channel_stack();
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/compression/compression_test.c b/test/core/compression/compression_test.c
index 633fbd9..35fadc0 100644
--- a/test/core/compression/compression_test.c
+++ b/test/core/compression/compression_test.c
@@ -35,6 +35,7 @@
 #include <string.h>
 
 #include <grpc/compression.h>
+#include <grpc/grpc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
@@ -71,7 +72,9 @@
 }
 
 int main(int argc, char **argv) {
+  grpc_init();
   test_compression_algorithm_parse();
+  grpc_shutdown();
 
   return 0;
 }
diff --git a/test/core/compression/message_compress_test.c b/test/core/compression/message_compress_test.c
index 98da6a1..dfaed84 100644
--- a/test/core/compression/message_compress_test.c
+++ b/test/core/compression/message_compress_test.c
@@ -36,10 +36,12 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "test/core/util/test_config.h"
-#include "src/core/support/murmur_hash.h"
+#include <grpc/grpc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
+
+#include "test/core/util/test_config.h"
+#include "src/core/support/murmur_hash.h"
 #include "test/core/util/slice_splitter.h"
 
 typedef enum { ONE_A = 0, ONE_KB_A, ONE_MB_A, TEST_VALUE_COUNT } test_value;
@@ -175,6 +177,7 @@
                                                     GRPC_SLICE_SPLIT_ONE_BYTE};
 
   grpc_test_init(argc, argv);
+  grpc_init();
 
   for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
     for (j = 0; j < GPR_ARRAY_SIZE(uncompressed_split_modes); j++) {
@@ -189,6 +192,7 @@
   }
 
   test_bad_data();
+  grpc_shutdown();
 
   return 0;
 }
diff --git a/test/core/end2end/fixtures/h2_sockpair+trace.c b/test/core/end2end/fixtures/h2_sockpair+trace.c
index 1f5051f..ccc8631 100644
--- a/test/core/end2end/fixtures/h2_sockpair+trace.c
+++ b/test/core/end2end/fixtures/h2_sockpair+trace.c
@@ -57,14 +57,13 @@
 /* chttp2 transport that is immediately available (used for testing
    connected_channel without a client_channel */
 
-static void server_setup_transport(void *ts, grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+static void server_setup_transport(void *ts, grpc_transport *transport) {
   grpc_end2end_test_fixture *f = ts;
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_server_setup_transport(&exec_ctx, f->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(f->server));
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -75,17 +74,15 @@
 } sp_client_setup;
 
 static void client_setup_transport(grpc_exec_ctx *exec_ctx, void *ts,
-                                   grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+                                   grpc_transport *transport) {
   sp_client_setup *cs = ts;
 
   const grpc_channel_filter *filters[] = {&grpc_http_client_filter,
                                           &grpc_compress_filter,
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
-  grpc_channel *channel =
-      grpc_channel_create_from_filters(exec_ctx, "socketpair-target", filters,
-                                       nfilters, cs->client_args, mdctx, 1);
+  grpc_channel *channel = grpc_channel_create_from_filters(
+      exec_ctx, "socketpair-target", filters, nfilters, cs->client_args, 1);
 
   cs->f->client = channel;
 
@@ -112,13 +109,12 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
   grpc_transport *transport;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   sp_client_setup cs;
   cs.client_args = client_args;
   cs.f = f;
-  transport = grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client,
-                                           mdctx, 1);
-  client_setup_transport(&exec_ctx, &cs, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
+  client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -128,15 +124,14 @@
                                           grpc_channel_args *server_args) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_transport *transport;
   GPR_ASSERT(!f->server);
   f->server = grpc_server_create_from_filters(NULL, 0, server_args);
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   grpc_server_start(f->server);
-  transport = grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server,
-                                           mdctx, 0);
-  server_setup_transport(f, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
+  server_setup_transport(f, transport);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/core/end2end/fixtures/h2_sockpair.c b/test/core/end2end/fixtures/h2_sockpair.c
index b61fe98..a6a84c9 100644
--- a/test/core/end2end/fixtures/h2_sockpair.c
+++ b/test/core/end2end/fixtures/h2_sockpair.c
@@ -56,14 +56,13 @@
 /* chttp2 transport that is immediately available (used for testing
    connected_channel without a client_channel */
 
-static void server_setup_transport(void *ts, grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+static void server_setup_transport(void *ts, grpc_transport *transport) {
   grpc_end2end_test_fixture *f = ts;
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_server_setup_transport(&exec_ctx, f->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(f->server));
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -74,17 +73,15 @@
 } sp_client_setup;
 
 static void client_setup_transport(grpc_exec_ctx *exec_ctx, void *ts,
-                                   grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+                                   grpc_transport *transport) {
   sp_client_setup *cs = ts;
 
   const grpc_channel_filter *filters[] = {&grpc_http_client_filter,
                                           &grpc_compress_filter,
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
-  grpc_channel *channel =
-      grpc_channel_create_from_filters(exec_ctx, "socketpair-target", filters,
-                                       nfilters, cs->client_args, mdctx, 1);
+  grpc_channel *channel = grpc_channel_create_from_filters(
+      exec_ctx, "socketpair-target", filters, nfilters, cs->client_args, 1);
 
   cs->f->client = channel;
 
@@ -111,13 +108,12 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
   grpc_transport *transport;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   sp_client_setup cs;
   cs.client_args = client_args;
   cs.f = f;
-  transport = grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client,
-                                           mdctx, 1);
-  client_setup_transport(&exec_ctx, &cs, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
+  client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -127,15 +123,14 @@
                                           grpc_channel_args *server_args) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_transport *transport;
   GPR_ASSERT(!f->server);
   f->server = grpc_server_create_from_filters(NULL, 0, server_args);
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   grpc_server_start(f->server);
-  transport = grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server,
-                                           mdctx, 0);
-  server_setup_transport(f, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
+  server_setup_transport(f, transport);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/core/end2end/fixtures/h2_sockpair_1byte.c b/test/core/end2end/fixtures/h2_sockpair_1byte.c
index 9f0fd2e..4b8f905 100644
--- a/test/core/end2end/fixtures/h2_sockpair_1byte.c
+++ b/test/core/end2end/fixtures/h2_sockpair_1byte.c
@@ -56,14 +56,13 @@
 /* chttp2 transport that is immediately available (used for testing
    connected_channel without a client_channel */
 
-static void server_setup_transport(void *ts, grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+static void server_setup_transport(void *ts, grpc_transport *transport) {
   grpc_end2end_test_fixture *f = ts;
   static grpc_channel_filter const *extra_filters[] = {
       &grpc_http_server_filter};
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_server_setup_transport(&exec_ctx, f->server, transport, extra_filters,
-                              GPR_ARRAY_SIZE(extra_filters), mdctx,
+                              GPR_ARRAY_SIZE(extra_filters),
                               grpc_server_get_channel_args(f->server));
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -74,17 +73,15 @@
 } sp_client_setup;
 
 static void client_setup_transport(grpc_exec_ctx *exec_ctx, void *ts,
-                                   grpc_transport *transport,
-                                   grpc_mdctx *mdctx) {
+                                   grpc_transport *transport) {
   sp_client_setup *cs = ts;
 
   const grpc_channel_filter *filters[] = {&grpc_http_client_filter,
                                           &grpc_compress_filter,
                                           &grpc_connected_channel_filter};
   size_t nfilters = sizeof(filters) / sizeof(*filters);
-  grpc_channel *channel =
-      grpc_channel_create_from_filters(exec_ctx, "socketpair-target", filters,
-                                       nfilters, cs->client_args, mdctx, 1);
+  grpc_channel *channel = grpc_channel_create_from_filters(
+      exec_ctx, "socketpair-target", filters, nfilters, cs->client_args, 1);
 
   cs->f->client = channel;
 
@@ -111,13 +108,12 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
   grpc_transport *transport;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   sp_client_setup cs;
   cs.client_args = client_args;
   cs.f = f;
-  transport = grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client,
-                                           mdctx, 1);
-  client_setup_transport(&exec_ctx, &cs, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
+  client_setup_transport(&exec_ctx, &cs, transport);
   GPR_ASSERT(f->client);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -127,15 +123,14 @@
                                           grpc_channel_args *server_args) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_endpoint_pair *sfd = f->fixture_data;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_transport *transport;
   GPR_ASSERT(!f->server);
   f->server = grpc_server_create_from_filters(NULL, 0, server_args);
   grpc_server_register_completion_queue(f->server, f->cq, NULL);
   grpc_server_start(f->server);
-  transport = grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server,
-                                           mdctx, 0);
-  server_setup_transport(f, transport, mdctx);
+  transport =
+      grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
+  server_setup_transport(f, transport);
   grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/core/end2end/fixtures/h2_uchannel.c b/test/core/end2end/fixtures/h2_uchannel.c
index d1f9d38..ee4a60c 100644
--- a/test/core/end2end/fixtures/h2_uchannel.c
+++ b/test/core/end2end/fixtures/h2_uchannel.c
@@ -66,8 +66,6 @@
 
   grpc_endpoint *tcp;
 
-  grpc_mdctx *mdctx;
-
   grpc_closure connected;
 } connector;
 
@@ -79,7 +77,6 @@
 static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) {
   connector *c = (connector *)con;
   if (gpr_unref(&c->refs)) {
-    grpc_mdctx_unref(c->mdctx);
     gpr_free(c);
   }
 }
@@ -89,8 +86,8 @@
   grpc_closure *notify;
   grpc_endpoint *tcp = c->tcp;
   if (tcp != NULL) {
-    c->result->transport = grpc_create_chttp2_transport(
-        exec_ctx, c->args.channel_args, tcp, c->mdctx, 1);
+    c->result->transport =
+        grpc_create_chttp2_transport(exec_ctx, c->args.channel_args, tcp, 1);
     grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL,
                                         0);
     GPR_ASSERT(c->result->transport);
@@ -130,7 +127,6 @@
 typedef struct {
   grpc_subchannel_factory base;
   gpr_refcount refs;
-  grpc_mdctx *mdctx;
   grpc_channel_args *merge_args;
   grpc_channel *master;
   grpc_subchannel **sniffed_subchannel;
@@ -147,7 +143,6 @@
   if (gpr_unref(&f->refs)) {
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory");
     grpc_channel_args_destroy(f->merge_args);
-    grpc_mdctx_unref(f->mdctx);
     gpr_free(f);
   }
 }
@@ -162,10 +157,7 @@
   grpc_subchannel *s;
   memset(c, 0, sizeof(*c));
   c->base.vtable = &connector_vtable;
-  c->mdctx = f->mdctx;
-  grpc_mdctx_ref(c->mdctx);
   gpr_ref_init(&c->refs, 1);
-  args->mdctx = f->mdctx;
   args->args = final_args;
   args->master = f->master;
   s = grpc_subchannel_create(&c->base, args);
@@ -188,22 +180,19 @@
   const grpc_channel_filter *filters[MAX_FILTERS];
   grpc_resolver *resolver;
   subchannel_factory *f;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   size_t n = 0;
 
   filters[n++] = &grpc_client_channel_filter;
   GPR_ASSERT(n <= MAX_FILTERS);
 
-  channel = grpc_channel_create_from_filters(&exec_ctx, target, filters, n,
-                                             args, mdctx, 1);
+  channel =
+      grpc_channel_create_from_filters(&exec_ctx, target, filters, n, args, 1);
 
   f = gpr_malloc(sizeof(*f));
   f->sniffed_subchannel = sniffed_subchannel;
   f->base.vtable = &test_subchannel_factory_vtable;
   gpr_ref_init(&f->refs, 1);
-  grpc_mdctx_ref(mdctx);
-  f->mdctx = mdctx;
   f->merge_args = grpc_channel_args_copy(args);
   f->master = channel;
   GRPC_CHANNEL_INTERNAL_REF(f->master, "test_subchannel_factory");
diff --git a/test/core/iomgr/fd_posix_test.c b/test/core/iomgr/fd_posix_test.c
index f592f63..4be6957 100644
--- a/test/core/iomgr/fd_posix_test.c
+++ b/test/core/iomgr/fd_posix_test.c
@@ -121,7 +121,7 @@
                                 int success) {
   session *se = arg;
   server *sv = se->sv;
-  grpc_fd_orphan(exec_ctx, se->em_fd, NULL, "a");
+  grpc_fd_orphan(exec_ctx, se->em_fd, NULL, NULL, "a");
   gpr_free(se);
   /* Start to shutdown listen fd. */
   grpc_fd_shutdown(exec_ctx, sv->em_fd);
@@ -177,7 +177,7 @@
                                int success) {
   server *sv = arg;
 
-  grpc_fd_orphan(exec_ctx, sv->em_fd, NULL, "b");
+  grpc_fd_orphan(exec_ctx, sv->em_fd, NULL, NULL, "b");
 
   gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
   sv->done = 1;
@@ -294,7 +294,7 @@
 static void client_session_shutdown_cb(grpc_exec_ctx *exec_ctx,
                                        void *arg /*client */, int success) {
   client *cl = arg;
-  grpc_fd_orphan(exec_ctx, cl->em_fd, NULL, "c");
+  grpc_fd_orphan(exec_ctx, cl->em_fd, NULL, NULL, "c");
   cl->done = 1;
   grpc_pollset_kick(&g_pollset, NULL);
 }
@@ -503,7 +503,7 @@
   GPR_ASSERT(b.cb_that_ran == second_read_callback);
   gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));
 
-  grpc_fd_orphan(&exec_ctx, em_fd, NULL, "d");
+  grpc_fd_orphan(&exec_ctx, em_fd, NULL, NULL, "d");
   grpc_exec_ctx_finish(&exec_ctx);
   destroy_change_data(&a);
   destroy_change_data(&b);
diff --git a/test/core/iomgr/tcp_posix_test.c b/test/core/iomgr/tcp_posix_test.c
index f676454..9feac93 100644
--- a/test/core/iomgr/tcp_posix_test.c
+++ b/test/core/iomgr/tcp_posix_test.c
@@ -383,6 +383,76 @@
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
+void on_fd_released(grpc_exec_ctx *exec_ctx, void *arg, int success) {
+  int *done = arg;
+  *done = 1;
+  grpc_pollset_kick(&g_pollset, NULL);
+}
+
+/* Do a read_test, then release fd and try to read/write again. */
+static void release_fd_test(size_t num_bytes, size_t slice_size) {
+  int sv[2];
+  grpc_endpoint *ep;
+  struct read_socket_state state;
+  size_t written_bytes;
+  int fd;
+  gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(20);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_closure fd_released_cb;
+  int fd_released_done = 0;
+  grpc_closure_init(&fd_released_cb, &on_fd_released, &fd_released_done);
+
+  gpr_log(GPR_INFO, "Release fd read_test of size %d, slice size %d", num_bytes,
+          slice_size);
+
+  create_sockets(sv);
+
+  ep = grpc_tcp_create(grpc_fd_create(sv[1], "read_test"), slice_size, "test");
+  grpc_endpoint_add_to_pollset(&exec_ctx, ep, &g_pollset);
+
+  written_bytes = fill_socket_partial(sv[0], num_bytes);
+  gpr_log(GPR_INFO, "Wrote %d bytes", written_bytes);
+
+  state.ep = ep;
+  state.read_bytes = 0;
+  state.target_read_bytes = written_bytes;
+  gpr_slice_buffer_init(&state.incoming);
+  grpc_closure_init(&state.read_cb, read_cb, &state);
+
+  grpc_endpoint_read(&exec_ctx, ep, &state.incoming, &state.read_cb);
+
+  gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
+  while (state.read_bytes < state.target_read_bytes) {
+    grpc_pollset_worker worker;
+    grpc_pollset_work(&exec_ctx, &g_pollset, &worker,
+                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+    gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));
+    grpc_exec_ctx_finish(&exec_ctx);
+    gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
+  }
+  GPR_ASSERT(state.read_bytes == state.target_read_bytes);
+  gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));
+
+  gpr_slice_buffer_destroy(&state.incoming);
+  grpc_tcp_destroy_and_release_fd(&exec_ctx, ep, &fd, &fd_released_cb);
+  gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset));
+  while (!fd_released_done) {
+    grpc_pollset_worker worker;
+    grpc_pollset_work(&exec_ctx, &g_pollset, &worker,
+                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+  }
+  gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset));
+  GPR_ASSERT(fd_released_done == 1);
+  GPR_ASSERT(fd == sv[1]);
+  grpc_exec_ctx_finish(&exec_ctx);
+
+  written_bytes = fill_socket_partial(sv[0], num_bytes);
+  drain_socket_blocking(fd, written_bytes, written_bytes);
+  written_bytes = fill_socket_partial(fd, num_bytes);
+  drain_socket_blocking(sv[0], written_bytes, written_bytes);
+  close(fd);
+}
+
 void run_tests(void) {
   size_t i = 0;
 
@@ -402,6 +472,8 @@
   for (i = 1; i < 1000; i = GPR_MAX(i + 1, i * 5 / 4)) {
     write_test(40320, i);
   }
+
+  release_fd_test(100, 8192);
 }
 
 static void clean_up(void) {}
diff --git a/test/core/transport/chttp2/hpack_encoder_test.c b/test/core/transport/chttp2/hpack_encoder_test.c
index 6553e0d..64ea1e3 100644
--- a/test/core/transport/chttp2/hpack_encoder_test.c
+++ b/test/core/transport/chttp2/hpack_encoder_test.c
@@ -46,7 +46,6 @@
 
 #define TEST(x) run_test(x, #x)
 
-grpc_mdctx *g_mdctx;
 grpc_chttp2_hpack_compressor g_compressor;
 int g_failure = 0;
 
@@ -76,7 +75,7 @@
       e[i - 1].next = &e[i];
       e[i].prev = &e[i - 1];
     }
-    e[i].md = grpc_mdelem_from_strings(g_mdctx, key, value);
+    e[i].md = grpc_mdelem_from_strings(key, value);
   }
   e[0].prev = NULL;
   e[nheaders - 1].next = NULL;
@@ -181,18 +180,18 @@
 
 static void run_test(void (*test)(), const char *name) {
   gpr_log(GPR_INFO, "RUN TEST: %s", name);
-  g_mdctx = grpc_mdctx_create_with_seed(0);
-  grpc_chttp2_hpack_compressor_init(&g_compressor, g_mdctx);
+  grpc_chttp2_hpack_compressor_init(&g_compressor);
   test();
   grpc_chttp2_hpack_compressor_destroy(&g_compressor);
-  grpc_mdctx_unref(g_mdctx);
 }
 
 int main(int argc, char **argv) {
   size_t i;
   grpc_test_init(argc, argv);
+  grpc_init();
   TEST(test_basic_headers);
   TEST(test_decode_table_overflow);
+  grpc_shutdown();
   for (i = 0; i < num_to_delete; i++) {
     gpr_free(to_delete[i]);
   }
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
index c6b35fb..4456e19 100644
--- a/test/core/transport/chttp2/hpack_parser_test.c
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -35,6 +35,7 @@
 
 #include <stdarg.h>
 
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/slice.h>
@@ -90,9 +91,8 @@
 
 static void test_vectors(grpc_slice_split_mode mode) {
   grpc_chttp2_hpack_parser parser;
-  grpc_mdctx *mdctx = grpc_mdctx_create();
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   /* D.2.1 */
   test_vector(&parser, mode,
               "400a 6375 7374 6f6d 2d6b 6579 0d63 7573"
@@ -110,7 +110,7 @@
   test_vector(&parser, mode, "82", ":method", "GET", NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   /* D.3.1 */
   test_vector(&parser, mode,
               "8286 8441 0f77 7777 2e65 7861 6d70 6c65"
@@ -130,7 +130,7 @@
               NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   /* D.4.1 */
   test_vector(&parser, mode,
               "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4"
@@ -150,7 +150,7 @@
               NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   grpc_chttp2_hptbl_set_max_bytes(&parser.table, 256);
   grpc_chttp2_hptbl_set_current_table_size(&parser.table, 256);
   /* D.5.1 */
@@ -184,7 +184,7 @@
               "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
 
-  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  grpc_chttp2_hpack_parser_init(&parser);
   grpc_chttp2_hptbl_set_max_bytes(&parser.table, 256);
   grpc_chttp2_hptbl_set_current_table_size(&parser.table, 256);
   /* D.6.1 */
@@ -214,12 +214,13 @@
               "set-cookie",
               "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
   grpc_chttp2_hpack_parser_destroy(&parser);
-  grpc_mdctx_unref(mdctx);
 }
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
+  grpc_init();
   test_vectors(GRPC_SLICE_SPLIT_MERGE_ALL);
   test_vectors(GRPC_SLICE_SPLIT_ONE_BYTE);
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c
index 5eb52d6..50496a3 100644
--- a/test/core/transport/chttp2/hpack_table_test.c
+++ b/test/core/transport/chttp2/hpack_table_test.c
@@ -36,10 +36,12 @@
 #include <string.h>
 #include <stdio.h>
 
-#include "src/core/support/string.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
+#include <grpc/grpc.h>
+
+#include "src/core/support/string.h"
 #include "test/core/util/test_config.h"
 
 #define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
@@ -58,10 +60,8 @@
 
 static void test_static_lookup(void) {
   grpc_chttp2_hptbl tbl;
-  grpc_mdctx *mdctx;
 
-  mdctx = grpc_mdctx_create();
-  grpc_chttp2_hptbl_init(&tbl, mdctx);
+  grpc_chttp2_hptbl_init(&tbl);
 
   LOG_TEST("test_static_lookup");
   assert_index(&tbl, 1, ":authority", "");
@@ -127,7 +127,6 @@
   assert_index(&tbl, 61, "www-authenticate", "");
 
   grpc_chttp2_hptbl_destroy(&tbl);
-  grpc_mdctx_unref(mdctx);
 }
 
 static void test_many_additions(void) {
@@ -135,18 +134,16 @@
   int i;
   char *key;
   char *value;
-  grpc_mdctx *mdctx;
 
   LOG_TEST("test_many_additions");
 
-  mdctx = grpc_mdctx_create();
-  grpc_chttp2_hptbl_init(&tbl, mdctx);
+  grpc_chttp2_hptbl_init(&tbl);
 
   for (i = 0; i < 1000000; i++) {
     grpc_mdelem *elem;
     gpr_asprintf(&key, "K:%d", i);
     gpr_asprintf(&value, "VALUE:%d", i);
-    elem = grpc_mdelem_from_strings(mdctx, key, value);
+    elem = grpc_mdelem_from_strings(key, value);
     GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
     GRPC_MDELEM_UNREF(elem);
     assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
@@ -162,13 +159,12 @@
   }
 
   grpc_chttp2_hptbl_destroy(&tbl);
-  grpc_mdctx_unref(mdctx);
 }
 
 static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
                                                  const char *key,
                                                  const char *value) {
-  grpc_mdelem *md = grpc_mdelem_from_strings(tbl->mdctx, key, value);
+  grpc_mdelem *md = grpc_mdelem_from_strings(key, value);
   grpc_chttp2_hptbl_find_result r = grpc_chttp2_hptbl_find(tbl, md);
   GRPC_MDELEM_UNREF(md);
   return r;
@@ -178,21 +174,19 @@
   grpc_chttp2_hptbl tbl;
   gpr_uint32 i;
   char buffer[32];
-  grpc_mdctx *mdctx;
   grpc_mdelem *elem;
   grpc_chttp2_hptbl_find_result r;
 
   LOG_TEST("test_find");
 
-  mdctx = grpc_mdctx_create();
-  grpc_chttp2_hptbl_init(&tbl, mdctx);
-  elem = grpc_mdelem_from_strings(mdctx, "abc", "xyz");
+  grpc_chttp2_hptbl_init(&tbl);
+  elem = grpc_mdelem_from_strings("abc", "xyz");
   GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
   GRPC_MDELEM_UNREF(elem);
-  elem = grpc_mdelem_from_strings(mdctx, "abc", "123");
+  elem = grpc_mdelem_from_strings("abc", "123");
   GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
   GRPC_MDELEM_UNREF(elem);
-  elem = grpc_mdelem_from_strings(mdctx, "x", "1");
+  elem = grpc_mdelem_from_strings("x", "1");
   GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
   GRPC_MDELEM_UNREF(elem);
 
@@ -243,7 +237,7 @@
   /* overflow the string buffer, check find still works */
   for (i = 0; i < 10000; i++) {
     gpr_ltoa(i, buffer);
-    elem = grpc_mdelem_from_strings(mdctx, "test", buffer);
+    elem = grpc_mdelem_from_strings("test", buffer);
     GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
     GRPC_MDELEM_UNREF(elem);
   }
@@ -274,13 +268,14 @@
   GPR_ASSERT(r.has_value == 0);
 
   grpc_chttp2_hptbl_destroy(&tbl);
-  grpc_mdctx_unref(mdctx);
 }
 
 int main(int argc, char **argv) {
   grpc_test_init(argc, argv);
+  grpc_init();
   test_static_lookup();
   test_many_additions();
   test_find();
+  grpc_shutdown();
   return 0;
 }
diff --git a/test/core/transport/metadata_test.c b/test/core/transport/metadata_test.c
index 080e86c..9c1eae9 100644
--- a/test/core/transport/metadata_test.c
+++ b/test/core/transport/metadata_test.c
@@ -35,11 +35,13 @@
 
 #include <stdio.h>
 
-#include "src/core/support/string.h"
-#include "src/core/transport/chttp2/bin_encoder.h"
+#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
+
+#include "src/core/support/string.h"
+#include "src/core/transport/chttp2/bin_encoder.h"
 #include "test/core/util/test_config.h"
 
 #define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
@@ -48,44 +50,39 @@
 #define MANY 10000
 
 static void test_no_op(void) {
-  grpc_mdctx *ctx;
-
   LOG_TEST("test_no_op");
-
-  ctx = grpc_mdctx_create();
-  grpc_mdctx_unref(ctx);
+  grpc_init();
+  grpc_shutdown();
 }
 
 static void test_create_string(void) {
-  grpc_mdctx *ctx;
   grpc_mdstr *s1, *s2, *s3;
 
   LOG_TEST("test_create_string");
 
-  ctx = grpc_mdctx_create();
-  s1 = grpc_mdstr_from_string(ctx, "hello");
-  s2 = grpc_mdstr_from_string(ctx, "hello");
-  s3 = grpc_mdstr_from_string(ctx, "very much not hello");
+  grpc_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(gpr_slice_str_cmp(s1->slice, "hello") == 0);
   GPR_ASSERT(gpr_slice_str_cmp(s3->slice, "very much not hello") == 0);
   GRPC_MDSTR_UNREF(s1);
   GRPC_MDSTR_UNREF(s2);
-  grpc_mdctx_unref(ctx);
   GRPC_MDSTR_UNREF(s3);
+  grpc_shutdown();
 }
 
 static void test_create_metadata(void) {
-  grpc_mdctx *ctx;
   grpc_mdelem *m1, *m2, *m3;
 
   LOG_TEST("test_create_metadata");
 
-  ctx = grpc_mdctx_create();
-  m1 = grpc_mdelem_from_strings(ctx, "a", "b");
-  m2 = grpc_mdelem_from_strings(ctx, "a", "b");
-  m3 = grpc_mdelem_from_strings(ctx, "a", "c");
+  grpc_init();
+  m1 = grpc_mdelem_from_strings("a", "b");
+  m2 = grpc_mdelem_from_strings("a", "b");
+  m3 = grpc_mdelem_from_strings("a", "c");
   GPR_ASSERT(m1 == m2);
   GPR_ASSERT(m3 != m1);
   GPR_ASSERT(m3->key == m1->key);
@@ -96,32 +93,25 @@
   GRPC_MDELEM_UNREF(m1);
   GRPC_MDELEM_UNREF(m2);
   GRPC_MDELEM_UNREF(m3);
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 }
 
 static void test_create_many_ephemeral_metadata(void) {
-  grpc_mdctx *ctx;
   char buffer[GPR_LTOA_MIN_BUFSIZE];
   long i;
-  size_t mdtab_capacity_before;
 
   LOG_TEST("test_create_many_ephemeral_metadata");
 
-  ctx = grpc_mdctx_create();
-  mdtab_capacity_before = grpc_mdctx_get_mdtab_capacity_test_only(ctx);
+  grpc_init();
   /* add, and immediately delete a bunch of different elements */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", buffer));
+    GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", buffer));
   }
-  /* capacity should not grow */
-  GPR_ASSERT(mdtab_capacity_before ==
-             grpc_mdctx_get_mdtab_capacity_test_only(ctx));
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 }
 
 static void test_create_many_persistant_metadata(void) {
-  grpc_mdctx *ctx;
   char buffer[GPR_LTOA_MIN_BUFSIZE];
   long i;
   grpc_mdelem **created = gpr_malloc(sizeof(grpc_mdelem *) * MANY);
@@ -129,16 +119,16 @@
 
   LOG_TEST("test_create_many_persistant_metadata");
 
-  ctx = grpc_mdctx_create();
+  grpc_init();
   /* add phase */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    created[i] = grpc_mdelem_from_strings(ctx, "a", buffer);
+    created[i] = grpc_mdelem_from_strings("a", buffer);
   }
   /* verify phase */
   for (i = 0; i < MANY; i++) {
     gpr_ltoa(i, buffer);
-    md = grpc_mdelem_from_strings(ctx, "a", buffer);
+    md = grpc_mdelem_from_strings("a", buffer);
     GPR_ASSERT(md == created[i]);
     GRPC_MDELEM_UNREF(md);
   }
@@ -146,37 +136,22 @@
   for (i = 0; i < MANY; i++) {
     GRPC_MDELEM_UNREF(created[i]);
   }
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 
   gpr_free(created);
 }
 
 static void test_spin_creating_the_same_thing(void) {
-  grpc_mdctx *ctx;
-
   LOG_TEST("test_spin_creating_the_same_thing");
 
-  ctx = grpc_mdctx_create();
-  GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 0);
-  GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 0);
-
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
-  GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
-  GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
-
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
-  GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
-  GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
-
-  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings(ctx, "a", "b"));
-  GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
-  GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
-
-  grpc_mdctx_unref(ctx);
+  grpc_init();
+  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
+  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
+  GRPC_MDELEM_UNREF(grpc_mdelem_from_strings("a", "b"));
+  grpc_shutdown();
 }
 
 static void test_things_stick_around(void) {
-  grpc_mdctx *ctx;
   size_t i, j;
   char *buffer;
   size_t nstrs = 1000;
@@ -186,11 +161,11 @@
 
   LOG_TEST("test_things_stick_around");
 
-  ctx = grpc_mdctx_create();
+  grpc_init();
 
   for (i = 0; i < nstrs; i++) {
     gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", i);
-    strs[i] = grpc_mdstr_from_string(ctx, buffer);
+    strs[i] = grpc_mdstr_from_string(buffer);
     shuf[i] = i;
     gpr_free(buffer);
   }
@@ -212,60 +187,58 @@
     GRPC_MDSTR_UNREF(strs[shuf[i]]);
     for (j = i + 1; j < nstrs; j++) {
       gpr_asprintf(&buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", shuf[j]);
-      test = grpc_mdstr_from_string(ctx, buffer);
+      test = grpc_mdstr_from_string(buffer);
       GPR_ASSERT(test == strs[shuf[j]]);
       GRPC_MDSTR_UNREF(test);
       gpr_free(buffer);
     }
   }
 
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
   gpr_free(strs);
   gpr_free(shuf);
 }
 
 static void test_slices_work(void) {
   /* ensure no memory leaks when switching representation from mdstr to slice */
-  grpc_mdctx *ctx;
   grpc_mdstr *str;
   gpr_slice slice;
 
   LOG_TEST("test_slices_work");
 
-  ctx = grpc_mdctx_create();
+  grpc_init();
 
   str = grpc_mdstr_from_string(
-      ctx, "123456789012345678901234567890123456789012345678901234567890");
+      "123456789012345678901234567890123456789012345678901234567890");
   slice = gpr_slice_ref(str->slice);
   GRPC_MDSTR_UNREF(str);
   gpr_slice_unref(slice);
 
   str = grpc_mdstr_from_string(
-      ctx, "123456789012345678901234567890123456789012345678901234567890");
+      "123456789012345678901234567890123456789012345678901234567890");
   slice = gpr_slice_ref(str->slice);
   gpr_slice_unref(slice);
   GRPC_MDSTR_UNREF(str);
 
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 }
 
 static void test_base64_and_huffman_works(void) {
-  grpc_mdctx *ctx;
   grpc_mdstr *str;
   gpr_slice slice1;
   gpr_slice slice2;
 
   LOG_TEST("test_base64_and_huffman_works");
 
-  ctx = grpc_mdctx_create();
-  str = grpc_mdstr_from_string(ctx, "abcdefg");
+  grpc_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 == gpr_slice_cmp(slice1, slice2));
 
   gpr_slice_unref(slice2);
   GRPC_MDSTR_UNREF(str);
-  grpc_mdctx_unref(ctx);
+  grpc_shutdown();
 }
 
 int main(int argc, char **argv) {
diff --git a/test/proto/metrics.proto b/test/proto/metrics.proto
index f5a08e0..f698620 100644
--- a/test/proto/metrics.proto
+++ b/test/proto/metrics.proto
@@ -43,9 +43,7 @@
   }
 }
 
-message GaugeRequest {
-  string name = 1;
-}
+message GaugeRequest { string name = 1; }
 
 message EmptyMessage {
 }
diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py
new file mode 100755
index 0000000..86cb414
--- /dev/null
+++ b/tools/codegen/core/gen_static_metadata.py
@@ -0,0 +1,310 @@
+#!/usr/bin/env python2.7
+
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import hashlib
+import itertools
+import os
+import sys
+
+# configuration: a list of either strings or 2-tuples of strings
+# a single string represents a static grpc_mdstr
+# a 2-tuple represents a static grpc_mdelem (and appropriate grpc_mdstrs will
+# also be created)
+
+CONFIG = [
+    'grpc-timeout',
+    'grpc-internal-encoding-request',
+    ':path',
+    'grpc-encoding',
+    'grpc-accept-encoding',
+    'user-agent',
+    ':authority',
+    'host',
+    'grpc-message',
+    'grpc-status',
+    '',
+    ('grpc-status', '0'),
+    ('grpc-status', '1'),
+    ('grpc-status', '2'),
+    ('grpc-encoding', 'identity'),
+    ('grpc-encoding', 'gzip'),
+    ('grpc-encoding', 'deflate'),
+    ('te', 'trailers'),
+    ('content-type', 'application/grpc'),
+    (':method', 'POST'),
+    (':status', '200'),
+    (':status', '404'),
+    (':scheme', 'http'),
+    (':scheme', 'https'),
+    (':scheme', 'grpc'),
+    (':authority', ''),
+    (':method', 'GET'),
+    (':path', '/'),
+    (':path', '/index.html'),
+    (':status', '204'),
+    (':status', '206'),
+    (':status', '304'),
+    (':status', '400'),
+    (':status', '500'),
+    ('accept-charset', ''),
+    ('accept-encoding', ''),
+    ('accept-encoding', 'gzip, deflate'),
+    ('accept-language', ''),
+    ('accept-ranges', ''),
+    ('accept', ''),
+    ('access-control-allow-origin', ''),
+    ('age', ''),
+    ('allow', ''),
+    ('authorization', ''),
+    ('cache-control', ''),
+    ('content-disposition', ''),
+    ('content-encoding', ''),
+    ('content-language', ''),
+    ('content-length', ''),
+    ('content-location', ''),
+    ('content-range', ''),
+    ('content-type', ''),
+    ('cookie', ''),
+    ('date', ''),
+    ('etag', ''),
+    ('expect', ''),
+    ('expires', ''),
+    ('from', ''),
+    ('host', ''),
+    ('if-match', ''),
+    ('if-modified-since', ''),
+    ('if-none-match', ''),
+    ('if-range', ''),
+    ('if-unmodified-since', ''),
+    ('last-modified', ''),
+    ('link', ''),
+    ('location', ''),
+    ('max-forwards', ''),
+    ('proxy-authenticate', ''),
+    ('proxy-authorization', ''),
+    ('range', ''),
+    ('referer', ''),
+    ('refresh', ''),
+    ('retry-after', ''),
+    ('server', ''),
+    ('set-cookie', ''),
+    ('strict-transport-security', ''),
+    ('transfer-encoding', ''),
+    ('user-agent', ''),
+    ('vary', ''),
+    ('via', ''),
+    ('www-authenticate', ''),
+]
+
+COMPRESSION_ALGORITHMS = [
+    'identity',
+    'deflate',
+    'gzip',
+]
+
+# utility: mangle the name of a config
+def mangle(elem):
+  xl = {
+      '-': '_',
+      ':': '',
+      '/': 'slash',
+      '.': 'dot',
+      ',': 'comma',
+      ' ': '_',
+  }
+  def m0(x):
+    if not x: return 'empty'
+    r = ''
+    for c in x:
+      put = xl.get(c, c.lower())
+      if not put: continue
+      last_is_underscore = r[-1] == '_' if r else True
+      if last_is_underscore and put == '_': continue
+      elif len(put) > 1:
+        if not last_is_underscore: r += '_'
+        r += put
+        r += '_'
+      else:
+        r += put
+    if r[-1] == '_': r = r[:-1]
+    return r
+  if isinstance(elem, tuple):
+    return 'grpc_mdelem_%s_%s' % (m0(elem[0]), m0(elem[1]))
+  else:
+    return 'grpc_mdstr_%s' % (m0(elem))
+
+# utility: generate some hash value for a string
+def fake_hash(elem):
+  return hashlib.md5(elem).hexdigest()[0:8]
+
+# utility: print a big comment block into a set of files
+def put_banner(files, banner):
+  for f in files:
+    print >>f, '/*'
+    for line in banner:
+      print >>f, ' * %s' % line
+    print >>f, ' */'
+    print >>f
+
+# build a list of all the strings we need
+all_strs = set()
+all_elems = set()
+static_userdata = {}
+for elem in CONFIG:
+  if isinstance(elem, tuple):
+    all_strs.add(elem[0])
+    all_strs.add(elem[1])
+    all_elems.add(elem)
+  else:
+    all_strs.add(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)
+  compression_elems.append(elem)
+  static_userdata[elem] = 1 + mask
+all_strs = sorted(list(all_strs), key=mangle)
+all_elems = sorted(list(all_elems), key=mangle)
+
+# output configuration
+args = sys.argv[1:]
+H = None
+C = None
+if args:
+  if 'header' in args:
+    H = sys.stdout
+  else:
+    H = open('/dev/null', 'w')
+  if 'source' in args:
+    C = sys.stdout
+  else:
+    C = open('/dev/null', 'w')
+else:
+  H = open(os.path.join(
+      os.path.dirname(sys.argv[0]), '../../../src/core/transport/static_metadata.h'), 'w')
+  C = open(os.path.join(
+      os.path.dirname(sys.argv[0]), '../../../src/core/transport/static_metadata.c'), 'w')
+
+# copy-paste copyright notice from this file
+with open(sys.argv[0]) as my_source:
+  copyright = []
+  for line in my_source:
+    if line[0] != '#': break
+  for line in my_source:
+    if line[0] == '#':
+      copyright.append(line)
+      break
+  for line in my_source:
+    if line[0] != '#':
+      break
+    copyright.append(line)
+  put_banner([H,C], [line[1:].strip() for line in copyright])
+
+put_banner([H,C],
+"""WARNING: Auto-generated code.
+
+To make changes to this file, change tools/codegen/core/gen_static_metadata.py,
+and then re-run it.
+
+See metadata.h for an explanation of the interface here, and metadata.c for an
+explanation of what's going on.
+""".splitlines())
+
+print >>H, '#ifndef GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H'
+print >>H, '#define GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H'
+print >>H
+print >>H, '#include "src/core/transport/metadata.h"'
+print >>H
+
+print >>C, '#include "src/core/transport/static_metadata.h"'
+print >>C
+
+print >>H, '#define GRPC_STATIC_MDSTR_COUNT %d' % len(all_strs)
+print >>H, 'extern grpc_mdstr grpc_static_mdstr_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
+print >>C, 'grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];'
+print >>C
+
+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 gpr_uintptr 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
+print >>C, 'grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];'
+print >>C, 'gpr_uintptr 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, '};'
+print >>C
+
+def str_idx(s):
+  for i, s2 in enumerate(all_strs):
+    if s == s2:
+      return i
+
+def md_idx(m):
+  for i, m2 in enumerate(all_elems):
+    if m == m2:
+      return i
+
+print >>H, 'extern const gpr_uint8 grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT*2];'
+print >>C, 'const gpr_uint8 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, '};'
+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 >>C
+
+print >>H, 'extern const gpr_uint8 grpc_static_accept_encoding_metadata[%d];' % (1 << len(COMPRESSION_ALGORITHMS))
+print >>C, 'const gpr_uint8 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, '#endif /* GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H */'
+
+H.close()
+C.close()
+
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index cf1563b..8dcd8cd 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -810,6 +810,7 @@
 src/core/client_config/subchannel_factory_decorators/add_channel_arg.h \
 src/core/client_config/subchannel_factory_decorators/merge_channel_args.h \
 src/core/client_config/uri_parser.h \
+src/core/compression/algorithm_metadata.h \
 src/core/compression/message_compress.h \
 src/core/debug/trace.h \
 src/core/httpcli/format_request.h \
@@ -892,6 +893,7 @@
 src/core/transport/connectivity_state.h \
 src/core/transport/metadata.h \
 src/core/transport/metadata_batch.h \
+src/core/transport/static_metadata.h \
 src/core/transport/transport.h \
 src/core/transport/transport_impl.h \
 src/core/census/aggregation.h \
@@ -1042,6 +1044,7 @@
 src/core/transport/connectivity_state.c \
 src/core/transport/metadata.c \
 src/core/transport/metadata_batch.c \
+src/core/transport/static_metadata.c \
 src/core/transport/transport.c \
 src/core/transport/transport_op_string.c \
 src/core/census/context.c \
diff --git a/tools/run_tests/post_tests_ruby.sh b/tools/run_tests/post_tests_ruby.sh
new file mode 100755
index 0000000..66a9fbc
--- /dev/null
+++ b/tools/run_tests/post_tests_ruby.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -ex
+
+if [ "$CONFIG" != "gcov" ] ; then exit ; fi
+
+root=$(readlink -f $(dirname $0)/../..)
+out=$root/reports/ruby_ext_coverage
+tmp1=$(mktemp)
+tmp2=$(mktemp)
+cd $root
+lcov --capture --directory . --output-file $tmp1
+lcov --extract $tmp1 "$root/src/ruby/*" --output-file $tmp2
+genhtml $tmp2 --output-directory $out
+rm $tmp2
+rm $tmp1
+
+cp -rv $root/src/ruby/coverage $root/reports/ruby
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 47f949b..f1595ac 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -322,7 +322,7 @@
     return [['tools/run_tests/build_ruby.sh']]
 
   def post_tests_steps(self):
-    return []
+    return [['tools/run_tests/post_tests_ruby.sh']]
 
   def makefile_name(self):
     return 'Makefile'
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 1f9be91..4ecffbe 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -14380,6 +14380,7 @@
       "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h", 
       "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h", 
       "src/core/client_config/uri_parser.h", 
+      "src/core/compression/algorithm_metadata.h", 
       "src/core/compression/message_compress.h", 
       "src/core/debug/trace.h", 
       "src/core/httpcli/format_request.h", 
@@ -14471,6 +14472,7 @@
       "src/core/transport/connectivity_state.h", 
       "src/core/transport/metadata.h", 
       "src/core/transport/metadata_batch.h", 
+      "src/core/transport/static_metadata.h", 
       "src/core/transport/transport.h", 
       "src/core/transport/transport_impl.h", 
       "src/core/tsi/fake_transport_security.h", 
@@ -14557,6 +14559,7 @@
       "src/core/client_config/uri_parser.c", 
       "src/core/client_config/uri_parser.h", 
       "src/core/compression/algorithm.c", 
+      "src/core/compression/algorithm_metadata.h", 
       "src/core/compression/message_compress.c", 
       "src/core/compression/message_compress.h", 
       "src/core/debug/trace.c", 
@@ -14758,6 +14761,8 @@
       "src/core/transport/metadata.h", 
       "src/core/transport/metadata_batch.c", 
       "src/core/transport/metadata_batch.h", 
+      "src/core/transport/static_metadata.c", 
+      "src/core/transport/static_metadata.h", 
       "src/core/transport/transport.c", 
       "src/core/transport/transport.h", 
       "src/core/transport/transport_impl.h", 
@@ -14896,6 +14901,7 @@
       "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h", 
       "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h", 
       "src/core/client_config/uri_parser.h", 
+      "src/core/compression/algorithm_metadata.h", 
       "src/core/compression/message_compress.h", 
       "src/core/debug/trace.h", 
       "src/core/httpcli/format_request.h", 
@@ -14978,6 +14984,7 @@
       "src/core/transport/connectivity_state.h", 
       "src/core/transport/metadata.h", 
       "src/core/transport/metadata_batch.h", 
+      "src/core/transport/static_metadata.h", 
       "src/core/transport/transport.h", 
       "src/core/transport/transport_impl.h"
     ], 
@@ -15059,6 +15066,7 @@
       "src/core/client_config/uri_parser.c", 
       "src/core/client_config/uri_parser.h", 
       "src/core/compression/algorithm.c", 
+      "src/core/compression/algorithm_metadata.h", 
       "src/core/compression/message_compress.c", 
       "src/core/compression/message_compress.h", 
       "src/core/debug/trace.c", 
@@ -15234,6 +15242,8 @@
       "src/core/transport/metadata.h", 
       "src/core/transport/metadata_batch.c", 
       "src/core/transport/metadata_batch.h", 
+      "src/core/transport/static_metadata.c", 
+      "src/core/transport/static_metadata.h", 
       "src/core/transport/transport.c", 
       "src/core/transport/transport.h", 
       "src/core/transport/transport_impl.h", 
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index 72aad27..89ab6ed 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -296,6 +296,7 @@
     <ClInclude Include="..\..\..\src\core\client_config\subchannel_factory_decorators\add_channel_arg.h" />
     <ClInclude Include="..\..\..\src\core\client_config\subchannel_factory_decorators\merge_channel_args.h" />
     <ClInclude Include="..\..\..\src\core\client_config\uri_parser.h" />
+    <ClInclude Include="..\..\..\src\core\compression\algorithm_metadata.h" />
     <ClInclude Include="..\..\..\src\core\compression\message_compress.h" />
     <ClInclude Include="..\..\..\src\core\debug\trace.h" />
     <ClInclude Include="..\..\..\src\core\httpcli\format_request.h" />
@@ -378,6 +379,7 @@
     <ClInclude Include="..\..\..\src\core\transport\connectivity_state.h" />
     <ClInclude Include="..\..\..\src\core\transport\metadata.h" />
     <ClInclude Include="..\..\..\src\core\transport\metadata_batch.h" />
+    <ClInclude Include="..\..\..\src\core\transport\static_metadata.h" />
     <ClInclude Include="..\..\..\src\core\transport\transport.h" />
     <ClInclude Include="..\..\..\src\core\transport\transport_impl.h" />
     <ClInclude Include="..\..\..\src\core\census\aggregation.h" />
@@ -675,6 +677,8 @@
     </ClCompile>
     <ClCompile Include="..\..\..\src\core\transport\metadata_batch.c">
     </ClCompile>
+    <ClCompile Include="..\..\..\src\core\transport\static_metadata.c">
+    </ClCompile>
     <ClCompile Include="..\..\..\src\core\transport\transport.c">
     </ClCompile>
     <ClCompile Include="..\..\..\src\core\transport\transport_op_string.c">
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index b8dc059..a44d6e9 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -436,6 +436,9 @@
     <ClCompile Include="..\..\..\src\core\transport\metadata_batch.c">
       <Filter>src\core\transport</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\src\core\transport\static_metadata.c">
+      <Filter>src\core\transport</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\..\src\core\transport\transport.c">
       <Filter>src\core\transport</Filter>
     </ClCompile>
@@ -608,6 +611,9 @@
     <ClInclude Include="..\..\..\src\core\client_config\uri_parser.h">
       <Filter>src\core\client_config</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\src\core\compression\algorithm_metadata.h">
+      <Filter>src\core\compression</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\..\src\core\compression\message_compress.h">
       <Filter>src\core\compression</Filter>
     </ClInclude>
@@ -854,6 +860,9 @@
     <ClInclude Include="..\..\..\src\core\transport\metadata_batch.h">
       <Filter>src\core\transport</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\src\core\transport\static_metadata.h">
+      <Filter>src\core\transport</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\..\src\core\transport\transport.h">
       <Filter>src\core\transport</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index 548586e..374220d 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -275,6 +275,7 @@
     <ClInclude Include="..\..\..\src\core\client_config\subchannel_factory_decorators\add_channel_arg.h" />
     <ClInclude Include="..\..\..\src\core\client_config\subchannel_factory_decorators\merge_channel_args.h" />
     <ClInclude Include="..\..\..\src\core\client_config\uri_parser.h" />
+    <ClInclude Include="..\..\..\src\core\compression\algorithm_metadata.h" />
     <ClInclude Include="..\..\..\src\core\compression\message_compress.h" />
     <ClInclude Include="..\..\..\src\core\debug\trace.h" />
     <ClInclude Include="..\..\..\src\core\httpcli\format_request.h" />
@@ -357,6 +358,7 @@
     <ClInclude Include="..\..\..\src\core\transport\connectivity_state.h" />
     <ClInclude Include="..\..\..\src\core\transport\metadata.h" />
     <ClInclude Include="..\..\..\src\core\transport\metadata_batch.h" />
+    <ClInclude Include="..\..\..\src\core\transport\static_metadata.h" />
     <ClInclude Include="..\..\..\src\core\transport\transport.h" />
     <ClInclude Include="..\..\..\src\core\transport\transport_impl.h" />
     <ClInclude Include="..\..\..\src\core\census\aggregation.h" />
@@ -614,6 +616,8 @@
     </ClCompile>
     <ClCompile Include="..\..\..\src\core\transport\metadata_batch.c">
     </ClCompile>
+    <ClCompile Include="..\..\..\src\core\transport\static_metadata.c">
+    </ClCompile>
     <ClCompile Include="..\..\..\src\core\transport\transport.c">
     </ClCompile>
     <ClCompile Include="..\..\..\src\core\transport\transport_op_string.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index 65e5310..faadd82 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -376,6 +376,9 @@
     <ClCompile Include="..\..\..\src\core\transport\metadata_batch.c">
       <Filter>src\core\transport</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\src\core\transport\static_metadata.c">
+      <Filter>src\core\transport</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\..\src\core\transport\transport.c">
       <Filter>src\core\transport</Filter>
     </ClCompile>
@@ -506,6 +509,9 @@
     <ClInclude Include="..\..\..\src\core\client_config\uri_parser.h">
       <Filter>src\core\client_config</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\src\core\compression\algorithm_metadata.h">
+      <Filter>src\core\compression</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\..\src\core\compression\message_compress.h">
       <Filter>src\core\compression</Filter>
     </ClInclude>
@@ -752,6 +758,9 @@
     <ClInclude Include="..\..\..\src\core\transport\metadata_batch.h">
       <Filter>src\core\transport</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\src\core\transport\static_metadata.h">
+      <Filter>src\core\transport</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\..\src\core\transport\transport.h">
       <Filter>src\core\transport</Filter>
     </ClInclude>